changeset 11:e9d12b36cfcc

Use PWM output to feed PIO trigger
author Daniel O'Connor <darius@dons.net.au>
date Mon, 24 Feb 2025 15:58:57 +1030
parents 98880b18bcc1
children 283955273884
files modulator.c
diffstat 1 files changed, 45 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/modulator.c	Mon Feb 24 12:12:09 2025 +1030
+++ b/modulator.c	Mon Feb 24 15:58:57 2025 +1030
@@ -45,6 +45,13 @@
 
 #include "shaped-trap.h"
 
+// Base of DAC pins
+#define DACOUT_GPIO	7
+// PWM output pin
+#define TRIGOUT_GPIO	22
+// PIO SM trigger input pin (connected to above for testing)
+#define TRIGIN_GPIO	27
+
 // Pulse control bits
 #define SENSE		0x01
 #define GATE		0x02
@@ -78,32 +85,20 @@
  */
 void
 dma_handler(void) {
-    // Clear the interrupt request.
-    dma_hw->ints0 = 1u << dma_chan;
+  // Clear the interrupt request.
+  dma_hw->ints0 = 1u << 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));
+
+  // Setup next pulse DMA address
+  dma_channel_set_read_addr(dma_chan, pulse_data, true);
 }
 
 
 void
 pwm_wrap(void) {
   pwm_clear_irq(slice_num);
-#if 0
-  static unsigned state = 0;
-
-  gpio_put(PICO_DEFAULT_LED_PIN, state);
-  state = !state;
-#endif
-
-  // 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));
-
-  // Setup next pulse DMA address
-  dma_channel_set_read_addr(dma_chan, pulse_data, true);
-
-  // Manually trigger DAQ SM (cleared by SM)
-  pio0->irq_force = 1 << 0;
-
-  gpio_put(2, 1);
-  gpio_put(2, 0);
 }
 
 // Calculate pulse shape data
@@ -251,7 +246,10 @@
     data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2)));
     ctrl[idx] |= PACTIVE;
   }
-  return idx - 1;
+
+  data[idx++] = 0;
+
+  return idx;
 }
 
 int
@@ -272,6 +270,7 @@
     gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
     gpio_init(2);
     gpio_set_dir(2, GPIO_OUT);
+
 #if 0
     for (unsigned i = 7; i < 7 + 9; i++) {
       printf("GPIO %d\n", i);
@@ -316,14 +315,14 @@
 	   diff, idx, (float)diff * 1000.0 / idx);
     //__breakpoint();
 
-    // Load the clocked_input program, and configure a free state machine
+    // 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);
-    uint pulse_sm = pio_claim_unused_sm(pulse_pio, true);
+    pulse_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, 7, 2);
+    dac_program_init(pulse_pio, pulse_sm, pulse_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.
@@ -338,7 +337,7 @@
         &dmac,
         &pio0_hw->txf[0], // Write address (only need to set this once)
         NULL,             // Don't provide a read address yet
-        (idx + 1) >> 2,	  // Transfer count (round up to 4 bytes)
+        (idx + 3) >> 2,	  // Transfer count (round up to 4 bytes)
         false             // Don't start yet
     );
 
@@ -349,6 +348,16 @@
     irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
     irq_set_enabled(DMA_IRQ_0, true);
 
+    // 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);
+    uint trigger_sm = pio_claim_unused_sm(pulse_pio, true);
+    trigger_program_init(pulse_pio, trigger_sm, trigger_pio_sm_offset, TRIGIN_GPIO, 2);
+
+    //
+    // Setup PWM
+    // Used here to output a trigger which gets fed back into the trigger SM
+    //
     // 120MHz / 250 = 480kHz base
     // Maximum divisor is only 256 which limits the low end,
     // could further subdivide in the IRQ handler
@@ -356,17 +365,26 @@
     pwm_config_set_clkdiv_int(&c, 250);
     // 8Hz
     pwm_config_set_wrap(&c, 60000 - 1);
+
+    gpio_set_function(TRIGOUT_GPIO, GPIO_FUNC_PWM);
+
+    slice_num = pwm_gpio_to_slice_num(TRIGOUT_GPIO);
     pwm_init(slice_num, &c, true);
     pwm_clear_irq(slice_num);
+    pwm_set_chan_level(slice_num, PWM_CHAN_A, 1);
+    pwm_set_enabled(slice_num, 1);
     pwm_set_irq_enabled(slice_num, true);
     irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap);
     irq_set_enabled(PWM_IRQ_WRAP, true);
-    pwm_init(slice_num, &c, 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) {
-      dma_channel_wait_for_finish_blocking(dma_chan);
 	gpio_put(PICO_DEFAULT_LED_PIN, 1);
 	sleep_ms(100);
 	gpio_put(PICO_DEFAULT_LED_PIN, 0);