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