diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f07022f --- /dev/null +++ b/CMakeLists.txt @@ -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.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) + diff --git a/doc/Proto.jpg b/doc/Proto.jpg new file mode 100644 index 0000000..3fffd8c Binary files /dev/null and b/doc/Proto.jpg differ diff --git a/doc/SQ-1MHz.jpg b/doc/SQ-1MHz.jpg new file mode 100644 index 0000000..b3ed11d Binary files /dev/null and b/doc/SQ-1MHz.jpg differ diff --git a/gen.c b/gen.c new file mode 100644 index 0000000..3237c69 --- /dev/null +++ b/gen.c @@ -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 +#include +#include +#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 + } +} + diff --git a/gen.h b/gen.h new file mode 100644 index 0000000..a39fdc9 --- /dev/null +++ b/gen.h @@ -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 \ No newline at end of file diff --git a/hmi.c b/hmi.c new file mode 100644 index 0000000..b97fe03 --- /dev/null +++ b/hmi.c @@ -0,0 +1,25 @@ +/* + * hmi.c + * + * Created: Dec 2021 + * Author: Arjan te Marvelde + * + * + */ + +#include +#include +#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" + diff --git a/hmi.h b/hmi.h new file mode 100644 index 0000000..cb181c3 --- /dev/null +++ b/hmi.h @@ -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 \ No newline at end of file diff --git a/monitor.c b/monitor.c new file mode 100644 index 0000000..3dcdf74 --- /dev/null +++ b/monitor.c @@ -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 +#include +#include +#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} ", "sine wave at sample rate clk"}, + {"sq", 2, &mon_sq, "sq {A|B} ", "square wave at sample rate clk"}, + {"sa", 2, &mon_sa, "sa {A|B} ", "sawtooth at sample rate clk"}, + {"cl", 2, &mon_cl, "cl {A|B} ", "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; i0) // 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/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}) diff --git a/uWFG.c b/uWFG.c new file mode 100644 index 0000000..5f2a599 --- /dev/null +++ b/uWFG.c @@ -0,0 +1,127 @@ +/* + * uWFG.c + * + * Created: Dec 2021 + * Author: Arjan te Marvelde + * + * The main loop of the application. + * + */ + +#include +#include +#include +#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; +} diff --git a/uWFG.h b/uWFG.h new file mode 100644 index 0000000..802f28a --- /dev/null +++ b/uWFG.h @@ -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 \ No newline at end of file diff --git a/wfgout.c b/wfgout.c new file mode 100644 index 0000000..1d07efc --- /dev/null +++ b/wfgout.c @@ -0,0 +1,15 @@ +#include +#include + + +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)); +} \ No newline at end of file diff --git a/wfgout.pio b/wfgout.pio new file mode 100644 index 0000000..9680f79 --- /dev/null +++ b/wfgout.pio @@ -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