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) {