mirror of
https://github.com/steve-m/hsdaoh-rp2350.git
synced 2025-12-09 23:34:39 +01:00
initial commit
This commit is contained in:
commit
2e644a593b
19 changed files with 1633 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
build/*
|
||||||
16
CMakeLists.txt
Normal file
16
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(hsdaoh-rp2350 C CXX ASM)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_compile_options(-Wall)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(libpicohsdaoh)
|
||||||
|
add_subdirectory(apps)
|
||||||
28
LICENSE
Normal file
28
LICENSE
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024 by Steve Markgraf
|
||||||
|
|
||||||
|
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 copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
||||||
49
README.md
Normal file
49
README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# hsdaoh-rp2350 - High Speed Data Acquisition over HDMI
|
||||||
|
## Stream up to 75 MByte/s from your Raspberry Pi Pico2 to your PC
|
||||||
|
|
||||||
|
Using $5 USB3 HDMI capture sticks based on the MacroSilicon MS2130, this project allows to stream out up to 75 MByte/s of real time data from an RP2350 (with overclocking) to a host computer with USB3.
|
||||||
|
For more information and the host library, see the [main repository](https://github.com/steve-m/hsdaoh) and the [talk at OsmoDevcon '24](https://media.ccc.de/v/osmodevcon2024-200-low-cost-high-speed-data-acquisition-over-hdmi).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Make sure you have the latest version of the [pico-sdk](https://github.com/raspberrypi/pico-sdk) installed together with an appropriate compiler. You should be able to build the [pico-examples](https://github.com/raspberrypi/pico-examples).
|
||||||
|
|
||||||
|
To build hsdaoh-rp2350:
|
||||||
|
|
||||||
|
git clone https://github.com/steve-m/hsdaoh-rp2350.git
|
||||||
|
mkdir hsdaoh-rp2350/build
|
||||||
|
cd hsdaoh-rp2350/build
|
||||||
|
export PICO_SDK_PATH=/<path-to>/pico-sdk
|
||||||
|
cmake -DPICO_PLATFORM=rp2350 -DPICO_BOARD=pico2 ../
|
||||||
|
make -j 8
|
||||||
|
|
||||||
|
After the build succeeds you can copy the resulting *.uf2 file of the application you want to run to the board.
|
||||||
|
|
||||||
|
Apart from the Pico2 with the [Pico-DVI-Sock](https://github.com/Wren6991/Pico-DVI-Sock), it also should work with the Adafruit Feather RP2350 with HSTX Port, but so far only the Pico2 was tested.
|
||||||
|
|
||||||
|
## Example applications
|
||||||
|
|
||||||
|
The repository contains a library - libpicohsdaoh - which implements the main functionality. It reads the data from a ringbuffer, and streams it out via the HSTX port.
|
||||||
|
In addition to that, the apps folder contains a couple of example applications:
|
||||||
|
|
||||||
|
### counter
|
||||||
|
|
||||||
|
This application uses the PIO to generate a 16-bit counter value which is written to a DMA ringbuffer, which is then streamed out via hsdaoh. The counter can be verified using the hsdaoh_test host application.
|
||||||
|
|
||||||
|
### internal_adc
|
||||||
|
|
||||||
|
The data from the internal ADC is streamed out via USB. Default configuration is overclocking the ADC to 3.33 MS/s. Using the USB PLL and overvolting beyond VREG_VOLTAGE_MAX, up to 7.9 MS/s can be achieved.
|
||||||
|
|
||||||
|
### 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 GP22, 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 320 MHz and driving the ADC with a 40 MHz clock. With higher overclocking up to 50.25 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).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.
|
||||||
3
apps/CMakeLists.txt
Normal file
3
apps/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
add_subdirectory(counter)
|
||||||
|
add_subdirectory(external_adc)
|
||||||
|
add_subdirectory(internal_adc)
|
||||||
21
apps/counter/CMakeLists.txt
Normal file
21
apps/counter/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
add_executable(counter
|
||||||
|
counter.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(counter PRIVATE -Wall)
|
||||||
|
|
||||||
|
target_link_libraries(counter
|
||||||
|
pico_stdlib
|
||||||
|
pico_multicore
|
||||||
|
pico_util
|
||||||
|
hardware_pio
|
||||||
|
hardware_dma
|
||||||
|
libpicohsdaoh
|
||||||
|
)
|
||||||
|
pico_generate_pio_header(counter ${CMAKE_CURRENT_LIST_DIR}/counter.pio)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(counter 1)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(counter)
|
||||||
136
apps/counter/counter.c
Normal file
136
apps/counter/counter.c
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks
|
||||||
|
* Implementation for the Raspberry Pi RP2350 HSTX peripheral
|
||||||
|
*
|
||||||
|
* PIO counter value example, counter can be verified with hsdaoh_test
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 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/dma.h"
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
|
||||||
|
#include "picohsdaoh.h"
|
||||||
|
#include "counter.pio.h"
|
||||||
|
|
||||||
|
#define SYS_CLK 250000
|
||||||
|
|
||||||
|
#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 = 0;
|
||||||
|
|
||||||
|
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 = RBUF_DATA_LEN;
|
||||||
|
|
||||||
|
hsdaoh_update_head(ringbuf_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_pio_input(void)
|
||||||
|
{
|
||||||
|
PIO pio = pio0;
|
||||||
|
uint offset = pio_add_program(pio, &counter_program);
|
||||||
|
uint sm_data = pio_claim_unused_sm(pio, true);
|
||||||
|
counter_program_init(pio, sm_data, offset);
|
||||||
|
|
||||||
|
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],
|
||||||
|
RBUF_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],
|
||||||
|
RBUF_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
set_sys_clock_khz(SYS_CLK, true);
|
||||||
|
|
||||||
|
/* set HSTX clock to sysclk/2 */
|
||||||
|
hw_write_masked(
|
||||||
|
&clocks_hw->clk[clk_hstx].div,
|
||||||
|
2 << CLOCKS_CLK_HSTX_DIV_INT_LSB,
|
||||||
|
CLOCKS_CLK_HSTX_DIV_INT_BITS
|
||||||
|
);
|
||||||
|
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
hsdaoh_init(ringbuffer);
|
||||||
|
hsdaoh_start();
|
||||||
|
init_pio_input();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
__wfi();
|
||||||
|
}
|
||||||
42
apps/counter/counter.pio
Normal file
42
apps/counter/counter.pio
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
;
|
||||||
|
; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
; Generate 16 bit counter in PIO
|
||||||
|
;
|
||||||
|
|
||||||
|
.pio_version 0
|
||||||
|
.program counter
|
||||||
|
|
||||||
|
public entry_point:
|
||||||
|
|
||||||
|
.wrap_target
|
||||||
|
jmp x-- dummylabel
|
||||||
|
dummylabel:
|
||||||
|
mov isr, ~x
|
||||||
|
push
|
||||||
|
.wrap
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
static inline void counter_program_init(PIO pio, uint sm, uint offset)
|
||||||
|
{
|
||||||
|
pio_sm_config c = counter_program_get_default_config(offset);
|
||||||
|
|
||||||
|
sm_config_set_in_shift(
|
||||||
|
&c,
|
||||||
|
false, // Shift-to-right = false (i.e. shift to left)
|
||||||
|
false, // Autopush enabled
|
||||||
|
1 // Autopush threshold, ignored
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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, 4.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);
|
||||||
|
}
|
||||||
|
%}
|
||||||
20
apps/external_adc/CMakeLists.txt
Normal file
20
apps/external_adc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
add_executable(external_adc
|
||||||
|
external_adc.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(external_adc PRIVATE -Wall)
|
||||||
|
|
||||||
|
target_link_libraries(external_adc
|
||||||
|
pico_stdlib
|
||||||
|
pico_util
|
||||||
|
hardware_pio
|
||||||
|
hardware_dma
|
||||||
|
libpicohsdaoh
|
||||||
|
)
|
||||||
|
pico_generate_pio_header(external_adc ${CMAKE_CURRENT_LIST_DIR}/adc_12bit_input.pio)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(external_adc 1)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(external_adc)
|
||||||
110
apps/external_adc/adc_12bit_input.pio
Normal file
110
apps/external_adc/adc_12bit_input.pio
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
;
|
||||||
|
; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
; Sample 12 bit parallel ADC (AD9226) every 4 PIO cycles on rising clock edge,
|
||||||
|
; 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: A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00 B03 B02 B01 B00
|
||||||
|
; Second word: B11 B10 B09 B08 B07 B06 B05 B04 C07 C06 C05 C04 C03 C02 C01 C00
|
||||||
|
; Third word: D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 C11 C10 C09 C08
|
||||||
|
|
||||||
|
.pio_version 0
|
||||||
|
.program adc_12bit_input
|
||||||
|
.side_set 1
|
||||||
|
|
||||||
|
public entry_point:
|
||||||
|
|
||||||
|
.wrap_target
|
||||||
|
;----------------------------------------------------------------------------------------
|
||||||
|
in pins, 12 side 1 ; 12 bits of first sample in ISR SAMP
|
||||||
|
nop side 1
|
||||||
|
nop side 0
|
||||||
|
nop side 0
|
||||||
|
; ISR is now: 0 0 0 0 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00
|
||||||
|
;----------------------------------------------------------------------------------------
|
||||||
|
mov osr, pins side 1 ; 12 bits of second sample in OSR SAMP
|
||||||
|
; OSR is now: 0 0 0 0 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00
|
||||||
|
|
||||||
|
in osr, 4 side 1 ; 12 of first, 4 of second sample in ISR AUTOPUSH
|
||||||
|
; ISR is now: A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00 B03 B02 B01 B00
|
||||||
|
|
||||||
|
out null, 4 side 0
|
||||||
|
; OSR is now: 0 0 0 0 0 0 0 0 B11 B10 B09 B08 B07 B06 B05 B04
|
||||||
|
|
||||||
|
nop side 0
|
||||||
|
;----------------------------------------------------------------------------------------
|
||||||
|
mov x, pins side 1 ; 12 bits of third sample in X, 8 of second remaining in OSR SAMP
|
||||||
|
in osr, 8 side 1 ; 8 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 B11 B10 B09 B08 B07 B06 B05 B04
|
||||||
|
|
||||||
|
mov osr, x side 0
|
||||||
|
; OSR is now 0 0 0 0 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00
|
||||||
|
|
||||||
|
in osr, 8 side 0 ; autopush happening AUTOPUSH
|
||||||
|
; ISR is now: B11 B10 B09 B08 B07 B06 B05 B04 C07 C06 C05 C04 C03 C02 C01 C00
|
||||||
|
;----------------------------------------------------------------------------------------
|
||||||
|
in pins, 12 side 1 ; sample fourth sample to ISR SAMP
|
||||||
|
; ISR is now 0 0 0 0 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00
|
||||||
|
|
||||||
|
out null, 8 side 1
|
||||||
|
; OSR is now 0 0 0 0 0 0 0 0 0 0 0 0 C11 C10 C09 C08
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
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, 1, false, false);
|
||||||
|
|
||||||
|
pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true);
|
||||||
|
pio_gpio_init(pio, clk_pin);
|
||||||
|
|
||||||
|
// 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, pin + 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);
|
||||||
|
}
|
||||||
|
%}
|
||||||
148
apps/external_adc/external_adc.c
Normal file
148
apps/external_adc/external_adc.c
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* 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 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/dma.h"
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
|
||||||
|
#include "picohsdaoh.h"
|
||||||
|
#include "adc_12bit_input.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
|
||||||
|
//#define SYS_CLK 384000 // 48 MHz ADC clock
|
||||||
|
//#define SYS_CLK 402000 // 50.25 MHz ADC clock, maximum that works on my Pico2 (with overvolting)
|
||||||
|
|
||||||
|
// ADC is attached to GP0 - GP11 with clock on GP22
|
||||||
|
#define PIO_INPUT_PIN_BASE 0
|
||||||
|
#define PIO_OUTPUT_CLK_PIN 22
|
||||||
|
|
||||||
|
#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 = 0;
|
||||||
|
|
||||||
|
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 = RBUF_DATA_LEN;
|
||||||
|
|
||||||
|
hsdaoh_update_head(ringbuf_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_pio_input(void)
|
||||||
|
{
|
||||||
|
PIO pio = pio0;
|
||||||
|
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_16);
|
||||||
|
|
||||||
|
dma_channel_configure(
|
||||||
|
DMACH_PIO_PING,
|
||||||
|
&c,
|
||||||
|
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||||
|
&pio->rxf[sm_data],
|
||||||
|
RBUF_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],
|
||||||
|
RBUF_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* set maximum 'allowed' voltage without voiding warranty */
|
||||||
|
vreg_set_voltage(VREG_VOLTAGE_MAX);
|
||||||
|
sleep_ms(1);
|
||||||
|
|
||||||
|
set_sys_clock_khz(SYS_CLK, true);
|
||||||
|
|
||||||
|
/* set HSTX clock to sysclk/2 */
|
||||||
|
hw_write_masked(
|
||||||
|
&clocks_hw->clk[clk_hstx].div,
|
||||||
|
2 << CLOCKS_CLK_HSTX_DIV_INT_LSB,
|
||||||
|
CLOCKS_CLK_HSTX_DIV_INT_BITS
|
||||||
|
);
|
||||||
|
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
hsdaoh_init(ringbuffer);
|
||||||
|
hsdaoh_start();
|
||||||
|
init_pio_input();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
__wfi();
|
||||||
|
}
|
||||||
19
apps/internal_adc/CMakeLists.txt
Normal file
19
apps/internal_adc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
add_executable(internal_adc
|
||||||
|
internal_adc.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(internal_adc PRIVATE -Wall)
|
||||||
|
|
||||||
|
target_link_libraries(internal_adc
|
||||||
|
pico_stdlib
|
||||||
|
hardware_adc
|
||||||
|
hardware_dma
|
||||||
|
pico_util
|
||||||
|
libpicohsdaoh
|
||||||
|
)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(internal_adc 1)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(internal_adc)
|
||||||
161
apps/internal_adc/internal_adc.c
Normal file
161
apps/internal_adc/internal_adc.c
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks
|
||||||
|
* Implementation for the Raspberry Pi RP2350 HSTX peripheral
|
||||||
|
*
|
||||||
|
* Internal ADC example, overclocked to 3,33 MHz sample rate
|
||||||
|
* When using the USB PLL for the ADC, almost 8 MHz sample rate
|
||||||
|
* can be achieved!
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 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/dma.h"
|
||||||
|
#include "hardware/adc.h"
|
||||||
|
#include "hardware/pll.h"
|
||||||
|
|
||||||
|
#include "picohsdaoh.h"
|
||||||
|
|
||||||
|
#define SYS_CLK 320000
|
||||||
|
|
||||||
|
// Channel 0 is GPIO26
|
||||||
|
#define CAPTURE_CHANNEL 1
|
||||||
|
|
||||||
|
#define DMACH_ADC_PING 0
|
||||||
|
#define DMACH_ADC_PONG 1
|
||||||
|
|
||||||
|
static bool dma_adc_pong = false;
|
||||||
|
uint16_t ringbuffer[RBUF_TOTAL_LEN];
|
||||||
|
int ringbuf_head = 0;
|
||||||
|
|
||||||
|
void __scratch_y("") adc_dma_irq_handler()
|
||||||
|
{
|
||||||
|
uint ch_num = dma_adc_pong ? DMACH_ADC_PONG : DMACH_ADC_PING;
|
||||||
|
dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
|
||||||
|
dma_hw->intr = 1u << ch_num;
|
||||||
|
dma_adc_pong = !dma_adc_pong;
|
||||||
|
|
||||||
|
ringbuf_head = (ringbuf_head + 1) % RBUF_SLICES;
|
||||||
|
|
||||||
|
ch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];
|
||||||
|
ch->transfer_count = RBUF_DATA_LEN;
|
||||||
|
|
||||||
|
hsdaoh_update_head(ringbuf_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_adc_input(void)
|
||||||
|
{
|
||||||
|
adc_init();
|
||||||
|
adc_select_input(CAPTURE_CHANNEL);
|
||||||
|
|
||||||
|
adc_fifo_setup(
|
||||||
|
true, // Write each completed conversion to the sample FIFO
|
||||||
|
true, // Enable DMA data request (DREQ)
|
||||||
|
1, // DREQ (and IRQ) asserted when at least 1 sample present
|
||||||
|
false, // Disable the ERR bit
|
||||||
|
false // No shift of samples, use full 12 bit resolution
|
||||||
|
);
|
||||||
|
|
||||||
|
adc_set_clkdiv(0);
|
||||||
|
|
||||||
|
dma_channel_config c;
|
||||||
|
c = dma_channel_get_default_config(DMACH_ADC_PING);
|
||||||
|
channel_config_set_chain_to(&c, DMACH_ADC_PONG);
|
||||||
|
channel_config_set_dreq(&c, DREQ_ADC);
|
||||||
|
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_ADC_PING,
|
||||||
|
&c,
|
||||||
|
&ringbuffer[0 * RBUF_SLICE_LEN],
|
||||||
|
&adc_hw->fifo,
|
||||||
|
RBUF_DATA_LEN,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
c = dma_channel_get_default_config(DMACH_ADC_PONG);
|
||||||
|
channel_config_set_chain_to(&c, DMACH_ADC_PING);
|
||||||
|
channel_config_set_dreq(&c, DREQ_ADC);
|
||||||
|
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_ADC_PONG,
|
||||||
|
&c,
|
||||||
|
&ringbuffer[1 * RBUF_SLICE_LEN],
|
||||||
|
&adc_hw->fifo,
|
||||||
|
RBUF_DATA_LEN,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
dma_hw->ints0 |= (1u << DMACH_ADC_PING) | (1u << DMACH_ADC_PONG);
|
||||||
|
dma_hw->inte0 |= (1u << DMACH_ADC_PING) | (1u << DMACH_ADC_PONG);
|
||||||
|
irq_set_exclusive_handler(DMA_IRQ_0, adc_dma_irq_handler);
|
||||||
|
irq_set_enabled(DMA_IRQ_0, true);
|
||||||
|
|
||||||
|
dma_channel_start(DMACH_ADC_PING);
|
||||||
|
adc_run(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* set maximum 'allowed' voltage without voiding warranty */
|
||||||
|
vreg_set_voltage(VREG_VOLTAGE_MAX);
|
||||||
|
sleep_ms(1);
|
||||||
|
|
||||||
|
set_sys_clock_khz(SYS_CLK, true);
|
||||||
|
|
||||||
|
/* set HSTX clock to sysclk/3 */
|
||||||
|
hw_write_masked(
|
||||||
|
&clocks_hw->clk[clk_hstx].div,
|
||||||
|
3 << CLOCKS_CLK_HSTX_DIV_INT_LSB,
|
||||||
|
CLOCKS_CLK_HSTX_DIV_INT_BITS
|
||||||
|
);
|
||||||
|
|
||||||
|
/* switch ADC clock source to sys_clk */
|
||||||
|
hw_write_masked(
|
||||||
|
&clocks_hw->clk[clk_adc].ctrl,
|
||||||
|
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS << CLOCKS_CLK_ADC_CTRL_AUXSRC_LSB,
|
||||||
|
CLOCKS_CLK_ADC_CTRL_AUXSRC_BITS
|
||||||
|
);
|
||||||
|
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
hsdaoh_init(ringbuffer);
|
||||||
|
hsdaoh_start();
|
||||||
|
init_adc_input();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
__wfi();
|
||||||
|
}
|
||||||
19
libpicohsdaoh/CMakeLists.txt
Normal file
19
libpicohsdaoh/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Note we are using INTERFACE so that the library can be configured per-app
|
||||||
|
# with compile-time defines
|
||||||
|
|
||||||
|
add_library(libpicohsdaoh INTERFACE)
|
||||||
|
|
||||||
|
target_sources(libpicohsdaoh INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/picohsdaoh.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/picohsdaoh.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/data_packet.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/data_packet.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(libpicohsdaoh INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
target_link_libraries(libpicohsdaoh INTERFACE
|
||||||
|
pico_base_headers
|
||||||
|
pico_util
|
||||||
|
pico_multicore
|
||||||
|
hardware_dma
|
||||||
|
)
|
||||||
271
libpicohsdaoh/data_packet.c
Normal file
271
libpicohsdaoh/data_packet.c
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* Implementation of HDMI data packet and info frame encoding
|
||||||
|
* (removed the audio frame encoding not needed by hsdaoh)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2022 by Shuichi Takano
|
||||||
|
* https://github.com/shuichitakano/pico_lib/blob/master/dvi/data_packet.cpp
|
||||||
|
*
|
||||||
|
* ported to C by Marcelo Lorenzati:
|
||||||
|
* https://github.com/mlorenzati/PicoDVI/blob/master/software/libdvi/data_packet.c
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "data_packet.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Compute 8 Parity Start
|
||||||
|
// Parity table is build statically with the following code
|
||||||
|
// for (int i = 0; i < 256; ++i){v_[i] = (i ^ (i >> 1) ^ (i >> 2) ^ (i >> 3) ^ (i >> 4) ^ (i >> 5) ^ (i >> 6) ^ (i >> 7)) & 1;}
|
||||||
|
const uint8_t parityTable[32] = { 0x96, 0x69, 0x69, 0x96, 0x69, 0x96, 0x96, 0x69,
|
||||||
|
0x69, 0x96, 0x96, 0x69, 0x96, 0x69, 0x69, 0x96,
|
||||||
|
0x69, 0x96, 0x96, 0x69, 0x96, 0x69, 0x69, 0x96,
|
||||||
|
0x96, 0x69, 0x69, 0x96, 0x69, 0x96, 0x96, 0x69 };
|
||||||
|
bool compute8(uint8_t index) {
|
||||||
|
return (parityTable[index / 8] >> (index % 8)) & 0x01;
|
||||||
|
}
|
||||||
|
bool compute8_2(uint8_t index1, uint8_t index2) {
|
||||||
|
return compute8(index1) ^ compute8(index2);
|
||||||
|
}
|
||||||
|
bool compute8_3(uint8_t index1, uint8_t index2, uint8_t index3) {
|
||||||
|
return compute8(index1) ^ compute8(index2) ^ compute8(index3);
|
||||||
|
}
|
||||||
|
// Compute 8 Parity End
|
||||||
|
|
||||||
|
// BCH Encoding Start
|
||||||
|
const uint8_t bchTable_[256] = {
|
||||||
|
0x00, 0xd9, 0xb5, 0x6c, 0x6d, 0xb4, 0xd8, 0x01,
|
||||||
|
0xda, 0x03, 0x6f, 0xb6, 0xb7, 0x6e, 0x02, 0xdb,
|
||||||
|
0xb3, 0x6a, 0x06, 0xdf, 0xde, 0x07, 0x6b, 0xb2,
|
||||||
|
0x69, 0xb0, 0xdc, 0x05, 0x04, 0xdd, 0xb1, 0x68,
|
||||||
|
0x61, 0xb8, 0xd4, 0x0d, 0x0c, 0xd5, 0xb9, 0x60,
|
||||||
|
0xbb, 0x62, 0x0e, 0xd7, 0xd6, 0x0f, 0x63, 0xba,
|
||||||
|
0xd2, 0x0b, 0x67, 0xbe, 0xbf, 0x66, 0x0a, 0xd3,
|
||||||
|
0x08, 0xd1, 0xbd, 0x64, 0x65, 0xbc, 0xd0, 0x09,
|
||||||
|
0xc2, 0x1b, 0x77, 0xae, 0xaf, 0x76, 0x1a, 0xc3,
|
||||||
|
0x18, 0xc1, 0xad, 0x74, 0x75, 0xac, 0xc0, 0x19,
|
||||||
|
0x71, 0xa8, 0xc4, 0x1d, 0x1c, 0xc5, 0xa9, 0x70,
|
||||||
|
0xab, 0x72, 0x1e, 0xc7, 0xc6, 0x1f, 0x73, 0xaa,
|
||||||
|
0xa3, 0x7a, 0x16, 0xcf, 0xce, 0x17, 0x7b, 0xa2,
|
||||||
|
0x79, 0xa0, 0xcc, 0x15, 0x14, 0xcd, 0xa1, 0x78,
|
||||||
|
0x10, 0xc9, 0xa5, 0x7c, 0x7d, 0xa4, 0xc8, 0x11,
|
||||||
|
0xca, 0x13, 0x7f, 0xa6, 0xa7, 0x7e, 0x12, 0xcb,
|
||||||
|
0x83, 0x5a, 0x36, 0xef, 0xee, 0x37, 0x5b, 0x82,
|
||||||
|
0x59, 0x80, 0xec, 0x35, 0x34, 0xed, 0x81, 0x58,
|
||||||
|
0x30, 0xe9, 0x85, 0x5c, 0x5d, 0x84, 0xe8, 0x31,
|
||||||
|
0xea, 0x33, 0x5f, 0x86, 0x87, 0x5e, 0x32, 0xeb,
|
||||||
|
0xe2, 0x3b, 0x57, 0x8e, 0x8f, 0x56, 0x3a, 0xe3,
|
||||||
|
0x38, 0xe1, 0x8d, 0x54, 0x55, 0x8c, 0xe0, 0x39,
|
||||||
|
0x51, 0x88, 0xe4, 0x3d, 0x3c, 0xe5, 0x89, 0x50,
|
||||||
|
0x8b, 0x52, 0x3e, 0xe7, 0xe6, 0x3f, 0x53, 0x8a,
|
||||||
|
0x41, 0x98, 0xf4, 0x2d, 0x2c, 0xf5, 0x99, 0x40,
|
||||||
|
0x9b, 0x42, 0x2e, 0xf7, 0xf6, 0x2f, 0x43, 0x9a,
|
||||||
|
0xf2, 0x2b, 0x47, 0x9e, 0x9f, 0x46, 0x2a, 0xf3,
|
||||||
|
0x28, 0xf1, 0x9d, 0x44, 0x45, 0x9c, 0xf0, 0x29,
|
||||||
|
0x20, 0xf9, 0x95, 0x4c, 0x4d, 0x94, 0xf8, 0x21,
|
||||||
|
0xfa, 0x23, 0x4f, 0x96, 0x97, 0x4e, 0x22, 0xfb,
|
||||||
|
0x93, 0x4a, 0x26, 0xff, 0xfe, 0x27, 0x4b, 0x92,
|
||||||
|
0x49, 0x90, 0xfc, 0x25, 0x24, 0xfd, 0x91, 0x48,
|
||||||
|
};
|
||||||
|
|
||||||
|
int encode_BCH_3(const uint8_t *p) {
|
||||||
|
uint8_t v = bchTable_[p[0]];
|
||||||
|
v = bchTable_[p[1] ^ v];
|
||||||
|
v = bchTable_[p[2] ^ v];
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int encode_BCH_7(const uint8_t *p) {
|
||||||
|
uint8_t v = bchTable_[p[0]];
|
||||||
|
v = bchTable_[p[1] ^ v];
|
||||||
|
v = bchTable_[p[2] ^ v];
|
||||||
|
v = bchTable_[p[3] ^ v];
|
||||||
|
v = bchTable_[p[4] ^ v];
|
||||||
|
v = bchTable_[p[5] ^ v];
|
||||||
|
v = bchTable_[p[6] ^ v];
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
// BCH Encoding End
|
||||||
|
|
||||||
|
// TERC4 Start
|
||||||
|
uint16_t TERC4Syms_[16] = {
|
||||||
|
0b1010011100,
|
||||||
|
0b1001100011,
|
||||||
|
0b1011100100,
|
||||||
|
0b1011100010,
|
||||||
|
0b0101110001,
|
||||||
|
0b0100011110,
|
||||||
|
0b0110001110,
|
||||||
|
0b0100111100,
|
||||||
|
0b1011001100,
|
||||||
|
0b0100111001,
|
||||||
|
0b0110011100,
|
||||||
|
0b1011000110,
|
||||||
|
0b1010001110,
|
||||||
|
0b1001110001,
|
||||||
|
0b0101100011,
|
||||||
|
0b1011000011,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t makeTERC4x2Char(int i) { return TERC4Syms_[i] | (TERC4Syms_[i] << 10); }
|
||||||
|
uint32_t makeTERC4x2Char_2(int i0, int i1) { return TERC4Syms_[i0] | (TERC4Syms_[i1] << 10); }
|
||||||
|
#define TERC4_0x2CharSym_ 0x000A729C // Build time generated -> makeTERC4x2Char(0);
|
||||||
|
#define dataGaurdbandSym_ 0x0004CD33 // Build time generated -> 0b0100110011'0100110011;
|
||||||
|
uint32_t defaultDataPacket12_[N_DATA_ISLAND_WORDS] = {
|
||||||
|
dataGaurdbandSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
TERC4_0x2CharSym_,
|
||||||
|
dataGaurdbandSym_,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This table is built in compilation time from a function that uses makeTERC4x2Char
|
||||||
|
uint32_t defaultDataPackets0_[4][N_DATA_ISLAND_WORDS] = {
|
||||||
|
{ 0xa3a8e, 0xa729c, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xa3a8e},
|
||||||
|
{ 0x9c671, 0x98e63, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x9c671},
|
||||||
|
{ 0x58d63, 0xb92e4, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x58d63},
|
||||||
|
{ 0xb0ec3, 0xb8ae2, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb0ec3}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t *getDefaultDataPacket0(bool vsync, bool hsync) {
|
||||||
|
return defaultDataPackets0_[(vsync << 1) | hsync];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TERC4 End
|
||||||
|
|
||||||
|
void compute_header_parity(data_packet_t *data_packet) {
|
||||||
|
data_packet->header[3] = encode_BCH_3(data_packet->header);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute_subpacket_parity(data_packet_t *data_packet, int i) {
|
||||||
|
data_packet->subpacket[i][7] = encode_BCH_7(data_packet->subpacket[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute_parity(data_packet_t *data_packet) {
|
||||||
|
compute_header_parity(data_packet);
|
||||||
|
compute_subpacket_parity(data_packet, 0);
|
||||||
|
compute_subpacket_parity(data_packet, 1);
|
||||||
|
compute_subpacket_parity(data_packet, 2);
|
||||||
|
compute_subpacket_parity(data_packet, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute_info_frame_checkSum(data_packet_t *data_packet) {
|
||||||
|
int s = 0;
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
s += data_packet->header[i];
|
||||||
|
}
|
||||||
|
int n = data_packet->header[2] + 1;
|
||||||
|
for (int j = 0; j < 4; ++j)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 7 && n; ++i, --n)
|
||||||
|
{
|
||||||
|
s += data_packet->subpacket[j][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_packet->subpacket[0][0] = -s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_header(const data_packet_t *data_packet, uint32_t *dst, int hv, bool firstPacket) {
|
||||||
|
int hv1 = hv | 8;
|
||||||
|
if (!firstPacket) {
|
||||||
|
hv = hv1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
uint8_t h = data_packet->header[i];
|
||||||
|
dst[0] = makeTERC4x2Char_2(((h << 2) & 4) | hv, ((h << 1) & 4) | hv1);
|
||||||
|
dst[1] = makeTERC4x2Char_2((h & 4) | hv1, ((h >> 1) & 4) | hv1);
|
||||||
|
dst[2] = makeTERC4x2Char_2(((h >> 2) & 4) | hv1, ((h >> 3) & 4) | hv1);
|
||||||
|
dst[3] = makeTERC4x2Char_2(((h >> 4) & 4) | hv1, ((h >> 5) & 4) | hv1);
|
||||||
|
dst += 4;
|
||||||
|
hv = hv1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_subpacket(const data_packet_t *data_packet, uint32_t *dst1, uint32_t *dst2) {
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
uint32_t v = (data_packet->subpacket[0][i] << 0) | (data_packet->subpacket[1][i] << 8) |
|
||||||
|
(data_packet->subpacket[2][i] << 16) | (data_packet->subpacket[3][i] << 24);
|
||||||
|
uint32_t t = (v ^ (v >> 7)) & 0x00aa00aa;
|
||||||
|
v = v ^ t ^ (t << 7);
|
||||||
|
t = (v ^ (v >> 14)) & 0x0000cccc;
|
||||||
|
v = v ^ t ^ (t << 14);
|
||||||
|
// 01234567 89abcdef ghijklmn opqrstuv
|
||||||
|
// 08go4cks 19hp5dlt 2aiq6emu 3bjr7fnv
|
||||||
|
dst1[0] = makeTERC4x2Char_2((v >> 0) & 15, (v >> 16) & 15);
|
||||||
|
dst1[1] = makeTERC4x2Char_2((v >> 4) & 15, (v >> 20) & 15);
|
||||||
|
dst2[0] = makeTERC4x2Char_2((v >> 8) & 15, (v >> 24) & 15);
|
||||||
|
dst2[1] = makeTERC4x2Char_2((v >> 12) & 15, (v >> 28) & 15);
|
||||||
|
dst1 += 2;
|
||||||
|
dst2 += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_null(data_packet_t *data_packet) {
|
||||||
|
memset(data_packet, 0, sizeof(data_packet_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_AVI_info_frame(data_packet_t *data_packet, scan_info s, pixel_format y, colorimetry c, picture_aspect_ratio m,
|
||||||
|
active_format_aspect_ratio r, RGB_quantization_range q, video_code vic) {
|
||||||
|
set_null(data_packet);
|
||||||
|
data_packet->header[0] = 0x82;
|
||||||
|
data_packet->header[1] = 2; // version
|
||||||
|
data_packet->header[2] = 13; // len
|
||||||
|
|
||||||
|
int sc = 0;
|
||||||
|
// int sc = 3; // scaled hv
|
||||||
|
|
||||||
|
data_packet->subpacket[0][1] = (int)(s) | (r == ACTIVE_FORMAT_ASPECT_RATIO_NO_DATA ? 0 : 16) | ((int)(y) << 5);
|
||||||
|
data_packet->subpacket[0][2] = (int)(r) | ((int)(m) << 4) | ((int)(c) << 6);
|
||||||
|
data_packet->subpacket[0][3] = sc | ((int)(q) << 2);
|
||||||
|
data_packet->subpacket[0][4] = (int)(vic);
|
||||||
|
|
||||||
|
compute_info_frame_checkSum(data_packet);
|
||||||
|
compute_parity(data_packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_data_island(data_island_stream_t *dst, const data_packet_t *packet, bool vsync, bool hsync) {
|
||||||
|
int hv = (vsync ? 2 : 0) | (hsync ? 1 : 0);
|
||||||
|
dst->data[0][0] = makeTERC4x2Char(0b1100 | hv);
|
||||||
|
dst->data[1][0] = dataGaurdbandSym_;
|
||||||
|
dst->data[2][0] = dataGaurdbandSym_;
|
||||||
|
|
||||||
|
encode_header(packet, &dst->data[0][1], hv, true);
|
||||||
|
encode_subpacket(packet, &dst->data[1][1], &dst->data[2][1]);
|
||||||
|
|
||||||
|
dst->data[0][N_DATA_ISLAND_WORDS - 1] = makeTERC4x2Char(0b1100 | hv);
|
||||||
|
dst->data[1][N_DATA_ISLAND_WORDS - 1] = dataGaurdbandSym_;
|
||||||
|
dst->data[2][N_DATA_ISLAND_WORDS - 1] = dataGaurdbandSym_;
|
||||||
|
}
|
||||||
97
libpicohsdaoh/data_packet.h
Normal file
97
libpicohsdaoh/data_packet.h
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
#ifndef DATA_PACKET_H
|
||||||
|
#define DATA_PACKET_H
|
||||||
|
#include "pico.h"
|
||||||
|
|
||||||
|
#define TMDS_CHANNELS 3
|
||||||
|
#define N_LINE_PER_DATA 2
|
||||||
|
#define W_GUARDBAND 2
|
||||||
|
#define W_PREAMBLE 8
|
||||||
|
#define W_DATA_PACKET 32
|
||||||
|
|
||||||
|
#ifndef DVI_SYMBOLS_PER_WORD
|
||||||
|
#define DVI_SYMBOLS_PER_WORD 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DVI_SYMBOLS_PER_WORD != 1 && DVI_SYMBOLS_PER_WORD !=2
|
||||||
|
#error "Unsupported value for DVI_SYMBOLS_PER_WORD"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define W_DATA_ISLAND (W_GUARDBAND * 2 + W_DATA_PACKET)
|
||||||
|
#define N_DATA_ISLAND_WORDS (W_DATA_ISLAND / DVI_SYMBOLS_PER_WORD)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCAN_INFO_NO_DATA,
|
||||||
|
OVERSCAN,
|
||||||
|
UNDERSCAN
|
||||||
|
} scan_info;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RGB,
|
||||||
|
YCBCR422,
|
||||||
|
YCBCR444
|
||||||
|
} pixel_format;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
COLORIMETRY_NO_DATA,
|
||||||
|
ITU601,
|
||||||
|
ITU709,
|
||||||
|
EXTENDED
|
||||||
|
} colorimetry;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PIC_ASPECT_RATIO_NO_DATA,
|
||||||
|
PIC_ASPECT_RATIO_4_3,
|
||||||
|
PIC_ASPECT_RATIO_16_9
|
||||||
|
} picture_aspect_ratio;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ACTIVE_FORMAT_ASPECT_RATIO_NO_DATA = -1,
|
||||||
|
SAME_AS_PAR = 8,
|
||||||
|
ACTIVE_FORMAT_ASPECT_RATIO_4_3,
|
||||||
|
ACTIVE_FORMAT_ASPECT_RATIO_16_9,
|
||||||
|
ACTIVE_FORMAT_ASPECT_RATIO_14_9
|
||||||
|
} active_format_aspect_ratio;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DEFAULT,
|
||||||
|
LIMITED,
|
||||||
|
FULL
|
||||||
|
} RGB_quantization_range;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
_640x480P60 = 1,
|
||||||
|
_720x480P60 = 2,
|
||||||
|
_1280x720P60 = 4,
|
||||||
|
_1920x1080I60 = 5,
|
||||||
|
_1920x1080P60 = 16,
|
||||||
|
} video_code;
|
||||||
|
|
||||||
|
typedef struct data_packet {
|
||||||
|
uint8_t header[4];
|
||||||
|
uint8_t subpacket[4][8];
|
||||||
|
} data_packet_t;
|
||||||
|
|
||||||
|
typedef struct data_island_stream {
|
||||||
|
uint32_t data[TMDS_CHANNELS][N_DATA_ISLAND_WORDS];
|
||||||
|
} data_island_stream_t;
|
||||||
|
|
||||||
|
// Functions related to the data_packet (requires a data_packet instance)
|
||||||
|
void compute_header_parity(data_packet_t *data_packet);
|
||||||
|
void compute_subpacket_parity(data_packet_t *data_packet, int i);
|
||||||
|
void compute_parity(data_packet_t *data_packet);
|
||||||
|
void compute_info_frame_checkSum(data_packet_t *data_packet);
|
||||||
|
void encode_header(const data_packet_t *data_packet, uint32_t *dst, int hv, bool firstPacket);
|
||||||
|
void encode_subpacket(const data_packet_t *data_packet, uint32_t *dst1, uint32_t *dst2);
|
||||||
|
void set_null(data_packet_t *data_packet);
|
||||||
|
void set_AVI_info_frame(data_packet_t *data_packet, scan_info s, pixel_format y, colorimetry c, picture_aspect_ratio m,
|
||||||
|
active_format_aspect_ratio r, RGB_quantization_range q, video_code vic);
|
||||||
|
|
||||||
|
// Public Functions
|
||||||
|
extern uint32_t defaultDataPacket12_[N_DATA_ISLAND_WORDS];
|
||||||
|
inline uint32_t *getDefaultDataPacket12() {
|
||||||
|
return defaultDataPacket12_;
|
||||||
|
}
|
||||||
|
uint32_t *getDefaultDataPacket0(bool vsync, bool hsync);
|
||||||
|
void encode_data_island(data_island_stream_t *dst, const data_packet_t *packet, bool vsync, bool hsync);
|
||||||
|
#endif
|
||||||
371
libpicohsdaoh/picohsdaoh.c
Normal file
371
libpicohsdaoh/picohsdaoh.c
Normal file
|
|
@ -0,0 +1,371 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
*
|
||||||
|
* based on the pico-examples/hstx/dvi_out_hstx_encoder example:
|
||||||
|
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* 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 "hardware/dma.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/irq.h"
|
||||||
|
#include "hardware/structs/bus_ctrl.h"
|
||||||
|
#include "hardware/structs/hstx_ctrl.h"
|
||||||
|
#include "hardware/structs/hstx_fifo.h"
|
||||||
|
#include "pico/multicore.h"
|
||||||
|
|
||||||
|
#include "data_packet.h"
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
|
||||||
|
#include "picohsdaoh.h"
|
||||||
|
|
||||||
|
// Section 5.4.2
|
||||||
|
#define TMDS_CTRL_00 0x354u
|
||||||
|
#define TMDS_CTRL_01 0x0abu
|
||||||
|
#define TMDS_CTRL_10 0x154u
|
||||||
|
#define TMDS_CTRL_11 0x2abu
|
||||||
|
|
||||||
|
#define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
|
||||||
|
#define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
|
||||||
|
#define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
|
||||||
|
#define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
|
||||||
|
#define SYNC_V1_H1_WITH_PREAMBLE (TMDS_CTRL_11 | (TMDS_CTRL_01 << 10) | (TMDS_CTRL_00 << 20))
|
||||||
|
#define SYNC_V0_H0_WITH_DATA_ISLAND_PREAMBLE (TMDS_CTRL_00 | (TMDS_CTRL_01 << 10) | (TMDS_CTRL_01 << 20))
|
||||||
|
#define VIDEO_LEADING_GUARD_BAND (0x2ccu | (0x133u << 10) | (0x2ccu << 20))
|
||||||
|
|
||||||
|
#define HSTX_CMD_RAW (0x0u << 12)
|
||||||
|
#define HSTX_CMD_RAW_REPEAT (0x1u << 12)
|
||||||
|
#define HSTX_CMD_TMDS (0x2u << 12)
|
||||||
|
#define HSTX_CMD_TMDS_REPEAT (0x3u << 12)
|
||||||
|
#define HSTX_CMD_NOP (0xfu << 12)
|
||||||
|
|
||||||
|
uint16_t *ring_buf = NULL;
|
||||||
|
uint16_t idle_line_buf[MODE_H_ACTIVE_PIXELS];
|
||||||
|
uint32_t info_p[64];
|
||||||
|
uint32_t info_len;
|
||||||
|
|
||||||
|
int fifo_tail = 0;
|
||||||
|
int fifo_head = 0;
|
||||||
|
|
||||||
|
static uint32_t vblank_line_vsync_off[] = {
|
||||||
|
HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH,
|
||||||
|
SYNC_V1_H1,
|
||||||
|
HSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH,
|
||||||
|
SYNC_V1_H0,
|
||||||
|
HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS),
|
||||||
|
SYNC_V1_H1,
|
||||||
|
HSTX_CMD_NOP
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t vblank_line_vsync_on[] = {
|
||||||
|
HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH,
|
||||||
|
SYNC_V0_H1,
|
||||||
|
HSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH,
|
||||||
|
SYNC_V0_H0,
|
||||||
|
HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS),
|
||||||
|
SYNC_V0_H1,
|
||||||
|
HSTX_CMD_NOP
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t vactive_line[] = {
|
||||||
|
HSTX_CMD_RAW_REPEAT | (MODE_H_FRONT_PORCH),
|
||||||
|
SYNC_V1_H1,
|
||||||
|
HSTX_CMD_RAW_REPEAT | (MODE_H_SYNC_WIDTH),
|
||||||
|
SYNC_V1_H0,
|
||||||
|
HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH-W_PREAMBLE-W_GUARDBAND),
|
||||||
|
SYNC_V1_H1,
|
||||||
|
HSTX_CMD_RAW_REPEAT | W_PREAMBLE,
|
||||||
|
SYNC_V1_H1_WITH_PREAMBLE,
|
||||||
|
HSTX_CMD_RAW_REPEAT | W_GUARDBAND,
|
||||||
|
VIDEO_LEADING_GUARD_BAND,
|
||||||
|
HSTX_CMD_TMDS | MODE_H_ACTIVE_PIXELS
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Pre-compute the HDMI info packet that is required to switch the MS2130 to YCbCr422 mode */
|
||||||
|
void init_info_packet(void)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
data_packet_t avi_info_frame;
|
||||||
|
data_island_stream_t di_str;
|
||||||
|
|
||||||
|
set_AVI_info_frame(&avi_info_frame, SCAN_INFO_NO_DATA, YCBCR422,
|
||||||
|
ITU601, PIC_ASPECT_RATIO_16_9, SAME_AS_PAR, FULL, _1920x1080P60);
|
||||||
|
encode_data_island(&di_str, &avi_info_frame, 0, 0);
|
||||||
|
|
||||||
|
info_p[len++] = HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH;
|
||||||
|
info_p[len++] = SYNC_V0_H1;
|
||||||
|
info_p[len++] = HSTX_CMD_RAW_REPEAT | (MODE_H_SYNC_WIDTH - W_DATA_ISLAND - W_PREAMBLE);
|
||||||
|
info_p[len++] = SYNC_V0_H0;
|
||||||
|
info_p[len++] = HSTX_CMD_RAW_REPEAT | W_PREAMBLE;
|
||||||
|
info_p[len++] = SYNC_V0_H0_WITH_DATA_ISLAND_PREAMBLE;
|
||||||
|
info_p[len++] = HSTX_CMD_RAW | W_DATA_ISLAND;
|
||||||
|
|
||||||
|
/* convert from the two symbols per word for each channel to one
|
||||||
|
* symbol per word containing all three channels format */
|
||||||
|
for (int i = 0; i < N_DATA_ISLAND_WORDS; i++) {
|
||||||
|
info_p[len++] = (di_str.data[0][i] & 0x3ff) |
|
||||||
|
((di_str.data[1][i] & 0x3ff) << 10) |
|
||||||
|
((di_str.data[2][i] & 0x3ff) << 20);
|
||||||
|
|
||||||
|
info_p[len++] = (di_str.data[0][i] >> 10) |
|
||||||
|
((di_str.data[1][i] >> 10) << 10) |
|
||||||
|
((di_str.data[2][i] >> 10) << 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
info_p[len++] = HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS);
|
||||||
|
info_p[len++] = SYNC_V0_H1;
|
||||||
|
|
||||||
|
info_len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hsdaoh_update_head(int head)
|
||||||
|
{
|
||||||
|
fifo_head = head;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DMACH_HSTX_PING 14
|
||||||
|
#define DMACH_HSTX_PONG 15
|
||||||
|
#define CRC16_INIT 0xffff
|
||||||
|
|
||||||
|
static bool hstx_dma_pong = false;
|
||||||
|
static uint v_scanline = 2;
|
||||||
|
static bool vactive_cmdlist_posted = false;
|
||||||
|
static uint16_t framecnt = 0;
|
||||||
|
|
||||||
|
uint8_t metadata[] = {
|
||||||
|
/* 0xda7acab1 magic word for hsdaoh */
|
||||||
|
0x1, 0xb, 0xa, 0xc, 0xa, 0x7, 0xa, 0xd,
|
||||||
|
/* u16: placeholder for framecounter */
|
||||||
|
0, 0, 0, 0,
|
||||||
|
/* u8: pack_state */
|
||||||
|
0, 0,
|
||||||
|
/* u8: crc_config: CRC16 of last line */
|
||||||
|
1, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* HSTX DMA IRQ handler, reconfigures the channel that just completed while
|
||||||
|
* ther other channel is currently busy */
|
||||||
|
void __scratch_x("") hstx_dma_irq_handler()
|
||||||
|
{
|
||||||
|
uint ch_num = hstx_dma_pong ? DMACH_HSTX_PONG : DMACH_HSTX_PING;
|
||||||
|
dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
|
||||||
|
dma_hw->intr = 1u << ch_num;
|
||||||
|
hstx_dma_pong = !hstx_dma_pong;
|
||||||
|
|
||||||
|
/* for raw commands we need to use 32 bit DMA transfers */
|
||||||
|
ch->al1_ctrl = (ch->al1_ctrl & ~DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) | (DMA_SIZE_32 << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB);
|
||||||
|
|
||||||
|
if (v_scanline >= MODE_V_FRONT_PORCH && v_scanline < (MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH)) {
|
||||||
|
/* on first line of actual VSYNC, output data packet */
|
||||||
|
if (v_scanline == MODE_V_FRONT_PORCH) {
|
||||||
|
dma_sniffer_disable();
|
||||||
|
ch->read_addr = (uintptr_t)info_p;
|
||||||
|
ch->transfer_count = info_len;
|
||||||
|
|
||||||
|
/* update frame counter in metadata */
|
||||||
|
metadata[8] = framecnt & 0xf;
|
||||||
|
metadata[9] = (framecnt >> 4) & 0xf;
|
||||||
|
metadata[10] = (framecnt >> 8) & 0xf;
|
||||||
|
metadata[11] = (framecnt >> 12) & 0xf;
|
||||||
|
framecnt++;
|
||||||
|
} else {
|
||||||
|
ch->read_addr = (uintptr_t)vblank_line_vsync_on;
|
||||||
|
ch->transfer_count = count_of(vblank_line_vsync_on);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (v_scanline < MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH + MODE_V_BACK_PORCH) {
|
||||||
|
ch->read_addr = (uintptr_t)vblank_line_vsync_off;
|
||||||
|
ch->transfer_count = count_of(vblank_line_vsync_off);
|
||||||
|
} else if (!vactive_cmdlist_posted) {
|
||||||
|
ch->read_addr = (uintptr_t)vactive_line;
|
||||||
|
ch->transfer_count = count_of(vactive_line);
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (fifo_head == next_tail) {
|
||||||
|
/* No data to send, use idle line */
|
||||||
|
next_line = idle_line_buf;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t cur_active_line = v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES);
|
||||||
|
|
||||||
|
/* fill in metadata word (last word of line) */
|
||||||
|
if (cur_active_line < sizeof(metadata))
|
||||||
|
next_line[RBUF_SLICE_LEN - 1] |= (metadata[cur_active_line] << 12);
|
||||||
|
|
||||||
|
/* on the second last word of the line, insert the CRC16 of the entire previous line */
|
||||||
|
next_line[RBUF_SLICE_LEN - 2] = dma_sniffer_get_data_accumulator() & 0xffff;
|
||||||
|
|
||||||
|
/* (re)initialize DMA CRC sniffer */
|
||||||
|
dma_sniffer_set_data_accumulator(CRC16_INIT);
|
||||||
|
dma_sniffer_enable(ch_num, DMA_SNIFF_CTRL_CALC_VALUE_CRC16, true);
|
||||||
|
|
||||||
|
/* switch to 16 bit DMA transfer size for the actual data,
|
||||||
|
* because for YCbCr422 TMDS channel 0 is unused */
|
||||||
|
ch->al1_ctrl = (ch->al1_ctrl & ~DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) | (DMA_SIZE_16 << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB);
|
||||||
|
ch->read_addr = (uintptr_t)next_line;
|
||||||
|
ch->transfer_count = MODE_H_ACTIVE_PIXELS;
|
||||||
|
vactive_cmdlist_posted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vactive_cmdlist_posted)
|
||||||
|
v_scanline = (v_scanline + 1) % MODE_V_TOTAL_LINES;
|
||||||
|
}
|
||||||
|
|
||||||
|
void core1_entry()
|
||||||
|
{
|
||||||
|
irq_set_exclusive_handler(DMA_IRQ_3, hstx_dma_irq_handler);
|
||||||
|
irq_set_enabled(DMA_IRQ_3, true);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
__wfi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void hsdaoh_start(void)
|
||||||
|
{
|
||||||
|
multicore_launch_core1(core1_entry);
|
||||||
|
dma_channel_start(DMACH_HSTX_PING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hsdaoh_init(uint16_t *ringbuf)//struct hsdaoh_inst *inst, uint16_t *ringbuf)
|
||||||
|
{
|
||||||
|
ring_buf = ringbuf;
|
||||||
|
|
||||||
|
init_info_packet();
|
||||||
|
|
||||||
|
/* Configure HSTX's TMDS encoder for YCbCr422 stream: L0 is unused in this
|
||||||
|
* mode on the MS2130 and carries the same data as L1. This way we can
|
||||||
|
* conveniently use 16-bit DMA transfers to transparently transfer data to
|
||||||
|
* libhsdaoh on the host */
|
||||||
|
hstx_ctrl_hw->expand_tmds =
|
||||||
|
7 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
|
||||||
|
8 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB |
|
||||||
|
7 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
|
||||||
|
0 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB |
|
||||||
|
7 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
|
||||||
|
0 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
|
||||||
|
|
||||||
|
|
||||||
|
/* Both to-be TMDS encoded pixels and raw control words arrive as one word
|
||||||
|
* per symbol. While the raw control words arrive as 32-bit to carry the 3x 10
|
||||||
|
* bit data for the three lanes, the actual data arrives as a 16-bit word
|
||||||
|
* that gets duplicated before entering the HSTX */
|
||||||
|
hstx_ctrl_hw->expand_shift =
|
||||||
|
1 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
|
||||||
|
0 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
|
||||||
|
1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
|
||||||
|
0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
|
||||||
|
|
||||||
|
/* Serial output config: clock period of 5 cycles, pop from command
|
||||||
|
* expander every 5 cycles, shift the output shiftreg by 2 every cycle. */
|
||||||
|
hstx_ctrl_hw->csr = 0;
|
||||||
|
hstx_ctrl_hw->csr =
|
||||||
|
HSTX_CTRL_CSR_EXPAND_EN_BITS |
|
||||||
|
5u << HSTX_CTRL_CSR_CLKDIV_LSB |
|
||||||
|
5u << HSTX_CTRL_CSR_N_SHIFTS_LSB |
|
||||||
|
2u << HSTX_CTRL_CSR_SHIFT_LSB |
|
||||||
|
HSTX_CTRL_CSR_EN_BITS;
|
||||||
|
|
||||||
|
|
||||||
|
// HSTX outputs 0 through 7 appear on GPIO 12 through 19.
|
||||||
|
// Pinout on Pico DVI sock:
|
||||||
|
//
|
||||||
|
// GP12 D0+ GP13 D0-
|
||||||
|
// GP14 CK+ GP15 CK-
|
||||||
|
// GP16 D2+ GP17 D2-
|
||||||
|
// GP18 D1+ GP19 D1-
|
||||||
|
|
||||||
|
// Assign clock pair to two neighbouring pins:
|
||||||
|
hstx_ctrl_hw->bit[2] = HSTX_CTRL_BIT0_CLK_BITS;
|
||||||
|
hstx_ctrl_hw->bit[3] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;
|
||||||
|
for (uint lane = 0; lane < 3; ++lane) {
|
||||||
|
// For each TMDS lane, assign it to the correct GPIO pair based on the
|
||||||
|
// desired pinout:
|
||||||
|
static const int lane_to_output_bit[3] = {0, 6, 4};
|
||||||
|
int bit = lane_to_output_bit[lane];
|
||||||
|
// Output even bits during first half of each HSTX cycle, and odd bits
|
||||||
|
// during second half. The shifter advances by two bits each cycle.
|
||||||
|
uint32_t lane_data_sel_bits =
|
||||||
|
(lane * 10 ) << HSTX_CTRL_BIT0_SEL_P_LSB |
|
||||||
|
(lane * 10 + 1) << HSTX_CTRL_BIT0_SEL_N_LSB;
|
||||||
|
// The two halves of each pair get identical data, but one pin is inverted.
|
||||||
|
hstx_ctrl_hw->bit[bit ] = lane_data_sel_bits;
|
||||||
|
hstx_ctrl_hw->bit[bit + 1] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 12; i <= 19; ++i)
|
||||||
|
gpio_set_function(i, 0); // HSTX
|
||||||
|
|
||||||
|
/* Both channels are set up identically, to transfer a whole scanline and
|
||||||
|
* then chain to the opposite channel. Each time a channel finishes, we
|
||||||
|
* reconfigure the one that just finished, meanwhile the opposite channel
|
||||||
|
* is already making progress. */
|
||||||
|
dma_channel_config c;
|
||||||
|
c = dma_channel_get_default_config(DMACH_HSTX_PING);
|
||||||
|
channel_config_set_chain_to(&c, DMACH_HSTX_PONG);
|
||||||
|
channel_config_set_dreq(&c, DREQ_HSTX);
|
||||||
|
channel_config_set_sniff_enable(&c, true);
|
||||||
|
dma_channel_configure(
|
||||||
|
DMACH_HSTX_PING,
|
||||||
|
&c,
|
||||||
|
&hstx_fifo_hw->fifo,
|
||||||
|
vblank_line_vsync_off,
|
||||||
|
count_of(vblank_line_vsync_off),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
c = dma_channel_get_default_config(DMACH_HSTX_PONG);
|
||||||
|
channel_config_set_chain_to(&c, DMACH_HSTX_PING);
|
||||||
|
channel_config_set_dreq(&c, DREQ_HSTX);
|
||||||
|
channel_config_set_sniff_enable(&c, true);
|
||||||
|
dma_channel_configure(
|
||||||
|
DMACH_HSTX_PONG,
|
||||||
|
&c,
|
||||||
|
&hstx_fifo_hw->fifo,
|
||||||
|
vblank_line_vsync_off,
|
||||||
|
count_of(vblank_line_vsync_off),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
dma_hw->ints3 = (1u << DMACH_HSTX_PING) | (1u << DMACH_HSTX_PONG);
|
||||||
|
dma_hw->inte3 = (1u << DMACH_HSTX_PING) | (1u << DMACH_HSTX_PONG);
|
||||||
|
|
||||||
|
/* give the DMA the priority over the CPU on the bus */
|
||||||
|
bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;
|
||||||
|
}
|
||||||
37
libpicohsdaoh/picohsdaoh.h
Normal file
37
libpicohsdaoh/picohsdaoh.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef _PICOHSDAOH_H
|
||||||
|
#define _PICOHSDAOH_H
|
||||||
|
|
||||||
|
// trimmed to absolute minimum timings the MS2130 accepts
|
||||||
|
#define MODE_H_FRONT_PORCH 6
|
||||||
|
#define MODE_H_SYNC_WIDTH 45
|
||||||
|
#define MODE_H_BACK_PORCH 11
|
||||||
|
#define MODE_H_ACTIVE_PIXELS 1920
|
||||||
|
|
||||||
|
#define MODE_V_FRONT_PORCH 1
|
||||||
|
#define MODE_V_SYNC_WIDTH 2
|
||||||
|
#define MODE_V_BACK_PORCH 1
|
||||||
|
#define MODE_V_ACTIVE_LINES 1080
|
||||||
|
|
||||||
|
#define MODE_H_TOTAL_PIXELS ( \
|
||||||
|
MODE_H_FRONT_PORCH + MODE_H_SYNC_WIDTH + \
|
||||||
|
MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS \
|
||||||
|
)
|
||||||
|
#define MODE_V_TOTAL_LINES ( \
|
||||||
|
MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH + \
|
||||||
|
MODE_V_BACK_PORCH + MODE_V_ACTIVE_LINES \
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 RBUF_SLICES 16
|
||||||
|
#define RBUF_SLICE_LEN MODE_H_ACTIVE_PIXELS
|
||||||
|
#define RBUF_DATA_LEN (RBUF_SLICE_LEN - NUM_RESERVED_WORDS)
|
||||||
|
#define RBUF_TOTAL_LEN (RBUF_SLICE_LEN * RBUF_SLICES)
|
||||||
|
|
||||||
|
void hsdaoh_start(void);
|
||||||
|
void hsdaoh_init(uint16_t *ringbuf);
|
||||||
|
void hsdaoh_update_head(int head);
|
||||||
|
|
||||||
|
#endif
|
||||||
84
pico_sdk_import.cmake
Normal file
84
pico_sdk_import.cmake
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
|
||||||
|
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
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 ()
|
||||||
|
# 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 ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||||
|
GIT_SUBMODULES_RECURSE FALSE
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
Loading…
Add table
Reference in a new issue