4 Commits

Author SHA1 Message Date
Wayne Venables
9ae2da1b65 Documentation cleanup 2025-08-25 21:53:50 -07:00
Wayne Venables
e416adccf0 Add header for SDK 2.1.1 support 2025-08-23 11:39:42 -07:00
Wayne Venables
dc450953ef Cleaned up version, tested balloons 2025-08-19 23:22:33 -07:00
Wayne Venables
3ec35313df Working version before cleanup 2025-08-19 23:07:14 -07:00
19 changed files with 107 additions and 14 deletions

1
.gitignore vendored
View File

@@ -56,3 +56,4 @@ vga.pio.h
Makefile
CMakeDoxyfile.in
CMakeDoxygenDefaults.cmake
picotool/

5
CHANGELOG.md Normal file
View 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

View File

@@ -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.

View File

@@ -7,6 +7,7 @@
#include "main.h"
#include <string.h>
#include <hardware/clocks.h>
// clouds copy
ALIGNED u8 CloudsImg_Copy[sizeof(CloudsImg)];

View File

@@ -7,6 +7,7 @@
#include "main.h"
#include <string.h>
#include <hardware/clocks.h>
// Draw box
#if FORMAT==CANVAS_PLANE2

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -13,6 +13,7 @@
// crash on hardfault.
#include "main.h"
#include <hardware/clocks.h>
// draw box
ALIGNED u8 Box[IMGW*IMGH];

View File

@@ -7,6 +7,7 @@
#include "main.h"
#include <string.h>
#include <hardware/clocks.h>
// copy of images
ALIGNED u8 Repro1Img_Copy[sizeof(Repro1Img)];

View File

@@ -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

View File

@@ -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

View File

@@ -7,6 +7,7 @@
#include "main.h"
#include <string.h>
#include <hardware/clocks.h>
// copy of images

View File

@@ -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()

View File

@@ -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)];

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1 @@
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = (2 * 1024 * 1024)

View File

@@ -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