Mercurial > ~darius > hgwebdir.cgi > modulator
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 } |