Mercurial > ~darius > hgwebdir.cgi > modulator
comparison modulator.c @ 8:0249d0cecac4
Use interpolator to compute pulse up.
Use interpolator to clamp values.
Calculate speed.
Do memory clear in routine to be more honest about the speed.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sun, 16 Feb 2025 14:36:33 +1030 |
parents | 4ad473648949 |
children | 3acdebd7eec7 |
comparison
equal
deleted
inserted
replaced
7:4ad473648949 | 8:0249d0cecac4 |
---|---|
71 // Trigger another pulse read out | 71 // Trigger another pulse read out |
72 dma_channel_set_read_addr(dma_chan, pulse_data, true); | 72 dma_channel_set_read_addr(dma_chan, pulse_data, true); |
73 } | 73 } |
74 | 74 |
75 // Calculate pulse shape data | 75 // Calculate pulse shape data |
76 // TODO: sense, gate, phase, active, T/R switch | 76 // TODO: predistortion, proper sense, gate, phase, active, T/R switch |
77 // Could encode them as bit stream like data but more compact would be | 77 // Could encode them as bit stream like data but more compact would be |
78 // (say) a list of counts to toggle pins at | 78 // (say) a list of counts to toggle pins at |
79 #define SENSE 0x01 | 79 #define SENSE 0x01 |
80 #define GATE 0x02 | 80 #define GATE 0x02 |
81 #define PHINV 0x04 | 81 #define PHINV 0x04 |
87 uint32_t shapesamples, nsamples, idx, bit1startup, bit1stopup; | 87 uint32_t shapesamples, nsamples, idx, bit1startup, bit1stopup; |
88 q_t dcscale, stepsize; | 88 q_t dcscale, stepsize; |
89 char tmps[20]; | 89 char tmps[20]; |
90 interp_config cfg; | 90 interp_config cfg; |
91 | 91 |
92 // Setup lane 0 to generate index into shape table | |
93 // Mask start is 0 because we use 8 bit samples | |
94 cfg = interp_default_config(); | |
95 interp_config_set_shift(&cfg, QBITS); | |
96 interp_config_set_mask(&cfg, 0, 32 - QBITS); | |
97 interp_config_set_blend(&cfg, true); | |
98 interp_set_config(interp0, 0, &cfg); | |
99 | |
100 // Setup lane 1 to LERP each sample pair | |
101 cfg = interp_default_config(); | |
102 interp_config_set_shift(&cfg, QBITS - 8); | |
103 interp_config_set_signed(&cfg, false); | |
104 interp_config_set_cross_input(&cfg, true); // unsigned blending | |
105 interp_set_config(interp0, 1, &cfg); | |
106 | |
107 interp0->accum[0] = 0; // Initial offset into shape table | |
108 interp0->base[2] = (uintptr_t)shape; // Start of shape table | |
109 | |
110 dcscale = qdiv(qsub(qint(255), qint(dcofs)), qint(255)); | |
111 qsprint(dcscale, tmps, sizeof(tmps)); | |
112 printf("dcscale = %s\n", tmps); | |
113 | |
114 if (ncode == 1) { | 92 if (ncode == 1) { |
115 // Number of samples for half of the pulse | 93 // Number of samples for half of the pulse |
116 // Do division first so we don't overflow Q16.16 | 94 // Do division first so we don't overflow Q16.16 |
117 shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2))); | 95 shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2))); |
118 // Number of samples for everything | 96 // Number of samples for everything |
119 nsamples = shapesamples * 2 + slew1 + slew2; | 97 // XXX: Need the +1 otherwise slew2 is truncated |
98 nsamples = shapesamples * 2 + slew1 + slew2 + 1; | |
120 } else { | 99 } else { |
121 shapesamples = plen / 2; | 100 shapesamples = plen / 2; |
122 nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2; | 101 nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2 + 1; |
123 } | 102 } |
124 | 103 |
125 // Number of samples per step in the pulse shape | 104 // Number of steps per samples in the pulse shape |
126 stepsize = qdiv(qint(shapesamples), qint(shapelen)); | 105 stepsize = qdiv(qint(shapelen), qint(shapesamples)); |
127 qsprint(stepsize, tmps, sizeof(tmps)); | 106 qsprint(stepsize, tmps, sizeof(tmps)); |
128 printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps); | 107 printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps); |
129 | 108 |
109 // Check the requested pulse will not overflow given data | |
130 if (nsamples > datalen) { | 110 if (nsamples > datalen) { |
131 printf("Pulse too long (%ld > %u)\n", nsamples, datalen); | 111 printf("Pulse too long (%ld > %u)\n", nsamples, datalen); |
132 return 0; | 112 return 0; |
133 } | 113 } |
114 // Check it is not too short | |
134 if (shapesamples < 2) { | 115 if (shapesamples < 2) { |
135 printf("Pulse too short (%lu < %d)\n", shapesamples, 2); | 116 printf("Pulse too short (%lu < %d)\n", shapesamples, 2); |
136 return 0; | 117 return 0; |
137 } | 118 } |
119 // Or too long (will overflow for loop variable) | |
138 if (qtoi(shapesamples) > 65535) { | 120 if (qtoi(shapesamples) > 65535) { |
139 printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535); | 121 printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535); |
140 return 0; | 122 return 0; |
141 } | 123 } |
124 | |
125 // Setup interp 0 lane 0 to generate index into shape table | |
126 // Mask start is 0 because we use 8 bit samples | |
127 cfg = interp_default_config(); | |
128 interp_config_set_shift(&cfg, QBITS); | |
129 interp_config_set_mask(&cfg, 0, 32 - QBITS); | |
130 interp_config_set_blend(&cfg, true); | |
131 interp_set_config(interp0, 0, &cfg); | |
132 | |
133 // Setup interp 0 lane 1 to LERP each sample pair | |
134 cfg = interp_default_config(); | |
135 interp_config_set_shift(&cfg, QBITS - 8); | |
136 interp_config_set_signed(&cfg, false); | |
137 interp_config_set_cross_input(&cfg, true); // unsigned blending | |
138 interp_set_config(interp0, 1, &cfg); | |
139 | |
140 // Setup interp 1 lane 0 to clamp 0-255 | |
141 cfg = interp_default_config(); | |
142 interp_config_set_clamp(&cfg, true); | |
143 interp_config_set_shift(&cfg, 0); | |
144 interp_config_set_mask(&cfg, 0, 8); | |
145 interp_config_set_signed(&cfg, false); | |
146 interp_set_config(interp1, 0, &cfg); | |
147 interp1->base[0] = 0; | |
148 interp1->base[1] = 255; | |
149 | |
150 interp0->accum[0] = 0; // Initial offset into shape table | |
151 interp0->base[2] = (uintptr_t)shape; // Start of shape table | |
152 | |
153 dcscale = qdiv(qsub(qint(256), qint(dcofs)), qint(255)); | |
154 qsprint(dcscale, tmps, sizeof(tmps)); | |
155 printf("dcscale = %s\n", tmps); | |
156 | |
157 memset(pulse_data, 0, sizeof(pulse_data)); | |
158 memset(pulse_ctrl, 0, sizeof(pulse_ctrl)); | |
142 idx = 0; | 159 idx = 0; |
143 | 160 |
144 // Up slew | 161 // Up slew |
145 for (uint16_t i = 0; i < slew1; i++) { | 162 for (uint16_t i = 0; i < slew1; i++) { |
146 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1))); | 163 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1))); |
153 uint ctrltmp = PACTIVE; | 170 uint ctrltmp = PACTIVE; |
154 if (code[c] == '0') | 171 if (code[c] == '0') |
155 ctrltmp |= PHINV; | 172 ctrltmp |= PHINV; |
156 | 173 |
157 // Pulse up | 174 // Pulse up |
175 if (c == 0) { | |
176 interp0->accum[0] = 0; // Initial offset into shape table | |
177 interp0->base[2] = (uintptr_t)shape; // Start of shape table | |
178 } | |
158 for (uint16_t i = 0; i < shapesamples; i++) { | 179 for (uint16_t i = 0; i < shapesamples; i++) { |
159 if (c == 0) | 180 if (c == 0) { |
160 data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(i), stepsize))]), dcscale)); | 181 // Get sample pair |
161 else | 182 uint8_t *sample_pair = (uint8_t *) interp0->peek[2]; |
183 // Ask lane 1 for a LERP, using the lane 0 accumulator | |
184 interp0->base[0] = sample_pair[0]; | |
185 interp0->base[1] = sample_pair[1]; | |
186 uint8_t peek = interp0->peek[1]; | |
187 // Apply DC offset scaling & clamp | |
188 interp1->accum[0] = dcofs + qtoi(qmul(qint(peek), dcscale)); | |
189 data[idx++] = interp1->peek[0]; | |
190 // Update interpolator for next point | |
191 interp0->add_raw[0] = stepsize; | |
192 } else | |
162 // Already done it before, just copy the previous instance | 193 // Already done it before, just copy the previous instance |
163 data[idx++] = data[bit1startup + i]; | 194 data[idx++] = data[bit1startup + i]; |
164 ctrl[idx] = ctrltmp; | 195 ctrl[idx] = ctrltmp; |
165 } | 196 } |
166 if (c == 0) | 197 if (c == 0) |
167 bit1stopup = idx - 1; | 198 bit1stopup = idx - 1; |
168 // Pulse down | 199 // Pulse down |
169 // Since the pulse is symmetrical just copy the up slope in reverse | 200 // Since the pulse is symmetrical just copy the up slope in reverse |
201 // 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 | |
170 for (uint16_t i = 0; i < shapesamples; i++) { | 204 for (uint16_t i = 0; i < shapesamples; i++) { |
171 data[idx++] = data[bit1stopup - i]; | 205 data[idx++] = data[bit1stopup - i]; |
172 // Could replace this with a separate loop to poke it into place | 206 // Could replace this with a separate loop to poke it into place |
173 // Similarly for TR switch when implemented | 207 // Similarly for TR switch when implemented |
174 if (i == 0 && c == 0) | 208 if (i == 0 && c == 0) |
184 ctrl[idx] = ctrltmp; | 218 ctrl[idx] = ctrltmp; |
185 } | 219 } |
186 } | 220 } |
187 | 221 |
188 // Down slew | 222 // Down slew |
189 for (uint16_t i = 0; i < slew2; i++) { | 223 for (uint16_t i = 0; i < slew2 + 1; i++) { |
190 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); | 224 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); |
191 ctrl[idx] |= PACTIVE; | 225 ctrl[idx] |= PACTIVE; |
192 } | 226 } |
193 return idx - 1; | 227 return idx - 1; |
194 } | 228 } |
256 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); | 290 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); |
257 | 291 |
258 //interp_test(pulse_data, shaped_trap); | 292 //interp_test(pulse_data, shaped_trap); |
259 | 293 |
260 uint32_t idx; | 294 uint32_t idx; |
261 uint16_t plen = 4000; | 295 uint16_t plen; |
262 char *code = "1110010"; | 296 char *code; |
263 //char *code = "10"; | 297 if (0) { |
298 plen = 8000; | |
299 code = "1110010"; | |
300 } else { | |
301 plen = 53000; | |
302 code = "1"; | |
303 } | |
304 | |
264 uint8_t codegap = 4; | 305 uint8_t codegap = 4; |
265 uint8_t slew1 = 10; | 306 uint8_t slew1 = 10; |
266 uint8_t slew2 = 10; | 307 uint8_t slew2 = 10; |
267 uint8_t dcofs = 110; | 308 uint8_t dcofs = 110; |
268 memset(pulse_data, 0, sizeof(pulse_data)); | |
269 memset(pulse_ctrl, 0, sizeof(pulse_ctrl)); | |
270 then = get_absolute_time(); | 309 then = get_absolute_time(); |
271 if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data), | 310 if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data), |
272 plen, code, strlen(code), | 311 plen, code, strlen(code), |
273 shaped_trap, sizeof(shaped_trap), | 312 shaped_trap, sizeof(shaped_trap), |
274 codegap, slew1, slew2, dcofs)) == 0) { | 313 codegap, slew1, slew2, dcofs)) == 0) { |
275 printf("Failed to compute pulse\n"); | 314 printf("Failed to compute pulse\n"); |
276 while (1) | 315 while (1) |
277 ; | 316 ; |
278 } | 317 } |
279 now = get_absolute_time(); | 318 now = get_absolute_time(); |
280 printf("Pulse computation took %lld usec and created %lu samples\n", absolute_time_diff_us(then, now), idx); | 319 unsigned long long diff = absolute_time_diff_us(then, now); |
281 | 320 printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n", |
321 diff, idx, (float)diff * 1000.0 / idx); | |
322 __breakpoint(); | |
282 pwm_config c = pwm_get_default_config(); | 323 pwm_config c = pwm_get_default_config(); |
283 // 120MHz / 250 = 480kHz base | 324 // 120MHz / 250 = 480kHz base |
284 // Maximum divisor is only 256 which limits the low end, | 325 // Maximum divisor is only 256 which limits the low end, |
285 // could further subdivide in the IRQ handler | 326 // could further subdivide in the IRQ handler |
286 pwm_config_set_clkdiv_int(&c, 250); | 327 pwm_config_set_clkdiv_int(&c, 250); |