Mercurial > ~darius > hgwebdir.cgi > modulator
changeset 16:56a79dce90e9
Commit WIP ctrl code
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Tue, 25 Feb 2025 13:31:27 +1030 |
parents | bf483219cb12 |
children | a249e4727b01 |
files | .hgignore CMakeLists.txt ctrl.pio modulator.c |
diffstat | 4 files changed, 176 insertions(+), 48 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Tue Feb 25 13:28:50 2025 +1030 +++ b/.hgignore Tue Feb 25 13:31:27 2025 +1030 @@ -1,1 +1,3 @@ build +build2 +q
--- a/CMakeLists.txt Tue Feb 25 13:28:50 2025 +1030 +++ b/CMakeLists.txt Tue Feb 25 13:31:27 2025 +1030 @@ -26,6 +26,7 @@ #target_compile_options(${NAME} PRIVATE -Wall -Wextra -Werror) pico_generate_pio_header(${NAME} ${CMAKE_CURRENT_LIST_DIR}/dac.pio) +pico_generate_pio_header(${NAME} ${CMAKE_CURRENT_LIST_DIR}/ctrl.pio) pico_generate_pio_header(${NAME} ${CMAKE_CURRENT_LIST_DIR}/trigger.pio) target_link_libraries(${NAME}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ctrl.pio Tue Feb 25 13:31:27 2025 +1030 @@ -0,0 +1,52 @@ +; +; Copyright (c) 2025 Daniel O'Connor +; + +.program ctrl +.define TRIGGER_IRQ 0 +; Assert all 0s + mov pins, null +; Wait for start trigger and clear IRQ + wait 1 irq TRIGGER_IRQ +.wrap_target + out pins 8 + nop + out pins 8 + nop + out pins 8 + nop + out pins 8 + nop +.wrap + +% c-sdk { +static inline void ctrl_program_init(PIO pio, uint sm, uint offset, uint pin, uint clkdiv) { + pio_sm_config c = dac_program_get_default_config(offset); + + // Set the OUT base pin to the provided `pin` parameter. + // Note: We only need 6 pins but pull a byte at a time to make + // generating the data simpler + sm_config_set_out_pins(&c, pin, 6); + // Set the pin directions to output at the PIO + pio_sm_set_consecutive_pindirs(pio, sm, pin, 6, true); + // Connect these GPIOs to this PIO block + for (int i = 0; i < 6; i++) + pio_gpio_init(pio, pin + i); + + sm_config_set_out_shift( + &c, + true, // Shift-to-right + true, // Autopull enabled + 32 // Autopull threshold (bits!) + ); + + // We only send, so disable the RX FIFO to make the TX FIFO deeper. + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + sm_config_set_clkdiv(&c, clkdiv); + + // Load our configuration, and start the program from the beginning + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%}
--- a/modulator.c Tue Feb 25 13:28:50 2025 +1030 +++ b/modulator.c Tue Feb 25 13:31:27 2025 +1030 @@ -5,7 +5,7 @@ ** of Genesis Software. Use or disclosure without prior ** agreement is expressly prohibited. ** -** Copyright (c) 2021 Genesis Software, all rights reserved. +** Copyright (c) 2025 Genesis Software, all rights reserved. ** ******************************************************************* ******************************************************************/ @@ -19,7 +19,6 @@ #include <stdio.h> #include <string.h> -#include "bitstring.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" @@ -37,6 +36,7 @@ #pragma GCC diagnostic pop #include "dac.pio.h" +#include "ctrl.pio.h" #include "trigger.pio.h" // https://github.com/howerj/q @@ -47,31 +47,45 @@ // Base of DAC pins #define DACOUT_GPIO 7 +// Base of ctrl pins +#define CTRLOUT_GPIO 16 // PWM output pin -#define TRIGOUT_GPIO 22 +#define TRIGOUT_GPIO 23 // PIO SM trigger input pin (connected to above for testing) #define TRIGIN_GPIO 27 // Pulse control bits -#define SENSE 0x01 -#define GATE 0x02 -#define PHINV 0x04 -#define PACTIVE 0x08 +#define PACTIVE 0x01 +#define PHINV 0x02 +#define SENSE1 0x04 +#define SENSE2 0x08 +#define GATE 0x10 +#define TRSW 0x20 + +// Pulse shape data +uint8_t pulse_data[65536] __attribute__((aligned(4))); +// Pulse control data +uint8_t pulse_ctrl[65536] __attribute__((aligned(4))); +// PWM slice for PRF timer +unsigned slice_num = 0; + +// PIO for pulse generation +PIO pulse_pio = pio0; // DMA channel to feed DAC PIO -static int dma_chan; -// Pulse shape data -uint8_t pulse_data[65536]; -// Pulse control data -uint8_t pulse_ctrl[65536]; -// PWM slice for PRF timer -unsigned slice_num = 0; -// DAC PIO -PIO pulse_pio = pio0; +static int dac_dma_chan; // DAC SM -uint pulse_sm; +uint dac_sm; // Instruction offset for DAC PIO program -uint pulse_pio_sm_offset; +uint dac_pio_sm_offset; + +// DMA channel to feed ctrl PIO +static int ctrl_dma_chan; +// Ctrl SM +uint ctrl_sm; +// Instruction offset for ctrl PIO program +uint ctrl_pio_sm_offset; + /* * Use a DMA channel to feed PIO0 SM0 with pulse data. * Each DMA transfer is a single pulse. @@ -86,19 +100,44 @@ void dma_handler(void) { // Clear the interrupt request. - dma_hw->ints0 = 1u << dma_chan; + dma_hw->ints0 = 1u << dac_dma_chan; - // Reset DAQ PIO SM so it is waiting for a trigger - pio_sm_exec(pulse_pio, pulse_sm, pio_encode_jmp(pulse_pio_sm_offset)); + // Disabled for now, manual trigger only +#if 0 + // Reset DAQ & ctrl PIO SMs so they are waiting for a trigger + pio_sm_exec(pulse_pio, dac_sm, pio_encode_jmp(dac_pio_sm_offset)); + pio_sm_exec(pulse_pio, ctrl_sm, pio_encode_jmp(ctrl_pio_sm_offset)); - // Setup next pulse DMA address - dma_channel_set_read_addr(dma_chan, pulse_data, true); + // Setup next pulse data & ctrl DMA addresses + dma_channel_set_read_addr(dac_dma_chan, pulse_data, true); + dma_channel_set_read_addr(ctrl_dma_chan, pulse_ctrl, true); +#endif } void pwm_wrap(void) { pwm_clear_irq(slice_num); + + // Reset DAQ & ctrl PIO SMs so they are waiting for a trigger + pio_sm_exec(pulse_pio, dac_sm, pio_encode_jmp(dac_pio_sm_offset)); + pio_sm_exec(pulse_pio, ctrl_sm, pio_encode_jmp(ctrl_pio_sm_offset)); + + printf("DAC: transfers %lu\n", dma_channel_hw_addr(dac_dma_chan)->transfer_count); + printf("DAC: transfers %lu\n", dma_channel_hw_addr(ctrl_dma_chan)->transfer_count); + + // Setup next pulse data & ctrl DMA addresses + dma_channel_wait_for_finish_blocking(dac_dma_chan); + dma_channel_set_read_addr(dac_dma_chan, pulse_data, true); + dma_channel_wait_for_finish_blocking(ctrl_dma_chan); + dma_channel_set_read_addr(ctrl_dma_chan, pulse_ctrl, true); + + // Manually trigger DAQ SM (cleared by SM) + pio0->irq_force = 1 << 0; + + // 'scope trigger + gpio_put(2, 1); + gpio_put(2, 0); } // Calculate pulse shape data @@ -178,8 +217,8 @@ qsprint(dcscale, tmps, sizeof(tmps)); printf("dcscale = %s\n", tmps); - memset(pulse_data, 0, sizeof(pulse_data)); - memset(pulse_ctrl, 0, sizeof(pulse_ctrl)); + memset(pulse_data, 0, datalen); + memset(pulse_ctrl, 0, datalen); idx = 0; // Up slew @@ -228,7 +267,7 @@ // Could replace this with a separate loop to poke it into place // Similarly for TR switch when implemented if (i == 0 && c == 0) - ctrl[idx] = ctrltmp | SENSE; + ctrl[idx] = ctrltmp | SENSE1; else ctrl[idx] = ctrltmp; } @@ -248,8 +287,9 @@ } data[idx++] = 0; + ctrl[idx] = 0; - return idx; + return idx + 1; } int @@ -272,6 +312,7 @@ gpio_set_dir(2, GPIO_OUT); #if 0 + // GPIO tester to check breadboard wiring for (unsigned i = 7; i < 7 + 9; i++) { printf("GPIO %d\n", i); gpio_init(i); @@ -317,43 +358,80 @@ // Load the DAC program, and configure a free state machine // to run the program. - pulse_pio_sm_offset = pio_add_program(pulse_pio, &dac_program); - pulse_sm = pio_claim_unused_sm(pulse_pio, true); + dac_pio_sm_offset = pio_add_program(pulse_pio, &dac_program); + if (dac_pio_sm_offset < 0) { + printf("Unable to load DAC program\n"); + __breakpoint(); + } + dac_sm = pio_claim_unused_sm(pulse_pio, true); // Data is GPIO7 to GPIO14, clock is GPIO15 // Clock divisor of 2 so it runs at 60MHz and // generates a 30MHz clock - dac_program_init(pulse_pio, pulse_sm, pulse_pio_sm_offset, DACOUT_GPIO, 2); + dac_program_init(pulse_pio, dac_sm, dac_pio_sm_offset, DACOUT_GPIO, 2); // Configure a channel to write 32 bits at a time to PIO0 // SM0's TX FIFO, paced by the data request signal from that peripheral. - dma_chan = dma_claim_unused_channel(true); - dma_channel_config dmac = dma_channel_get_default_config(dma_chan); - channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32); - channel_config_set_read_increment(&dmac, true); - channel_config_set_dreq(&dmac, DREQ_PIO0_TX0); + dac_dma_chan = dma_claim_unused_channel(true); + dma_channel_config dac_dmac = dma_channel_get_default_config(dac_dma_chan); + channel_config_set_transfer_data_size(&dac_dmac, DMA_SIZE_32); + channel_config_set_read_increment(&dac_dmac, true); + channel_config_set_dreq(&dac_dmac, PIO_DREQ_NUM(pulse_pio, dac_sm, true)); dma_channel_configure( - dma_chan, - &dmac, - &pio0_hw->txf[0], // Write address (only need to set this once) - NULL, // Don't provide a read address yet - (idx + 3) >> 2, // Transfer count (round up to 4 bytes) - false // Don't start yet + dac_dma_chan, + &dac_dmac, + &pulse_pio->txf[dac_sm], // Write address + pulse_data, // Pulse data + (idx + 3) >> 2, // Transfer count (round up to 4 bytes) + true // Start, SM will wait for trigger ); // Tell the DMA to raise IRQ line 0 when the channel finishes a block - dma_channel_set_irq0_enabled(dma_chan, true); + dma_channel_set_irq0_enabled(dac_dma_chan, true); // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); irq_set_enabled(DMA_IRQ_0, true); + // Load the ctrl program, and configure a free state machine + // to run the program. + ctrl_pio_sm_offset = pio_add_program(pulse_pio, &ctrl_program); + if (ctrl_pio_sm_offset < 0) { + printf("Unable to load ctrl program\n"); + __breakpoint(); + } + ctrl_sm = pio_claim_unused_sm(pulse_pio, true); + ctrl_program_init(pulse_pio, ctrl_sm, ctrl_pio_sm_offset, CTRLOUT_GPIO, 2); + + // Configure a channel to write 32 bits at a time to PIO0 + // SM0's TX FIFO, paced by the data request signal from that peripheral. + ctrl_dma_chan = dma_claim_unused_channel(true); + dma_channel_config ctrl_dmac = dma_channel_get_default_config(ctrl_dma_chan); + channel_config_set_transfer_data_size(&ctrl_dmac, DMA_SIZE_32); + channel_config_set_read_increment(&ctrl_dmac, true); + channel_config_set_dreq(&ctrl_dmac, PIO_DREQ_NUM(pulse_pio, ctrl_sm, true)); + + dma_channel_configure( + ctrl_dma_chan, + &ctrl_dmac, + &pulse_pio->txf[ctrl_sm], // Write address + pulse_ctrl, // Ctrl data + (idx + 3) >> 2, // Transfer count (round up to 4 bytes) + true // Start, SM will wait for trigger + ); + // No IRQ, piggyback on the data one + +#if 0 // Load the trigger program, and configure a free state machine // to run the program. uint trigger_pio_sm_offset = pio_add_program(pulse_pio, &trigger_program); + if (trigger_pio_sm_offset < 0) { + printf("Unable to load trigger program\n"); + __breakpoint(); + } uint trigger_sm = pio_claim_unused_sm(pulse_pio, true); trigger_program_init(pulse_pio, trigger_sm, trigger_pio_sm_offset, TRIGIN_GPIO, 2); - +#endif // // Setup PWM // Used here to output a trigger which gets fed back into the trigger SM @@ -377,11 +455,6 @@ irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap); irq_set_enabled(PWM_IRQ_WRAP, true); - // Setup next pulse DMA address - // XXX: This works but telling dma_channel_configure above to start - // immediately does not - dma_channel_set_read_addr(dma_chan, pulse_data, true); - // Everything else from this point is interrupt-driven. The processor has // time to sit and think about its early retirement -- maybe open a bakery? while (true) {