/* * 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/gpio.h" #include "hardware/pio.h" #include "hardware/dma.h" #include "hardware/pll.h" #include "wfgout.pio.h" #include "gen.h" float _fsys; // System clock frequency /* * DMA pipe definitions, assume channels 0, 1, 2 and 3 for A-DATA, A-CTRL, B-DATA and B-CTRL */ #define DMA_DC(i) (((i)==0) ? 0x0020081f : 0x0020981f) // See derivation above #define DMA_CC(i) (((i)==0) ? 0x003f800f : 0x003f900f) /* * Global variables that hold the active parameters for both output channels */ #define PINA 0 // PIO channe A and B start pin numbers #define PINB 8 #define GEN_MINBUFLEN 20 // Minimum nr of samples (10msec - 160nsec) #define GEN_MAXBUFLEN 2000 // Maximum buffer size (1sec - 16usec) wfg_t wfg_ctrl[2]; // Active // Allocate maximum size samplebuffers for channel A and B uint8_t a_buf[GEN_MAXBUFLEN] __attribute__((aligned(4))); // DMA requires to align on 32 bit boundary uint8_t b_buf[GEN_MAXBUFLEN] __attribute__((aligned(4))); /* * Unit initialization, !only call this once! * This function initializes the WFG parameters, the PIO statemachines and teh DMA channels. */ void gen_init() { float div; uint i, offset; int ch; /* Retrieve system clock frequency */ _fsys = 1.2e7; // Assume 12MHz XOSC _fsys *= pll_sys_hw->fbdiv_int&0xfff; // Feedback divider _fsys /= (pll_sys_hw->prim&0x00070000)>>16; // Primary divider 1 _fsys /= (pll_sys_hw->prim&0x00007000)>>12; // Primary divider 2 /* Set GPIO pin behaviour */ for (i=0; i<8; i++) // Initialize the channel A and B 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); } /* Initialize the buffers and channel control structures */ for (i= 0; i<64; i++) {a_buf[i] = 0x00; a_buf[i+64] = 0xff;} // Square wave wfg_ctrl[0].buf = a_buf; // in A sample buffer wfg_ctrl[0].len = 128; // of 128 samples wfg_ctrl[0].dur = 1.0e-6; // and 1 usec duration for (i= 0; i<64; i++) {b_buf[i] = i*4; b_buf[i+64] = 0xff-(i*4);} // Triangle wave wfg_ctrl[1].buf = b_buf; // in B sample buffer wfg_ctrl[1].len = 128; // of 128 samples wfg_ctrl[1].dur = 1.0e-6; // and 1usec duration /* Initialize PIO and channel A and B statemachines */ offset = pio_add_program(pio0, &wfgout_program); // Move program to PIO space and obtain its offset for (ch=0; ch<2; ch++) { div = _fsys * wfg_ctrl[ch].dur / wfg_ctrl[ch].len; // Ratio of fsys and channel B sampleclock if (div < 1.0) div=1.0; // Cannot get higher than FSYS wfgout_program_init(pio0, ch, offset, (uint)(ch*PINB), (uint)8, div); // Invoke PIO initializer for channel B dma_hw->ch[2*ch].read_addr = (io_rw_32)wfg_ctrl[ch].buf; // Read from waveform buffer dma_hw->ch[2*ch].write_addr = (io_rw_32)&pio0->txf[ch]; // Write to PIO TX fifo dma_hw->ch[2*ch].transfer_count = wfg_ctrl[ch].len/4; // Nr of 32 bit words to transfer dma_hw->ch[2*ch].al1_ctrl = DMA_DC(ch); // Write ctrl word without starting the DMA dma_hw->ch[2*ch+1].read_addr = (io_rw_32)&(wfg_ctrl[ch].buf); // Read from waveform buffer address reference dma_hw->ch[2*ch+1].write_addr = (io_rw_32)&dma_hw->ch[2*ch].read_addr; // Write to data channel read address dma_hw->ch[2*ch+1].transfer_count = 1; // One word to transfer dma_hw->ch[2*ch+1].ctrl_trig = DMA_CC(ch); // Write ctrl word and start DMA } } /* * This function is the main API of the generator on channel ch. * 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 gen_play(int ch, wfg_t *wave) { uint32_t clkdiv; // 31:16 int part, 15:8 frac part (in 1/256) uint32_t len; float div; ch &= 1; // Truncate channel into range len = (uint32_t)wave->len; len &= ~3; // Force multiple of 4 if (lenGEN_MAXBUFLEN) len = GEN_MAXBUFLEN; // Truncate to maximum /* Calculate PIO clock divider */ div = _fsys * wave->dur / len; // Sample rate to fsys ratio if (div < 1.0) div=1.0; // Sample rate too high: top off 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 /* Store waveform */ dma_channel_abort(2*ch); // Stop DMA transfers, to prevent collisions dma_channel_abort(2*ch+1); memcpy(wfg_ctrl[ch].buf, wave->buf, len); // Copy samples from input wfg_ctrl[ch].len = len; wfg_ctrl[ch].dur = wave->dur; /* Re-program PIO */ pio0_hw->sm[ch].clkdiv = (io_rw_32)clkdiv; // Set new value pio_sm_clkdiv_restart(pio0, ch); // Restart clock /* Re-program DMA */ dma_hw->ch[2*ch].read_addr = (io_rw_32)wfg_ctrl[ch].buf; // Read from waveform buffer dma_hw->ch[2*ch].write_addr = (io_rw_32)&pio0->txf[ch]; // Write to PIO TX fifo dma_hw->ch[2*ch].transfer_count = wfg_ctrl[ch].len/4; // Nr of 32 bit words to transfer dma_hw->ch[2*ch].al1_ctrl = DMA_DC(ch); // Write ctrl word without starting the DMA dma_hw->ch[2*ch+1].read_addr = (io_rw_32)&(wfg_ctrl[ch].buf); // Read from waveform buffer address reference dma_hw->ch[2*ch+1].write_addr = (io_rw_32)&dma_hw->ch[2*ch].read_addr; // Write to data channel read address dma_hw->ch[2*ch+1].transfer_count = 1; // One word to transfer dma_hw->ch[2*ch+1].ctrl_trig = DMA_CC(ch); // Write ctrl word and start DMA }