/** * @file main.c * @author giomba@glgprograms.it * @brief * * @copyright Copyright Retrofficina GLG Programs (c) 2022 * */ #include "hardware/clocks.h" #include "hardware/dma.h" #include "hardware/pio.h" #include "hardware/pll.h" #include "hardware/structs/pio.h" #include "pico/stdlib.h" #include #include #include #include "vga.pio.h" #define FRAMES 2 #define HPIXEL 640 #define VPIXEL 480 #define BPP 1 typedef struct Frame { uint8_t data[HPIXEL * VPIXEL * BPP / 8]; } Frame; static Frame frames[FRAMES] = {0}; static void vga_pixel_program_init(PIO pio, uint sm, uint offset) { // magic definition of the called function? pio_sm_config config = vga_free_run_program_get_default_config(offset); // destination pins for OUT instructions sm_config_set_out_pins(&config, 22, 1); sm_config_set_out_shift(&config, true, true, 0); pio_gpio_init(pio, 22); pio_sm_set_consecutive_pindirs(pio, sm, 22, 1, true); sm_config_set_wrap(&config, offset + vga_free_run_wrap_target, offset + vga_free_run_wrap); pio_sm_init(pio, sm, offset, &config); pio_sm_set_clkdiv_int_frac(pio, sm, 1, 0); pio_sm_set_enabled(pio, sm, true); } static void vga_hsync_program_init(PIO pio, uint sm, uint offset) { pio_sm_config config = vga_hsync_program_get_default_config(offset); // destination pins for SET instructions sm_config_set_set_pins(&config, 20, 1); pio_gpio_init(pio, 20); pio_sm_set_consecutive_pindirs(pio, sm, 20, 1, true); sm_config_set_wrap(&config, offset + vga_hsync_wrap_target, offset + vga_hsync_wrap); pio_sm_init(pio, sm, offset, &config); pio_sm_set_clkdiv_int_frac(pio, sm, 1, 0); pio->sm->shiftctrl = (1 << PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); pio_sm_set_enabled(pio, sm, true); } static void vga_vsync_program_init(PIO pio, uint sm, uint offset) { pio_sm_config config = vga_vsync_program_get_default_config(offset); // destination pins for SET instructions sm_config_set_set_pins(&config, 21, 1); pio_gpio_init(pio, 21); pio_sm_set_consecutive_pindirs(pio, sm, 21, 1, true); sm_config_set_wrap(&config, offset + vga_vsync_wrap_target, offset + vga_vsync_wrap); pio_sm_init(pio, sm, offset, &config); pio_sm_set_clkdiv_int_frac(pio, sm, 1, 0); pio->sm->shiftctrl = (1 << PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); pio_sm_set_enabled(pio, sm, true); } void setup_clocks(void) { // disable resuscitation clock clocks_hw->resus.ctrl = 0; // before changing PLL, switch sys and ref cleanly away from their aux sources hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS); while (clocks_hw->clk[clk_sys].selected != 0x1) tight_loop_contents(); hw_clear_bits(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS); while (clocks_hw->clk[clk_ref].selected != 0x1) tight_loop_contents(); // set PLL at 126 MHz, and wait for it to stabilize pll_init(pll_sys, 1, 1512 * MHZ, 6, 2); // re-configure sys_clk to use PLL clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, 126 * MHZ, 126 * MHZ); // re-configure CLK PERI = clk_sys clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 126 * MHZ, 126 * MHZ); } static bool dma_ready = false; static int dma_channel; void new_frame_handler(void) { // restart DMA if (dma_ready) dma_channel_set_read_addr(dma_channel, &frames[0].data[0], true); pio_interrupt_clear(pio0_hw, 1); } void dma_handler(void) { // acknoweledge DMA dma_hw->ints0 = 1 << dma_channel; } void set_pixel(uint16_t x, uint16_t y) { frames[0].data[y * 80 + x / 8] |= (1 << x % 8); } /** * @brief Represents a program running on the SM of a PIO. * */ typedef struct PIORun { PIO pio; //< executing PIO uint offset; //< PIO memory offset for program uint sm; //< executing SM } PIORun; int main() { setup_clocks(); stdio_init_all(); sleep_ms(1000); // clear frame buffer memset(&frames[0].data[0], 0, sizeof(frames[0].data)); // draw a cross // horizontal lines for (uint16_t x = 0; x < 639; ++x) // last pixel high -> bad { set_pixel(x, 0); set_pixel(x, 239); set_pixel(x, 479); } // vertical lines for (uint16_t y = 0; y < 480; ++y) { set_pixel(0, y); set_pixel(239, y); set_pixel(638, y); } // Running programs on PIO PIORun vga_hsync, vga_vsync, vga_pixel; vga_hsync.pio = vga_vsync.pio = vga_pixel.pio = pio0; // Prepare frame interrupt // Frame interrupt is asserted on PIO0 interrupt 1 by SM1 pio_set_irq1_source_enabled(vga_vsync.pio, pis_interrupt1, true); // allow SM1 of PIO to fire the interrupt irq_set_exclusive_handler(PIO0_IRQ_1, new_frame_handler); // set handler for IRQ1 of PIO irq_set_enabled(PIO0_IRQ_1, true); // enable interrupt // VGA HSYNC program printf("Starting VGA hsync machine... "); if (!pio_can_add_program(vga_hsync.pio, &vga_hsync_program)) panic("cannot add program"); vga_hsync.offset = pio_add_program(vga_hsync.pio, &vga_hsync_program); vga_hsync.sm = pio_claim_unused_sm(vga_hsync.pio, true); vga_hsync_program_init(vga_hsync.pio, vga_hsync.sm, vga_hsync.offset); // low pulse is X pixels long, and SM runs at 4x of pixel clock pio_sm_put_blocking(vga_hsync.pio, vga_hsync.sm, (64 - 1)); pio_sm_put_blocking(vga_hsync.pio, vga_hsync.sm, (640 - 1)); printf("OK\n"); // VGA VSYNC program printf("Starting VGA vsync machine... "); if (!pio_can_add_program(vga_vsync.pio, &vga_vsync_program)) panic("cannot add program"); vga_vsync.offset = pio_add_program(vga_vsync.pio, &vga_vsync_program); vga_vsync.sm = pio_claim_unused_sm(vga_vsync.pio, true); vga_vsync_program_init(vga_vsync.pio, vga_vsync.sm, vga_vsync.offset); // low pulse assert lasts for 3 horizontal lines, then adjust by one pio_sm_put_blocking(vga_vsync.pio, vga_vsync.sm, 3 - 1); pio_sm_put_blocking(vga_vsync.pio, vga_vsync.sm, 500 - 3 - 1); printf("OK\n"); // VGA pixel program printf("Starting VGA pixel machine... "); if (!pio_can_add_program(vga_pixel.pio, &vga_free_run_program)) panic("cannot add program"); vga_pixel.offset = pio_add_program(vga_pixel.pio, &vga_free_run_program); vga_pixel.sm = pio_claim_unused_sm(vga_pixel.pio, false); vga_pixel_program_init(vga_pixel.pio, vga_pixel.sm, vga_pixel.offset); pio_sm_put_blocking(vga_pixel.pio, vga_pixel.sm, 640 - 1); printf("OK\n"); printf("Setting up 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); channel_config_set_read_increment(&dma_config, true); channel_config_set_write_increment(&dma_config, false); channel_config_set_dreq(&dma_config, DREQ_PIO0_TX2); dma_channel_set_irq0_enabled(dma_channel, true); irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); irq_set_enabled(DMA_IRQ_0, true); dma_channel_configure(dma_channel, &dma_config, &pio0_hw->txf[2], &frames[0].data[0], 9600, false); dma_ready = true; printf("OK\n"); printf("Now running.\n"); for (;;) tight_loop_contents(); }