Initial version

main
ArjanteMarvelde 2021-12-31 13:20:19 +01:00
rodzic 7e18caf33c
commit 613f922bf5
14 zmienionych plików z 805 dodań i 0 usunięć

56
CMakeLists.txt 100644
Wyświetl plik

@ -0,0 +1,56 @@
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# initalize pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "C:/Users/Arjan/Documents/Pico/pico-sdk")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
project(uWFG C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
# This creates a pico-sdk subdirectory in our project for the libraries
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(uWFG uWFG.c gen.c monitor.c)
pico_set_program_name(uWFG "uWFG")
pico_set_program_version(uWFG "0.1")
# Create C header file with the name <pio program>.pio.h
pico_generate_pio_header(uWFG ${CMAKE_CURRENT_LIST_DIR}/wfgout.pio)
# Pull in our pico_stdlib which aggregates commonly used features
target_link_libraries(uWFG pico_stdlib)
# Disable uart output, enable usb output
pico_enable_stdio_uart(uWFG 0)
pico_enable_stdio_usb(uWFG 1)
# Add any user requested libraries
target_link_libraries(uWFG
pico_stdlib
pico_multicore
hardware_irq
hardware_i2c
hardware_pwm
hardware_gpio
hardware_timer
hardware_clocks
hardware_pll
hardware_adc
hardware_pio
hardware_dma
)
# Create map/bin/hex/uf2 files
pico_add_extra_outputs(uWFG)

BIN
doc/Proto.jpg 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 301 KiB

BIN
doc/SQ-1MHz.jpg 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 429 KiB

211
gen.c 100644
Wyświetl plik

@ -0,0 +1,211 @@
/*
* gen.c
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* The generation of the output.
*
* The output is generated by simply taking an array of samples which is repeatedly output on a set of GPIO pins.
* To accomplish highest speed, the generator utilizes the Pico PIO feature, which enables outputting new data
* every system clock cycle.
* A PIO consists of common instruction memory, that may contain several PIO programs at different offsets. These
* programs can then be executed by each of 4 statemachines contained in the PIO. Each SM has its own I/O FIFOs to
* the system, its own GPIO interfacing and a set of internal registers to let the program do its work.
* The SM clock rate is derived from the system clock by means of a divider, [1.0 - 65536.0], in 1/256 steps.
*
* For the waveform generator, a simple PIO program is defined that moves a word from the SM TX fifo into the SM output
* shift register (OSR), and then clocks out one byte on the designated pins on every SM clock tick.
* A separate SM is allocated for each output channel, thus providing two independent outputs.
*
* Two DMA channels are used for each output, chained in a loop to keep the flow going.
* The dma_data channel transfers from *buffer to the PIO TX FIFO (paced by the PIO DREQ signal), chained to dma_ctrl channel.
* The dma_ctrl channel transfers the buffer address back into the dma_data channel read_addr, and chains back to dma_data.
From RP2040 datasheet, DMA Control / Status word layout:
0x80000000 [31] : AHB_ERROR (0): Logical OR of the READ_ERROR and WRITE_ERROR flags
0x40000000 [30] : READ_ERROR (0): If 1, the channel received a read bus error
0x20000000 [29] : WRITE_ERROR (0): If 1, the channel received a write bus error
0x01000000 [24] : BUSY (0): This flag goes high when the channel starts a new transfer sequence, and low when the...
0x00800000 [23] : SNIFF_EN (0): If 1, this channel's data transfers are visible to the sniff hardware, and each...
0x00400000 [22] : BSWAP (0): Apply byte-swap transformation to DMA data
0x00200000 [21] : IRQ_QUIET (0): In QUIET mode, the channel does not generate IRQs at the end of every transfer block
0x001f8000 [20:15] : TREQ_SEL (0): Select a Transfer Request signal
0x00007800 [14:11] : CHAIN_TO (0): When this channel completes, it will trigger the channel indicated by CHAIN_TO
0x00000400 [10] : RING_SEL (0): Select whether RING_SIZE applies to read or write addresses
0x000003c0 [9:6] : RING_SIZE (0): Size of address wrap region
0x00000020 [5] : INCR_WRITE (0): If 1, the write address increments with each transfer
0x00000010 [4] : INCR_READ (0): If 1, the read address increments with each transfer
0x0000000c [3:2] : DATA_SIZE (0): Set the size of each bus transfer (byte/halfword/word)
0x00000002 [1] : HIGH_PRIORITY (0): HIGH_PRIORITY gives a channel preferential treatment in issue scheduling: in...
0x00000001 [0] : EN (0): DMA Channel Enable
* DMA channel CTRL words, assuming DMA CH0..3 and PIO0 fifo TX0 and TX1:
* CH0: 0x0020081f (IRQ_QUIET=0x1, TREQ_SEL=0x00, CHAIN_TO=1, INCR_WRITE=0, INCR_READ=1, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
* CH1: 0x003f800f (IRQ_QUIET=0x1, TREQ_SEL=0x3f, CHAIN_TO=0, INCR_WRITE=0, INCR_READ=0, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
* CH2: 0x0020981f (IRQ_QUIET=0x1, TREQ_SEL=0x01, CHAIN_TO=3, INCR_WRITE=0, INCR_READ=1, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
* CH3: 0x003f900f (IRQ_QUIET=0x1, TREQ_SEL=0x3f, CHAIN_TO=2, INCR_WRITE=0, INCR_READ=0, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "pico/stdlib.h"
#include "pico/sem.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "wfgout.pio.h"
#include "gen.h"
#define FSYS 1.25e8f
/*
* Global variables that hold the active parameters for both channels
*/
uint a_sm = 0, b_sm = 1; // PIO0 state machine numbers
uint32_t *a_buffer, *b_buffer; // Buffer address
uint32_t a_buflen, b_buflen; // Buffer length (in 32bit words)
float a_freq, b_freq; // Buffer frequency
/*
* DMA channel definitions
*/
#define DMA_ADATA 0
#define DMA_ACTRL 1
#define DMA_BDATA 2
#define DMA_BCTRL 3
#define DMA_ADATA_C 0x0020081f
#define DMA_ACTRL_C 0x003f800f
#define DMA_BDATA_C 0x0020981f
#define DMA_BCTRL_C 0x003f900f
/*
* Define initial waveforms
*/
#define A_FREQ 1.0e6f
#define A_BUFLEN 4
uint32_t a_buf[A_BUFLEN] = {0x00000000, 0x00000000, 0xffffffff, 0xffffffff};
#define B_FREQ 1.0e6f
#define B_BUFLEN 4
uint32_t b_buf[B_BUFLEN] = {0x00000000, 0x00000000, 0xffffffff, 0xffffffff};
/*
* Unit initialization, only call this once!
* This function initializes the WFG parameters, the PIO statemachines and teh DMA channels.
*/
void wfg_init()
{
float fsam;
float div;
uint i;
uint offset;
for (i=0; i<8; i++) // Initialize the used pins
{
gpio_set_function(PINA+i, GPIO_FUNC_PIO0); // Function: PIO
gpio_set_slew_rate(PINA+i, GPIO_SLEW_RATE_FAST); // No slewrate limiting
gpio_set_drive_strength(PINA+i, GPIO_DRIVE_STRENGTH_8MA); // Drive 8mA (might increase to 12)
gpio_set_function(PINB+i, GPIO_FUNC_PIO0);
gpio_set_slew_rate(PINB+i, GPIO_SLEW_RATE_FAST);
gpio_set_drive_strength(PINB+i, GPIO_DRIVE_STRENGTH_8MA);
}
a_buffer = &a_buf[0]; // Initialize active parameters
a_buflen = A_BUFLEN;
a_freq = A_FREQ;
b_buffer = &b_buf[0];
b_buflen = B_BUFLEN;
b_freq = B_FREQ;
offset = pio_add_program(pio0, &wfgout_program); // Move program to PIO space and obtain offset
fsam = a_freq * 4.0 * (float)a_buflen; // Required sample rate = samples in buffer * freq
div = FSYS / fsam; // Ratio FSYS and sampleclock
if (div < 1.0) div=1.0; // Cannot get higher than FSYS
wfgout_program_init(pio0, a_sm, offset, (uint)PINA, (uint)8, div); // Invoke PIO initializer (see wfgout.pio.h)
fsam = b_freq * 4.0 * (float)b_buflen; // Required sample rate = samples in buffer * freq
div = FSYS / fsam; // Ratio FSYS and sampleclock
if (div < 1.0) div=1.0; // Cannot get higher than FSYS
wfgout_program_init(pio0, b_sm, offset, (uint)PINB, (uint)8, div); // Invoke PIO initializer (see wfgout.pio.h)
dma_hw->ch[DMA_ADATA].read_addr = (io_rw_32)a_buffer; // Read from waveform buffer
dma_hw->ch[DMA_ADATA].write_addr = (io_rw_32)&pio0->txf[a_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_ADATA].transfer_count = a_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_ADATA].al1_ctrl = DMA_ADATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_ACTRL].read_addr = (io_rw_32)&a_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_ACTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_ADATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_ACTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_ACTRL].ctrl_trig = DMA_ACTRL_C; // Write ctrl word and start DMA
dma_hw->ch[DMA_BDATA].read_addr = (io_rw_32)b_buffer; // Read from waveform buffer
dma_hw->ch[DMA_BDATA].write_addr = (io_rw_32)&pio0->txf[b_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_BDATA].transfer_count = b_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_BDATA].al1_ctrl = DMA_BDATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_BCTRL].read_addr = (io_rw_32)&b_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_BCTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_BDATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_BCTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_BCTRL].ctrl_trig = DMA_BCTRL_C; // Write ctrl word and start DMA
}
/*
* This function is the main API of the generator.
* Parameters are a waveform samples buffer, its length and a desired frequency.
* It is assumed that the buffer contains one wave, and the frequency will be maximized at Fsys/buflen
*/
void wfg_play(int output, wfg_t *wave)
{
uint32_t clkdiv; // 31:16 int part, 15:8 frac part (in 1/256)
float div;
div = FSYS/((wave->freq)*(wave->len)*4.0); // Calculate divider
if (div < 1.0) div=1.0;
clkdiv = (uint32_t)div; // Extract integer part
div = (div - clkdiv)*256; // Fraction x 256
clkdiv = (clkdiv << 8) + (uint32_t)div; // Add 8bit integer part of fraction
clkdiv = clkdiv << 8; // Final shift to match required format
if (output)
{
b_buffer = wave->buf;
b_buflen = wave->len;
b_freq = wave->freq;
dma_hw->ch[DMA_BDATA].read_addr = (io_rw_32)b_buffer; // Read from waveform buffer
dma_hw->ch[DMA_BDATA].write_addr = (io_rw_32)&pio0->txf[b_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_BDATA].transfer_count = b_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_BDATA].al1_ctrl = DMA_BDATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_BCTRL].read_addr = (io_rw_32)&b_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_BCTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_BDATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_BCTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_BCTRL].ctrl_trig = DMA_BCTRL_C; // Write ctrl word and start DMA
pio0_hw->sm[b_sm].clkdiv = (io_rw_32)clkdiv; // Set new value
pio_sm_clkdiv_restart(pio0, b_sm); // Restart clock
}
else
{
a_buffer = wave->buf;
a_buflen = wave->len;
a_freq = wave->freq;
dma_hw->ch[DMA_ADATA].read_addr = (io_rw_32)a_buffer; // Read from waveform buffer
dma_hw->ch[DMA_ADATA].write_addr = (io_rw_32)&pio0->txf[a_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_ADATA].transfer_count = a_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_ADATA].al1_ctrl = DMA_ADATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_ACTRL].read_addr = (io_rw_32)&a_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_ACTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_ADATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_ACTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_ACTRL].ctrl_trig = DMA_ACTRL_C; // Write ctrl word and start DMA
pio0_hw->sm[a_sm].clkdiv = (io_rw_32)clkdiv; // Set new value
pio_sm_clkdiv_restart(pio0, a_sm); // Restart clock
}
}

32
gen.h 100644
Wyświetl plik

@ -0,0 +1,32 @@
#ifndef __GEN_H__
#define __GEN_H__
/*
* gen.h
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* See gen.c for more information
*/
#define OUTA 0 // Channel A indicator
#define PINA 0 // LSB pin Channel A
#define OUTB 1 // Channel B indicator
#define PINB 8 // LSB pin Channel B
typedef struct wfg
{
uint32_t *buf; // Points to waveform buffer
uint32_t len; // Buffer length in 32 bit words
float freq; // Buffer repetition rate
} wfg_t;
/* Initialize channel indicated by output */
void wfg_init(void);
/* Play a waveform on the channel indicate by output */
void wfg_play(int output, wfg_t *wave);
#endif

25
hmi.c 100644
Wyświetl plik

@ -0,0 +1,25 @@
/*
* hmi.c
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
*
*/
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/sem.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hmi.h"
#include "wfgout.pio.h"
#include "gen.h"

13
hmi.h 100644
Wyświetl plik

@ -0,0 +1,13 @@
#ifndef __HMI_H__
#define __HMI_H__
/*
* hmi.h
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* See hmi.c for more information
*/
#endif

199
monitor.c 100644
Wyświetl plik

@ -0,0 +1,199 @@
/*
* monitor.c
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* Command shell on stdin/stdout.
* Collects characters and parses commandstring.
* Additional commands can easily be added.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "uWFG.h"
#include "gen.h"
#include "monitor.h"
#define CR 13
#define LF 10
#define SP 32
#define CMD_LEN 80
#define CMD_ARGS 16
char mon_cmd[CMD_LEN+1]; // Command string buffer
char *argv[CMD_ARGS]; // Argument pointers, first is command
int nargs; // Nr of arguments, including command
typedef struct
{
char *cmdstr; // Command string
int cmdlen; // Command string length
void (*cmd)(void); // Command executive
char *cmdsyn; // Command syntax
char *help; // Command help text
} shell_t;
/*** Initialisation, called at startup ***/
void mon_init()
{
stdio_init_all(); // Initialize Standard IO
mon_cmd[CMD_LEN] = '\0'; // Termination to be sure
printf("\n");
printf("=============\n");
printf(" uWFG-Pico \n");
printf(" PE1ATM \n");
printf(" 2021, Udjat \n");
printf("=============\n");
printf("Pico> "); // prompt
}
wfg_t mon_wave;
/*** ------------------------------------------------------------- ***/
/*** Below the definitions of the shell commands, add where needed ***/
/*** ------------------------------------------------------------- ***/
/*
* Dumps a defined range of Si5351 registers
*/
void mon_si(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
mon_wave.buf = (uint32_t *)sine16;
mon_wave.len = 16/4;
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Dumps the entire built-in and programmed characterset on the LCD
*/
void mon_sq(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
mon_wave.buf = (uint32_t *)block16;
mon_wave.len = 16/4;
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Checks for inter-core fifo overruns
*/
void mon_sa(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
mon_wave.buf = (uint32_t *)saw256;
mon_wave.len = 256/4;
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Sets sample clock
*/
void mon_cl(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Command shell table, organize the command functions above
*/
#define NCMD 4
shell_t shell[NCMD]=
{
{"si", 2, &mon_si, "si {A|B} <clk>", "sine wave at sample rate clk"},
{"sq", 2, &mon_sq, "sq {A|B} <clk>", "square wave at sample rate clk"},
{"sa", 2, &mon_sa, "sa {A|B} <clk>", "sawtooth at sample rate clk"},
{"cl", 2, &mon_cl, "cl {A|B} <clk>", "set sample rate clk"}
};
/*** ---------------------------------------- ***/
/*** Commandstring parser and monitor process ***/
/*** ---------------------------------------- ***/
/*
* Command line parser
*/
void mon_parse(char* s)
{
char *p;
int i;
p = s; // Set to start of string
nargs = 0;
while (*p!='\0') // Assume stringlength >0
{
while (*p==' ') p++; // Skip whitespace
if (*p=='\0') break; // String might end in spaces
argv[nargs++] = p; // Store first valid char loc after whitespace
while ((*p!=' ')&&(*p!='\0')) p++; // Skip non-whitespace
}
if (nargs==0) return; // No command or parameter
for (i=0; i<NCMD; i++) // Lookup shell command
if (strncmp(argv[0], shell[i].cmdstr, shell[i].cmdlen) == 0) break;
if (i<NCMD)
(*shell[i].cmd)();
else // Unknown command
{
for (i=0; i<NCMD; i++) // Print help if no match
printf("%s\n %s\n", shell[i].cmdsyn, shell[i].help);
}
}
/*
* Monitor process
* This function collects characters from stdin until CR
* Then the command is send to a parser and executed.
*/
void mon_evaluate(void)
{
static int i = 0;
int c = getchar_timeout_us(10L); // NOTE: this is the only SDK way to read from stdin
if (c==PICO_ERROR_TIMEOUT) return; // Early bail out
switch (c)
{
case CR: // CR : need to parse command string
putchar('\n'); // Echo character, assume terminal appends CR
mon_cmd[i] = '\0'; // Terminate command string
if (i>0) // something to parse?
mon_parse(mon_cmd); // --> process command
i=0; // reset index
printf("Pico> "); // prompt
break;
case LF:
break; // Ignore, assume CR as terminator
default:
if ((c<32)||(c>=128)) break; // Only allow alfanumeric
putchar((char)c); // Echo character
mon_cmd[i] = (char)c; // store in command string
if (i<CMD_LEN) i++; // check range and increment
break;
}
}

15
monitor.h 100644
Wyświetl plik

@ -0,0 +1,15 @@
#ifndef __MONITOR_H__
#define __MONITOR_H__
/*
* monitor.h
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* See monitor.c for more information
*/
void mon_init();
void mon_evaluate(void);
#endif

Wyświetl plik

@ -0,0 +1,62 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

127
uWFG.c 100644
Wyświetl plik

@ -0,0 +1,127 @@
/*
* uWFG.c
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* The main loop of the application.
*
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "pico/stdlib.h"
#include "pico/sem.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#include "uWFG.h"
#include "gen.h"
#include "monitor.h"
/** Some predefined waveforms **/
// Pico is little endian, so with proper alignment word and byte addressing overlap nicely
uint8_t sine16[16] __attribute__((aligned(4))) = \
{ \
128, 176, 218, 245, 255, 245, 218, 176, 128, 79, 37, 10, 0, 10, 37, 79 \
};
uint8_t sine64[64] __attribute__((aligned(4))) = \
{ \
128, 140, 152, 165, 176, 188, 198, 208, 218, 226, 234, 240, 245, 250, 253, 254, \
255, 254, 253, 250, 245, 240, 234, 226, 218, 208, 198, 188, 176, 165, 152, 140, \
128, 115, 103, 90, 79, 67, 57, 47, 37, 29, 21, 15, 10, 5, 2, 1, \
0, 1, 2, 5, 10, 15, 21, 29, 37, 47, 57, 67, 79, 90, 103, 115 \
};
uint8_t sine256[256] __attribute__((aligned(4))) = \
{ \
128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, 173, \
176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, 213, 215, \
218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244, \
245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, \
255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, \
245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, \
218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179, \
176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137, 134, 131, \
128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, 93, 90, 88, 85, 82, \
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, \
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, \
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, \
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, \
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, \
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, \
79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124 \
};
uint8_t saw256[256] __attribute__((aligned(4)));
uint8_t block16[16] __attribute__((aligned(4))) = \
{ \
0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255 \
};
/*
* LED TIMER definition and callback routine
*/
#define LED_MS 1000
struct repeating_timer led_timer;
bool led_callback(struct repeating_timer *t)
{
static bool led_state;
gpio_put(PICO_DEFAULT_LED_PIN, led_state);
led_state = (led_state?false:true);
return true;
}
/*
* Scheduler timer callback function.
* This executes every LOOP_MS msec.
*/
#define LOOP_MS 100
semaphore_t loop_sem;
struct repeating_timer loop_timer;
bool loop_callback(struct repeating_timer *t)
{
sem_release(&loop_sem);
return(true);
}
int main()
{
/* Initialize LED pin output */
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
gpio_put(PICO_DEFAULT_LED_PIN, true); // Set LED on
add_repeating_timer_ms(-LED_MS, led_callback, NULL, &led_timer);
/* Initialize Pico power supply */
gpio_init(23);
gpio_set_dir(23, GPIO_OUT);
gpio_put(23, true); // Set PWM mode for less ripple
for (int i=0; i<256; i++)
saw256[i] = (uint8_t)i;
wfg_init();
mon_init(); // Monitor shell on stdio
/* A simple round-robin scheduler */
sem_init(&loop_sem, 1, 1) ;
add_repeating_timer_ms(-LOOP_MS, loop_callback, NULL, &loop_timer);
while (1)
{
sem_acquire_blocking(&loop_sem);
mon_evaluate(); // Check monitor input
}
return 0;
}

19
uWFG.h 100644
Wyświetl plik

@ -0,0 +1,19 @@
#ifndef __UWFG_H__
#define __UWFG_H__
/*
* uWFG.h
*
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* See uWFG.c for more information
*/
extern uint8_t sine16[16];
extern uint8_t sine64[64];
extern uint8_t sine256[256];
extern uint8_t saw256[256];
extern uint8_t block16[16];
#endif

15
wfgout.c 100644
Wyświetl plik

@ -0,0 +1,15 @@
#include <hardware/pio.h>
#include <wfgout.pio.h>
void wfgout_init()
{
PIO pio = pio0;
uint offset = pio_add_program(pio, &wfgout_program);
uint sm = 0;
// Set up DMA channels
// Initialize state machine
wfgout_program_init(pio, sm, offset, (uint)8, (uint)8));
}

31
wfgout.pio 100644
Wyświetl plik

@ -0,0 +1,31 @@
.program wfgout
; PIO assembly code
; Just output next 8 bits from OSR to the pins
.wrap_target
out pins, 8
.wrap
; This function is inserted in the C environment
%c-sdk {
static inline void wfgout_program_init(PIO pio, uint sm, uint offset, uint pinbase, uint pincount, float divide)
{
pio_sm_config config;
uint i;
for (i=0; i<pincount; i++)
pio_gpio_init(pio, (pinbase+i)); // Initialize pins for PIO, required when output
pio_sm_set_consecutive_pindirs(pio, sm, pinbase, pincount, true); // Set as output
config = wfgout_program_get_default_config(offset); // Define the config object for program allocated at offset
sm_config_set_out_pins(&config, pinbase, pincount); // Set and initialize the output pins
sm_config_set_clkdiv(&config, divide); // Set run speed SysCLK/divide
sm_config_set_out_shift(&config, true, true, 32); // OSR (c, rightshift, autopull, #bits before pull)
sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); // Join unused RX fifo to obtain double depth
pio_sm_init(pio, sm, offset, &config); // Apply config
pio_sm_set_enabled(pio, sm, true); // Activate the state machine
}
%}