Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ae2da1b65 | ||
|
|
e416adccf0 | ||
|
|
dc450953ef | ||
|
|
3ec35313df |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -56,3 +56,4 @@ vga.pio.h
|
||||
Makefile
|
||||
CMakeDoxyfile.in
|
||||
CMakeDoxygenDefaults.cmake
|
||||
picotool/
|
||||
|
||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 1.2 (2025-08-23)
|
||||
* Updated to support Raspberry Pi Pico SDK 2.2.0
|
||||
|
||||
# 1.0 (2023-03-11)
|
||||
* Initial release of picocga-cmake
|
||||
@@ -3,6 +3,10 @@
|
||||
## About this Fork
|
||||
This is a fork of the [PicoVGA project](https://github.com/Panda381/PicoVGA) created by Miroslav Nemecek (Panda38@seznam.cz). It has been altered from the original to use the standard Linux-based Raspberry Pi Pico SDK. The header files have also been altered to support automatically generating the documentation.
|
||||
|
||||
This version is compatible with the [Raspberry Pi Pico SDK 2.2.0](https://github.com/raspberrypi/pico-sdk).
|
||||
|
||||
* [Change Log](CHANGELOG.md)
|
||||
|
||||
## About PicoVGA
|
||||
|
||||
The PicoVGA library enables the Raspberry Pico to output to a VGA monitor or PAL/NTSC TV with ease, making it ideal for technical and gaming applications. It provides four graphic overlay layers with transparency and supports nearly 30 frame buffer formats that can be freely combined while using limited RAM memory. The output is limited to 8 bits, which helps to save on RAM.
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// clouds copy
|
||||
ALIGNED u8 CloudsImg_Copy[sizeof(CloudsImg)];
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// Draw box
|
||||
#if FORMAT==CANVAS_PLANE2
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "main.h"
|
||||
#include "pico/printf.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
u8 Text[TEXTMAX]; // row of text
|
||||
ALIGNED u8 TextCol[TEXTMAX*8]; // text color gradient
|
||||
|
||||
@@ -12,5 +12,8 @@ target_include_directories(vga_hello PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/src
|
||||
)
|
||||
|
||||
# pico_enable_stdio_usb(vga_hello 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(vga_hello)
|
||||
|
||||
|
||||
@@ -7,13 +7,23 @@
|
||||
|
||||
|
||||
#include "picovga.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/time.h"
|
||||
#include <stdio.h>
|
||||
#include <hardware/structs/dma.h>
|
||||
#include "vga_config.h"
|
||||
#include "hardware/pio.h"
|
||||
|
||||
extern volatile uint32_t g_isr_count; // how many times VgaLine() fired
|
||||
extern volatile uint32_t g_last_scanline; // last ScanLine observed in ISR
|
||||
extern volatile uint32_t g_last_linetype; // last linetype observed in ISR
|
||||
|
||||
// Draw box
|
||||
ALIGNED u8 Box[512*400];
|
||||
|
||||
int main()
|
||||
{
|
||||
// initialize videomode
|
||||
// initialize videomode
|
||||
Video(DEV_VGA, RES_EGA, FORM_8BIT, Box);
|
||||
|
||||
// draw text
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// crash on hardfault.
|
||||
|
||||
#include "main.h"
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// draw box
|
||||
ALIGNED u8 Box[IMGW*IMGH];
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// copy of images
|
||||
ALIGNED u8 Repro1Img_Copy[sizeof(Repro1Img)];
|
||||
|
||||
@@ -32,6 +32,7 @@ We will use 32-bit integer with highest 7 bits as integer part and 25 lower bits
|
||||
#include "fixed.h"
|
||||
#include "pico/printf.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
#define USE_INT 0 // use integer arithmetics
|
||||
#define USE_FLT 1 // use float arithmetics
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
#define DELAY 10 // delay in [ms]
|
||||
#define LEN_MIN 10 // minimal length of drop
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
|
||||
// copy of images
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "open.h" // open screen
|
||||
#include "pico/printf.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// initialize buffers on program start
|
||||
void BufInit()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string.h>
|
||||
#include "pico/printf.h"
|
||||
#include "hardware/sync.h"
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// copy of tiles images
|
||||
ALIGNED u8 TilesImg_Copy[sizeof(Tiles32Img)];
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "levels.h" // game levels
|
||||
#include <string.h>
|
||||
#include "pico/printf.h"
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
u8 Text[TEXTMAX*2]; // 2 rows of text (2nd row is empty, it is used to center line vertically)
|
||||
u8 TextCol[TEXTMAX*2*8] __attribute__ ((aligned(4))); // text color gradient
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
// Draw box
|
||||
ALIGNED u8 Box[WIDTHBYTE*HEIGHT];
|
||||
|
||||
1
pico_flash_region.ld
Normal file
1
pico_flash_region.ld
Normal file
@@ -0,0 +1 @@
|
||||
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = (2 * 1024 * 1024)
|
||||
83
src/vga.cpp
83
src/vga.cpp
@@ -14,8 +14,12 @@
|
||||
|
||||
#include <string.h>
|
||||
#include "hardware/divider.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/clocks.h" // SDK 2.x: ensure clock helpers available
|
||||
#include "pico/platform.h" // memory barriers (__dmb/__dsb)
|
||||
|
||||
// scanline type
|
||||
u8 ScanlineType[MAXLINE];
|
||||
@@ -48,8 +52,8 @@ u32 LineBufSync[10]; // vertical synchronization
|
||||
ALIGNED u8 LineBuf0[BLACK_MAX]; // line buffer with black color (used to clear rest of scanline)
|
||||
|
||||
// control buffers (BufInx = 0 running CtrlBuf1 and preparing CtrlBuf2, BufInx = 1 running CtrlBuf2 and preparing CtrlBuf1)
|
||||
u32 CtrlBuf1[CBUF_MAX]; // base layer control pairs: u32 count, read address (must be terminated with [0,0])
|
||||
u32 CtrlBuf2[CBUF_MAX]; // base layer control pairs: u32 count, read address (must be terminated with [0,0])
|
||||
__attribute__((aligned(8))) u32 CtrlBuf1[CBUF_MAX]; // base layer control pairs: u32 count, read address (must be terminated with [0,0])
|
||||
__attribute__((aligned(8))) u32 CtrlBuf2[CBUF_MAX]; // base layer control pairs: u32 count, read address (must be terminated with [0,0])
|
||||
|
||||
int CtrlBufSize[LAYERS_MAX] = { CBUF0_MAX, CBUF1_MAX, CBUF2_MAX, CBUF3_MAX }; // size of control buffers
|
||||
|
||||
@@ -62,11 +66,20 @@ u32 RenderTextMask[512];
|
||||
// saved integer divider state
|
||||
hw_divider_state_t DividerState;
|
||||
|
||||
// --- DEBUG (USB serial) ---
|
||||
// Safe to read from main loop; written by ISR.
|
||||
// Define VGA_DEBUG in your build to enable these counters.
|
||||
#ifdef VGA_DEBUG
|
||||
volatile uint32_t g_isr_count = 0; // how many times VgaLine() fired
|
||||
volatile uint32_t g_last_scanline = 0; // last ScanLine observed in ISR
|
||||
volatile uint32_t g_last_linetype = 0; // last linetype observed in ISR
|
||||
#endif
|
||||
|
||||
// process scanline buffers (will save integer divider state into DividerState)
|
||||
int __not_in_flash_func(VgaBufProcess)()
|
||||
{
|
||||
// Clear the interrupt request for DMA control channel
|
||||
dma_hw->ints0 = (1u << VGA_DMA_PIO0);
|
||||
dma_channel_acknowledge_irq0(VGA_DMA_PIO0);
|
||||
|
||||
// switch current buffer index
|
||||
// BufInx = 0 running CtrlBuf1 and preparing CtrlBuf2, BufInx = 1 running CtrlBuf2 and preparing CtrlBuf1
|
||||
@@ -75,6 +88,7 @@ int __not_in_flash_func(VgaBufProcess)()
|
||||
BufInx = bufinx ^ 1;
|
||||
|
||||
// update DMA control channels of base layer, and run it
|
||||
__dmb(); // ensure control words are visible before arming CB0 (SDK 2.x)
|
||||
dma_channel_set_read_addr(VGA_DMA_CB0, CtrlBufNext[0], true);
|
||||
|
||||
// save integer divider state
|
||||
@@ -182,6 +196,7 @@ int __not_in_flash_func(VgaBufProcess)()
|
||||
pio_sm_exec(VGA_PIO, sm, pio_encode_jmp(CurLayerProg.entry+LAYER_OFFSET));
|
||||
|
||||
// start DMA
|
||||
__dmb();
|
||||
dma_channel_set_read_addr(VGA_DMA_CB(layer), CtrlBufNext[layer], true);
|
||||
}
|
||||
}
|
||||
@@ -389,6 +404,10 @@ u32* __not_in_flash_func(VgaBufRender)(u32* cbuf, u32* cbuf0, u8* dbuf, int y0)
|
||||
// VGA DMA handler - called on end of every scanline
|
||||
extern "C" void __not_in_flash_func(VgaLine)()
|
||||
{
|
||||
#ifdef VGA_DEBUG
|
||||
g_isr_count++;
|
||||
#endif
|
||||
|
||||
// process scanline buffers (will save integer divider state into DividerState)
|
||||
int bufinx = VgaBufProcess();
|
||||
|
||||
@@ -485,6 +504,12 @@ extern "C" void __not_in_flash_func(VgaLine)()
|
||||
*cbuf++ = 0; // end mark
|
||||
*cbuf++ = 0; // end mark
|
||||
|
||||
#ifdef VGA_DEBUG
|
||||
// Capture last-known values for printing from main (do NOT printf in ISR)
|
||||
g_last_linetype = linetype;
|
||||
g_last_scanline = (uint32_t)ScanLine; // ScanLine updated in VgaBufProcess()
|
||||
#endif
|
||||
|
||||
// restore integer divider state
|
||||
hw_divider_restore_state(&DividerState);
|
||||
}
|
||||
@@ -514,6 +539,15 @@ void VgaDmaInit()
|
||||
// increment address on read from memory
|
||||
channel_config_set_read_increment(&cfg, true);
|
||||
|
||||
// In SDK 2.x do NOT rely on default pacing; force unpaced writes so the two
|
||||
// 32-bit writes (TRANS_COUNT -> READ_ADDR_TRIG) happen back-to-back.
|
||||
// This mirrors the effective behavior under 1.5.1 and avoids stalls where
|
||||
// the control stream blocks waiting for a peripheral TREQ. WV
|
||||
channel_config_set_dreq(&cfg, DREQ_FORCE);
|
||||
|
||||
// Keep CB channel quiet (we don't enable its IRQ anyway). WV
|
||||
channel_config_set_irq_quiet(&cfg, true);
|
||||
|
||||
// increment address on write to DMA port
|
||||
channel_config_set_write_increment(&cfg, true);
|
||||
|
||||
@@ -576,7 +610,8 @@ void VgaDmaInit()
|
||||
// ==== initialize IRQ0, raised from base layer 0
|
||||
|
||||
// enable DMA channel IRQ0
|
||||
dma_channel_set_irq0_enabled(VGA_DMA_PIO0, true);
|
||||
// dma_channel_set_irq0_enabled(VGA_DMA_PIO0, true);
|
||||
dma_set_irq0_channel_mask_enabled(1u << VGA_DMA_PIO0, true);
|
||||
|
||||
// set DMA IRQ handler
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, VgaLine);
|
||||
@@ -605,7 +640,16 @@ void VgaPioInit()
|
||||
prg.instructions = ins;
|
||||
prg.length = vga_program.length;
|
||||
prg.origin = BASE_OFFSET;
|
||||
pio_add_program(VGA_PIO, &prg);
|
||||
prg.pio_version = vga_program.pio_version;
|
||||
#if PICO_PIO_VERSION > 0
|
||||
prg.used_gpio_ranges = vga_program.used_gpio_ranges;
|
||||
#endif
|
||||
|
||||
// pio_add_program(VGA_PIO, &prg); // WV
|
||||
int load_offset = pio_add_program_at_offset(VGA_PIO, &prg, BASE_OFFSET);
|
||||
if (load_offset < 0) {
|
||||
panic("vga_program won't fit at BASE_OFFSET=%d\n", BASE_OFFSET);
|
||||
}
|
||||
|
||||
// load layer program
|
||||
if (LayerProgInx != LAYERPROG_BASE)
|
||||
@@ -623,7 +667,16 @@ void VgaPioInit()
|
||||
prg.instructions = ins;
|
||||
prg.length = CurLayerProg.length;
|
||||
prg.origin = LAYER_OFFSET;
|
||||
pio_add_program(VGA_PIO, &prg);
|
||||
prg.pio_version = CurLayerProg.prg->pio_version;
|
||||
#if PICO_PIO_VERSION > 0
|
||||
prg.used_gpio_ranges = CurLayerProg.prg->used_gpio_ranges;
|
||||
#endif
|
||||
|
||||
// pio_add_program(VGA_PIO, &prg); // WV
|
||||
int load_offset = pio_add_program_at_offset(VGA_PIO, &prg, LAYER_OFFSET);
|
||||
if (load_offset < 0) {
|
||||
panic("layer program won't fit at LAYER_OFFSET=%d\n", LAYER_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
// connect PIO to the pad
|
||||
@@ -773,7 +826,7 @@ void VgaTerm()
|
||||
dma_channel_set_irq0_enabled(VGA_DMA_PIO0, false);
|
||||
|
||||
// Clear the interrupt request for DMA control channel
|
||||
dma_hw->ints0 = (1u << VGA_DMA_PIO0);
|
||||
dma_channel_acknowledge_irq0(VGA_DMA_PIO0);
|
||||
|
||||
// stop all state machines
|
||||
pio_set_sm_mask_enabled(VGA_PIO, VGA_SMALL, false);
|
||||
@@ -1008,14 +1061,18 @@ void VgaInit(const sVmode* vmode)
|
||||
// initialize DMA
|
||||
VgaDmaInit();
|
||||
|
||||
// enable DMA IRQ
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
// Clear any stale IRQ before enabling and starting
|
||||
dma_channel_acknowledge_irq0(VGA_DMA_PIO0);
|
||||
|
||||
// start DMA with base layer 0
|
||||
dma_channel_start(VGA_DMA_CB0);
|
||||
__dmb();
|
||||
dma_channel_start(VGA_DMA_CB0);
|
||||
|
||||
// run state machines
|
||||
// Run state machines FIRST so the PIO is consuming immediately when TX is fed.
|
||||
// (In SDK 2.x this ordering avoids a rare DREQ/chain race seen at start-of-frame.)
|
||||
pio_enable_sm_mask_in_sync(VGA_PIO, LayerMask);
|
||||
|
||||
// Now enable DMA IRQ and kick the first control pair
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
|
||||
const sVmode* volatile VgaVmodeReq = NULL; // request to reinitialize videomode, 1=only stop driver
|
||||
|
||||
Reference in New Issue
Block a user