Mercurial > ~darius > hgwebdir.cgi > modulator
comparison modulator.c @ 16:56a79dce90e9
Commit WIP ctrl code
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Tue, 25 Feb 2025 13:31:27 +1030 |
parents | e9d12b36cfcc |
children | a249e4727b01 |
comparison
equal
deleted
inserted
replaced
15:bf483219cb12 | 16:56a79dce90e9 |
---|---|
3 ** | 3 ** |
4 ** This is proprietary unpublished source code, property | 4 ** This is proprietary unpublished source code, property |
5 ** of Genesis Software. Use or disclosure without prior | 5 ** of Genesis Software. Use or disclosure without prior |
6 ** agreement is expressly prohibited. | 6 ** agreement is expressly prohibited. |
7 ** | 7 ** |
8 ** Copyright (c) 2021 Genesis Software, all rights reserved. | 8 ** Copyright (c) 2025 Genesis Software, all rights reserved. |
9 ** | 9 ** |
10 ******************************************************************* | 10 ******************************************************************* |
11 ******************************************************************/ | 11 ******************************************************************/ |
12 | 12 |
13 /* | 13 /* |
17 ** | 17 ** |
18 */ | 18 */ |
19 | 19 |
20 #include <stdio.h> | 20 #include <stdio.h> |
21 #include <string.h> | 21 #include <string.h> |
22 #include "bitstring.h" | |
23 | 22 |
24 #pragma GCC diagnostic push | 23 #pragma GCC diagnostic push |
25 #pragma GCC diagnostic ignored "-Wtype-limits" | 24 #pragma GCC diagnostic ignored "-Wtype-limits" |
26 #pragma GCC diagnostic ignored "-Wsign-compare" | 25 #pragma GCC diagnostic ignored "-Wsign-compare" |
27 #include "pico/stdlib.h" | 26 #include "pico/stdlib.h" |
35 #include "hardware/structs/pll.h" | 34 #include "hardware/structs/pll.h" |
36 #include "hardware/structs/clocks.h" | 35 #include "hardware/structs/clocks.h" |
37 #pragma GCC diagnostic pop | 36 #pragma GCC diagnostic pop |
38 | 37 |
39 #include "dac.pio.h" | 38 #include "dac.pio.h" |
39 #include "ctrl.pio.h" | |
40 #include "trigger.pio.h" | 40 #include "trigger.pio.h" |
41 | 41 |
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 | 47 |
48 // Base of DAC pins | 48 // Base of DAC pins |
49 #define DACOUT_GPIO 7 | 49 #define DACOUT_GPIO 7 |
50 // Base of ctrl pins | |
51 #define CTRLOUT_GPIO 16 | |
50 // PWM output pin | 52 // PWM output pin |
51 #define TRIGOUT_GPIO 22 | 53 #define TRIGOUT_GPIO 23 |
52 // PIO SM trigger input pin (connected to above for testing) | 54 // PIO SM trigger input pin (connected to above for testing) |
53 #define TRIGIN_GPIO 27 | 55 #define TRIGIN_GPIO 27 |
54 | 56 |
55 // Pulse control bits | 57 // Pulse control bits |
56 #define SENSE 0x01 | 58 #define PACTIVE 0x01 |
57 #define GATE 0x02 | 59 #define PHINV 0x02 |
58 #define PHINV 0x04 | 60 #define SENSE1 0x04 |
59 #define PACTIVE 0x08 | 61 #define SENSE2 0x08 |
60 | 62 #define GATE 0x10 |
61 // DMA channel to feed DAC PIO | 63 #define TRSW 0x20 |
62 static int dma_chan; | 64 |
63 // Pulse shape data | 65 // Pulse shape data |
64 uint8_t pulse_data[65536]; | 66 uint8_t pulse_data[65536] __attribute__((aligned(4))); |
65 // Pulse control data | 67 // Pulse control data |
66 uint8_t pulse_ctrl[65536]; | 68 uint8_t pulse_ctrl[65536] __attribute__((aligned(4))); |
67 // PWM slice for PRF timer | 69 // PWM slice for PRF timer |
68 unsigned slice_num = 0; | 70 unsigned slice_num = 0; |
69 // DAC PIO | 71 |
72 // PIO for pulse generation | |
70 PIO pulse_pio = pio0; | 73 PIO pulse_pio = pio0; |
74 | |
75 // DMA channel to feed DAC PIO | |
76 static int dac_dma_chan; | |
71 // DAC SM | 77 // DAC SM |
72 uint pulse_sm; | 78 uint dac_sm; |
73 // Instruction offset for DAC PIO program | 79 // Instruction offset for DAC PIO program |
74 uint pulse_pio_sm_offset; | 80 uint dac_pio_sm_offset; |
81 | |
82 // DMA channel to feed ctrl PIO | |
83 static int ctrl_dma_chan; | |
84 // Ctrl SM | |
85 uint ctrl_sm; | |
86 // Instruction offset for ctrl PIO program | |
87 uint ctrl_pio_sm_offset; | |
88 | |
75 /* | 89 /* |
76 * Use a DMA channel to feed PIO0 SM0 with pulse data. | 90 * Use a DMA channel to feed PIO0 SM0 with pulse data. |
77 * Each DMA transfer is a single pulse. | 91 * Each DMA transfer is a single pulse. |
78 * | 92 * |
79 * The PIO state machine waits to be triggered before starting | 93 * The PIO state machine waits to be triggered before starting |
84 * back to waiting for an edge and re-arm the DMA. | 98 * back to waiting for an edge and re-arm the DMA. |
85 */ | 99 */ |
86 void | 100 void |
87 dma_handler(void) { | 101 dma_handler(void) { |
88 // Clear the interrupt request. | 102 // Clear the interrupt request. |
89 dma_hw->ints0 = 1u << dma_chan; | 103 dma_hw->ints0 = 1u << dac_dma_chan; |
90 | 104 |
91 // Reset DAQ PIO SM so it is waiting for a trigger | 105 // Disabled for now, manual trigger only |
92 pio_sm_exec(pulse_pio, pulse_sm, pio_encode_jmp(pulse_pio_sm_offset)); | 106 #if 0 |
93 | 107 // Reset DAQ & ctrl PIO SMs so they are waiting for a trigger |
94 // Setup next pulse DMA address | 108 pio_sm_exec(pulse_pio, dac_sm, pio_encode_jmp(dac_pio_sm_offset)); |
95 dma_channel_set_read_addr(dma_chan, pulse_data, true); | 109 pio_sm_exec(pulse_pio, ctrl_sm, pio_encode_jmp(ctrl_pio_sm_offset)); |
110 | |
111 // Setup next pulse data & ctrl DMA addresses | |
112 dma_channel_set_read_addr(dac_dma_chan, pulse_data, true); | |
113 dma_channel_set_read_addr(ctrl_dma_chan, pulse_ctrl, true); | |
114 #endif | |
96 } | 115 } |
97 | 116 |
98 | 117 |
99 void | 118 void |
100 pwm_wrap(void) { | 119 pwm_wrap(void) { |
101 pwm_clear_irq(slice_num); | 120 pwm_clear_irq(slice_num); |
121 | |
122 // Reset DAQ & ctrl PIO SMs so they are waiting for a trigger | |
123 pio_sm_exec(pulse_pio, dac_sm, pio_encode_jmp(dac_pio_sm_offset)); | |
124 pio_sm_exec(pulse_pio, ctrl_sm, pio_encode_jmp(ctrl_pio_sm_offset)); | |
125 | |
126 printf("DAC: transfers %lu\n", dma_channel_hw_addr(dac_dma_chan)->transfer_count); | |
127 printf("DAC: transfers %lu\n", dma_channel_hw_addr(ctrl_dma_chan)->transfer_count); | |
128 | |
129 // Setup next pulse data & ctrl DMA addresses | |
130 dma_channel_wait_for_finish_blocking(dac_dma_chan); | |
131 dma_channel_set_read_addr(dac_dma_chan, pulse_data, true); | |
132 dma_channel_wait_for_finish_blocking(ctrl_dma_chan); | |
133 dma_channel_set_read_addr(ctrl_dma_chan, pulse_ctrl, true); | |
134 | |
135 // Manually trigger DAQ SM (cleared by SM) | |
136 pio0->irq_force = 1 << 0; | |
137 | |
138 // 'scope trigger | |
139 gpio_put(2, 1); | |
140 gpio_put(2, 0); | |
102 } | 141 } |
103 | 142 |
104 // Calculate pulse shape data | 143 // Calculate pulse shape data |
105 // TODO: predistortion, proper sense, gate, phase, active, T/R switch | 144 // TODO: predistortion, proper sense, gate, phase, active, T/R switch |
106 // Could encode them as bit stream like data but more compact would be | 145 // Could encode them as bit stream like data but more compact would be |
176 | 215 |
177 dcscale = qdiv(qsub(qint(256), qint(dcofs)), qint(255)); | 216 dcscale = qdiv(qsub(qint(256), qint(dcofs)), qint(255)); |
178 qsprint(dcscale, tmps, sizeof(tmps)); | 217 qsprint(dcscale, tmps, sizeof(tmps)); |
179 printf("dcscale = %s\n", tmps); | 218 printf("dcscale = %s\n", tmps); |
180 | 219 |
181 memset(pulse_data, 0, sizeof(pulse_data)); | 220 memset(pulse_data, 0, datalen); |
182 memset(pulse_ctrl, 0, sizeof(pulse_ctrl)); | 221 memset(pulse_ctrl, 0, datalen); |
183 idx = 0; | 222 idx = 0; |
184 | 223 |
185 // Up slew | 224 // Up slew |
186 for (uint16_t i = 0; i < slew1; i++) { | 225 for (uint16_t i = 0; i < slew1; i++) { |
187 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1))); | 226 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1))); |
226 for (uint16_t i = 0; i < shapesamples; i++) { | 265 for (uint16_t i = 0; i < shapesamples; i++) { |
227 data[idx++] = data[bit1stopup - i]; | 266 data[idx++] = data[bit1stopup - i]; |
228 // Could replace this with a separate loop to poke it into place | 267 // Could replace this with a separate loop to poke it into place |
229 // Similarly for TR switch when implemented | 268 // Similarly for TR switch when implemented |
230 if (i == 0 && c == 0) | 269 if (i == 0 && c == 0) |
231 ctrl[idx] = ctrltmp | SENSE; | 270 ctrl[idx] = ctrltmp | SENSE1; |
232 else | 271 else |
233 ctrl[idx] = ctrltmp; | 272 ctrl[idx] = ctrltmp; |
234 } | 273 } |
235 | 274 |
236 // Code gap | 275 // Code gap |
246 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); | 285 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); |
247 ctrl[idx] |= PACTIVE; | 286 ctrl[idx] |= PACTIVE; |
248 } | 287 } |
249 | 288 |
250 data[idx++] = 0; | 289 data[idx++] = 0; |
251 | 290 ctrl[idx] = 0; |
252 return idx; | 291 |
292 return idx + 1; | |
253 } | 293 } |
254 | 294 |
255 int | 295 int |
256 main(void) { | 296 main(void) { |
257 absolute_time_t then, now; | 297 absolute_time_t then, now; |
270 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); | 310 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); |
271 gpio_init(2); | 311 gpio_init(2); |
272 gpio_set_dir(2, GPIO_OUT); | 312 gpio_set_dir(2, GPIO_OUT); |
273 | 313 |
274 #if 0 | 314 #if 0 |
315 // GPIO tester to check breadboard wiring | |
275 for (unsigned i = 7; i < 7 + 9; i++) { | 316 for (unsigned i = 7; i < 7 + 9; i++) { |
276 printf("GPIO %d\n", i); | 317 printf("GPIO %d\n", i); |
277 gpio_init(i); | 318 gpio_init(i); |
278 gpio_set_dir(i, GPIO_OUT); | 319 gpio_set_dir(i, GPIO_OUT); |
279 printf("on\n"); | 320 printf("on\n"); |
315 diff, idx, (float)diff * 1000.0 / idx); | 356 diff, idx, (float)diff * 1000.0 / idx); |
316 //__breakpoint(); | 357 //__breakpoint(); |
317 | 358 |
318 // Load the DAC program, and configure a free state machine | 359 // Load the DAC program, and configure a free state machine |
319 // to run the program. | 360 // to run the program. |
320 pulse_pio_sm_offset = pio_add_program(pulse_pio, &dac_program); | 361 dac_pio_sm_offset = pio_add_program(pulse_pio, &dac_program); |
321 pulse_sm = pio_claim_unused_sm(pulse_pio, true); | 362 if (dac_pio_sm_offset < 0) { |
363 printf("Unable to load DAC program\n"); | |
364 __breakpoint(); | |
365 } | |
366 dac_sm = pio_claim_unused_sm(pulse_pio, true); | |
322 // Data is GPIO7 to GPIO14, clock is GPIO15 | 367 // Data is GPIO7 to GPIO14, clock is GPIO15 |
323 // Clock divisor of 2 so it runs at 60MHz and | 368 // Clock divisor of 2 so it runs at 60MHz and |
324 // generates a 30MHz clock | 369 // generates a 30MHz clock |
325 dac_program_init(pulse_pio, pulse_sm, pulse_pio_sm_offset, DACOUT_GPIO, 2); | 370 dac_program_init(pulse_pio, dac_sm, dac_pio_sm_offset, DACOUT_GPIO, 2); |
326 | 371 |
327 // Configure a channel to write 32 bits at a time to PIO0 | 372 // Configure a channel to write 32 bits at a time to PIO0 |
328 // SM0's TX FIFO, paced by the data request signal from that peripheral. | 373 // SM0's TX FIFO, paced by the data request signal from that peripheral. |
329 dma_chan = dma_claim_unused_channel(true); | 374 dac_dma_chan = dma_claim_unused_channel(true); |
330 dma_channel_config dmac = dma_channel_get_default_config(dma_chan); | 375 dma_channel_config dac_dmac = dma_channel_get_default_config(dac_dma_chan); |
331 channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32); | 376 channel_config_set_transfer_data_size(&dac_dmac, DMA_SIZE_32); |
332 channel_config_set_read_increment(&dmac, true); | 377 channel_config_set_read_increment(&dac_dmac, true); |
333 channel_config_set_dreq(&dmac, DREQ_PIO0_TX0); | 378 channel_config_set_dreq(&dac_dmac, PIO_DREQ_NUM(pulse_pio, dac_sm, true)); |
334 | 379 |
335 dma_channel_configure( | 380 dma_channel_configure( |
336 dma_chan, | 381 dac_dma_chan, |
337 &dmac, | 382 &dac_dmac, |
338 &pio0_hw->txf[0], // Write address (only need to set this once) | 383 &pulse_pio->txf[dac_sm], // Write address |
339 NULL, // Don't provide a read address yet | 384 pulse_data, // Pulse data |
340 (idx + 3) >> 2, // Transfer count (round up to 4 bytes) | 385 (idx + 3) >> 2, // Transfer count (round up to 4 bytes) |
341 false // Don't start yet | 386 true // Start, SM will wait for trigger |
342 ); | 387 ); |
343 | 388 |
344 // Tell the DMA to raise IRQ line 0 when the channel finishes a block | 389 // Tell the DMA to raise IRQ line 0 when the channel finishes a block |
345 dma_channel_set_irq0_enabled(dma_chan, true); | 390 dma_channel_set_irq0_enabled(dac_dma_chan, true); |
346 | 391 |
347 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted | 392 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted |
348 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); | 393 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); |
349 irq_set_enabled(DMA_IRQ_0, true); | 394 irq_set_enabled(DMA_IRQ_0, true); |
350 | 395 |
396 // Load the ctrl program, and configure a free state machine | |
397 // to run the program. | |
398 ctrl_pio_sm_offset = pio_add_program(pulse_pio, &ctrl_program); | |
399 if (ctrl_pio_sm_offset < 0) { | |
400 printf("Unable to load ctrl program\n"); | |
401 __breakpoint(); | |
402 } | |
403 ctrl_sm = pio_claim_unused_sm(pulse_pio, true); | |
404 ctrl_program_init(pulse_pio, ctrl_sm, ctrl_pio_sm_offset, CTRLOUT_GPIO, 2); | |
405 | |
406 // Configure a channel to write 32 bits at a time to PIO0 | |
407 // SM0's TX FIFO, paced by the data request signal from that peripheral. | |
408 ctrl_dma_chan = dma_claim_unused_channel(true); | |
409 dma_channel_config ctrl_dmac = dma_channel_get_default_config(ctrl_dma_chan); | |
410 channel_config_set_transfer_data_size(&ctrl_dmac, DMA_SIZE_32); | |
411 channel_config_set_read_increment(&ctrl_dmac, true); | |
412 channel_config_set_dreq(&ctrl_dmac, PIO_DREQ_NUM(pulse_pio, ctrl_sm, true)); | |
413 | |
414 dma_channel_configure( | |
415 ctrl_dma_chan, | |
416 &ctrl_dmac, | |
417 &pulse_pio->txf[ctrl_sm], // Write address | |
418 pulse_ctrl, // Ctrl data | |
419 (idx + 3) >> 2, // Transfer count (round up to 4 bytes) | |
420 true // Start, SM will wait for trigger | |
421 ); | |
422 // No IRQ, piggyback on the data one | |
423 | |
424 #if 0 | |
351 // Load the trigger program, and configure a free state machine | 425 // Load the trigger program, and configure a free state machine |
352 // to run the program. | 426 // to run the program. |
353 uint trigger_pio_sm_offset = pio_add_program(pulse_pio, &trigger_program); | 427 uint trigger_pio_sm_offset = pio_add_program(pulse_pio, &trigger_program); |
428 if (trigger_pio_sm_offset < 0) { | |
429 printf("Unable to load trigger program\n"); | |
430 __breakpoint(); | |
431 } | |
354 uint trigger_sm = pio_claim_unused_sm(pulse_pio, true); | 432 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); | 433 trigger_program_init(pulse_pio, trigger_sm, trigger_pio_sm_offset, TRIGIN_GPIO, 2); |
356 | 434 #endif |
357 // | 435 // |
358 // Setup PWM | 436 // Setup PWM |
359 // Used here to output a trigger which gets fed back into the trigger SM | 437 // Used here to output a trigger which gets fed back into the trigger SM |
360 // | 438 // |
361 // 120MHz / 250 = 480kHz base | 439 // 120MHz / 250 = 480kHz base |
375 pwm_set_enabled(slice_num, 1); | 453 pwm_set_enabled(slice_num, 1); |
376 pwm_set_irq_enabled(slice_num, true); | 454 pwm_set_irq_enabled(slice_num, true); |
377 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap); | 455 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap); |
378 irq_set_enabled(PWM_IRQ_WRAP, true); | 456 irq_set_enabled(PWM_IRQ_WRAP, true); |
379 | 457 |
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); | |
384 | |
385 // Everything else from this point is interrupt-driven. The processor has | 458 // Everything else from this point is interrupt-driven. The processor has |
386 // time to sit and think about its early retirement -- maybe open a bakery? | 459 // time to sit and think about its early retirement -- maybe open a bakery? |
387 while (true) { | 460 while (true) { |
388 gpio_put(PICO_DEFAULT_LED_PIN, 1); | 461 gpio_put(PICO_DEFAULT_LED_PIN, 1); |
389 sleep_ms(100); | 462 sleep_ms(100); |