comparison modulator.c @ 9:3acdebd7eec7

Make it actually work
author Daniel O'Connor <darius@dons.net.au>
date Fri, 21 Feb 2025 17:27:22 +1030
parents 0249d0cecac4
children 98880b18bcc1
comparison
equal deleted inserted replaced
8:0249d0cecac4 9:3acdebd7eec7
35 #include "hardware/structs/pll.h" 35 #include "hardware/structs/pll.h"
36 #include "hardware/structs/clocks.h" 36 #include "hardware/structs/clocks.h"
37 #pragma GCC diagnostic pop 37 #pragma GCC diagnostic pop
38 38
39 #include "dac.pio.h" 39 #include "dac.pio.h"
40 #include "trigger.pio.h"
40 41
41 // https://github.com/howerj/q 42 // https://github.com/howerj/q
42 // Modified to be Q20.12 rather than Q16.16 43 // Modified to be Q20.12 rather than Q16.16
43 #include "q/q.h" 44 #include "q/q.h"
44 45
45 #include "shaped-trap.h" 46 #include "shaped-trap.h"
46 47
48 // Pulse control bits
49 #define SENSE 0x01
50 #define GATE 0x02
51 #define PHINV 0x04
52 #define PACTIVE 0x08
53
47 static int dma_chan; 54 static int dma_chan;
48 uint8_t pulse_data[65536]; 55 uint8_t pulse_data[65536];
49 uint8_t pulse_ctrl[65536]; 56 uint8_t pulse_ctrl[65536];
50 57 unsigned slice_num = 0;
58 PIO pulse_pio;
59 uint pulse_sm;
60
61 /*
62 * Use a DMA channel to feed PIO0 SM0 with pulse data.
63 * Each DMA transfer is a single pulse.
64 *
65 * The PIO state machine waits to be triggered before starting
66 * so we can use another state machine to look for the trigger edge.
67 *
68 * When the DMA is done the IRQ handler will configure it for the next
69 * pulse (or not if it should stop). ie reset the PIO state machine
70 * back to waiting for an edge and re-arm the DMA.
71 */
51 void 72 void
52 dma_handler(void) { 73 dma_handler(void) {
53 // Clear the interrupt request. 74 // Clear the interrupt request.
54 dma_hw->ints0 = 1u << dma_chan; 75 dma_hw->ints0 = 1u << dma_chan;
55 // Give the channel a new wave table entry to read from, and re-trigger it 76 // Give the channel a new wave table entry to read from, and re-trigger it
56 //dma_channel_set_read_addr(dma_chan, shaped_trap_dat_start, true); 77 //dma_channel_set_read_addr(dma_chan, shaped_trap_dat_start, true);
78 // Reset pulse state machine back to waiting for trigger
79 //pio_sm_exec(pulse_pio, pulse_sm, pio_encode_jump(0);
57 } 80 }
58 81
59 unsigned slice_num = 0;
60 82
61 void 83 void
62 pwm_wrap(void) { 84 pwm_wrap(void) {
63 pwm_clear_irq(slice_num); 85 pwm_clear_irq(slice_num);
64 #if 0 86 #if 0
68 state = !state; 90 state = !state;
69 #endif 91 #endif
70 92
71 // Trigger another pulse read out 93 // Trigger another pulse read out
72 dma_channel_set_read_addr(dma_chan, pulse_data, true); 94 dma_channel_set_read_addr(dma_chan, pulse_data, true);
95 gpio_put(2, 1);
96 gpio_put(2, 0);
73 } 97 }
74 98
75 // Calculate pulse shape data 99 // Calculate pulse shape data
76 // TODO: predistortion, proper sense, gate, phase, active, T/R switch 100 // TODO: predistortion, proper sense, gate, phase, active, T/R switch
77 // Could encode them as bit stream like data but more compact would be 101 // Could encode them as bit stream like data but more compact would be
78 // (say) a list of counts to toggle pins at 102 // (say) a list of counts to toggle pins at
79 #define SENSE 0x01
80 #define GATE 0x02
81 #define PHINV 0x04
82 #define PACTIVE 0x08
83
84 // Need to add pre/postgate/sense/phase counters 103 // Need to add pre/postgate/sense/phase counters
85 unsigned 104 unsigned
86 compute_pulse(uint8_t *data, uint8_t *ctrl, unsigned datalen, uint16_t plen, char *code, uint8_t ncode, const uint8_t *shape, uint8_t shapelen, uint8_t codegap, uint8_t slew1, uint8_t slew2, uint8_t dcofs) { 105 compute_pulse(uint8_t *data, uint8_t *ctrl, unsigned datalen, uint16_t plen, char *code, uint8_t ncode, const uint8_t *shape, uint8_t shapelen, uint8_t codegap, uint8_t slew1, uint8_t slew2, uint8_t dcofs) {
87 uint32_t shapesamples, nsamples, idx, bit1startup, bit1stopup; 106 uint32_t shapesamples, nsamples, idx, bit1startup, bit1stopup;
88 q_t dcscale, stepsize; 107 q_t dcscale, stepsize;
197 if (c == 0) 216 if (c == 0)
198 bit1stopup = idx - 1; 217 bit1stopup = idx - 1;
199 // Pulse down 218 // Pulse down
200 // Since the pulse is symmetrical just copy the up slope in reverse 219 // Since the pulse is symmetrical just copy the up slope in reverse
201 // XXX: if we had asymmetrical predistortion this wouldn't be true 220 // XXX: if we had asymmetrical predistortion this wouldn't be true
202 // In that case probably best to do a single pulse up/down, then
203 // add predistortion and copy it to other bits
204 for (uint16_t i = 0; i < shapesamples; i++) { 221 for (uint16_t i = 0; i < shapesamples; i++) {
205 data[idx++] = data[bit1stopup - i]; 222 data[idx++] = data[bit1stopup - i];
206 // Could replace this with a separate loop to poke it into place 223 // Could replace this with a separate loop to poke it into place
207 // Similarly for TR switch when implemented 224 // Similarly for TR switch when implemented
208 if (i == 0 && c == 0) 225 if (i == 0 && c == 0)
225 ctrl[idx] |= PACTIVE; 242 ctrl[idx] |= PACTIVE;
226 } 243 }
227 return idx - 1; 244 return idx - 1;
228 } 245 }
229 246
230 // As per hello_interp.c
231 void interp_test(uint8_t *data, const uint8_t *shape) {
232 interp_config cfg;
233 const int uv_fractional_bits = 12;
234
235 // Step size is 0.25 (ie stretching by 4x)
236 unsigned step = (1 << uv_fractional_bits) / 4;
237
238 puts("interp_test\n");
239 // Setup lane 0 to generate index into shape table
240 // Mask start is 0 because we use 8 bit samples
241 cfg = interp_default_config();
242 interp_config_set_shift(&cfg, uv_fractional_bits);
243 interp_config_set_mask(&cfg, 0, 32 - uv_fractional_bits);
244 interp_config_set_blend(&cfg, true);
245 interp_set_config(interp0, 0, &cfg);
246
247 // Setup lane 1 to LERP each sample pair
248 cfg = interp_default_config();
249 interp_config_set_shift(&cfg, uv_fractional_bits - 8);
250 interp_config_set_signed(&cfg, false);
251 interp_config_set_cross_input(&cfg, true); // unsigned blending
252 interp_set_config(interp0, 1, &cfg);
253
254 interp0->accum[0] = 0; // Initial offset into shape table
255 interp0->base[2] = (uintptr_t)shape; // Start of shape table
256 for (unsigned i = 0; i < 122 * 4; i++) {
257 // Get sample pair
258 uint8_t *sample_pair = (uint8_t *) interp0->peek[2];
259 // Ask lane 1 for a LERP, using the lane 0 accumulator
260 interp0->base[0] = sample_pair[0];
261 interp0->base[1] = sample_pair[1];
262 uint32_t peek1 = interp0->peek[1];
263 uint32_t add_raw1 = interp0->add_raw[1];
264 data[i] = peek1;
265 printf("%d\t(%lu%% between %d and %d) %lu\n",
266 (int)peek1,
267 100 * (add_raw1 & 0xff) / 0xff,
268 sample_pair[0], sample_pair[1],
269 interp_get_accumulator(interp0, 0));
270 interp0->add_raw[0] = step;
271 }
272 puts("done");
273 }
274
275 int 247 int
276 main(void) { 248 main(void) {
277 absolute_time_t then, now; 249 absolute_time_t then, now;
278 250
279 // Set sysclk to 120MHz 251 // Set sysclk to 120MHz
286 // https://github.com/raspberrypi/pico-sdk/issues/1152#issuecomment-1418248639 258 // https://github.com/raspberrypi/pico-sdk/issues/1152#issuecomment-1418248639
287 timer_hw->dbgpause = 0; 259 timer_hw->dbgpause = 0;
288 260
289 gpio_init(PICO_DEFAULT_LED_PIN); 261 gpio_init(PICO_DEFAULT_LED_PIN);
290 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 262 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
291 263 gpio_init(2);
292 //interp_test(pulse_data, shaped_trap); 264 gpio_set_dir(2, GPIO_OUT);
265 #if 0
266 for (unsigned i = 7; i < 7 + 9; i++) {
267 printf("GPIO %d\n", i);
268 gpio_init(i);
269 gpio_set_dir(i, GPIO_OUT);
270 printf("on\n");
271 gpio_put(i, 1);
272 __breakpoint();
273 printf("off\n");
274 gpio_put(i, 0);
275 __breakpoint();
276 }
277 #endif
293 278
294 uint32_t idx; 279 uint32_t idx;
295 uint16_t plen; 280 uint16_t plen;
296 char *code; 281 char *code;
297 if (0) { 282 if (1) {
298 plen = 8000; 283 plen = 8000;
299 code = "1110010"; 284 code = "1110010";
300 } else { 285 } else {
301 plen = 53000; 286 plen = 53000;
302 code = "1"; 287 code = "1";
318 now = get_absolute_time(); 303 now = get_absolute_time();
319 unsigned long long diff = absolute_time_diff_us(then, now); 304 unsigned long long diff = absolute_time_diff_us(then, now);
320 printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n", 305 printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n",
321 diff, idx, (float)diff * 1000.0 / idx); 306 diff, idx, (float)diff * 1000.0 / idx);
322 __breakpoint(); 307 __breakpoint();
323 pwm_config c = pwm_get_default_config(); 308
309 // Load the clocked_input program, and configure a free state machine
310 // to run the program.
311 PIO pulse_pio = pio0;
312 uint offset = pio_add_program(pulse_pio, &dac_program);
313 uint pulse_sm = pio_claim_unused_sm(pulse_pio, true);
314 // Data is GPIO7 to GPIO14, clock is GPIO15
315 // Clock divisor of 2 so it runs at 60MHz and
316 // generates a 30MHz clock
317 dac_program_init(pulse_pio, pulse_sm, offset, 7, 2);
318
319 // Configure a channel to write 32 bits at a time to PIO0
320 // SM0's TX FIFO, paced by the data request signal from that peripheral.
321 dma_chan = dma_claim_unused_channel(true);
322 dma_channel_config dmac = dma_channel_get_default_config(dma_chan);
323 channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32);
324 channel_config_set_read_increment(&dmac, true);
325 channel_config_set_dreq(&dmac, DREQ_PIO0_TX0);
326
327 dma_channel_configure(
328 dma_chan,
329 &dmac,
330 &pio0_hw->txf[0], // Write address (only need to set this once)
331 NULL, // Don't provide a read address yet
332 (idx + 1) >> 2, // Transfer count (round up to 4 bytes)
333 false // Don't start yet
334 );
335
336 // Tell the DMA to raise IRQ line 0 when the channel finishes a block
337 dma_channel_set_irq0_enabled(dma_chan, true);
338
339 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
340 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
341 irq_set_enabled(DMA_IRQ_0, true);
342
324 // 120MHz / 250 = 480kHz base 343 // 120MHz / 250 = 480kHz base
325 // Maximum divisor is only 256 which limits the low end, 344 // Maximum divisor is only 256 which limits the low end,
326 // could further subdivide in the IRQ handler 345 // could further subdivide in the IRQ handler
346 pwm_config c = pwm_get_default_config();
327 pwm_config_set_clkdiv_int(&c, 250); 347 pwm_config_set_clkdiv_int(&c, 250);
328 // 8Hz 348 // 8Hz
329 pwm_config_set_wrap(&c, 60000 - 1); 349 pwm_config_set_wrap(&c, 60000 - 1);
330 pwm_init(slice_num, &c, true); 350 pwm_init(slice_num, &c, true);
331 pwm_clear_irq(slice_num); 351 pwm_clear_irq(slice_num);
332 pwm_set_irq_enabled(slice_num, true); 352 pwm_set_irq_enabled(slice_num, true);
333 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap); 353 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap);
334 irq_set_enabled(PWM_IRQ_WRAP, true); 354 irq_set_enabled(PWM_IRQ_WRAP, true);
335 pwm_init(slice_num, &c, true); 355 pwm_init(slice_num, &c, true);
336 356
337 // Load the clocked_input program, and configure a free state machine
338 // to run the program.
339 PIO pio = pio0;
340 uint offset = pio_add_program(pio, &dac_program);
341 uint sm = pio_claim_unused_sm(pio, true);
342 // XXX: I would prefer starting at GPIO16 but in that case the top 2
343 // bits don't seem to work
344 // Clock divisor of 2 so it runs at 60MHz and
345 // generates a 30MHz clock
346 dac_program_init(pio, sm, offset, 14, 2);
347
348 // Configure a channel to write the same word (8 bits) repeatedly to PIO0
349 // SM0's TX FIFO, paced by the data request signal from that peripheral.
350 dma_chan = dma_claim_unused_channel(true);
351 dma_channel_config dmac = dma_channel_get_default_config(dma_chan);
352 // XXX: need to get pulse generator to pad to 32 bits
353 channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32);
354 channel_config_set_read_increment(&dmac, true);
355 channel_config_set_dreq(&dmac, DREQ_PIO0_TX0);
356
357 dma_channel_configure(
358 dma_chan,
359 &dmac,
360 &pio0_hw->txf[0], // Write address (only need to set this once)
361 NULL, // Don't provide a read address yet
362 idx >> 2, // Transfer count
363 false // Don't start yet
364 );
365
366 // Tell the DMA to raise IRQ line 0 when the channel finishes a block
367 dma_channel_set_irq0_enabled(dma_chan, true);
368
369 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
370 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
371 irq_set_enabled(DMA_IRQ_0, true);
372
373 // Everything else from this point is interrupt-driven. The processor has 357 // Everything else from this point is interrupt-driven. The processor has
374 // time to sit and think about its early retirement -- maybe open a bakery? 358 // time to sit and think about its early retirement -- maybe open a bakery?
375 while (true) { 359 while (true) {
376 dma_channel_wait_for_finish_blocking(dma_chan); 360 dma_channel_wait_for_finish_blocking(dma_chan);
377 gpio_put(PICO_DEFAULT_LED_PIN, 1); 361 gpio_put(PICO_DEFAULT_LED_PIN, 1);