New dma layout
This commit is contained in:
101
main.c
101
main.c
@@ -20,20 +20,16 @@
|
||||
#define DATA_PIN 2 // pixel / luma bit pin
|
||||
#define SYNC_PIN 3 // sync pin
|
||||
|
||||
// Set to 1 to bypass DMA/framebuffer and generate fixed black/white bars.
|
||||
#define VIDEO_DIAGNOSTIC_BARS 0
|
||||
|
||||
// ---------------------------
|
||||
// Video format
|
||||
// ---------------------------
|
||||
// NTSC-compatible 240p timing using the same 64us line structure as the
|
||||
// original PAL code, but with 262 lines/frame at about 59.6Hz.
|
||||
#define VIDEO_WIDTH 768
|
||||
#define VIDEO_HEIGHT 240
|
||||
#define VIDEO_HEIGHT 576
|
||||
#define WORDS_PER_LINE (VIDEO_WIDTH / 32)
|
||||
#define LINES_PER_FIELD (VIDEO_HEIGHT / 2)
|
||||
|
||||
#define SYNC_INTERVAL_S 0.000002f // 2 us per sync-SM instruction
|
||||
#define ACTIVE_VIDEO_S 0.000050f // 52 us active video per line
|
||||
#define ACTIVE_VIDEO_S 0.000052f // 52 us active video per line
|
||||
|
||||
// Match the .pio program's published constant.
|
||||
// pioasm will emit CLOCKS_PER_BIT in the generated header.
|
||||
@@ -47,8 +43,11 @@
|
||||
// ---------------------------
|
||||
static uint32_t framebuffer[VIDEO_HEIGHT][WORDS_PER_LINE];
|
||||
|
||||
// Current active scanline within the progressive frame.
|
||||
static volatile uint line_in_frame = 0;
|
||||
// Field/line tracking for line-based DMA.
|
||||
// This is intentionally kept in software to match the sync SM's 288 active
|
||||
// lines per field.
|
||||
static volatile bool even_field = true;
|
||||
static volatile uint line_in_field = 0;
|
||||
|
||||
// DMA channel that feeds the PIO TX FIFO
|
||||
static int video_dma_chan;
|
||||
@@ -57,6 +56,7 @@ static int video_dma_chan;
|
||||
// Optional test pattern
|
||||
// Replace this with your own drawing code.
|
||||
// ---------------------------
|
||||
#define BORDER 40
|
||||
static void fill_test_pattern(void) {
|
||||
memset(framebuffer, 0x00, sizeof(framebuffer));
|
||||
|
||||
@@ -66,7 +66,7 @@ static void fill_test_pattern(void) {
|
||||
|
||||
// Simple visible pattern:
|
||||
// border + checker
|
||||
if (x < 20 || x >= VIDEO_WIDTH - 20 || y < 20 || y >= VIDEO_HEIGHT - 20) {
|
||||
if (x < BORDER || x >= VIDEO_WIDTH - BORDER || y < BORDER || y >= VIDEO_HEIGHT - BORDER) {
|
||||
on = true;
|
||||
} else if (((x >> 4) ^ (y >> 4)) & 1) {
|
||||
on = true;
|
||||
@@ -107,18 +107,6 @@ static void init_cvdata_program(PIO pio, uint sm, uint offset, float clkdiv, uin
|
||||
pio_sm_exec(pio, sm, pio_encode_out(pio_null, 32));
|
||||
}
|
||||
|
||||
static void init_cvdata_diag_program(PIO pio, uint sm, uint offset, float clkdiv, uint data_pin) {
|
||||
pio_sm_config c = cvdata_diag_program_get_default_config(offset);
|
||||
|
||||
sm_config_set_set_pins(&c, data_pin, 1);
|
||||
sm_config_set_clkdiv(&c, clkdiv);
|
||||
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
static void init_cvsync_program(PIO pio, uint sm, uint offset, float clkdiv, uint sync_pin) {
|
||||
pio_sm_config c = cvsync_program_get_default_config(offset);
|
||||
|
||||
@@ -130,9 +118,12 @@ static void init_cvsync_program(PIO pio, uint sm, uint offset, float clkdiv, uin
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
|
||||
// Preload OSR = active_lines_per_frame - 1
|
||||
pio_sm_put_blocking(pio, sm, VIDEO_HEIGHT - 1);
|
||||
// Preload OSR = lines_per_field - 1
|
||||
pio_sm_put_blocking(pio, sm, LINES_PER_FIELD - 1);
|
||||
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
|
||||
|
||||
// Start with Y = 0, meaning "even field" in this program.
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0));
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
@@ -163,29 +154,28 @@ static void init_video_dma(PIO pio, uint data_sm) {
|
||||
// Start DMA for one scanline
|
||||
// ---------------------------
|
||||
static inline void start_dma_for_current_line(void) {
|
||||
dma_channel_set_read_addr(video_dma_chan, framebuffer[line_in_frame], false);
|
||||
uint src_line = even_field ? (line_in_field * 2u) : (line_in_field * 2u + 1u);
|
||||
|
||||
dma_channel_set_read_addr(video_dma_chan, framebuffer[src_line], false);
|
||||
dma_channel_set_trans_count(video_dma_chan, WORDS_PER_LINE, true);
|
||||
|
||||
line_in_frame++;
|
||||
if (line_in_frame >= VIDEO_HEIGHT) {
|
||||
line_in_frame = 0;
|
||||
line_in_field++;
|
||||
if (line_in_field >= LINES_PER_FIELD) {
|
||||
line_in_field = 0;
|
||||
even_field = !even_field;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// PIO IRQ handler:
|
||||
// fires once per active scanline when cvsync executes `irq set LINE_IRQ`.
|
||||
// We clear the PIO IRQ and arm one DMA transfer for the next active line.
|
||||
// The sync SM pushes one token into its RX FIFO during each line's back porch.
|
||||
// As soon as the previous line DMA has finished, consume one token and arm the
|
||||
// next scanline so the data FIFO is primed before visible pixels start.
|
||||
// ---------------------------
|
||||
static void pio0_irq0_handler(void) {
|
||||
if (pio_interrupt_get(VIDEO_PIO, 1)) {
|
||||
pio_interrupt_clear(VIDEO_PIO, 1);
|
||||
|
||||
// A new line request arrives during hsync/back porch so the DMA can
|
||||
// prime the data SM before visible pixels begin.
|
||||
if (!dma_channel_is_busy(video_dma_chan)) {
|
||||
start_dma_for_current_line();
|
||||
}
|
||||
if (!pio_sm_is_rx_fifo_empty(VIDEO_PIO, SYNC_SM) && !dma_channel_is_busy(video_dma_chan)) {
|
||||
(void)pio_sm_get(VIDEO_PIO, SYNC_SM);
|
||||
start_dma_for_current_line();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,41 +188,30 @@ static void video_init(void) {
|
||||
}
|
||||
|
||||
uint sync_offset = pio_add_program(VIDEO_PIO, &cvsync_program);
|
||||
uint data_offset;
|
||||
uint data_offset = pio_add_program(VIDEO_PIO, &cvdata_program);
|
||||
|
||||
// sync SM: 1 instruction every 0.5 us
|
||||
// Same timing idea as the original repo:
|
||||
// sync SM: 1 instruction every 2 us
|
||||
// data SM: enough PIO cycles to emit VIDEO_WIDTH bits in 52 us
|
||||
float sys_hz = (float)clock_get_hz(clk_sys);
|
||||
float data_clkdiv = (sys_hz / ((float)VIDEO_WIDTH / ACTIVE_VIDEO_S)) / (float)CLOCKS_PER_BIT;
|
||||
float sync_clkdiv = sys_hz * SYNC_INTERVAL_S;
|
||||
|
||||
init_cvsync_program(VIDEO_PIO, SYNC_SM, sync_offset, sync_clkdiv, SYNC_PIN);
|
||||
|
||||
#if VIDEO_DIAGNOSTIC_BARS
|
||||
// 0.5 us per instruction gives a visible bar pattern over the 52 us active period.
|
||||
float diag_clkdiv = sys_hz * 0.0000005f;
|
||||
data_offset = pio_add_program(VIDEO_PIO, &cvdata_diag_program);
|
||||
init_cvdata_diag_program(VIDEO_PIO, DATA_SM, data_offset, diag_clkdiv, DATA_PIN);
|
||||
#else
|
||||
// data SM: enough PIO cycles to emit VIDEO_WIDTH bits in 52 us
|
||||
float data_clkdiv = (sys_hz / ((float)VIDEO_WIDTH / ACTIVE_VIDEO_S)) / (float)CLOCKS_PER_BIT;
|
||||
data_offset = pio_add_program(VIDEO_PIO, &cvdata_program);
|
||||
init_cvdata_program(VIDEO_PIO, DATA_SM, data_offset, data_clkdiv, DATA_PIN);
|
||||
init_cvsync_program(VIDEO_PIO, SYNC_SM, sync_offset, sync_clkdiv, SYNC_PIN);
|
||||
init_video_dma(VIDEO_PIO, DATA_SM);
|
||||
#endif
|
||||
|
||||
// Route PIO internal IRQ 1 to CPU IRQ 0. IRQ 0 stays reserved for the
|
||||
// data SM's wait instruction inside the PIO block.
|
||||
pio_interrupt_clear(VIDEO_PIO, 0);
|
||||
pio_interrupt_clear(VIDEO_PIO, 1);
|
||||
pio_set_irq0_source_enabled(VIDEO_PIO, pis_interrupt1, true);
|
||||
// Route sync-SM RX FIFO notifications to CPU IRQ 0.
|
||||
pio_set_irq0_source_enabled(VIDEO_PIO, pis_sm1_rx_fifo_not_empty, true);
|
||||
irq_set_exclusive_handler(PIO0_IRQ_0, pio0_irq0_handler);
|
||||
irq_set_enabled(PIO0_IRQ_0, true);
|
||||
|
||||
// Start with the first active line of the frame.
|
||||
line_in_frame = 0;
|
||||
// Start with the first line of the even field
|
||||
even_field = true;
|
||||
line_in_field = 0;
|
||||
|
||||
#if !VIDEO_DIAGNOSTIC_BARS
|
||||
// Prime the FIFO so the very first active line does not depend on IRQ latency.
|
||||
start_dma_for_current_line();
|
||||
#endif
|
||||
|
||||
pio_sm_set_enabled(VIDEO_PIO, DATA_SM, true);
|
||||
pio_sm_set_enabled(VIDEO_PIO, SYNC_SM, true);
|
||||
|
||||
Reference in New Issue
Block a user