FlashMyPico.com
log in about discover docs scratchpad
This website requires a Chrome-based browser in order to access the WebUSB API. Without it, you may not be able to flash your device.

dvi_out_hstx_encoder
for Raspberry Pi Picoβ„’ 2 W, submitted by flashmypico

Loading...

// Copyright (c) 2024 Raspberry Pi (Trading) Ltd. // Generate DVI output using the command expander and TMDS encoder in HSTX. // This example requires an external digital video connector connected to // GPIOs 12 through 19 (the HSTX-capable GPIOs) with appropriate // current-limiting resistors, e.g. 270 ohms. The pinout used in this example // matches the Pico DVI Sock board, which can be soldered onto a Pico 2: // https://github.com/Wren6991/Pico-DVI-Sock #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 "hardware/structs/sio.h" #include "pico/multicore.h" #include "pico/sem.h" #include "mountains_640x480_rgb332.h" #define framebuf mountains_640x480 // ---------------------------------------------------------------------------- // DVI constants #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 MODE_H_SYNC_POLARITY 0 #define MODE_H_FRONT_PORCH 16 #define MODE_H_SYNC_WIDTH 96 #define MODE_H_BACK_PORCH 48 #define MODE_H_ACTIVE_PIXELS 640 #define MODE_V_SYNC_POLARITY 0 #define MODE_V_FRONT_PORCH 10 #define MODE_V_SYNC_WIDTH 2 #define MODE_V_BACK_PORCH 33 #define MODE_V_ACTIVE_LINES 480 #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 \ ) #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) // ---------------------------------------------------------------------------- // HSTX command lists // Lists are padded with NOPs to be >= HSTX FIFO size, to avoid DMA rapidly // pingponging and tripping up the IRQs. 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_NOP, HSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH, SYNC_V1_H0, HSTX_CMD_NOP, HSTX_CMD_RAW_REPEAT | MODE_H_BACK_PORCH, SYNC_V1_H1, HSTX_CMD_TMDS | MODE_H_ACTIVE_PIXELS }; // ---------------------------------------------------------------------------- // DMA logic #define DMACH_PING 0 #define DMACH_PONG 1 // First we ping. Then we pong. Then... we ping again. static bool dma_pong = false; // A ping and a pong are cued up initially, so the first time we enter this // handler it is to cue up the second ping after the first ping has completed. // This is the third scanline overall (-> =2 because zero-based). static uint v_scanline = 2; // During the vertical active period, we take two IRQs per scanline: one to // post the command list, and another to post the pixels. static bool vactive_cmdlist_posted = false; void __scratch_x("") dma_irq_handler() { // dma_pong indicates the channel that just finished, which is the one // we're about to reload. uint ch_num = dma_pong ? DMACH_PONG : DMACH_PING; dma_channel_hw_t *ch = &dma_hw->ch[ch_num]; dma_hw->intr = 1u << ch_num; dma_pong = !dma_pong; if (v_scanline >= MODE_V_FRONT_PORCH && v_scanline < (MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH)) { 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 { ch->read_addr = (uintptr_t)&framebuf[(v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES)) * MODE_H_ACTIVE_PIXELS]; ch->transfer_count = MODE_H_ACTIVE_PIXELS / sizeof(uint32_t); vactive_cmdlist_posted = false; } if (!vactive_cmdlist_posted) { v_scanline = (v_scanline + 1) % MODE_V_TOTAL_LINES; } } // ---------------------------------------------------------------------------- // Main program static __force_inline uint16_t colour_rgb565(uint8_t r, uint8_t g, uint8_t b) { return ((uint16_t)r & 0xf8) >> 3 | ((uint16_t)g & 0xfc) << 3 | ((uint16_t)b & 0xf8) << 8; } static __force_inline uint8_t colour_rgb332(uint8_t r, uint8_t g, uint8_t b) { return (r & 0xc0) >> 6 | (g & 0xe0) >> 3 | (b & 0xe0) >> 0; } void scroll_framebuffer(void); int main(void) { // Configure HSTX's TMDS encoder for RGB332 hstx_ctrl_hw->expand_tmds = 2 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB | 0 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB | 2 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB | 29 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB | 1 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB | 26 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB; // Pixels (TMDS) come in 4 8-bit chunks. Control symbols (RAW) are an // entire 32-bit word. hstx_ctrl_hw->expand_shift = 4 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB | 8 << 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; // Note we are leaving the HSTX clock at the SDK default of 125 MHz; since // we shift out two bits per HSTX clock cycle, this gives us an output of // 250 Mbps, which is very close to the bit clock for 480p 60Hz (252 MHz). // If we want the exact rate then we'll have to reconfigure PLLs. // 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_PING); channel_config_set_chain_to(&c, DMACH_PONG); channel_config_set_dreq(&c, DREQ_HSTX); dma_channel_configure( DMACH_PING, &c, &hstx_fifo_hw->fifo, vblank_line_vsync_off, count_of(vblank_line_vsync_off), false ); c = dma_channel_get_default_config(DMACH_PONG); channel_config_set_chain_to(&c, DMACH_PING); channel_config_set_dreq(&c, DREQ_HSTX); dma_channel_configure( DMACH_PONG, &c, &hstx_fifo_hw->fifo, vblank_line_vsync_off, count_of(vblank_line_vsync_off), false ); dma_hw->ints0 = (1u << DMACH_PING) | (1u << DMACH_PONG); dma_hw->inte0 = (1u << DMACH_PING) | (1u << DMACH_PONG); irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler); irq_set_enabled(DMA_IRQ_0, true); bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS; dma_channel_start(DMACH_PING); while (1) __wfi(); }
This firmware image was imported from the pico-examples repository. 


Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.

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.
Homepage: https://github.com/raspberrypi/pico-examples
Repository: https://github.com/raspberrypi/pico-examples/tree/master/hstx/dvi_out_hstx_encoder

β“˜ Tips