comparison modulator.c @ 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 56a79dce90e9
comparison
equal deleted inserted replaced
10:98880b18bcc1 11:e9d12b36cfcc
42 // https://github.com/howerj/q 42 // https://github.com/howerj/q
43 // Modified to be Q20.12 rather than Q16.16 43 // Modified to be Q20.12 rather than Q16.16
44 #include "q/q.h" 44 #include "q/q.h"
45 45
46 #include "shaped-trap.h" 46 #include "shaped-trap.h"
47
48 // Base of DAC pins
49 #define DACOUT_GPIO 7
50 // PWM output pin
51 #define TRIGOUT_GPIO 22
52 // PIO SM trigger input pin (connected to above for testing)
53 #define TRIGIN_GPIO 27
47 54
48 // Pulse control bits 55 // Pulse control bits
49 #define SENSE 0x01 56 #define SENSE 0x01
50 #define GATE 0x02 57 #define GATE 0x02
51 #define PHINV 0x04 58 #define PHINV 0x04
76 * pulse (or not if it should stop). ie reset the PIO state machine 83 * pulse (or not if it should stop). ie reset the PIO state machine
77 * back to waiting for an edge and re-arm the DMA. 84 * back to waiting for an edge and re-arm the DMA.
78 */ 85 */
79 void 86 void
80 dma_handler(void) { 87 dma_handler(void) {
81 // Clear the interrupt request. 88 // Clear the interrupt request.
82 dma_hw->ints0 = 1u << dma_chan; 89 dma_hw->ints0 = 1u << dma_chan;
90
91 // Reset DAQ PIO SM so it is waiting for a trigger
92 pio_sm_exec(pulse_pio, pulse_sm, pio_encode_jmp(pulse_pio_sm_offset));
93
94 // Setup next pulse DMA address
95 dma_channel_set_read_addr(dma_chan, pulse_data, true);
83 } 96 }
84 97
85 98
86 void 99 void
87 pwm_wrap(void) { 100 pwm_wrap(void) {
88 pwm_clear_irq(slice_num); 101 pwm_clear_irq(slice_num);
89 #if 0
90 static unsigned state = 0;
91
92 gpio_put(PICO_DEFAULT_LED_PIN, state);
93 state = !state;
94 #endif
95
96 // Reset DAQ PIO SM so it is waiting for a trigger
97 pio_sm_exec(pulse_pio, pulse_sm, pio_encode_jmp(pulse_pio_sm_offset));
98
99 // Setup next pulse DMA address
100 dma_channel_set_read_addr(dma_chan, pulse_data, true);
101
102 // Manually trigger DAQ SM (cleared by SM)
103 pio0->irq_force = 1 << 0;
104
105 gpio_put(2, 1);
106 gpio_put(2, 0);
107 } 102 }
108 103
109 // Calculate pulse shape data 104 // Calculate pulse shape data
110 // TODO: predistortion, proper sense, gate, phase, active, T/R switch 105 // TODO: predistortion, proper sense, gate, phase, active, T/R switch
111 // Could encode them as bit stream like data but more compact would be 106 // Could encode them as bit stream like data but more compact would be
249 // Down slew 244 // Down slew
250 for (uint16_t i = 0; i < slew2 + 1; i++) { 245 for (uint16_t i = 0; i < slew2 + 1; i++) {
251 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); 246 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2)));
252 ctrl[idx] |= PACTIVE; 247 ctrl[idx] |= PACTIVE;
253 } 248 }
254 return idx - 1; 249
250 data[idx++] = 0;
251
252 return idx;
255 } 253 }
256 254
257 int 255 int
258 main(void) { 256 main(void) {
259 absolute_time_t then, now; 257 absolute_time_t then, now;
270 268
271 gpio_init(PICO_DEFAULT_LED_PIN); 269 gpio_init(PICO_DEFAULT_LED_PIN);
272 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 270 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
273 gpio_init(2); 271 gpio_init(2);
274 gpio_set_dir(2, GPIO_OUT); 272 gpio_set_dir(2, GPIO_OUT);
273
275 #if 0 274 #if 0
276 for (unsigned i = 7; i < 7 + 9; i++) { 275 for (unsigned i = 7; i < 7 + 9; i++) {
277 printf("GPIO %d\n", i); 276 printf("GPIO %d\n", i);
278 gpio_init(i); 277 gpio_init(i);
279 gpio_set_dir(i, GPIO_OUT); 278 gpio_set_dir(i, GPIO_OUT);
314 unsigned long long diff = absolute_time_diff_us(then, now); 313 unsigned long long diff = absolute_time_diff_us(then, now);
315 printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n", 314 printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n",
316 diff, idx, (float)diff * 1000.0 / idx); 315 diff, idx, (float)diff * 1000.0 / idx);
317 //__breakpoint(); 316 //__breakpoint();
318 317
319 // Load the clocked_input program, and configure a free state machine 318 // Load the DAC program, and configure a free state machine
320 // to run the program. 319 // to run the program.
321 pulse_pio_sm_offset = pio_add_program(pulse_pio, &dac_program); 320 pulse_pio_sm_offset = pio_add_program(pulse_pio, &dac_program);
322 uint pulse_sm = pio_claim_unused_sm(pulse_pio, true); 321 pulse_sm = pio_claim_unused_sm(pulse_pio, true);
323 // Data is GPIO7 to GPIO14, clock is GPIO15 322 // Data is GPIO7 to GPIO14, clock is GPIO15
324 // Clock divisor of 2 so it runs at 60MHz and 323 // Clock divisor of 2 so it runs at 60MHz and
325 // generates a 30MHz clock 324 // generates a 30MHz clock
326 dac_program_init(pulse_pio, pulse_sm, pulse_pio_sm_offset, 7, 2); 325 dac_program_init(pulse_pio, pulse_sm, pulse_pio_sm_offset, DACOUT_GPIO, 2);
327 326
328 // Configure a channel to write 32 bits at a time to PIO0 327 // Configure a channel to write 32 bits at a time to PIO0
329 // SM0's TX FIFO, paced by the data request signal from that peripheral. 328 // SM0's TX FIFO, paced by the data request signal from that peripheral.
330 dma_chan = dma_claim_unused_channel(true); 329 dma_chan = dma_claim_unused_channel(true);
331 dma_channel_config dmac = dma_channel_get_default_config(dma_chan); 330 dma_channel_config dmac = dma_channel_get_default_config(dma_chan);
336 dma_channel_configure( 335 dma_channel_configure(
337 dma_chan, 336 dma_chan,
338 &dmac, 337 &dmac,
339 &pio0_hw->txf[0], // Write address (only need to set this once) 338 &pio0_hw->txf[0], // Write address (only need to set this once)
340 NULL, // Don't provide a read address yet 339 NULL, // Don't provide a read address yet
341 (idx + 1) >> 2, // Transfer count (round up to 4 bytes) 340 (idx + 3) >> 2, // Transfer count (round up to 4 bytes)
342 false // Don't start yet 341 false // Don't start yet
343 ); 342 );
344 343
345 // Tell the DMA to raise IRQ line 0 when the channel finishes a block 344 // Tell the DMA to raise IRQ line 0 when the channel finishes a block
346 dma_channel_set_irq0_enabled(dma_chan, true); 345 dma_channel_set_irq0_enabled(dma_chan, true);
347 346
348 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted 347 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
349 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); 348 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
350 irq_set_enabled(DMA_IRQ_0, true); 349 irq_set_enabled(DMA_IRQ_0, true);
351 350
351 // Load the trigger program, and configure a free state machine
352 // to run the program.
353 uint trigger_pio_sm_offset = pio_add_program(pulse_pio, &trigger_program);
354 uint trigger_sm = pio_claim_unused_sm(pulse_pio, true);
355 trigger_program_init(pulse_pio, trigger_sm, trigger_pio_sm_offset, TRIGIN_GPIO, 2);
356
357 //
358 // Setup PWM
359 // Used here to output a trigger which gets fed back into the trigger SM
360 //
352 // 120MHz / 250 = 480kHz base 361 // 120MHz / 250 = 480kHz base
353 // Maximum divisor is only 256 which limits the low end, 362 // Maximum divisor is only 256 which limits the low end,
354 // could further subdivide in the IRQ handler 363 // could further subdivide in the IRQ handler
355 pwm_config c = pwm_get_default_config(); 364 pwm_config c = pwm_get_default_config();
356 pwm_config_set_clkdiv_int(&c, 250); 365 pwm_config_set_clkdiv_int(&c, 250);
357 // 8Hz 366 // 8Hz
358 pwm_config_set_wrap(&c, 60000 - 1); 367 pwm_config_set_wrap(&c, 60000 - 1);
368
369 gpio_set_function(TRIGOUT_GPIO, GPIO_FUNC_PWM);
370
371 slice_num = pwm_gpio_to_slice_num(TRIGOUT_GPIO);
359 pwm_init(slice_num, &c, true); 372 pwm_init(slice_num, &c, true);
360 pwm_clear_irq(slice_num); 373 pwm_clear_irq(slice_num);
374 pwm_set_chan_level(slice_num, PWM_CHAN_A, 1);
375 pwm_set_enabled(slice_num, 1);
361 pwm_set_irq_enabled(slice_num, true); 376 pwm_set_irq_enabled(slice_num, true);
362 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap); 377 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap);
363 irq_set_enabled(PWM_IRQ_WRAP, true); 378 irq_set_enabled(PWM_IRQ_WRAP, true);
364 pwm_init(slice_num, &c, true); 379
380 // Setup next pulse DMA address
381 // XXX: This works but telling dma_channel_configure above to start
382 // immediately does not
383 dma_channel_set_read_addr(dma_chan, pulse_data, true);
365 384
366 // Everything else from this point is interrupt-driven. The processor has 385 // Everything else from this point is interrupt-driven. The processor has
367 // time to sit and think about its early retirement -- maybe open a bakery? 386 // time to sit and think about its early retirement -- maybe open a bakery?
368 while (true) { 387 while (true) {
369 dma_channel_wait_for_finish_blocking(dma_chan);
370 gpio_put(PICO_DEFAULT_LED_PIN, 1); 388 gpio_put(PICO_DEFAULT_LED_PIN, 1);
371 sleep_ms(100); 389 sleep_ms(100);
372 gpio_put(PICO_DEFAULT_LED_PIN, 0); 390 gpio_put(PICO_DEFAULT_LED_PIN, 0);
373 sleep_ms(100); 391 sleep_ms(100);
374 } 392 }