kopia lustrzana https://github.com/ArjanteMarvelde/uWFG-Pico
Initial version
rodzic
7e18caf33c
commit
613f922bf5
|
@ -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)
|
||||
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 301 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 429 KiB |
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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
|
||||
}
|
||||
%}
|
Ładowanie…
Reference in New Issue