diff --git a/README.md b/README.md index a8a940f..8360e40 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The data from the internal ADC is streamed out via USB. Default configuration is ### external_adc -This app contains a PIO program that reads the data from a 12-bit ADC connected to GP0-GP11, outputs the ADC clock on GP20, and packs the 12 bit samples to 16-bit words to achieve maximum throughput. +This app contains a PIO program that reads the data from a 12-bit ADC connected to GP0-GP11, outputs the ADC clock on GP20, and packs the 12-bit samples to 16-bit words to achieve maximum throughput. It is meant to be used with cheap AD9226 ADC boards. The default setting is overclocking the RP2350 to 160 MHz and driving the ADC with a 40 MHz clock. With higher overclocking up to 96 MHz ADC clock can be used. This can be used for sampling the IF of a tuner/downcoverter, as a direct-sampling HF SDR, or for capturing a video signal e.g. with [vhsdecode](https://github.com/oyvindln/vhs-decode). @@ -51,9 +51,13 @@ For the vhsdecode use-case, there is also an [adapter PCB](https://github.com/Se ![Pico2 with AD9226 ADC board](https://steve-m.de/projects/hsdaoh/rp2350_external_adc_rot.jpg) +### external_dualchan_adc + +Similar to the external_adc app, but samples a AD9238 dual channel 12-bit ADC connected to GP0-GP11, with clock for both channels on GP20. The benefit of using the AD9238 is that only 12 data lines are required to transfer the data DDR-style, so it works with a regular RP2350A/Pico2 board. + ### dual_external_adc -Similar to the external_adc app, but samples two 12 bit ADCs connected to a RP2350B, as well as two PCM1802 modules. Intended for use with vhs-decode, see [this PCB](https://github.com/Sev5000/RP2350B_DualADC_DualPCM) for the matching hardware. +Also similar to the external_adc app, but samples two 12-bit ADCs connected to a RP2350B, as well as two PCM1802 modules. Intended for use with vhs-decode, see [this PCB](https://github.com/Sev5000/RP2350B_DualADC_DualPCM) for the matching hardware. This example needs to be built with an RP2350B-board in order to work correctly: cmake -DPICO_PLATFORM=rp2350 -DPICO_BOARD=solderparty_rp2350_stamp_xl ../ diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 3da1dd8..e53c62c 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(counter) add_subdirectory(external_adc) add_subdirectory(dual_external_adc) +add_subdirectory(external_dualchan_adc) add_subdirectory(sdr) add_subdirectory(internal_adc) add_subdirectory(logic_analyzer) diff --git a/apps/counter/counter.c b/apps/counter/counter.c index ee6ca38..6fea40a 100644 --- a/apps/counter/counter.c +++ b/apps/counter/counter.c @@ -117,7 +117,7 @@ void init_pio_input(void) int main() { vreg_set_voltage(VREG_VOLTAGE_MAX); - sleep_ms(1); + sleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US); hsdaoh_set_sys_clock_khz(SYS_CLK); /* set HSTX clock to sysclk/1 */ diff --git a/apps/dual_external_adc/adc_24bit_input.pio b/apps/dual_external_adc/adc_24bit_input.pio index 7e952b1..00f2d51 100644 --- a/apps/dual_external_adc/adc_24bit_input.pio +++ b/apps/dual_external_adc/adc_24bit_input.pio @@ -3,7 +3,7 @@ ; ; SPDX-License-Identifier: BSD-3-Clause ; -; Sample 2x12 bit parallel ADC (AD9226) every 4 PIO cycles on rising clock edge, +; Sample 2x12 bit parallel ADC (AD9226) every 2 PIO cycles on rising clock edge, ; pack four 24 bit samples in three 32 bit words ; ADC clock output as side-set ; @@ -64,7 +64,7 @@ static inline void adc_24bit_input_program_init(PIO pio, uint sm, uint offset, u 32 // Autopush threshold = 32 ); - // required in order to set shift-to-right to true (for the out, null ops) + // required in order to set shift-to-right to true (for the out ops) sm_config_set_out_shift( &c, true, // Shift-to-right = true diff --git a/apps/external_adc/adc_12bit_input.pio b/apps/external_adc/adc_12bit_input.pio index beb4eae..f0dca5e 100644 --- a/apps/external_adc/adc_12bit_input.pio +++ b/apps/external_adc/adc_12bit_input.pio @@ -3,7 +3,7 @@ ; ; SPDX-License-Identifier: BSD-3-Clause ; -; Sample 12 bit parallel ADC (AD9226) every 4 PIO cycles on rising clock edge, +; Sample 12 bit parallel ADC (AD9226) every 2 PIO cycles on rising clock edge, ; pack four 12 bit samples in three 16 bit words ; ADC clock output as side-set ; @@ -63,7 +63,7 @@ static inline void adc_12bit_input_program_init(PIO pio, uint sm, uint offset, u 16 // Autopush threshold = 16 ); - // required in order to set shift-to-right to true (for the out, null ops) + // required in order to set shift-to-right to true (for the out ops) sm_config_set_out_shift( &c, true, // Shift-to-right = true diff --git a/apps/external_adc/external_adc.c b/apps/external_adc/external_adc.c index f2c4c1b..8cb0e4a 100644 --- a/apps/external_adc/external_adc.c +++ b/apps/external_adc/external_adc.c @@ -208,7 +208,7 @@ int main() #ifdef OVERVOLT /* set maximum 'allowed' voltage without voiding warranty */ vreg_set_voltage(VREG_VOLTAGE_MAX); - sleep_ms(1); + sleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US); #endif hsdaoh_set_sys_clock_khz(SYS_CLK); diff --git a/apps/external_dualchan_adc/CMakeLists.txt b/apps/external_dualchan_adc/CMakeLists.txt new file mode 100644 index 0000000..a0a970d --- /dev/null +++ b/apps/external_dualchan_adc/CMakeLists.txt @@ -0,0 +1,21 @@ +add_executable(external_dualchan_adc + external_dualchan_adc +) + +target_compile_options(external_dualchan_adc PRIVATE -Wall) + +target_link_libraries(external_dualchan_adc + pico_stdlib + pico_util + hardware_pio + hardware_dma + libpicohsdaoh +) +pico_generate_pio_header(external_dualchan_adc ${CMAKE_CURRENT_LIST_DIR}/dualchan_adc_12bit_input.pio) +pico_generate_pio_header(external_dualchan_adc ${CMAKE_CURRENT_LIST_DIR}/../external_adc/pcm1802_fmt00.pio) + +# enable usb output, disable uart output +pico_enable_stdio_usb(external_dualchan_adc 1) + +# create map/bin/hex file etc. +pico_add_extra_outputs(external_dualchan_adc) diff --git a/apps/external_dualchan_adc/dualchan_adc_12bit_input.pio b/apps/external_dualchan_adc/dualchan_adc_12bit_input.pio new file mode 100644 index 0000000..c56dc49 --- /dev/null +++ b/apps/external_dualchan_adc/dualchan_adc_12bit_input.pio @@ -0,0 +1,83 @@ +; +; Copyright (c) 2024-2025 Steve Markgraf +; +; SPDX-License-Identifier: BSD-3-Clause +; +; Sample 12 bit dualchannel parallel ADC (AD9238) every 2 PIO cycles on both +; clock edges (DDR), pack four 12 bit samples in three 16 bit words +; ADC clock output as side-set +; +; Data being pushed to the FIFO, four 12 bit samples A-D +; First word: A03 A02 A01 A00 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00 +; Second word: A07 A06 A05 A04 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00 +; Third word: A11 A10 A09 A08 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 + +.pio_version 1 +.program dualchan_adc_12bit_input +.side_set 2 + +public entry_point: + +.wrap_target + mov osr, pins side 3 ; sample A + out isr, 4 side 0 + + in pins, 12 side 0 ; sample B, autopush + out isr, 4 side 3 + + in pins, 12 side 3 ; sample C, autopush + out isr, 4 side 0 + + in pins, 12 side 0 ; sample D, autopush + nop side 3 +.wrap + +% c-sdk { +static inline void dualchan_adc_12bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin) +{ + pio_sm_config c = dualchan_adc_12bit_input_program_get_default_config(offset); + + // Set the IN base pin to the provided `pin` parameter. + sm_config_set_in_pins(&c, pin); + + // configure CLK pin for side-set + sm_config_set_sideset_pins(&c, clk_pin); + sm_config_set_sideset(&c, 2, false, false); + + pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 2, true); + pio_gpio_init(pio, clk_pin); + pio_gpio_init(pio, clk_pin+1); + + // Set the pin directions to input at the PIO + // Set D0-D11 of the ADC as input + pio_sm_set_consecutive_pindirs(pio, sm, pin, 12, false); + + // Connect these GPIOs to this PIO block + for (int i = pin; i < (pin+12); i++) + pio_gpio_init(pio, i); + + sm_config_set_in_shift( + &c, + false, // Shift-to-right = false (i.e. shift to left) + true, // Autopush enabled + 16 // Autopush threshold = 16 + ); + + // required in order to set shift-to-right to true (for the out, null ops) + sm_config_set_out_shift( + &c, + true, // Shift-to-right = true + false, // Autopush disabled + 1 // Autopush threshold: ignored + ); + + // We only receive, so disable the TX FIFO to make the RX FIFO deeper. + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + + sm_config_set_clkdiv(&c, 2.f); + + // Load our configuration, and start the program from the beginning + pio_sm_init(pio, sm, offset, &c); +// pio_sm_set_enabled(pio, sm, true); +} +%} diff --git a/apps/external_dualchan_adc/external_dualchan_adc.c b/apps/external_dualchan_adc/external_dualchan_adc.c new file mode 100644 index 0000000..786cdf7 --- /dev/null +++ b/apps/external_dualchan_adc/external_dualchan_adc.c @@ -0,0 +1,236 @@ +/* + * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks + * Implementation for the Raspberry Pi RP2350 HSTX peripheral + * + * External dualchannel 12-bit ADC example, connected to the PIO + * + * Copyright (c) 2024-2025 by Steve Markgraf + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors may + * be used to endorse or promote products derived from this software + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "pico/stdlib.h" +#include "hardware/clocks.h" +#include "hardware/irq.h" +#include "hardware/sync.h" +#include "hardware/vreg.h" +#include "hardware/pll.h" +#include "hardware/dma.h" +#include "hardware/pio.h" + +#include "picohsdaoh.h" +#include "dualchan_adc_12bit_input.pio.h" +#include "pcm1802_fmt00.pio.h" + +/* The PIO is running with sys_clk/1, and needs 4 cycles per sample, + * so the ADC clock is sys_clk/4 */ +#define SYS_CLK 320000 // 40 MHz ADC clock + +// For alignment of 3x16 bit words in the payload, so that every line starts with word 0 +#define ADC_DATA_LEN (RBUF_SLICE_LEN - 3) + +// Same here for 2x32 bit words +#define AUDIO_DATA_LEN (RBUF_SLICE_LEN - 4) +#define AUDIO_RBUF_SLICES 8 + +// ADC is attached to GP0 - GP11 with clock on GP20 +#define PIO_INPUT_PIN_BASE 0 +#define PIO_OUTPUT_CLK_PIN 20 + +#define DMACH_PIO_PING 0 +#define DMACH_PIO_PONG 1 + +static bool pio_dma_pong = false; +uint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN]; +int ringbuf_head = 2; + +void __scratch_y("") pio_dma_irq_handler() +{ + uint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING; + dma_channel_hw_t *ch = &dma_hw->ch[ch_num]; + dma_hw->intr = 1u << ch_num; + pio_dma_pong = !pio_dma_pong; + + ringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES; + + ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN]; + ch->transfer_count = ADC_DATA_LEN; + + hsdaoh_update_head(0, ringbuf_head); +} + +void init_pio_input(void) +{ + PIO pio = pio0; + uint offset = pio_add_program(pio, &dualchan_adc_12bit_input_program); + uint sm_data = pio_claim_unused_sm(pio, true); + dualchan_adc_12bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE, PIO_OUTPUT_CLK_PIN); + + dma_channel_config c; + c = dma_channel_get_default_config(DMACH_PIO_PING); + channel_config_set_chain_to(&c, DMACH_PIO_PONG); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false)); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_16); + + dma_channel_configure( + DMACH_PIO_PING, + &c, + &ringbuffer[0 * RBUF_SLICE_LEN], + &pio->rxf[sm_data], + ADC_DATA_LEN, + false + ); + c = dma_channel_get_default_config(DMACH_PIO_PONG); + channel_config_set_chain_to(&c, DMACH_PIO_PING); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false)); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_16); + + dma_channel_configure( + DMACH_PIO_PONG, + &c, + &ringbuffer[1 * RBUF_SLICE_LEN], + &pio->rxf[sm_data], + ADC_DATA_LEN, + false + ); + + dma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG); + dma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG); + irq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler); + irq_set_enabled(DMA_IRQ_0, true); + + dma_channel_start(DMACH_PIO_PING); +} + +#define PCM1802_DATA_PIN 22 + +#define DMACH_AUDIO_PIO_PING 2 +#define DMACH_AUDIO_PIO_PONG 3 + +static bool audio_pio_dma_pong = false; +uint16_t audio_ringbuffer[AUDIO_RBUF_SLICES * RBUF_SLICE_LEN]; +int audio_ringbuf_head = 2; + +void __scratch_y("") audio_pio_dma_irq_handler() +{ + uint ch_num = audio_pio_dma_pong ? DMACH_AUDIO_PIO_PONG : DMACH_AUDIO_PIO_PING; + dma_channel_hw_t *ch = &dma_hw->ch[ch_num]; + dma_hw->intr = 1u << ch_num; + audio_pio_dma_pong = !audio_pio_dma_pong; + + audio_ringbuf_head = (audio_ringbuf_head + 1) % AUDIO_RBUF_SLICES; + + ch->write_addr = (uintptr_t)&audio_ringbuffer[audio_ringbuf_head * RBUF_SLICE_LEN]; + ch->transfer_count = AUDIO_DATA_LEN/2; + + hsdaoh_update_head(2, audio_ringbuf_head); +} + +void init_audio_pio_input(void) +{ + PIO pio = pio0; + uint offset = pio_add_program(pio, &pcm1802_fmt00_program); + uint sm_data = pio_claim_unused_sm(pio, true); + pcm1802_fmt00_program_init(pio, sm_data, offset, PCM1802_DATA_PIN); + + dma_channel_config c; + c = dma_channel_get_default_config(DMACH_AUDIO_PIO_PING); + channel_config_set_chain_to(&c, DMACH_AUDIO_PIO_PONG); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false)); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + + dma_channel_configure( + DMACH_AUDIO_PIO_PING, + &c, + &audio_ringbuffer[0 * RBUF_SLICE_LEN], + &pio->rxf[sm_data], + AUDIO_DATA_LEN/2, + false + ); + c = dma_channel_get_default_config(DMACH_AUDIO_PIO_PONG); + channel_config_set_chain_to(&c, DMACH_AUDIO_PIO_PING); + channel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false)); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + + dma_channel_configure( + DMACH_AUDIO_PIO_PONG, + &c, + &audio_ringbuffer[1 * RBUF_SLICE_LEN], + &pio->rxf[sm_data], + AUDIO_DATA_LEN/2, + false + ); + + dma_hw->ints1 |= (1u << DMACH_AUDIO_PIO_PING) | (1u << DMACH_AUDIO_PIO_PONG); + dma_hw->inte1 |= (1u << DMACH_AUDIO_PIO_PING) | (1u << DMACH_AUDIO_PIO_PONG); + irq_set_exclusive_handler(DMA_IRQ_1, audio_pio_dma_irq_handler); + irq_set_enabled(DMA_IRQ_1, true); + + dma_channel_start(DMACH_AUDIO_PIO_PING); +} + +#define OVERVOLT 1 + +int main() +{ +#ifdef OVERVOLT + /* set maximum 'allowed' voltage without voiding warranty */ + vreg_set_voltage(VREG_VOLTAGE_MAX); + sleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US); +#endif + + hsdaoh_set_sys_clock_khz(SYS_CLK); + + /* set HSTX clock to sysclk/1 */ + hw_write_masked( + &clocks_hw->clk[clk_hstx].div, + 1 << CLOCKS_CLK_HSTX_DIV_INT_LSB, + CLOCKS_CLK_HSTX_DIV_INT_BITS + ); + + stdio_init_all(); + + hsdaoh_init(GPIO_DRIVE_STRENGTH_12MA, GPIO_SLEW_RATE_FAST); + hsdaoh_add_stream(0, PIO_DUALCHAN_12BIT, (SYS_CLK/4) * 1000, ADC_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer); + hsdaoh_add_stream(2, PIO_PCM1802_AUDIO, 78125, AUDIO_DATA_LEN, AUDIO_RBUF_SLICES, audio_ringbuffer); + hsdaoh_start(); + init_pio_input(); + init_audio_pio_input(); + + /* synchronously start data input */ + pio_set_sm_mask_enabled(pio0, 3, true); + + while (1) + __wfi(); +} diff --git a/apps/internal_adc/internal_adc.c b/apps/internal_adc/internal_adc.c index 97903c9..c134af0 100644 --- a/apps/internal_adc/internal_adc.c +++ b/apps/internal_adc/internal_adc.c @@ -132,7 +132,7 @@ int main() { /* set maximum 'allowed' voltage without voiding warranty */ vreg_set_voltage(VREG_VOLTAGE_MAX); - sleep_ms(1); + sleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US); hsdaoh_set_sys_clock_khz(SYS_CLK); diff --git a/apps/logic_analyzer/logic_analyzer.c b/apps/logic_analyzer/logic_analyzer.c index 676d6fc..6634fe7 100644 --- a/apps/logic_analyzer/logic_analyzer.c +++ b/apps/logic_analyzer/logic_analyzer.c @@ -122,7 +122,7 @@ int main() { /* set maximum 'allowed' voltage without voiding warranty */ vreg_set_voltage(VREG_VOLTAGE_MAX); - sleep_ms(1); + sleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US); hsdaoh_set_sys_clock_khz(SYS_CLK); diff --git a/apps/sdr/sdr.c b/apps/sdr/sdr.c index 5016d34..a547794 100644 --- a/apps/sdr/sdr.c +++ b/apps/sdr/sdr.c @@ -247,7 +247,7 @@ int main() //vreg_set_voltage(VREG_VOLTAGE_MAX); vreg_disable_voltage_limit(); vreg_set_voltage(VREG_VOLTAGE_1_65); - sleep_ms(1); + sleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US); #endif hsdaoh_set_sys_clock_khz(enable_8bit_mode ? SYS_CLK_8BIT : SYS_CLK_10BIT); int usbdiv = HSTX_CLK_MHZ / 48; diff --git a/libpicohsdaoh/picohsdaoh.h b/libpicohsdaoh/picohsdaoh.h index 31a4af4..e743a22 100644 --- a/libpicohsdaoh/picohsdaoh.h +++ b/libpicohsdaoh/picohsdaoh.h @@ -104,6 +104,29 @@ enum PIO_32BIT, PIO_32BIT_IQ, PIO_PCM1802_AUDIO, + PIO_AUDIO_PLACEHOLDER1, + PIO_AUDIO_PLACEHOLDER2, + PIO_AUDIO_PLACEHOLDER3, + PIO_DUALCHAN_1BIT, + PIO_DUALCHAN_1BIT_IQ, + PIO_DUALCHAN_2BIT, + PIO_DUALCHAN_2BIT_IQ, + PIO_DUALCHAN_4BIT, + PIO_DUALCHAN_4BIT_IQ, + PIO_DUALCHAN_8BIT, + PIO_DUALCHAN_8BIT_IQ, + PIO_DUALCHAN_10BIT, + PIO_DUALCHAN_10BIT_IQ, + PIO_DUALCHAN_12BIT, + PIO_DUALCHAN_12BIT_IQ, + PIO_DUALCHAN_14BIT, + PIO_DUALCHAN_14BIT_IQ, + PIO_DUALCHAN_16BIT, + PIO_DUALCHAN_16BIT_IQ, + PIO_DUALCHAN_24BIT, + PIO_DUALCHAN_24BIT_IQ, + PIO_DUALCHAN_32BIT, + PIO_DUALCHAN_32BIT_IQ, }; void hsdaoh_start(void);