0

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:

Photo of display

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)
localhost
  • 3
  • 5

0 Answers0