diff --git a/CMakeLists.txt b/CMakeLists.txt index 3afbdd3..45fca61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ add_compile_options(-Wall ) add_executable(ceda2vga + src/crtc.c src/main.c src/draw_demo.c src/framebuffer.c @@ -27,6 +28,10 @@ pico_generate_pio_header(ceda2vga ${CMAKE_CURRENT_LIST_DIR}/src/vga.pio ) +pico_generate_pio_header(ceda2vga + ${CMAKE_CURRENT_LIST_DIR}/src/crtc.pio +) + # configure stdio to make I/O on virtual usb serial port pico_enable_stdio_usb(ceda2vga 1) pico_enable_stdio_uart(ceda2vga 0) diff --git a/src/crtc.c b/src/crtc.c new file mode 100644 index 0000000..c000cfc --- /dev/null +++ b/src/crtc.c @@ -0,0 +1,148 @@ +#include "crtc.h" + +#include + +#include "hardware/pio.h" +#include "hardware/structs/pio.h" +#include "hardware/dma.h" + +#include "framebuffer.h" + +#include "crtc.pio.h" + +typedef struct PIORun +{ + PIO pio; //< executing PIO + uint offset; //< PIO memory offset for program + uint sm; //< executing SM +} PIORun; + +static PIORun crtc_vsync, crtc_hsync, crtc_pixel; +static int dma_channel; +bool dma_ready = false; + +static void crtc_pixel_program_init(PIO pio, uint sm, uint offset) +{ + pio_sm_config config = crtc_pixel_program_get_default_config(offset); + + pio_gpio_init(pio, 11); + sm_config_set_in_pins(&config, 11); + sm_config_set_in_shift(&config, true, true, 0); + + pio_sm_set_consecutive_pindirs(pio, sm, 11, 1, false); + sm_config_set_wrap(&config, offset + crtc_pixel_wrap_target, offset + crtc_pixel_wrap); + + pio_sm_init(pio, sm, offset, &config); + + // not quite 4x pixel clock (2.1 = 2,25.6) + pio_sm_set_clkdiv_int_frac(pio, sm, 2, 26); + + pio_sm_set_enabled(pio, sm, true); +} + +static void crtc_hsync_program_init(PIO pio, uint sm, uint offset) +{ + pio_sm_config config = crtc_hsync_program_get_default_config(offset); + + sm_config_set_wrap(&config, offset + crtc_hsync_wrap_target, offset + crtc_hsync_wrap); + + pio_sm_init(pio, sm, offset, &config); + pio_sm_set_clkdiv_int_frac(pio, sm, 2, 26); + pio_sm_set_enabled(pio, sm, true); +} + +static void crtc_vsync_program_init(PIO pio, uint sm, uint offset) +{ + pio_sm_config config = crtc_vsync_program_get_default_config(offset); + + pio_gpio_init(pio, 15); + sm_config_set_wrap(&config, offset + crtc_vsync_wrap_target, offset + crtc_vsync_wrap); + + pio_sm_init(pio, sm, offset, &config); + pio_sm_set_clkdiv_int_frac(pio, sm, 2, 26); + pio_sm_set_enabled(pio, sm, true); +} + +int dma_counter = 0; +static void dma_handler(void) +{ + dma_counter++; + + // reset DMA + dma_channel_set_write_addr(dma_channel, &frame[2560], true); + + // acknoweledge DMA + dma_hw->ints1 = 1 << dma_channel; +} + +int crtc_frame_counter = 0; + +static void new_frame_handler(void) +{ + pio_sm_clear_fifos(crtc_pixel.pio, crtc_pixel.sm); + + crtc_frame_counter++; + + pio_interrupt_clear(pio1_hw, 1); +} + +void crtc_machines_init(void) +{ + crtc_hsync.pio = crtc_vsync.pio = crtc_pixel.pio = pio1; + + printf("Starting CRTC pixel machine... "); + if (!pio_can_add_program(crtc_pixel.pio, &crtc_pixel_program)) + panic("cannot add program"); + crtc_pixel.offset = pio_add_program(crtc_pixel.pio, &crtc_pixel_program); + crtc_pixel.sm = pio_claim_unused_sm(crtc_pixel.pio, true); + crtc_pixel_program_init(crtc_pixel.pio, crtc_pixel.sm, crtc_pixel.offset); + pio_sm_put_blocking(crtc_pixel.pio, crtc_pixel.sm, 640 - 1); + printf("OK\n"); + + printf("Starting CRTC vsync machine... "); + if (!pio_can_add_program(crtc_vsync.pio, &crtc_vsync_program)) + panic("cannot add program"); + crtc_vsync.offset = pio_add_program(crtc_vsync.pio, &crtc_vsync_program); + crtc_vsync.sm = pio_claim_unused_sm(crtc_vsync.pio, true); + crtc_vsync_program_init(crtc_vsync.pio, crtc_vsync.sm, crtc_vsync.offset); + printf("OK\n"); + + printf("Starting CRTC hsync machine... "); + if (!pio_can_add_program(crtc_hsync.pio, &crtc_hsync_program)) + panic("cannot add program"); + crtc_hsync.offset = pio_add_program(crtc_hsync.pio, &crtc_hsync_program); + crtc_hsync.sm = pio_claim_unused_sm(crtc_hsync.pio, true); + crtc_hsync_program_init(crtc_hsync.pio, crtc_hsync.sm, crtc_hsync.offset); + pio_sm_put_blocking(crtc_hsync.pio, crtc_hsync.sm, 128 - 1); + printf("OK\n"); + + printf("Setting up new frame interrupt... "); + // Frame interrupt is asserted on PIO1 interrupt 1 by SM1 + pio_set_irq1_source_enabled(crtc_vsync.pio, pis_interrupt1, + true); // allow SM1 of PIO to fire the interrupt + irq_set_exclusive_handler(PIO1_IRQ_1, new_frame_handler); // set handler for IRQ1 of PIO + irq_set_enabled(PIO1_IRQ_1, true); // enable interrupt + printf("OK\n"); + + printf("Setting up CRTC DMA... "); + dma_channel = dma_claim_unused_channel(true); + printf("channel %d... ", dma_channel); + dma_channel_config dma_config = dma_channel_get_default_config(dma_channel); + channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32); + channel_config_set_read_increment(&dma_config, false); + channel_config_set_write_increment(&dma_config, true); + + channel_config_set_dreq(&dma_config, DREQ_PIO1_RX0); + + // setup transfer acknowledge interrupt + dma_channel_set_irq1_enabled(dma_channel, true); + + irq_set_exclusive_handler(DMA_IRQ_1, dma_handler); + irq_set_enabled(DMA_IRQ_1, true); + + dma_channel_configure(dma_channel, &dma_config, &frame[2560], &pio1_hw->rxf[0], 640 * 128 / 32, + true); + + dma_ready = true; + printf("OK\n"); +} diff --git a/src/crtc.h b/src/crtc.h new file mode 100644 index 0000000..55486b3 --- /dev/null +++ b/src/crtc.h @@ -0,0 +1,6 @@ +#ifndef CRTC_H +#define CRTC_H + +void crtc_machines_init(void); + +#endif diff --git a/src/crtc.pio b/src/crtc.pio new file mode 100644 index 0000000..5074e8e --- /dev/null +++ b/src/crtc.pio @@ -0,0 +1,37 @@ +.program crtc_pixel + +entrypoint_crtc_pixel: + pull + +.wrap_target + mov y, osr + wait irq 6 +visible_line_loop: + in pins, 1 [2] + jmp y-- visible_line_loop +.wrap + +.program crtc_hsync + pull + +.wrap_target + mov y, osr + wait irq 1 +scanlines_loop: + set x, 7 + wait 1 gpio 14 + wait 0 gpio 14 +back_porch_loop: + jmp x-- back_porch_loop [31] + irq set 6 + jmp y-- scanlines_loop +.wrap + + +.program crtc_vsync +entrypoint_ceda_crt_vsync: +.wrap_target + wait 0 gpio 15 + wait 1 gpio 15 + irq set 1 +.wrap diff --git a/src/main.c b/src/main.c index 4278441..19d9715 100644 --- a/src/main.c +++ b/src/main.c @@ -17,15 +17,42 @@ #include "draw_demo.h" #include "framebuffer.h" #include "vga.h" +#include "crtc.h" + +extern int crtc_frame_counter; +extern int dma_counter; int main() { stdio_init_all(); + sleep_ms(5000); - draw_demo(); + // draw_demo(); + // horizontal lines + draw_line(0, 0, 640 - 1, 0, true); + draw_line(0, 240 - 1, 640 - 1, 240 - 1, true); + draw_line(0, 480 - 1, 640 - 1, 480 - 1, true); + + // vertical lines + draw_line(0, 0, 0, 480 - 1, true); + draw_line(320 - 1, 0, 320 - 1, 480 - 1, true); + draw_line(638, 0, 638, 480 - 1, true); // last pixel of a line can't be set, see set_pixel() vga_machines_init(); + sleep_ms(10); + + crtc_machines_init(); + + printf("All ready: OK\n"); + + for (;;) + { + printf("CRTC frames: %d\n", crtc_frame_counter); + printf("DMA counter: %d\n", dma_counter); + sleep_ms(1000); + } + for (;;) { // moving sine diff --git a/src/vga.c b/src/vga.c index 97e71d6..0915f31 100644 --- a/src/vga.c +++ b/src/vga.c @@ -237,7 +237,7 @@ void vga_machines_init(void) pio_sm_put_blocking(vga_pixel.pio, vga_pixel.sm, 640 - 1); printf("OK\n"); - printf("Setting up DMA... "); + printf("Setting up VGA DMA... "); dma_channel = dma_claim_unused_channel(true); dma_channel_config dma_config = dma_channel_get_default_config(dma_channel); channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32); @@ -260,8 +260,6 @@ void vga_machines_init(void) dma_ready = true; printf("OK\n"); - - printf("Now running.\n"); } /**