comparison modulator.c @ 5:2db42eaba3c8

WIP
author Daniel O'Connor <darius@dons.net.au>
date Sat, 15 Feb 2025 22:57:18 +1030
parents b10097c3383d
children 5bed069f0c33
comparison
equal deleted inserted replaced
4:b6416c4aadc8 5:2db42eaba3c8
15 ** 15 **
16 ** Create modulation shape 16 ** Create modulation shape
17 ** 17 **
18 */ 18 */
19 19
20 #include <stdio.h>
21 #include <string.h>
22 #include "bitstring.h"
23
24 #pragma GCC diagnostic push
25 #pragma GCC diagnostic ignored "-Wtype-limits"
26 #pragma GCC diagnostic ignored "-Wsign-compare"
20 #include "pico/stdlib.h" 27 #include "pico/stdlib.h"
21 #include "hardware/clocks.h" 28 #include "hardware/clocks.h"
22 #include "hardware/dma.h" 29 #include "hardware/dma.h"
30 #include "hardware/interp.h"
23 #include "hardware/irq.h" 31 #include "hardware/irq.h"
32 #include "hardware/pll.h"
24 #include "hardware/pio.h" 33 #include "hardware/pio.h"
34 #include "hardware/pwm.h"
35 #include "hardware/structs/pll.h"
36 #include "hardware/structs/clocks.h"
37 #pragma GCC diagnostic pop
25 38
26 #include "dac.pio.h" 39 #include "dac.pio.h"
27 40
28 #if 0 41 // https://github.com/howerj/q
29 extern void* shaped_trap_dat_size; 42 #include "q/q.h"
30 extern void* shaped_trap_dat_start; 43
31 44 #include "shaped-trap.h"
32 extern void* sgauss_dat_size;
33 extern void* sgauss_dat_start;
34 #endif
35
36 uint8_t shaped_trap_dat_start[] = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0 };
37 //uint8_t shaped_trap_dat_start[] = { 0, 1, 2, 3, 1, 2, 0, 3, 1, 1, 2, 3, 0, 1, 2, 3 };
38 #define SHAPED_TRAP_DAT_BITS 4
39 _Static_assert((1 << SHAPED_TRAP_DAT_BITS) == sizeof(shaped_trap_dat_start));
40 45
41 static int dma_chan; 46 static int dma_chan;
47 uint8_t pulse_data[65536];
48 uint8_t pulse_ctrl[65536];
42 49
43 void 50 void
44 dma_handler(void) { 51 dma_handler(void) {
45 // Clear the interrupt request. 52 // Clear the interrupt request.
46 dma_hw->ints0 = 1u << dma_chan; 53 dma_hw->ints0 = 1u << dma_chan;
47 // Give the channel a new wave table entry to read from, and re-trigger it 54 // Give the channel a new wave table entry to read from, and re-trigger it
48 dma_channel_set_read_addr(dma_chan, shaped_trap_dat_start, true); 55 //dma_channel_set_read_addr(dma_chan, shaped_trap_dat_start, true);
56 }
57
58 unsigned slice_num = 0;
59
60 void
61 pwm_wrap(void) {
62 pwm_clear_irq(slice_num);
63 #if 0
64 static unsigned state = 0;
65
66 gpio_put(PICO_DEFAULT_LED_PIN, state);
67 state = !state;
68 #endif
69
70 // Trigger another pulse read out
71 dma_channel_set_read_addr(dma_chan, pulse_data, true);
72 }
73
74 // Calculate pulse shape data
75 // TODO: sense, gate, phase, active, T/R switch
76 // Could encode them as bit stream like data but more compact would be
77 // (say) a list of counts to toggle pins at
78 #define SENSE 0x01
79 #define GATE 0x02
80 #define PHINV 0x04
81 #define PACTIVE 0x08
82
83 // Need to add pre/postgate/sense/phase counters
84 unsigned
85 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) {
86 uint32_t shapesamples, nsamples, idx;
87 q_t dcscale, stepsize;
88 char tmps[20];
89
90 dcscale = qdiv(qsub(qint(255), qint(dcofs)), qint(255));
91 qsprint(dcscale, tmps, sizeof(tmps));
92 printf("dcscale = %s\n", tmps);
93
94 if (ncode == 1) {
95 // Number of samples for half of the pulse
96 // Do division first so we don't overflow Q16.16
97 shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2)));
98 // Number of samples for everything
99 nsamples = shapesamples * 2 + slew1 + slew2;
100 } else {
101 shapesamples = plen / 2;
102 nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2;
103 }
104
105 // Number of samples per step in the pulse shape
106 stepsize = qdiv(qint(shapesamples), qint(shapelen));
107 qsprint(stepsize, tmps, sizeof(tmps));
108 printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps);
109
110 if (nsamples > datalen) {
111 printf("Pulse too long (%ld > %u)\n", nsamples, datalen);
112 return 0;
113 }
114 if (shapesamples < 2) {
115 printf("Pulse too short (%lu < %d)\n", shapesamples, 2);
116 return 0;
117 }
118 if (qtoi(shapesamples) > 65535) {
119 printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535);
120 return 0;
121 }
122 idx = 0;
123
124 // Up slew
125 for (uint16_t i = 0; i < slew1; i++) {
126 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1)));
127 ctrl[idx] |= PACTIVE;
128 }
129 for (uint16_t c = 0; c < ncode; c++) {
130 uint ctrltmp = PACTIVE;
131 if (code[c] == '0')
132 ctrltmp |= PHINV;
133
134 // Pulse up
135 for (uint16_t i = 0; i < shapesamples; i++) {
136 data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(i), stepsize))]), dcscale));
137 ctrl[idx] = ctrltmp;
138 }
139 // Pulse down
140 for (uint16_t i = 0; i < shapesamples; i++) {
141 data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(shapesamples - i - 1), stepsize))]), dcscale));
142 // Could replace this with a separate loop to poke it into place
143 // Similarly for TR switch when implemented
144 if (i == 0 && c == 0)
145 ctrl[idx] = ctrltmp | SENSE;
146 else
147 ctrl[idx] = ctrltmp;
148 }
149
150 // Code gap
151 if (c < ncode - 1)
152 for (uint16_t i = 0; i < codegap; i++) {
153 data[idx++] = dcofs;
154 ctrl[idx] = ctrltmp;
155 }
156 }
157
158 // Down slew
159 for (uint16_t i = 0; i < slew2; i++) {
160 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2)));
161 ctrl[idx] |= PACTIVE;
162 }
163 return idx - 1;
164 }
165
166 // As per hello_interp.c
167 void interp_test(uint8_t *data, const uint8_t *shape) {
168 interp_config cfg;
169 const int uv_fractional_bits = 12;
170
171 // Step size is 0.25 (ie stretching by 4x)
172 unsigned step = (1 << uv_fractional_bits) / 4;
173
174 puts("interp_test\n");
175 // Setup lane 0 to generate index into shape table
176 cfg = interp_default_config();
177 interp_config_set_shift(&cfg, uv_fractional_bits);
178 interp_config_set_mask(&cfg, 0, 32 - uv_fractional_bits);
179 interp_config_set_blend(&cfg, true);
180 interp_set_config(interp0, 0, &cfg);
181
182 // Setup lane 1 to LERP each sample pair
183 // shift XXXX XXXX XXXX XXXX XXXX FFFF FFFF FFFF (accum 0 via cross input)
184 // to 0000 XXXX XXXX XXXX XXXX FFFF FFFF FFFF
185 cfg = interp_default_config();
186 interp_config_set_shift(&cfg, uv_fractional_bits - 8);
187 interp_config_set_signed(&cfg, false);
188 interp_config_set_cross_input(&cfg, true); // unsigned blending
189 interp_set_config(interp0, 1, &cfg);
190
191 interp0->accum[0] = 0; // Initial offset into shape table
192 interp0->base[2] = (uintptr_t)shape; // Start of shape table
193 for (unsigned i = 0; i < 122 * 4; i++) {
194 // Get sample pair
195 uint8_t *sample_pair = (uint8_t *) interp0->peek[2];
196 // Ask lane 1 for a LERP, using the lane 0 accumulator
197 interp0->base[0] = sample_pair[0];
198 interp0->base[1] = sample_pair[1];
199 uint32_t peek1 = interp0->peek[1];
200 uint32_t add_raw1 = interp0->add_raw[1];
201 data[i] = peek1;
202 printf("%d\t(%lu%% between %d and %d) %lu\n",
203 (int)peek1,
204 100 * (add_raw1 & 0xff) / 0xff,
205 sample_pair[0], sample_pair[1],
206 interp_get_accumulator(interp0, 0));
207 interp0->add_raw[0] = step;
208 }
209 puts("done");
49 } 210 }
50 211
51 int 212 int
52 main(void) { 213 main(void) {
53 int i; 214 absolute_time_t then, now;
54 const uint LED_PIN = PICO_DEFAULT_LED_PIN; 215
216 // Set sysclk to 120MHz
217 set_sys_clock_khz(120000, true);
55 218
56 stdio_init_all(); 219 stdio_init_all();
57 220 printf("\n\n\nIniting\n");
58 gpio_init(LED_PIN); 221
59 gpio_set_dir(LED_PIN, GPIO_OUT); 222 // Needed otherwise timer related functions hang under debugging
223 // https://github.com/raspberrypi/pico-sdk/issues/1152#issuecomment-1418248639
224 timer_hw->dbgpause = 0;
225
226 gpio_init(PICO_DEFAULT_LED_PIN);
227 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
228
229 interp_config cfg = interp_default_config();
230 interp_config_set_blend(&cfg, true);
231 interp_set_config(interp0, 0, &cfg);
232
233 cfg = interp_default_config();
234 interp_set_config(interp0, 1, &cfg);
235
236 interp_test(pulse_data, shaped_trap);
237
238 uint32_t idx;
239 uint16_t plen = 4000;
240 char *code = "1110010";
241 //char *code = "10";
242 uint8_t codegap = 4;
243 uint8_t slew1 = 10;
244 uint8_t slew2 = 10;
245 uint8_t dcofs = 110;
246 memset(pulse_data, 0, sizeof(pulse_data));
247 memset(pulse_ctrl, 0, sizeof(pulse_ctrl));
248 then = get_absolute_time();
249 if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data),
250 plen, code, strlen(code),
251 shaped_trap, sizeof(shaped_trap),
252 codegap, slew1, slew2, dcofs)) == 0) {
253 printf("Failed to compute pulse\n");
254 while (1)
255 ;
256 }
257 now = get_absolute_time();
258 printf("Pulse computation took %lld usec and created %lu samples\n", absolute_time_diff_us(then, now), idx);
259
260 pwm_config c = pwm_get_default_config();
261 // 120MHz / 250 = 480kHz base
262 // Maximum divisor is only 256 which limits the low end,
263 // could further subdivide in the IRQ handler
264 pwm_config_set_clkdiv_int(&c, 250);
265 // 8Hz
266 pwm_config_set_wrap(&c, 60000 - 1);
267 pwm_init(slice_num, &c, true);
268 pwm_clear_irq(slice_num);
269 pwm_set_irq_enabled(slice_num, true);
270 irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap);
271 irq_set_enabled(PWM_IRQ_WRAP, true);
272 pwm_init(slice_num, &c, true);
60 273
61 // Load the clocked_input program, and configure a free state machine 274 // Load the clocked_input program, and configure a free state machine
62 // to run the program. 275 // to run the program.
63 PIO pio = pio0; 276 PIO pio = pio0;
64 uint offset = pio_add_program(pio, &dac_program); 277 uint offset = pio_add_program(pio, &dac_program);
65 uint sm = pio_claim_unused_sm(pio, true); 278 uint sm = pio_claim_unused_sm(pio, true);
66 // XXX: I would prefer starting at GPIO16 but in that case the top 2 279 // XXX: I would prefer starting at GPIO16 but in that case the top 2
67 // bits don't seem to work 280 // bits don't seem to work
68 dac_program_init(pio, sm, offset, 14); 281 // Clock divisor of 2 so it runs at 60MHz and
69 282 // generates a 30MHz clock
70 // Configure a channel to write the same word (32 bits) repeatedly to PIO0 283 dac_program_init(pio, sm, offset, 14, 2);
284
285 // Configure a channel to write the same word (8 bits) repeatedly to PIO0
71 // SM0's TX FIFO, paced by the data request signal from that peripheral. 286 // SM0's TX FIFO, paced by the data request signal from that peripheral.
72 dma_chan = dma_claim_unused_channel(true); 287 dma_chan = dma_claim_unused_channel(true);
73 dma_channel_config dmac = dma_channel_get_default_config(dma_chan); 288 dma_channel_config dmac = dma_channel_get_default_config(dma_chan);
289 // XXX: need to get pulse generator to pad to 32 bits
74 channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32); 290 channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32);
75 channel_config_set_read_increment(&dmac, true); 291 channel_config_set_read_increment(&dmac, true);
76 channel_config_set_ring(&dmac, false, SHAPED_TRAP_DAT_BITS);
77 channel_config_set_dreq(&dmac, DREQ_PIO0_TX0); 292 channel_config_set_dreq(&dmac, DREQ_PIO0_TX0);
78 293
79 dma_channel_configure( 294 dma_channel_configure(
80 dma_chan, 295 dma_chan,
81 &dmac, 296 &dmac,
82 &pio0_hw->txf[0], // Write address (only need to set this once) 297 &pio0_hw->txf[0], // Write address (only need to set this once)
83 NULL, // Don't provide a read address yet 298 NULL, // Don't provide a read address yet
84 100, // Write this many times then interrupt 299 idx >> 2, // Transfer count
85 false // Don't start yet 300 false // Don't start yet
86 ); 301 );
87 302
88 // Tell the DMA to raise IRQ line 0 when the channel finishes a block 303 // Tell the DMA to raise IRQ line 0 when the channel finishes a block
89 dma_channel_set_irq0_enabled(dma_chan, true); 304 dma_channel_set_irq0_enabled(dma_chan, true);
90 305
91 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted 306 // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
92 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); 307 irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
93 irq_set_enabled(DMA_IRQ_0, true); 308 irq_set_enabled(DMA_IRQ_0, true);
94 309
95 // Manually call the handler once, to trigger the first transfer
96 dma_handler();
97
98 // Everything else from this point is interrupt-driven. The processor has 310 // Everything else from this point is interrupt-driven. The processor has
99 // time to sit and think about its early retirement -- maybe open a bakery? 311 // time to sit and think about its early retirement -- maybe open a bakery?
100 while (true) { 312 while (true) {
101 #if 0 313 dma_channel_wait_for_finish_blocking(dma_chan);
102 for (int i = 0; i < sizeof(shaped_trap_dat_start) / 4; i++) { 314 gpio_put(PICO_DEFAULT_LED_PIN, 1);
103 pio_sm_put_blocking(pio, sm, ((uint32_t *)shaped_trap_dat_start)[i]);
104 }
105 #endif
106 #if 0
107 gpio_put(LED_PIN, 1);
108 sleep_ms(100); 315 sleep_ms(100);
109 gpio_put(LED_PIN, 0); 316 gpio_put(PICO_DEFAULT_LED_PIN, 0);
110 #endif 317 sleep_ms(100);
111 } 318 }
112 } 319
320 __breakpoint();
321 }