I'm trying to get this VGA driver for the Pi Pico to work with framebuf.FrameBuffer
. I've modified the code to use 1 bit per pixel instead of 3.
Everything works fine when using the (modified) drawing functions from the original code. But I really want to use FrameBuffer
. And with FrameBuffer
, everything appears to be shifted to the right by 32 pixels:
Here's the modified code:
from array import array
from gc import collect, mem_free
from framebuf import FrameBuffer, MONO_HMSB
from machine import Pin
from micropython import const
from rp2 import PIO, StateMachine, asm_pio
from uctypes import addressof
COLOR_PIN = Pin(2)
VSYNC_PIN = Pin(5)
HSYNC_PIN = Pin(4)
SCREEN_WIDTH = const(640)
SCREEN_HEIGHT = const(480)
PIXEL_BITMASK = const(0b1)
USABLE_BITS = const(32)
SM0_FREQ = 25175000
SM1_FREQ = 125000000
SM2_FREQ = 100700000
@asm_pio(set_init=PIO.OUT_HIGH, autopull=True, pull_thresh=32)
def _hsync():
wrap_target()
# ACTIVE + FRONTPORCH
mov(x, osr)
label('activeporch')
jmp(x_dec, 'activeporch')
# SYNC PULSE
set(pins, 0) [31]
set(pins, 0) [31]
set(pins, 0) [31]
# BACKPORCH
set(pins, 1) [31]
set(pins, 1) [13]
irq(0)
wrap()
@asm_pio(sideset_init=(PIO.OUT_HIGH,) * 1, autopull=True, pull_thresh=32)
def _vsync():
pull(block)
wrap_target()
# ACTIVE
mov(x, osr)
label('active')
wait(1, irq, 0)
irq(1)
jmp(x_dec, 'active')
# FRONTPORCH
set(y, 9)
label('frontporch')
wait(1, irq, 0)
jmp(y_dec, 'frontporch')
# SYNC PULSE
wait(1, irq, 0).side(0)
wait(1, irq, 0)
# BACKPORCH
set(y, 31)
label('backporch')
wait(1, irq, 0).side(1)
jmp(y_dec, 'backporch')
wait(1, irq, 0)
wrap()
@asm_pio(
out_init=PIO.OUT_LOW,
out_shiftdir=PIO.SHIFT_RIGHT,
sideset_init=PIO.OUT_LOW,
autopull=True,
pull_thresh=USABLE_BITS
)
def _mono_output():
pull(block)
mov(y, osr)
wrap_target()
mov(x, y).side(0)
wait(1, irq, 1)
label('colorout')
out(pins, 1)
nop()[1]
jmp(x_dec, 'colorout')
wrap()
@micropython.viper
def configure_DMAs(nword: int, screen_buffer_add: ptr32):
# chan0 as 'configure' DMA and chan1 as 'data transfer' DMA
# shared parameters
IRQ_QUIET = 0
RING_SEL = 0
RING_SIZE = 0
HIGH_PRIORITY = 1
INCR_WRITE = 0
# DMA channel 1
TREQ_SEL = 2
INCR_READ = 1
DATA_SIZE = 2
CHAIN_TO = 0
EN = 1
DMA_control_word = (
(IRQ_QUIET << 21) | (TREQ_SEL << 15) | (CHAIN_TO << 11) | (RING_SEL << 10) |
(RING_SIZE << 9) | (INCR_WRITE << 5) | (INCR_READ << 4) | (DATA_SIZE << 2) |
(HIGH_PRIORITY << 1) | (EN << 0)
)
ptr32(0x50000040)[0] = 0
ptr32(0x50000044)[0] = uint(0x50200018)
ptr32(0x50000048)[0] = nword
ptr32(0x50000060)[0] = DMA_control_word
# DMA channel 0
TREQ_SEL = 0x3f
INCR_READ = 0
CHAIN_TO = 0
EN = 1
DMA_control_word = (
(IRQ_QUIET << 21) | (TREQ_SEL << 15) | (CHAIN_TO << 11) | (RING_SEL << 10) |
(RING_SIZE << 9) | (INCR_WRITE << 5) | (INCR_READ << 4) | (DATA_SIZE << 2) |
(HIGH_PRIORITY << 1) | (EN << 0)
)
ptr32(0x50000000)[0] = uint(screen_buffer_add)
ptr32(0x50000004)[0] = uint(0x5000007c)
ptr32(0x50000008)[0] = 1
ptr32(0x50000010)[0] = DMA_control_word
@micropython.viper
def startsync():
v = int(ptr16(SCREEN_HEIGHT))
h = int(ptr16(SCREEN_WIDTH))
sm_hsync.put(655)
sm_vsync.put(int(v - 1))
sm_mono_output.put(int(h - 1))
ptr32(0x50000430)[0] |= 0b00001
ptr32(0x50200000)[0] |= 0b111
sm_hsync = StateMachine(0, _hsync, freq=SM0_FREQ, set_base=HSYNC_PIN)
sm_vsync = StateMachine(1, _vsync, freq=SM1_FREQ, sideset_base=VSYNC_PIN)
sm_mono_output = StateMachine(
2, _mono_output, freq=SM2_FREQ, out_base=COLOR_PIN, sideset_base=COLOR_PIN
)
screen_buffer = array('L', [0] * int(SCREEN_WIDTH * SCREEN_HEIGHT / USABLE_BITS))
screen_buffer_address = array('L', [addressof(screen_buffer)])
configure_DMAs(len(screen_buffer), screen_buffer_address)
startsync()
collect()
buf = FrameBuffer(screen_buffer, 640, 480, MONO_HMSB)
buf.fill(1)
buf.rect(0, 0, 640, 480, 0)
buf.text(f'{100 - round(mem_free() / 1024 / 264 * 100)}% of RAM in use', 1, 1, 0)