files
This commit is contained in:
@@ -1,35 +1,35 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
# Pull in Raspberry Pi Pico SDK (must be before project)
|
|
||||||
set(PICO_SDK_FETCH_FROM_GIT on)
|
set(PICO_SDK_FETCH_FROM_GIT on)
|
||||||
include(pico_sdk_import.cmake)
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
project(pico_vga C CXX ASM)
|
project(pico_vga C CXX ASM)
|
||||||
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR})
|
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wall -O2")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
|
||||||
|
|
||||||
pico_sdk_init()
|
pico_sdk_init()
|
||||||
|
|
||||||
add_executable(pico_vga
|
add_executable(pico_vga
|
||||||
src/main.c
|
src/main.c
|
||||||
|
src/vga.c
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the standard library to the build
|
|
||||||
target_link_libraries(pico_vga
|
target_link_libraries(pico_vga
|
||||||
pico_stdlib
|
pico_stdlib
|
||||||
hardware_pio
|
hardware_pio
|
||||||
hardware_dma
|
hardware_dma
|
||||||
hardware_irq
|
hardware_irq
|
||||||
|
hardware_clocks
|
||||||
)
|
)
|
||||||
|
|
||||||
pico_generate_pio_header(pico_vga ${CMAKE_CURRENT_LIST_DIR}/src/vga.pio)
|
pico_generate_pio_header(pico_vga ${CMAKE_CURRENT_LIST_DIR}/src/vga.pio)
|
||||||
|
|
||||||
|
pico_enable_stdio_usb(pico_vga 1)
|
||||||
|
pico_enable_stdio_uart(pico_vga 0)
|
||||||
|
|
||||||
pico_add_extra_outputs(pico_vga)
|
pico_add_extra_outputs(pico_vga)
|
||||||
|
|||||||
104
src/main.c
104
src/main.c
@@ -1,94 +1,32 @@
|
|||||||
// Copyright Benjamin Kyd 2021 All Rights Reserved
|
// Copyright Benjamin Kyd 2023 All Rights Reserved
|
||||||
|
//
|
||||||
|
// GPIO 0-3: RED DAC, 4-7: GREEN DAC, 8-11: BLUE DAC
|
||||||
|
// GPIO 12: HSYNC, GPIO 13: VSYNC
|
||||||
|
|
||||||
// PIN LAYOUT
|
|
||||||
// PIN 0-3 RED DAC
|
|
||||||
// PIN 4-7 GREEN DAC
|
|
||||||
// PIN 8-11 BLUE DAC
|
|
||||||
// PIN 12 HSYNC
|
|
||||||
// PIN 13 VSYNC
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "hardware/clocks.h"
|
#include "vga.h"
|
||||||
#include "hardware/pio.h"
|
|
||||||
#include "hardware/dma.h"
|
|
||||||
#include "hardware/irq.h"
|
|
||||||
|
|
||||||
#include "vga.pio.h"
|
static void draw_test_pattern(void) {
|
||||||
|
for (int y = 0; y < VGA_FB_HEIGHT; y++) {
|
||||||
|
for (int x = 0; x < VGA_FB_WIDTH; x++) {
|
||||||
|
uint8_t r = (uint8_t)((x * 15) / VGA_FB_WIDTH);
|
||||||
|
uint8_t g = (uint8_t)((y * 15) / VGA_FB_HEIGHT);
|
||||||
|
uint8_t b = 8;
|
||||||
|
|
||||||
typedef struct video_timing {
|
if (x % 10 == 0 || y % 10 == 0) {
|
||||||
uint32_t clock_freq;
|
r = g = b = 15;
|
||||||
|
|
||||||
uint16_t h_active;
|
|
||||||
uint16_t v_active;
|
|
||||||
|
|
||||||
uint16_t h_front_porch;
|
|
||||||
uint16_t h_pulse;
|
|
||||||
uint16_t h_total;
|
|
||||||
uint8_t h_sync_polarity;
|
|
||||||
|
|
||||||
uint16_t v_front_porch;
|
|
||||||
uint16_t v_pulse;
|
|
||||||
uint16_t v_total;
|
|
||||||
uint8_t v_sync_polarity;
|
|
||||||
|
|
||||||
uint8_t enable_clock;
|
|
||||||
uint8_t clock_polarity;
|
|
||||||
|
|
||||||
uint8_t enable_den;
|
|
||||||
} video_timing_t;
|
|
||||||
|
|
||||||
const video_timing_t vga_timing_800x600_60 =
|
|
||||||
{
|
|
||||||
.clock_freq = 38400000,
|
|
||||||
|
|
||||||
.h_active = 800,
|
|
||||||
.v_active = 600,
|
|
||||||
|
|
||||||
.h_front_porch = 4 * 8,
|
|
||||||
.h_pulse = 10 * 8,
|
|
||||||
.h_total = 128 * 8,
|
|
||||||
.h_sync_polarity = 0,
|
|
||||||
|
|
||||||
.v_front_porch = 1,
|
|
||||||
.v_pulse = 3,
|
|
||||||
.v_total = 625,
|
|
||||||
.v_sync_polarity = 0,
|
|
||||||
|
|
||||||
.enable_clock = 0,
|
|
||||||
.clock_polarity = 0,
|
|
||||||
|
|
||||||
.enable_den = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void vga_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
|
||||||
|
|
||||||
pio_gpio_init(pio, pin);
|
|
||||||
pio_gpio_init(pio, pin+1);
|
|
||||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, true);
|
|
||||||
|
|
||||||
pio_sm_config c = vga_program_get_default_config(offset);
|
|
||||||
|
|
||||||
sm_config_set_out_pins(&c, pin, 2);
|
|
||||||
|
|
||||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
|
||||||
sm_config_set_out_shift(&c, false, true, 2);
|
|
||||||
|
|
||||||
pio_sm_init(pio, sm, offset, &c);
|
|
||||||
pio_sm_set_enabled(pio, sm, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
vga_framebuffer[y][x] = vga_rgb(r, g, b);
|
||||||
PIO pio = pio0;
|
}
|
||||||
uint offset = pio_add_program(pio, &vga_program);
|
}
|
||||||
uint sm = pio_claim_unused_sm(pio, true);
|
}
|
||||||
|
|
||||||
vga_program_init(pio, sm, offset, 12);
|
int main(void) {
|
||||||
|
vga_init();
|
||||||
|
draw_test_pattern();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
pio_sm_put_blocking(pio, sm, 0b01);
|
tight_loop_contents();
|
||||||
sleep_ms(20);
|
|
||||||
pio_sm_put_blocking(pio, sm, 0b10);
|
|
||||||
sleep_ms(20);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
142
src/vga.c
142
src/vga.c
@@ -1,7 +1,4 @@
|
|||||||
// Copyright Benjamin Kyd 2021 All Rights Reserved
|
#include <string.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "hardware/clocks.h"
|
#include "hardware/clocks.h"
|
||||||
#include "hardware/pio.h"
|
#include "hardware/pio.h"
|
||||||
@@ -9,52 +6,113 @@
|
|||||||
#include "hardware/irq.h"
|
#include "hardware/irq.h"
|
||||||
|
|
||||||
#include "vga.h"
|
#include "vga.h"
|
||||||
#include "video.pio.h"
|
#include "vga.pio.h"
|
||||||
|
|
||||||
// const video_timing_t vga_timing_800x600_60 =
|
#define H_ACTIVE 800
|
||||||
// {
|
#define H_FRONT_PORCH 40
|
||||||
// .clock_freq = 38400000,
|
#define H_SYNC_PULSE 128
|
||||||
//
|
#define H_BACK_PORCH 88
|
||||||
// .h_active = 800,
|
#define H_TOTAL 1056
|
||||||
// .v_active = 600,
|
|
||||||
//
|
|
||||||
// .h_front_porch = 4 * 8,
|
|
||||||
// .h_pulse = 10 * 8,
|
|
||||||
// .h_total = 128 * 8,
|
|
||||||
// .h_sync_polarity = 0,
|
|
||||||
//
|
|
||||||
// .v_front_porch = 1,
|
|
||||||
// .v_pulse = 3,
|
|
||||||
// .v_total = 625,
|
|
||||||
// .v_sync_polarity = 0,
|
|
||||||
//
|
|
||||||
// .enable_clock = 0,
|
|
||||||
// .clock_polarity = 0,
|
|
||||||
//
|
|
||||||
// .enable_den = 0
|
|
||||||
// };
|
|
||||||
|
|
||||||
#define PIN_START 8
|
#define V_ACTIVE 600
|
||||||
#define PIN_COUNT 2
|
#define V_FRONT_PORCH 1
|
||||||
|
#define V_SYNC_PULSE 4
|
||||||
|
#define V_BACK_PORCH 23
|
||||||
|
#define V_TOTAL 628
|
||||||
|
|
||||||
void vga_init(const video_timing_t* timing)
|
#define HSYNC_BIT (1u << 12)
|
||||||
{
|
#define VSYNC_BIT (1u << 13)
|
||||||
// Calculate the PIO clock divider
|
|
||||||
uint sys_clock = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
|
|
||||||
float clock_div = ((float)sys_clock * 1000.f) / (float)timing->clock_freq;
|
|
||||||
|
|
||||||
printf("System clock frequency %i\n", sys_clock);
|
#define VGA_PIO pio0
|
||||||
printf("Pixel clock frequency %i\n", timing->clock_freq);
|
#define VGA_SM 0
|
||||||
printf("Clock divider %f\n", clock_div);
|
#define VGA_DMA_CHAN 0
|
||||||
|
|
||||||
|
static uint32_t scanline_buf[2][H_TOTAL];
|
||||||
|
static volatile int active_buf = 0;
|
||||||
|
static volatile uint32_t current_line = 0;
|
||||||
|
|
||||||
|
vga_pixel_t vga_framebuffer[VGA_FB_HEIGHT][VGA_FB_WIDTH];
|
||||||
|
|
||||||
// setup 2 buffers with DMA
|
static void build_scanline(uint32_t *buf, uint32_t line) {
|
||||||
// setup swap
|
const bool vsync = (line >= (uint32_t)(V_ACTIVE + V_FRONT_PORCH)) &&
|
||||||
|
(line < (uint32_t)(V_ACTIVE + V_FRONT_PORCH + V_SYNC_PULSE));
|
||||||
// use pin mask (5 pins for 1 bit colour, 12 for 4)
|
const uint32_t vsync_bit = vsync ? VSYNC_BIT : 0u;
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t *ptr = buf;
|
||||||
|
|
||||||
|
if (line < V_ACTIVE) {
|
||||||
|
const uint32_t fy = line >> 2;
|
||||||
|
const vga_pixel_t *row = vga_framebuffer[fy < VGA_FB_HEIGHT ? fy : VGA_FB_HEIGHT - 1u];
|
||||||
|
for (int x = 0; x < H_ACTIVE; x++) {
|
||||||
|
*ptr++ = (uint32_t)row[x >> 2] | vsync_bit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int x = 0; x < H_ACTIVE; x++) {
|
||||||
|
*ptr++ = vsync_bit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < H_FRONT_PORCH; x++) *ptr++ = vsync_bit;
|
||||||
|
|
||||||
|
const uint32_t hsync_word = HSYNC_BIT | vsync_bit;
|
||||||
|
for (int x = 0; x < H_SYNC_PULSE; x++) *ptr++ = hsync_word;
|
||||||
|
|
||||||
|
for (int x = 0; x < H_BACK_PORCH; x++) *ptr++ = vsync_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dma_irq_handler(void) {
|
||||||
|
dma_hw->ints0 = 1u << VGA_DMA_CHAN;
|
||||||
|
|
||||||
|
current_line++;
|
||||||
|
if (current_line >= V_TOTAL) current_line = 0;
|
||||||
|
|
||||||
|
active_buf ^= 1;
|
||||||
|
dma_channel_set_read_addr(VGA_DMA_CHAN, scanline_buf[active_buf], false);
|
||||||
|
dma_channel_set_trans_count(VGA_DMA_CHAN, H_TOTAL, true);
|
||||||
|
|
||||||
|
const uint32_t next_line = (current_line + 1u) % V_TOTAL;
|
||||||
|
build_scanline(scanline_buf[active_buf ^ 1], next_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_init(void) {
|
||||||
|
set_sys_clock_khz(120000, true);
|
||||||
|
|
||||||
|
const uint offset = pio_add_program(VGA_PIO, &vga_out_program);
|
||||||
|
|
||||||
|
pio_sm_config c = vga_out_program_get_default_config(offset);
|
||||||
|
sm_config_set_out_pins(&c, 0, 14);
|
||||||
|
sm_config_set_out_shift(&c, true, true, 14);
|
||||||
|
sm_config_set_clkdiv(&c, 3.0f);
|
||||||
|
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||||
|
|
||||||
|
for (int i = 0; i < 14; i++) pio_gpio_init(VGA_PIO, i);
|
||||||
|
pio_sm_set_consecutive_pindirs(VGA_PIO, VGA_SM, 0, 14, true);
|
||||||
|
pio_sm_init(VGA_PIO, VGA_SM, offset, &c);
|
||||||
|
|
||||||
|
dma_channel_config dc = dma_channel_get_default_config(VGA_DMA_CHAN);
|
||||||
|
channel_config_set_transfer_data_size(&dc, DMA_SIZE_32);
|
||||||
|
channel_config_set_read_increment(&dc, true);
|
||||||
|
channel_config_set_write_increment(&dc, false);
|
||||||
|
channel_config_set_dreq(&dc, pio_get_dreq(VGA_PIO, VGA_SM, true));
|
||||||
|
|
||||||
|
dma_channel_configure(
|
||||||
|
VGA_DMA_CHAN, &dc,
|
||||||
|
&VGA_PIO->txf[VGA_SM],
|
||||||
|
scanline_buf[0],
|
||||||
|
H_TOTAL,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
dma_channel_set_irq0_enabled(VGA_DMA_CHAN, true);
|
||||||
|
irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler);
|
||||||
|
irq_set_enabled(DMA_IRQ_0, true);
|
||||||
|
|
||||||
|
build_scanline(scanline_buf[0], 0);
|
||||||
|
build_scanline(scanline_buf[1], 1);
|
||||||
|
active_buf = 0;
|
||||||
|
current_line = 0;
|
||||||
|
|
||||||
|
pio_sm_set_enabled(VGA_PIO, VGA_SM, true);
|
||||||
|
dma_channel_set_read_addr(VGA_DMA_CHAN, scanline_buf[0], false);
|
||||||
|
dma_channel_set_trans_count(VGA_DMA_CHAN, H_TOTAL, true);
|
||||||
|
}
|
||||||
|
|||||||
55
src/vga.h
55
src/vga.h
@@ -1,40 +1,23 @@
|
|||||||
#ifndef PICOVGA_VGA_H_
|
#ifndef PICOVGA_VGA_H_
|
||||||
#define PICOVGA_VGA_H_
|
#define PICOVGA_VGA_H_
|
||||||
|
|
||||||
#include "pico/types.h"
|
#include <stdint.h>
|
||||||
//
|
|
||||||
// typedef struct video_timing {
|
#define VGA_FB_WIDTH 200
|
||||||
// uint32_t clock_freq;
|
#define VGA_FB_HEIGHT 150
|
||||||
//
|
|
||||||
// uint16_t h_active;
|
typedef uint16_t vga_pixel_t;
|
||||||
// uint16_t v_active;
|
|
||||||
//
|
static inline vga_pixel_t vga_rgb(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
// uint16_t h_front_porch;
|
return (uint16_t)((r & 0xF) | ((g & 0xF) << 4) | ((b & 0xF) << 8));
|
||||||
// uint16_t h_pulse;
|
}
|
||||||
// uint16_t h_total;
|
|
||||||
// uint8_t h_sync_polarity;
|
static inline vga_pixel_t vga_rgb8(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
//
|
return vga_rgb(r >> 4, g >> 4, b >> 4);
|
||||||
// uint16_t v_front_porch;
|
}
|
||||||
// uint16_t v_pulse;
|
|
||||||
// uint16_t v_total;
|
extern vga_pixel_t vga_framebuffer[VGA_FB_HEIGHT][VGA_FB_WIDTH];
|
||||||
// uint8_t v_sync_polarity;
|
|
||||||
//
|
void vga_init(void);
|
||||||
// uint8_t enable_clock;
|
|
||||||
// uint8_t clock_polarity;
|
|
||||||
//
|
|
||||||
// uint8_t enable_den;
|
|
||||||
// } video_timing_t;
|
|
||||||
//
|
|
||||||
// const video_timing_t vga_timing_800x600_60;
|
|
||||||
//
|
|
||||||
// // typedef struct video {
|
|
||||||
// // uint16_t*
|
|
||||||
// // } video_t;
|
|
||||||
//
|
|
||||||
// void vga_init(const video_timing_t* timing);
|
|
||||||
//
|
|
||||||
// void vga_start();
|
|
||||||
//
|
|
||||||
// void vga_swap_buffers();
|
|
||||||
//
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
23
src/vga.pio
23
src/vga.pio
@@ -1,4 +1,19 @@
|
|||||||
.program vga
|
;
|
||||||
loop:
|
; VGA pixel output PIO program
|
||||||
out pins, 2
|
;
|
||||||
jmp loop
|
; Outputs 14 bits per pixel clock cycle to GPIO 0-13:
|
||||||
|
; bits [3:0] = RED (GPIO 0-3)
|
||||||
|
; bits [7:4] = GREEN (GPIO 4-7)
|
||||||
|
; bits [11:8] = BLUE (GPIO 8-11)
|
||||||
|
; bit [12] = HSYNC (GPIO 12) - positive polarity for 800x600@60Hz
|
||||||
|
; bit [13] = VSYNC (GPIO 13) - positive polarity for 800x600@60Hz
|
||||||
|
;
|
||||||
|
; Each 32-bit FIFO word = one pixel (14 bits used, 18 bits discarded via autopull).
|
||||||
|
; PIO clock divider must be set so instruction rate = pixel clock (40 MHz).
|
||||||
|
;
|
||||||
|
|
||||||
|
.program vga_out
|
||||||
|
|
||||||
|
.wrap_target
|
||||||
|
out pins, 14 ; output 14 bits to GPIO 0-13
|
||||||
|
.wrap
|
||||||
|
|||||||
Reference in New Issue
Block a user