Mercurial > ~darius > hgwebdir.cgi > modulator
changeset 5:2db42eaba3c8
WIP
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sat, 15 Feb 2025 22:57:18 +1030 |
parents | b6416c4aadc8 |
children | 5bed069f0c33 |
files | CMakeLists.txt dac.pio modulator.c pico_sdk_import.cmake test.c |
diffstat | 5 files changed, 326 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Tue Feb 04 14:20:07 2025 +1030 +++ b/CMakeLists.txt Sat Feb 15 22:57:18 2025 +1030 @@ -16,15 +16,25 @@ add_executable(${NAME} modulator.c -) + q/q.c + ) + +# Bring the pain +set_source_files_properties(modulator.c PROPERTIES COMPILE_OPTIONS -Wall -Wextra -Werror) +set_source_files_properties(q/q.c PROPERTIES COMPILE_OPTIONS -DQVERSION=0x902) +# XXX: this didn't work - everything got the flags +#target_compile_options(${NAME} PRIVATE -Wall -Wextra -Werror) pico_generate_pio_header(${NAME} ${CMAKE_CURRENT_LIST_DIR}/dac.pio) target_link_libraries(${NAME} pico_stdlib + hardware_clocks hardware_dma + hardware_interp hardware_irq hardware_pio + hardware_pwm ) #bin2elf(${NAME} shaped-trap.dat)
--- a/dac.pio Tue Feb 04 14:20:07 2025 +1030 +++ b/dac.pio Sat Feb 15 22:57:18 2025 +1030 @@ -19,7 +19,7 @@ .wrap % c-sdk { -static inline void dac_program_init(PIO pio, uint sm, uint offset, uint pin) { +static inline void dac_program_init(PIO pio, uint sm, uint offset, uint pin, uint clkdiv) { pio_sm_config c = dac_program_get_default_config(offset); // Set the OUT base pin to the provided `pin` parameter. @@ -35,7 +35,7 @@ &c, true, // Shift-to-right false, // Autopull enabled - 8 // Autopull threshold (bits!) + 32 // Autopull threshold (bits!) ); // Configure clock as sideset pin @@ -44,7 +44,7 @@ // We only send, so disable the RX FIFO to make the TX FIFO deeper. sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); -// sm_config_set_clkdiv(&c, 100); + sm_config_set_clkdiv(&c, clkdiv); // Load our configuration, and start the program from the beginning pio_sm_init(pio, sm, offset, &c);
--- a/modulator.c Tue Feb 04 14:20:07 2025 +1030 +++ b/modulator.c Sat Feb 15 22:57:18 2025 +1030 @@ -17,46 +17,259 @@ ** */ +#include <stdio.h> +#include <string.h> +#include "bitstring.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wsign-compare" #include "pico/stdlib.h" #include "hardware/clocks.h" #include "hardware/dma.h" +#include "hardware/interp.h" #include "hardware/irq.h" +#include "hardware/pll.h" #include "hardware/pio.h" +#include "hardware/pwm.h" +#include "hardware/structs/pll.h" +#include "hardware/structs/clocks.h" +#pragma GCC diagnostic pop #include "dac.pio.h" -#if 0 -extern void* shaped_trap_dat_size; -extern void* shaped_trap_dat_start; +// https://github.com/howerj/q +#include "q/q.h" -extern void* sgauss_dat_size; -extern void* sgauss_dat_start; -#endif - -uint8_t shaped_trap_dat_start[] = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0 }; -//uint8_t shaped_trap_dat_start[] = { 0, 1, 2, 3, 1, 2, 0, 3, 1, 1, 2, 3, 0, 1, 2, 3 }; -#define SHAPED_TRAP_DAT_BITS 4 -_Static_assert((1 << SHAPED_TRAP_DAT_BITS) == sizeof(shaped_trap_dat_start)); +#include "shaped-trap.h" static int dma_chan; +uint8_t pulse_data[65536]; +uint8_t pulse_ctrl[65536]; void dma_handler(void) { // Clear the interrupt request. dma_hw->ints0 = 1u << dma_chan; // Give the channel a new wave table entry to read from, and re-trigger it - dma_channel_set_read_addr(dma_chan, shaped_trap_dat_start, true); + //dma_channel_set_read_addr(dma_chan, shaped_trap_dat_start, true); +} + +unsigned slice_num = 0; + +void +pwm_wrap(void) { + pwm_clear_irq(slice_num); +#if 0 + static unsigned state = 0; + + gpio_put(PICO_DEFAULT_LED_PIN, state); + state = !state; +#endif + + // Trigger another pulse read out + dma_channel_set_read_addr(dma_chan, pulse_data, true); +} + +// Calculate pulse shape data +// TODO: sense, gate, phase, active, T/R switch +// Could encode them as bit stream like data but more compact would be +// (say) a list of counts to toggle pins at +#define SENSE 0x01 +#define GATE 0x02 +#define PHINV 0x04 +#define PACTIVE 0x08 + +// Need to add pre/postgate/sense/phase counters +unsigned +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) { + uint32_t shapesamples, nsamples, idx; + q_t dcscale, stepsize; + char tmps[20]; + + dcscale = qdiv(qsub(qint(255), qint(dcofs)), qint(255)); + qsprint(dcscale, tmps, sizeof(tmps)); + printf("dcscale = %s\n", tmps); + + if (ncode == 1) { + // Number of samples for half of the pulse + // Do division first so we don't overflow Q16.16 + shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2))); + // Number of samples for everything + nsamples = shapesamples * 2 + slew1 + slew2; + } else { + shapesamples = plen / 2; + nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2; + } + + // Number of samples per step in the pulse shape + stepsize = qdiv(qint(shapesamples), qint(shapelen)); + qsprint(stepsize, tmps, sizeof(tmps)); + printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps); + + if (nsamples > datalen) { + printf("Pulse too long (%ld > %u)\n", nsamples, datalen); + return 0; + } + if (shapesamples < 2) { + printf("Pulse too short (%lu < %d)\n", shapesamples, 2); + return 0; + } + if (qtoi(shapesamples) > 65535) { + printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535); + return 0; + } + idx = 0; + + // Up slew + for (uint16_t i = 0; i < slew1; i++) { + data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1))); + ctrl[idx] |= PACTIVE; + } + for (uint16_t c = 0; c < ncode; c++) { + uint ctrltmp = PACTIVE; + if (code[c] == '0') + ctrltmp |= PHINV; + + // Pulse up + for (uint16_t i = 0; i < shapesamples; i++) { + data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(i), stepsize))]), dcscale)); + ctrl[idx] = ctrltmp; + } + // Pulse down + for (uint16_t i = 0; i < shapesamples; i++) { + data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(shapesamples - i - 1), stepsize))]), dcscale)); + // Could replace this with a separate loop to poke it into place + // Similarly for TR switch when implemented + if (i == 0 && c == 0) + ctrl[idx] = ctrltmp | SENSE; + else + ctrl[idx] = ctrltmp; + } + + // Code gap + if (c < ncode - 1) + for (uint16_t i = 0; i < codegap; i++) { + data[idx++] = dcofs; + ctrl[idx] = ctrltmp; + } + } + + // Down slew + for (uint16_t i = 0; i < slew2; i++) { + data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); + ctrl[idx] |= PACTIVE; + } + return idx - 1; +} + +// As per hello_interp.c +void interp_test(uint8_t *data, const uint8_t *shape) { + interp_config cfg; + const int uv_fractional_bits = 12; + + // Step size is 0.25 (ie stretching by 4x) + unsigned step = (1 << uv_fractional_bits) / 4; + + puts("interp_test\n"); + // Setup lane 0 to generate index into shape table + cfg = interp_default_config(); + interp_config_set_shift(&cfg, uv_fractional_bits); + interp_config_set_mask(&cfg, 0, 32 - uv_fractional_bits); + interp_config_set_blend(&cfg, true); + interp_set_config(interp0, 0, &cfg); + + // Setup lane 1 to LERP each sample pair + // shift XXXX XXXX XXXX XXXX XXXX FFFF FFFF FFFF (accum 0 via cross input) + // to 0000 XXXX XXXX XXXX XXXX FFFF FFFF FFFF + cfg = interp_default_config(); + interp_config_set_shift(&cfg, uv_fractional_bits - 8); + interp_config_set_signed(&cfg, false); + interp_config_set_cross_input(&cfg, true); // unsigned blending + interp_set_config(interp0, 1, &cfg); + + interp0->accum[0] = 0; // Initial offset into shape table + interp0->base[2] = (uintptr_t)shape; // Start of shape table + for (unsigned i = 0; i < 122 * 4; i++) { + // Get sample pair + uint8_t *sample_pair = (uint8_t *) interp0->peek[2]; + // Ask lane 1 for a LERP, using the lane 0 accumulator + interp0->base[0] = sample_pair[0]; + interp0->base[1] = sample_pair[1]; + uint32_t peek1 = interp0->peek[1]; + uint32_t add_raw1 = interp0->add_raw[1]; + data[i] = peek1; + printf("%d\t(%lu%% between %d and %d) %lu\n", + (int)peek1, + 100 * (add_raw1 & 0xff) / 0xff, + sample_pair[0], sample_pair[1], + interp_get_accumulator(interp0, 0)); + interp0->add_raw[0] = step; + } + puts("done"); } int main(void) { - int i; - const uint LED_PIN = PICO_DEFAULT_LED_PIN; + absolute_time_t then, now; + + // Set sysclk to 120MHz + set_sys_clock_khz(120000, true); stdio_init_all(); + printf("\n\n\nIniting\n"); - gpio_init(LED_PIN); - gpio_set_dir(LED_PIN, GPIO_OUT); + // Needed otherwise timer related functions hang under debugging + // https://github.com/raspberrypi/pico-sdk/issues/1152#issuecomment-1418248639 + timer_hw->dbgpause = 0; + + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + interp_config cfg = interp_default_config(); + interp_config_set_blend(&cfg, true); + interp_set_config(interp0, 0, &cfg); + + cfg = interp_default_config(); + interp_set_config(interp0, 1, &cfg); + + interp_test(pulse_data, shaped_trap); + + uint32_t idx; + uint16_t plen = 4000; + char *code = "1110010"; + //char *code = "10"; + uint8_t codegap = 4; + uint8_t slew1 = 10; + uint8_t slew2 = 10; + uint8_t dcofs = 110; + memset(pulse_data, 0, sizeof(pulse_data)); + memset(pulse_ctrl, 0, sizeof(pulse_ctrl)); + then = get_absolute_time(); + if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data), + plen, code, strlen(code), + shaped_trap, sizeof(shaped_trap), + codegap, slew1, slew2, dcofs)) == 0) { + printf("Failed to compute pulse\n"); + while (1) + ; + } + now = get_absolute_time(); + printf("Pulse computation took %lld usec and created %lu samples\n", absolute_time_diff_us(then, now), idx); + + pwm_config c = pwm_get_default_config(); + // 120MHz / 250 = 480kHz base + // Maximum divisor is only 256 which limits the low end, + // could further subdivide in the IRQ handler + pwm_config_set_clkdiv_int(&c, 250); + // 8Hz + pwm_config_set_wrap(&c, 60000 - 1); + pwm_init(slice_num, &c, true); + pwm_clear_irq(slice_num); + pwm_set_irq_enabled(slice_num, true); + irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_wrap); + irq_set_enabled(PWM_IRQ_WRAP, true); + pwm_init(slice_num, &c, true); // Load the clocked_input program, and configure a free state machine // to run the program. @@ -65,15 +278,17 @@ uint sm = pio_claim_unused_sm(pio, true); // XXX: I would prefer starting at GPIO16 but in that case the top 2 // bits don't seem to work - dac_program_init(pio, sm, offset, 14); + // Clock divisor of 2 so it runs at 60MHz and + // generates a 30MHz clock + dac_program_init(pio, sm, offset, 14, 2); -// Configure a channel to write the same word (32 bits) repeatedly to PIO0 + // Configure a channel to write the same word (8 bits) repeatedly to PIO0 // SM0's TX FIFO, paced by the data request signal from that peripheral. dma_chan = dma_claim_unused_channel(true); dma_channel_config dmac = dma_channel_get_default_config(dma_chan); + // XXX: need to get pulse generator to pad to 32 bits channel_config_set_transfer_data_size(&dmac, DMA_SIZE_32); channel_config_set_read_increment(&dmac, true); - channel_config_set_ring(&dmac, false, SHAPED_TRAP_DAT_BITS); channel_config_set_dreq(&dmac, DREQ_PIO0_TX0); dma_channel_configure( @@ -81,7 +296,7 @@ &dmac, &pio0_hw->txf[0], // Write address (only need to set this once) NULL, // Don't provide a read address yet - 100, // Write this many times then interrupt + idx >> 2, // Transfer count false // Don't start yet ); @@ -92,21 +307,15 @@ irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); irq_set_enabled(DMA_IRQ_0, true); - // Manually call the handler once, to trigger the first transfer - dma_handler(); - // Everything else from this point is interrupt-driven. The processor has // time to sit and think about its early retirement -- maybe open a bakery? while (true) { -#if 0 - for (int i = 0; i < sizeof(shaped_trap_dat_start) / 4; i++) { - pio_sm_put_blocking(pio, sm, ((uint32_t *)shaped_trap_dat_start)[i]); - } -#endif -#if 0 - gpio_put(LED_PIN, 1); + dma_channel_wait_for_finish_blocking(dma_chan); + gpio_put(PICO_DEFAULT_LED_PIN, 1); + sleep_ms(100); + gpio_put(PICO_DEFAULT_LED_PIN, 0); sleep_ms(100); - gpio_put(LED_PIN, 0); -#endif } + + __breakpoint(); }
--- a/pico_sdk_import.cmake Tue Feb 04 14:20:07 2025 +1030 +++ b/pico_sdk_import.cmake Sat Feb 15 22:57:18 2025 +1030 @@ -29,11 +29,22 @@ if (PICO_SDK_FETCH_FROM_GIT_PATH) get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") endif () - FetchContent_Declare( - pico_sdk - GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk - GIT_TAG master - ) + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + if (NOT pico_sdk) message("Downloading Raspberry Pi Pico SDK") FetchContent_Populate(pico_sdk)
--- a/test.c Tue Feb 04 14:20:07 2025 +1030 +++ b/test.c Sat Feb 15 22:57:18 2025 +1030 @@ -4,30 +4,28 @@ #include <stdlib.h> #include <string.h> +#if 1 #include <q.h> +#else +// We want Q24.8 - Q16.16 is too small for sample calculations +#define FPT_WBITS 24 +#include "fptc-lib/src/fptc.h" +#define q_t fpt +#define qmul fpt_mul +#define qdiv fpt_div +#define qtoi fpt2i +#define qint i2fpt +#define qsub(x, y) ((x) - (y)) +#define qsprint(n, s, len) fpt_str(n, s, 3) +#endif #include "shaped-trap.h" -int -main(int argc, char **argv) { - uint8_t data[50000], shapelen, codegap, slew1, slew2, dcofs; - uint16_t plen, ncode; +unsigned +compute_pulse(uint8_t *data, unsigned datalen, uint16_t plen, char *code, uint8_t ncode, uint8_t shapelen, const uint8_t *shape, uint8_t codegap, uint8_t slew1, uint8_t slew2, uint8_t dcofs) { uint32_t shapesamples, nsamples, idx; q_t dcscale, stepsize; - char *code, tmps[20]; - - if (argc != 7) { - printf("Bad usage\n"); - exit(1); - } - plen = atoi(argv[1]); - code = argv[2]; - ncode = strlen(code); - shapelen = sizeof(shaped_trap); - codegap = atoi(argv[3]); - slew1 = atoi(argv[4]); - slew2 = atoi(argv[5]); - dcofs = atoi(argv[6]); + char tmps[20]; dcscale = qdiv(qsub(qint(255), qint(dcofs)), qint(255)); qsprint(dcscale, tmps, sizeof(tmps)); @@ -35,8 +33,8 @@ if (ncode == 1) { // Number of samples for half of the pulse - // Can't use q library here because it is Q16.16 so not large enough - shapesamples = (plen * shapelen) / 100; + // Do division first so we don't overflow Q16.16 + shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen))); // Number of samples for everything nsamples = shapesamples * 2 + slew1 + slew2; } else { @@ -44,21 +42,23 @@ nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2; } - if (nsamples > sizeof(data)) { - printf("Pulse too long (%d > %lu)\n", nsamples, sizeof(data)); - exit(1); - } - // Number of samples per step in the pulse shape stepsize = qdiv(qint(shapesamples), qint(shapelen)); qsprint(stepsize, tmps, sizeof(tmps)); printf("shapelen = %d shapesamples = %u nsamples = %u stepsize = %s\n", shapelen, shapesamples, nsamples, tmps); + if (nsamples > datalen) { + printf("Pulse too long (%d > %u)\n", nsamples, datalen); + return 0; + } if (shapesamples < 2) { printf("Pulse too short (%u < %d)\n", shapesamples, 2); - exit(1); + return 0; } - + if (qtoi(shapesamples) > 65535) { + printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535); + return 0; + } idx = 0; // Up slew @@ -68,11 +68,11 @@ for (uint16_t c = 0; c < ncode; c++) { // Pulse up for (uint16_t i = 0; i < shapesamples; i++) - data[idx++] = dcofs + qtoi(qmul(qint(shaped_trap[qtoi(qdiv(qint(i), stepsize))]), dcscale)); + data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(i), stepsize))]), dcscale)); // Pulse down for (uint16_t i = 0; i < shapesamples; i++) - data[idx++] = dcofs + qtoi(qmul(qint(shaped_trap[qtoi(qdiv(qint(shapesamples - i - 1), stepsize))]), dcscale)); + data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(shapesamples - i - 1), stepsize))]), dcscale)); // Code gap if (c < ncode - 1) @@ -84,8 +84,33 @@ for (uint16_t i = 0; i < slew2; i++) data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); + return idx - 1; +} + +int +main(int argc, char **argv) { + uint8_t data[65536], codegap, slew1, slew2, dcofs; + uint16_t plen; + uint32_t idx; + char *code; + + if (argc != 7) { + printf("Bad usage\n"); + exit(1); + } + plen = atoi(argv[1]); + code = argv[2]; + codegap = atoi(argv[3]); + slew1 = atoi(argv[4]); + slew2 = atoi(argv[5]); + dcofs = atoi(argv[6]); + + if ((idx = compute_pulse(data, sizeof(data), plen, code, strlen(code), sizeof(shaped_trap), shaped_trap, codegap, slew1, slew2, dcofs)) == 0) { + printf("Failed to build pulse\n"); + exit(1); + } + FILE *fh; - if ((fh = fopen("/tmp/out.bin", "w")) == NULL) { printf("Unable to open file: %s\n", strerror(errno)); exit(1);