mirror of
https://github.com/steve-m/hsdaoh-rp2350.git
synced 2026-01-27 16:57:18 +01:00
add support for multiple streams, PCM1802 and dual ADC example
This commit is contained in:
parent
32f23da176
commit
4b96c498ae
15 changed files with 790 additions and 64 deletions
|
|
@ -47,10 +47,14 @@ This app contains a PIO program that reads the data from a 12-bit ADC connected
|
|||
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).
|
||||
For the vhsdecode use-case, there is also an [adapter PCB](https://github.com/Sev5000/Pico2_12bitADC_PCMAudio).
|
||||
For the vhsdecode use-case, there is also an [adapter PCB](https://github.com/Sev5000/Pico2_12bitADC_PCMAudio). It also supports sampling a PCM1802 audio ADC 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.
|
||||
|
||||
## Credits
|
||||
|
||||
hsdaoh-rp2350 is developed by Steve Markgraf, and is based on the [dvi_out_hstx_encoder](https://github.com/raspberrypi/pico-examples/tree/master/hstx/dvi_out_hstx_encoder) example, and code by Shuichi Takano [implementing the HDMI data island encoding](https://github.com/shuichitakano/pico_lib/blob/master/dvi/data_packet.cpp), required to send HDMI info frames.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
add_subdirectory(counter)
|
||||
add_subdirectory(external_adc)
|
||||
add_subdirectory(dual_external_adc)
|
||||
add_subdirectory(internal_adc)
|
||||
add_subdirectory(logic_analyzer)
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ void __scratch_y("") pio_dma_irq_handler()
|
|||
ringbuf_head = (ringbuf_head + 1) % RBUF_SLICES;
|
||||
|
||||
ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];
|
||||
ch->transfer_count = RBUF_DATA_LEN;
|
||||
ch->transfer_count = RBUF_MAX_DATA_LEN;
|
||||
|
||||
hsdaoh_update_head(ringbuf_head);
|
||||
hsdaoh_update_head(0, ringbuf_head);
|
||||
}
|
||||
|
||||
void init_pio_input(void)
|
||||
|
|
@ -87,7 +87,7 @@ void init_pio_input(void)
|
|||
&c,
|
||||
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
RBUF_DATA_LEN,
|
||||
RBUF_MAX_DATA_LEN,
|
||||
false
|
||||
);
|
||||
c = dma_channel_get_default_config(DMACH_PIO_PONG);
|
||||
|
|
@ -102,7 +102,7 @@ void init_pio_input(void)
|
|||
&c,
|
||||
&ringbuffer[1 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
RBUF_DATA_LEN,
|
||||
RBUF_MAX_DATA_LEN,
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -129,7 +129,8 @@ int main()
|
|||
|
||||
stdio_init_all();
|
||||
|
||||
hsdaoh_init(ringbuffer);
|
||||
hsdaoh_init();
|
||||
hsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, RBUF_MAX_DATA_LEN, ringbuffer);
|
||||
hsdaoh_start();
|
||||
init_pio_input();
|
||||
|
||||
|
|
|
|||
21
apps/dual_external_adc/CMakeLists.txt
Normal file
21
apps/dual_external_adc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
add_executable(dual_external_adc
|
||||
dual_external_adc.c
|
||||
)
|
||||
|
||||
target_compile_options(dual_external_adc PRIVATE -Wall)
|
||||
|
||||
target_link_libraries(dual_external_adc
|
||||
pico_stdlib
|
||||
pico_util
|
||||
hardware_pio
|
||||
hardware_dma
|
||||
libpicohsdaoh
|
||||
)
|
||||
pico_generate_pio_header(dual_external_adc ${CMAKE_CURRENT_LIST_DIR}/adc_24bit_input.pio)
|
||||
pico_generate_pio_header(dual_external_adc ${CMAKE_CURRENT_LIST_DIR}/pcm1802_fmt00.pio)
|
||||
|
||||
# enable usb output, disable uart output
|
||||
pico_enable_stdio_usb(dual_external_adc 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(dual_external_adc)
|
||||
110
apps/dual_external_adc/adc_24bit_input.pio
Normal file
110
apps/dual_external_adc/adc_24bit_input.pio
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
;
|
||||
; Copyright (c) 2024-2025 Steve Markgraf <steve@steve-m.de>
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
; Sample 2x12 bit parallel ADC (AD9226) every 4 PIO cycles on rising clock edge,
|
||||
; pack four 24 bit samples in three 32 bit words
|
||||
; ADC clock output as side-set
|
||||
;
|
||||
; Data being pushed to the FIFO, four 12 bit samples A-D
|
||||
; First word: A23 A22 A21 A20 A19 A18 A17 A16 A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00 B07 B06 B05 B04 B03 B02 B01 B00
|
||||
; Second word: B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08 C15 C14 C13 C12 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00
|
||||
; Third word: D23 D22 D21 D20 D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 C23 C22 C21 C20 C19 C18 C17 C16
|
||||
|
||||
.pio_version 1
|
||||
.program adc_12bit_input
|
||||
.side_set 3
|
||||
|
||||
public entry_point:
|
||||
|
||||
.wrap_target
|
||||
;----------------------------------------------------------------------------------------
|
||||
in pins, 24 side 5 ; 24 bits of first sample in ISR SAMP
|
||||
nop side 5
|
||||
nop[1] side 0
|
||||
; ISR is now: 0 0 0 0 0 0 0 0 A23 A22 A21 A20 A19 A18 A17 A16 A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00
|
||||
;----------------------------------------------------------------------------------------
|
||||
|
||||
mov osr, pins side 5 ; 24 bits of second sample in OSR SAMP
|
||||
; OSR is now: 0 0 0 0 0 0 0 0 B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00
|
||||
|
||||
in osr, 8 side 5 ; 24 of first, 8 of second sample in ISR AUTOPUSH
|
||||
; ISR is now: A23 A22 A21 A20 A19 A18 A17 A16 A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00 B07 B06 B05 B04 B03 B02 B01 B00
|
||||
|
||||
out null, 8 side 0
|
||||
; OSR is now: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08
|
||||
|
||||
nop side 0
|
||||
;----------------------------------------------------------------------------------------
|
||||
mov x, pins side 5 ; 24 bits of third sample in X, 16 of second remaining in OSR SAMP
|
||||
in osr, 16 side 5 ; 16 of second sample now in ISR, OSR is now empty an can be re-used
|
||||
; ISR is now: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08
|
||||
|
||||
mov osr, x side 0
|
||||
; OSR is now 0 0 0 0 0 0 0 0 C23 C22 C21 C20 C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00
|
||||
|
||||
in osr, 16 side 0 ; autopush happening AUTOPUSH
|
||||
; ISR is now: B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08 C15 C14 C13 C12 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00
|
||||
;----------------------------------------------------------------------------------------
|
||||
in pins, 24 side 5 ; sample fourth sample to ISR SAMP
|
||||
; ISR is now 0 0 0 0 0 0 0 0 D23 D22 D21 D20 D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00
|
||||
|
||||
out null, 16 side 5
|
||||
; OSR is now 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 C23 C22 C21 C20 C19 C18 C17 C16
|
||||
|
||||
in osr, 8 side 0 ; send out remaining part of third sample and fourth sample AUTOPUSH
|
||||
; ISR is now D23 D22 D21 D20 D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 C23 C22 C21 C20 C19 C18 C17 C16
|
||||
nop side 0
|
||||
;----------------------------------------------------------------------------------------
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
static inline void adc_12bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin)
|
||||
{
|
||||
pio_sm_config c = 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, 3, false, false);
|
||||
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 3, true);
|
||||
pio_gpio_init(pio, clk_pin);
|
||||
pio_gpio_init(pio, clk_pin+2);
|
||||
|
||||
// Set the pin directions to input at the PIO
|
||||
// Set D0-D23 of the ADC(s) as input
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 24, false);
|
||||
|
||||
// Connect these GPIOs to this PIO block
|
||||
for (int i = pin; i < (pin+24); 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
|
||||
32 // Autopush threshold = 32
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
%}
|
||||
251
apps/dual_external_adc/dual_external_adc.c
Normal file
251
apps/dual_external_adc/dual_external_adc.c
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks
|
||||
* Implementation for the Raspberry Pi RP2350 HSTX peripheral
|
||||
*
|
||||
* External 12-bit ADC example, connected to the PIO
|
||||
*
|
||||
* Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>
|
||||
*
|
||||
* 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 "adc_24bit_input.pio.h"
|
||||
#include "pcm1802_fmt00.pio.h"
|
||||
|
||||
/* The PIO is running with sys_clk/2, and needs 4 cycles per sample,
|
||||
* so the ADC clock is sys_clk/8 */
|
||||
#define SYS_CLK 320000 // 40 MHz ADC clock
|
||||
|
||||
// For alignment of 3x32 bit words in the payload, so that every line starts with word 0
|
||||
#define ADC_DATA_LEN (RBUF_SLICE_LEN - 6)
|
||||
|
||||
// Same here for 2x32 bit words
|
||||
#define AUDIO_DATA_LEN (RBUF_SLICE_LEN - 4)
|
||||
|
||||
// ADC is attached to GP0 - GP11 with clock on GP20
|
||||
#define PIO_INPUT_PIN_BASE 23
|
||||
#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_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_SLICES;
|
||||
|
||||
ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];
|
||||
ch->transfer_count = ADC_DATA_LEN/2;
|
||||
|
||||
hsdaoh_update_head(0, ringbuf_head);
|
||||
}
|
||||
|
||||
void init_pio_input(void)
|
||||
{
|
||||
PIO pio = pio0;
|
||||
|
||||
/* move up GPIO base of PIO to access all ADC pins */
|
||||
pio_set_gpio_base(pio, 16);
|
||||
|
||||
uint offset = pio_add_program(pio, &adc_12bit_input_program);
|
||||
uint sm_data = pio_claim_unused_sm(pio, true);
|
||||
|
||||
|
||||
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_32);
|
||||
|
||||
dma_channel_configure(
|
||||
DMACH_PIO_PING,
|
||||
&c,
|
||||
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
ADC_DATA_LEN/2,
|
||||
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_32);
|
||||
|
||||
dma_channel_configure(
|
||||
DMACH_PIO_PONG,
|
||||
&c,
|
||||
&ringbuffer[1 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
ADC_DATA_LEN/2,
|
||||
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 0
|
||||
|
||||
#define DMACH_AUDIO_PIO_PING 2
|
||||
#define DMACH_AUDIO_PIO_PONG 3
|
||||
|
||||
static bool audio_pio_dma_pong = false;
|
||||
uint16_t audio_ringbuffer[RBUF_TOTAL_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) % 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(1, audio_ringbuf_head);
|
||||
}
|
||||
|
||||
void init_audio_pio_input(void)
|
||||
{
|
||||
PIO pio = pio1;
|
||||
|
||||
pio_set_gpio_base(pio, 0);
|
||||
|
||||
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_ms(1);
|
||||
#endif
|
||||
|
||||
set_sys_clock_khz(SYS_CLK, true);
|
||||
|
||||
/* 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
|
||||
);
|
||||
|
||||
pll_init(pll_usb, 1, 1536 * MHZ, 4, 2);
|
||||
// pll_init(pll_usb, 1, (983 * MHZ) + (40 * KHZ), 4, 2);
|
||||
|
||||
/* set USB clock to clk_usb/4 */
|
||||
hw_write_masked(&clocks_hw->clk[clk_usb].div, 4 << CLOCKS_CLK_USB_DIV_INT_LSB, CLOCKS_CLK_USB_DIV_INT_BITS);
|
||||
|
||||
/* set GPOUT0 clock to USB PLL/10 -> 19.2 MHz, resulting in 75 kHz ADC sample rate (19.2M/256) */
|
||||
clock_gpio_init(21, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 10);
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
hsdaoh_init();
|
||||
hsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, ADC_DATA_LEN, ringbuffer);
|
||||
hsdaoh_add_stream(1, 1, 75000, AUDIO_DATA_LEN, audio_ringbuffer);
|
||||
hsdaoh_start();
|
||||
init_pio_input();
|
||||
init_audio_pio_input();
|
||||
|
||||
while (1)
|
||||
__wfi();
|
||||
}
|
||||
103
apps/dual_external_adc/pcm1802_fmt00.pio
Normal file
103
apps/dual_external_adc/pcm1802_fmt00.pio
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
;
|
||||
; Copyright (c) 2023 Rene Wolf
|
||||
;
|
||||
; adapted for hsdaoh-rp2350:
|
||||
; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
; See PCM1802 data sheet, figure 25, format 00
|
||||
; This output technically has 4 wires, however we don't need the frame sync:
|
||||
; - Data is sampled on a positive edge of the bit clock
|
||||
; - LRClk changes on the preceeding falling edge of bit clock
|
||||
; - So we wait for an edge on LRClk which tells us
|
||||
; - the next falling edge on bit clock is MSB of a channel
|
||||
; - what channel follows determined by eitehr positive (left) or negative (right) edge
|
||||
; - Frame sync would tell us how many bits are valid, but we know its 24
|
||||
|
||||
|
||||
.define PUBLIC pcm1802_index_data 0
|
||||
.define PUBLIC pcm1802_index_bitclk 1
|
||||
.define PUBLIC pcm1802_index_lrclk 2
|
||||
.define PUBLIC pcm1802_index_auxin 6
|
||||
.define polarity_left 1
|
||||
.define polarity_right 0
|
||||
.program pcm1802_fmt00
|
||||
|
||||
public entry_point:
|
||||
|
||||
right_ch:
|
||||
set x, 1 ; set jump to left next
|
||||
wait polarity_right pin pcm1802_index_lrclk ; right channel started, next rising edge on bitclk is msb
|
||||
jmp read_sample
|
||||
left_ch:
|
||||
set x, 0 ; set jump to right next
|
||||
wait polarity_left pin pcm1802_index_lrclk ; left channel started, next rising edge on bitclk is msb
|
||||
jmp read_sample
|
||||
|
||||
read_sample:
|
||||
in x, 1
|
||||
set y, 23 ; load 24-1 into y, because jmp is a pre-decrement check
|
||||
read_bit:
|
||||
wait 0 pin pcm1802_index_bitclk
|
||||
wait 1 pin pcm1802_index_bitclk ; positive edge -> data valid now
|
||||
in pins, 1 ; read one bit from data pin into ISR
|
||||
jmp y-- read_bit ; jump if Y!=0 && decrement Y if jump taken
|
||||
push
|
||||
|
||||
; sample the auxilary input pin (e.g. for headswitch signal)
|
||||
mov osr, pins
|
||||
out null, pcm1802_index_auxin
|
||||
in osr, 1
|
||||
|
||||
jmp !x right_ch ; jmp to right_ch if X==0
|
||||
jmp left_ch ; go for next sample start
|
||||
|
||||
% c-sdk {
|
||||
static inline void pcm1802_fmt00_program_init(PIO pio, uint sm, uint offset, uint pin)
|
||||
{
|
||||
pio_sm_config c = pcm1802_fmt00_program_get_default_config(offset);
|
||||
|
||||
// Set the IN base pin to the provided `pin` parameter.
|
||||
sm_config_set_in_pins(&c, pin);
|
||||
|
||||
uint32_t pin_mask = (1 << pcm1802_index_auxin) |
|
||||
(1 << pcm1802_index_lrclk) |
|
||||
(1 << pcm1802_index_bitclk) |
|
||||
(1 << pcm1802_index_data);
|
||||
|
||||
pio_sm_set_pindirs_with_mask (pio, sm, 0, pin_mask << pin);
|
||||
|
||||
sm_config_set_jmp_pin(&c, pin + pcm1802_index_data);
|
||||
|
||||
// Connect these GPIOs to this PIO block
|
||||
pio_gpio_init(pio, pin + pcm1802_index_data);
|
||||
pio_gpio_init(pio, pin + pcm1802_index_bitclk);
|
||||
pio_gpio_init(pio, pin + pcm1802_index_lrclk);
|
||||
pio_gpio_init(pio, pin + pcm1802_index_auxin);
|
||||
|
||||
sm_config_set_in_shift(
|
||||
&c,
|
||||
false, // Shift-to-right = false (i.e. shift to left)
|
||||
false, // Autopush disabled
|
||||
32 // Autopush threshold: ignored
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
%}
|
||||
|
|
@ -12,6 +12,7 @@ target_link_libraries(external_adc
|
|||
libpicohsdaoh
|
||||
)
|
||||
pico_generate_pio_header(external_adc ${CMAKE_CURRENT_LIST_DIR}/adc_12bit_input.pio)
|
||||
pico_generate_pio_header(external_adc ${CMAKE_CURRENT_LIST_DIR}/pcm1802_fmt00.pio)
|
||||
|
||||
# enable usb output, disable uart output
|
||||
pico_enable_stdio_usb(external_adc 1)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public entry_point:
|
|||
|
||||
|
||||
in osr, 4 side 0 ; send out remaining part of third sample and fourth sample AUTOPUSH
|
||||
; ISR is now 0 0 0 0 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 C11 C10 C09 C08
|
||||
; ISR is now D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 C11 C10 C09 C08
|
||||
nop side 0
|
||||
;----------------------------------------------------------------------------------------
|
||||
.wrap
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* External 12-bit ADC example, connected to the PIO
|
||||
*
|
||||
* Copyright (c) 2024 by Steve Markgraf <steve@steve-m.de>
|
||||
* Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
|
|
@ -37,11 +37,13 @@
|
|||
#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 "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 */
|
||||
|
|
@ -50,6 +52,12 @@
|
|||
//#define SYS_CLK 320000 // 80 MHz ADC clock
|
||||
//#define SYS_CLK 384000 // 96 MHz ADC clock, maximum that works on my Pico2 (with overvolting)
|
||||
|
||||
// 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)
|
||||
|
||||
// ADC is attached to GP0 - GP11 with clock on GP20
|
||||
#define PIO_INPUT_PIN_BASE 0
|
||||
#define PIO_OUTPUT_CLK_PIN 20
|
||||
|
|
@ -71,9 +79,9 @@ void __scratch_y("") pio_dma_irq_handler()
|
|||
ringbuf_head = (ringbuf_head + 1) % RBUF_SLICES;
|
||||
|
||||
ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];
|
||||
ch->transfer_count = RBUF_DATA_LEN;
|
||||
ch->transfer_count = ADC_DATA_LEN;
|
||||
|
||||
hsdaoh_update_head(ringbuf_head);
|
||||
hsdaoh_update_head(0, ringbuf_head);
|
||||
}
|
||||
|
||||
void init_pio_input(void)
|
||||
|
|
@ -96,7 +104,7 @@ void init_pio_input(void)
|
|||
&c,
|
||||
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
RBUF_DATA_LEN,
|
||||
ADC_DATA_LEN,
|
||||
false
|
||||
);
|
||||
c = dma_channel_get_default_config(DMACH_PIO_PONG);
|
||||
|
|
@ -111,7 +119,7 @@ void init_pio_input(void)
|
|||
&c,
|
||||
&ringbuffer[1 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
RBUF_DATA_LEN,
|
||||
ADC_DATA_LEN,
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -123,6 +131,77 @@ void init_pio_input(void)
|
|||
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[RBUF_TOTAL_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) % 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(1, audio_ringbuf_head);
|
||||
}
|
||||
|
||||
void init_audio_pio_input(void)
|
||||
{
|
||||
PIO pio = pio1;
|
||||
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);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef OVERVOLT
|
||||
|
|
@ -140,11 +219,23 @@ int main()
|
|||
CLOCKS_CLK_HSTX_DIV_INT_BITS
|
||||
);
|
||||
|
||||
pll_init(pll_usb, 1, 1536 * MHZ, 4, 2);
|
||||
// pll_init(pll_usb, 1, (983 * MHZ) + (40 * KHZ), 4, 2);
|
||||
|
||||
/* set USB clock to clk_usb/4 */
|
||||
hw_write_masked(&clocks_hw->clk[clk_usb].div, 4 << CLOCKS_CLK_USB_DIV_INT_LSB, CLOCKS_CLK_USB_DIV_INT_BITS);
|
||||
|
||||
/* set GPOUT0 clock to USB PLL/10 -> 19.2 MHz, resulting in 75 kHz ADC sample rate (19.2M/256) */
|
||||
clock_gpio_init(21, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 10);
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
hsdaoh_init(ringbuffer);
|
||||
hsdaoh_init();
|
||||
hsdaoh_add_stream(0, 1, (SYS_CLK/4) * 1000, ADC_DATA_LEN, ringbuffer);
|
||||
hsdaoh_add_stream(1, 1, 75000, AUDIO_DATA_LEN, audio_ringbuffer);
|
||||
hsdaoh_start();
|
||||
init_pio_input();
|
||||
init_audio_pio_input();
|
||||
|
||||
while (1)
|
||||
__wfi();
|
||||
|
|
|
|||
103
apps/external_adc/pcm1802_fmt00.pio
Normal file
103
apps/external_adc/pcm1802_fmt00.pio
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
;
|
||||
; Copyright (c) 2023 Rene Wolf
|
||||
;
|
||||
; adapted for hsdaoh-rp2350:
|
||||
; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
; See PCM1802 data sheet, figure 25, format 00
|
||||
; This output technically has 4 wires, however we don't need the frame sync:
|
||||
; - Data is sampled on a positive edge of the bit clock
|
||||
; - LRClk changes on the preceeding falling edge of bit clock
|
||||
; - So we wait for an edge on LRClk which tells us
|
||||
; - the next falling edge on bit clock is MSB of a channel
|
||||
; - what channel follows determined by eitehr positive (left) or negative (right) edge
|
||||
; - Frame sync would tell us how many bits are valid, but we know its 24
|
||||
|
||||
|
||||
.define PUBLIC pcm1802_index_data 0
|
||||
.define PUBLIC pcm1802_index_bitclk 4
|
||||
.define PUBLIC pcm1802_index_lrclk 5
|
||||
.define PUBLIC pcm1802_index_auxin 6
|
||||
.define polarity_left 1
|
||||
.define polarity_right 0
|
||||
.program pcm1802_fmt00
|
||||
|
||||
public entry_point:
|
||||
|
||||
right_ch:
|
||||
set x, 1 ; set jump to left next
|
||||
wait polarity_right pin pcm1802_index_lrclk ; right channel started, next rising edge on bitclk is msb
|
||||
jmp read_sample
|
||||
left_ch:
|
||||
set x, 0 ; set jump to right next
|
||||
wait polarity_left pin pcm1802_index_lrclk ; left channel started, next rising edge on bitclk is msb
|
||||
jmp read_sample
|
||||
|
||||
read_sample:
|
||||
in x, 1
|
||||
set y, 23 ; load 24-1 into y, because jmp is a pre-decrement check
|
||||
read_bit:
|
||||
wait 0 pin pcm1802_index_bitclk
|
||||
wait 1 pin pcm1802_index_bitclk ; positive edge -> data valid now
|
||||
in pins, 1 ; read one bit from data pin into ISR
|
||||
jmp y-- read_bit ; jump if Y!=0 && decrement Y if jump taken
|
||||
push
|
||||
|
||||
; sample the auxilary input pin (e.g. for headswitch signal)
|
||||
mov osr, pins
|
||||
out null, pcm1802_index_auxin
|
||||
in osr, 1
|
||||
|
||||
jmp !x right_ch ; jmp to right_ch if X==0
|
||||
jmp left_ch ; go for next sample start
|
||||
|
||||
% c-sdk {
|
||||
static inline void pcm1802_fmt00_program_init(PIO pio, uint sm, uint offset, uint pin)
|
||||
{
|
||||
pio_sm_config c = pcm1802_fmt00_program_get_default_config(offset);
|
||||
|
||||
// Set the IN base pin to the provided `pin` parameter.
|
||||
sm_config_set_in_pins(&c, pin);
|
||||
|
||||
uint32_t pin_mask = (1 << pcm1802_index_auxin) |
|
||||
(1 << pcm1802_index_lrclk) |
|
||||
(1 << pcm1802_index_bitclk) |
|
||||
(1 << pcm1802_index_data);
|
||||
|
||||
pio_sm_set_pindirs_with_mask (pio, sm, 0, pin_mask << pin);
|
||||
|
||||
sm_config_set_jmp_pin(&c, pin + pcm1802_index_data);
|
||||
|
||||
// Connect these GPIOs to this PIO block
|
||||
pio_gpio_init(pio, pin + pcm1802_index_data);
|
||||
pio_gpio_init(pio, pin + pcm1802_index_bitclk);
|
||||
pio_gpio_init(pio, pin + pcm1802_index_lrclk);
|
||||
pio_gpio_init(pio, pin + pcm1802_index_auxin);
|
||||
|
||||
sm_config_set_in_shift(
|
||||
&c,
|
||||
false, // Shift-to-right = false (i.e. shift to left)
|
||||
false, // Autopush disabled
|
||||
32 // Autopush threshold: ignored
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
%}
|
||||
|
|
@ -67,9 +67,9 @@ void __scratch_y("") adc_dma_irq_handler()
|
|||
ringbuf_head = (ringbuf_head + 1) % RBUF_SLICES;
|
||||
|
||||
ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];
|
||||
ch->transfer_count = RBUF_DATA_LEN;
|
||||
ch->transfer_count = RBUF_MAX_DATA_LEN;
|
||||
|
||||
hsdaoh_update_head(ringbuf_head);
|
||||
hsdaoh_update_head(0, ringbuf_head);
|
||||
}
|
||||
|
||||
void init_adc_input(void)
|
||||
|
|
@ -100,7 +100,7 @@ void init_adc_input(void)
|
|||
&c,
|
||||
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||
&adc_hw->fifo,
|
||||
RBUF_DATA_LEN,
|
||||
RBUF_MAX_DATA_LEN,
|
||||
false
|
||||
);
|
||||
c = dma_channel_get_default_config(DMACH_ADC_PONG);
|
||||
|
|
@ -115,7 +115,7 @@ void init_adc_input(void)
|
|||
&c,
|
||||
&ringbuffer[1 * RBUF_SLICE_LEN],
|
||||
&adc_hw->fifo,
|
||||
RBUF_DATA_LEN,
|
||||
RBUF_MAX_DATA_LEN,
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -152,7 +152,8 @@ int main()
|
|||
|
||||
stdio_init_all();
|
||||
|
||||
hsdaoh_init(ringbuffer);
|
||||
hsdaoh_init();
|
||||
hsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, RBUF_MAX_DATA_LEN, ringbuffer);
|
||||
hsdaoh_start();
|
||||
init_adc_input();
|
||||
|
||||
|
|
|
|||
|
|
@ -66,9 +66,9 @@ void __scratch_y("") pio_dma_irq_handler()
|
|||
ringbuf_head = (ringbuf_head + 1) % RBUF_SLICES;
|
||||
|
||||
ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];
|
||||
ch->transfer_count = RBUF_DATA_LEN;
|
||||
ch->transfer_count = RBUF_MAX_DATA_LEN;
|
||||
|
||||
hsdaoh_update_head(ringbuf_head);
|
||||
hsdaoh_update_head(0, ringbuf_head);
|
||||
}
|
||||
|
||||
void init_pio_input(void)
|
||||
|
|
@ -91,7 +91,7 @@ void init_pio_input(void)
|
|||
&c,
|
||||
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
RBUF_DATA_LEN,
|
||||
RBUF_MAX_DATA_LEN,
|
||||
false
|
||||
);
|
||||
c = dma_channel_get_default_config(DMACH_PIO_PONG);
|
||||
|
|
@ -106,7 +106,7 @@ void init_pio_input(void)
|
|||
&c,
|
||||
&ringbuffer[1 * RBUF_SLICE_LEN],
|
||||
&pio->rxf[sm_data],
|
||||
RBUF_DATA_LEN,
|
||||
RBUF_MAX_DATA_LEN,
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -135,7 +135,8 @@ int main()
|
|||
|
||||
stdio_init_all();
|
||||
|
||||
hsdaoh_init(ringbuffer);
|
||||
hsdaoh_init();
|
||||
hsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, RBUF_MAX_DATA_LEN, ringbuffer);
|
||||
hsdaoh_start();
|
||||
init_pio_input();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks
|
||||
* Implementation for the Raspberry Pi RP2350 HSTX peripheral
|
||||
*
|
||||
* Copyright (c) 2024 by Steve Markgraf <steve@steve-m.de>
|
||||
* Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>
|
||||
*
|
||||
* based on the pico-examples/hstx/dvi_out_hstx_encoder example:
|
||||
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
|
||||
|
|
@ -67,14 +67,24 @@
|
|||
#define HSTX_CMD_TMDS_REPEAT (0x3u << 12)
|
||||
#define HSTX_CMD_NOP (0xfu << 12)
|
||||
|
||||
uint16_t *ring_buf = NULL;
|
||||
uint16_t idle_line_buf1[MODE_H_ACTIVE_PIXELS];
|
||||
uint16_t idle_line_buf2[MODE_H_ACTIVE_PIXELS];
|
||||
uint32_t info_p[64];
|
||||
uint32_t info_len;
|
||||
|
||||
int fifo_tail = RBUF_SLICES-1;
|
||||
int fifo_head = 0;
|
||||
typedef struct
|
||||
{
|
||||
bool active;
|
||||
uint16_t *rbuf;
|
||||
uint tail;
|
||||
uint head;
|
||||
uint16_t format;
|
||||
uint32_t srate;
|
||||
uint16_t len;
|
||||
uint64_t data_cnt;
|
||||
} stream_t;
|
||||
|
||||
stream_t streams[MAX_STREAMS];
|
||||
|
||||
static uint32_t vblank_line_vsync_off[] = {
|
||||
HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH,
|
||||
|
|
@ -148,9 +158,9 @@ void init_info_packet(void)
|
|||
info_len = len;
|
||||
}
|
||||
|
||||
void hsdaoh_update_head(int head)
|
||||
void hsdaoh_update_head(int stream_id, int head)
|
||||
{
|
||||
fifo_head = head;
|
||||
streams[stream_id].head = head;
|
||||
}
|
||||
|
||||
#define DMACH_HSTX_START 13
|
||||
|
|
@ -164,21 +174,7 @@ static bool vactive_cmdlist_posted = false;
|
|||
static uint8_t dma_sniff_pipelined_ch = 0;
|
||||
static bool dma_sniff_pipelined_disable = false;
|
||||
|
||||
enum crc_config {
|
||||
CRC_NONE, /* No CRC, just 16 bit idle counter */
|
||||
CRC16_1_LINE, /* Line contains CRC of the last line */
|
||||
CRC16_2_LINE /* Line contains CRC of the line before the last line */
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic;
|
||||
uint16_t framecounter;
|
||||
uint8_t pack_state;
|
||||
uint8_t crc_config;
|
||||
} __attribute__((packed, aligned(1))) metadata_t;
|
||||
|
||||
metadata_t metadata = (metadata_t) { .magic = 0xda7acab1, .crc_config = CRC16_2_LINE };
|
||||
metadata_t metadata = (metadata_t) { .magic = 0xda7acab1, .crc_config = CRC16_2_LINE, .version = 1 };
|
||||
|
||||
/* HSTX DMA IRQ handler, reconfigures the channel that just completed while
|
||||
* ther other channel is currently busy */
|
||||
|
|
@ -231,18 +227,24 @@ void __scratch_x("") hstx_dma_irq_handler()
|
|||
vactive_cmdlist_posted = true;
|
||||
} else {
|
||||
/* Output of actual data in active video lines */
|
||||
uint16_t *next_line;
|
||||
int next_tail = (fifo_tail + 1) % RBUF_SLICES;
|
||||
uint16_t cur_active_line = v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES);
|
||||
|
||||
if (fifo_head == next_tail) {
|
||||
/* No data to send, use idle line */
|
||||
next_line = (cur_active_line % 2) ? idle_line_buf1 : idle_line_buf2;
|
||||
uint16_t *next_line = (cur_active_line % 2) ? idle_line_buf1 : idle_line_buf2;
|
||||
next_line[RBUF_SLICE_LEN - 1] = 0;
|
||||
} else {
|
||||
next_line = &ring_buf[fifo_tail * RBUF_SLICE_LEN];
|
||||
fifo_tail = next_tail;
|
||||
next_line[RBUF_SLICE_LEN - 1] = RBUF_DATA_LEN;
|
||||
|
||||
for (uint i = 0; i < MAX_STREAMS; i++) {
|
||||
stream_t *stream = &streams[i];
|
||||
|
||||
if (!stream->active)
|
||||
continue;
|
||||
|
||||
int next_tail = (stream->tail + 1) % RBUF_SLICES;
|
||||
if (stream->head != next_tail) {
|
||||
next_line = &stream->rbuf[stream->tail * RBUF_SLICE_LEN];
|
||||
stream->tail = next_tail;
|
||||
next_line[RBUF_SLICE_LEN - 1] = stream->len;
|
||||
next_line[RBUF_SLICE_LEN - 3] = i; // stream ID
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in metadata word (last word of line) */
|
||||
|
|
@ -256,8 +258,6 @@ void __scratch_x("") hstx_dma_irq_handler()
|
|||
|
||||
/* on the second last word of the line, insert the CRC16 of the entire line before the last line */
|
||||
next_line[RBUF_SLICE_LEN - 2] = saved_crc;
|
||||
next_line[RBUF_SLICE_LEN - 3] = 0;
|
||||
|
||||
dma_sniff_pipelined_ch = ch_num;
|
||||
|
||||
/* switch to 16 bit DMA transfer size for the actual data,
|
||||
|
|
@ -287,9 +287,28 @@ void hsdaoh_start(void)
|
|||
dma_channel_start(DMACH_HSTX_START);
|
||||
}
|
||||
|
||||
void hsdaoh_init(uint16_t *ringbuf)//struct hsdaoh_inst *inst, uint16_t *ringbuf)
|
||||
int hsdaoh_add_stream(uint16_t stream_id, uint16_t format, uint32_t samplerate, uint length, uint16_t *ringbuf)
|
||||
{
|
||||
ring_buf = ringbuf;
|
||||
if (stream_id >= MAX_STREAMS)
|
||||
return -1;
|
||||
|
||||
stream_t *stream = &streams[stream_id];
|
||||
stream->rbuf = ringbuf;
|
||||
stream->format = format;
|
||||
stream->srate = samplerate;
|
||||
stream->len = length;
|
||||
stream->tail = RBUF_SLICES-1;
|
||||
stream->head = 0;
|
||||
stream->data_cnt = 0;
|
||||
stream->active = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hsdaoh_init(void)
|
||||
{
|
||||
for (uint i = 0; i < MAX_STREAMS; i++)
|
||||
streams[i].active = false;
|
||||
|
||||
init_info_packet();
|
||||
|
||||
|
|
|
|||
|
|
@ -22,16 +22,35 @@
|
|||
)
|
||||
|
||||
// CRC word and length/metadata word, so 2 reserved words
|
||||
// padded to 3 so that for the 12bit/16bit packed format
|
||||
// each line starts with word 0
|
||||
#define NUM_RESERVED_WORDS 3
|
||||
#define NUM_RESERVED_WORDS 2
|
||||
#define RBUF_SLICES 16
|
||||
#define RBUF_SLICE_LEN MODE_H_ACTIVE_PIXELS
|
||||
#define RBUF_DATA_LEN (RBUF_SLICE_LEN - NUM_RESERVED_WORDS)
|
||||
#define RBUF_MAX_DATA_LEN (RBUF_SLICE_LEN - NUM_RESERVED_WORDS)
|
||||
#define RBUF_TOTAL_LEN (RBUF_SLICE_LEN * RBUF_SLICES)
|
||||
|
||||
#define MAX_STREAMS 8
|
||||
|
||||
enum crc_config {
|
||||
CRC_NONE, /* No CRC, just 16 bit idle counter */
|
||||
CRC16_1_LINE, /* Line contains CRC of the last line */
|
||||
CRC16_2_LINE /* Line contains CRC of the line before the last line */
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic;
|
||||
uint16_t framecounter;
|
||||
uint8_t reserved1;
|
||||
uint8_t crc_config;
|
||||
uint16_t version;
|
||||
uint32_t flags;
|
||||
uint8_t reserved2[116];
|
||||
uint8_t stream_cnt;
|
||||
} __attribute__((packed, aligned(1))) metadata_t;
|
||||
|
||||
void hsdaoh_start(void);
|
||||
void hsdaoh_init(uint16_t *ringbuf);
|
||||
void hsdaoh_update_head(int head);
|
||||
void hsdaoh_init(void);
|
||||
void hsdaoh_update_head(int stream_id, int head);
|
||||
int hsdaoh_add_stream(uint16_t stream_id, uint16_t format, uint32_t samplerate, uint length, uint16_t *ringbuf);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue