Skip to content

Instantly share code, notes, and snippets.

@azechi
Last active January 10, 2023 00:40
Show Gist options
  • Save azechi/13e27f9c00814699520d62fde7e6d6a1 to your computer and use it in GitHub Desktop.
Save azechi/13e27f9c00814699520d62fde7e6d6a1 to your computer and use it in GitHub Desktop.
LEDマトリクスに流れる文字を表示する
from uctypes import BF_POS, BF_LEN, BFUINT32, addressof, struct
# control register structure
DMA_CTRL_LAYOUT = {
"AHB_ERROR": 31<<BF_POS | 1<<BF_LEN | BFUINT32,
"READ_ERROR": 30<<BF_POS | 1<<BF_LEN | BFUINT32,
"WRITE_ERROR": 29<<BF_POS | 1<<BF_LEN | BFUINT32,
"BUSY": 24<<BF_POS | 1<<BF_LEN | BFUINT32,
"SNIFF_EN": 23<<BF_POS | 1<<BF_LEN | BFUINT32,
"BSWAP": 22<<BF_POS | 1<<BF_LEN | BFUINT32,
"IRQ_QUIET": 21<<BF_POS | 1<<BF_LEN | BFUINT32,
"TREQ_SEL": 15<<BF_POS | 6<<BF_LEN | BFUINT32,
"CHAIN_TO": 11<<BF_POS | 4<<BF_LEN | BFUINT32,
"RING_SEL": 10<<BF_POS | 1<<BF_LEN | BFUINT32,
"RING_SIZE": 6<<BF_POS | 4<<BF_LEN | BFUINT32,
"INCR_WRITE": 5<<BF_POS | 1<<BF_LEN | BFUINT32,
"INCR_READ": 4<<BF_POS | 1<<BF_LEN | BFUINT32,
"DATA_SIZE": 2<<BF_POS | 2<<BF_LEN | BFUINT32,
"HIGH_PRIORITY": 1<<BF_POS | 1<<BF_LEN | BFUINT32,
"EN": 0<<BF_POS | 1<<BF_LEN | BFUINT32
}
DMA_BASE = const(0x5000_0000)
# alias CSRs offset address
DMA_CSR_LENGTH = const(0x040)
DMA_0_READ_ADDR = const(0x000)
DMA_0_WRITE_ADDR = const(0x004)
DMA_0_TRANS_COUNT = const(0x008)
DMA_0_CTRL_TRIG = const(0x00c)
DMA_1_CTRL = const(0x010)
DMA_1_TRANS_COUNT_TRIG = const(0x01c)
DMA_3_READ_ADD_TRIG = const(0x03c)
# ... ch 0..11 (CH11_ALIAS_3_READ_ADD_TRIG = 0x2fc)
DMA_CHAN_ABORT = const(0x444)
# DMA_BASE + (DMA_DGB_LENGTH * ch) + DMA_DBG_(CTDREQ | TCR)
DMA_DBG_LENGTH = const(0x040)
DMA_DBG_CTDREQ = const(0x800)
DMA_DBG_TCR = const(0x804)
DREQ_PIO0_TX0 = const(0)
DREQ_PIO0_TX1 = const(1)
@micropython.viper
def print_dma(channel: uint):
csr_addr = uint(DMA_BASE) + (DMA_CSR_LENGTH * channel) + DMA_0_READ_ADDR
dbg_addr = uint(DMA_BASE) + (DMA_DBG_LENGTH * channel) + DMA_DBG_CTDREQ
csr = ptr32(csr_addr)
dbg = ptr32(dbg_addr)
ctrl = csr[3]
print(f'''\
DMA {channel=} CSRs 0x{csr_addr:04x} DBG 0x{dbg_addr:04x}
Read=0x{csr[0]:04x} Write=0x{csr[1]:04x}
Trans={uint(csr[2])}({uint(dbg[1])})
ERROR={ctrl>>29 & 0b111:03b} BUSY={ctrl>>24 & 1} EN={ctrl&0b1}
DREQ count={dbg[0] & 0b11_1111}
''')
@micropython.viper
def clear_dreq(channel: uint):
dbg = ptr32(uint(DMA_BASE) + (DMA_DBG_LENGTH * channel) + DMA_DBG_CTDREQ)
dbg[0] = 0b11_1111
@micropython.viper
def abort(channel: uint):
p = ptr32(DMA_BASE + DMA_CHAN_ABORT)
p[0] = (1 << channel)
while True:
if p[0] == 0:
return
@micropython.viper
def pause(channel: uint):
p = ptr32(uint(DMA_BASE) + (DMA_CSR_LENGTH * channel) + DMA_1_CTRL)
p[0] &= uint(0xFFFF_FFFE)
@micropython.viper
def unpause(channel:uint):
p = ptr32(uint(DMA_BASE) + (DMA_CSR_LENGTH * channel) + DMA_1_CTRL)
p[0] |= 1
from rp2 import *
from machine import Pin, mem32
from sys import byteorder
from uctypes import addressof, struct
import pio
import dma
clk = Pin(26, Pin.OUT, value=0) #GPIO26, GPIO27
# row
_oe = Pin(21, Pin.OUT, value=1)
ini = Pin(20, Pin.OUT, value=0)
# row init
_oe.high()
ini.high()
for i in range(16):
clk.high()
clk.low()
ini.low()
clk.high()
clk.low()
ini.high()
clk.high()
clk.low()
#column
serial_input = Pin(10, Pin.OUT, value=0)
serial_clock = Pin(11, Pin.OUT, value=0)
# column init
serial_input.low()
for i in range(32):
serial_clock.high()
serial_clock.low()
@asm_pio(
sideset_init=(PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW)
)
def timer():
irq(4).side(0b111)
nop().side(0)
@asm_pio(
sideset_init=PIO.OUT_LOW,
out_init=PIO.OUT_LOW,
out_shiftdir=PIO.SHIFT_RIGHT,
pull_thresh=32
)
def tx():
#wait(1, irq, 4)
pull()
# CLK pulse width > 5 cycle
# SER setup > 6 cycle
out(pins, 1).side(0) [5]
label("loop")
nop().side(1)
out(pins, 1) [3]
jmp(not_osre, "loop").side(0) [4]
nop().side(1) [4]
wait(1, irq, 4)
FREQ = 20_000 # 0.05ms/cycle
timer = StateMachine(0, timer, freq=FREQ, sideset_base=Pin(25)) #sideset 25, 26, 27
tx = StateMachine(1, tx, sideset_base=serial_clock, out_base=serial_input)
dma_ch_data = 9
dma_ch_config = 10
# clear dma channel state
dma.pause(dma_ch_data)
dma.abort(dma_ch_data)
dma.pause(dma_ch_config)
dma.abort(dma_ch_config)
addr_dma_ch_data = dma.DMA_BASE + (dma.DMA_CSR_LENGTH * dma_ch_data)
addr_dma_ch_config = dma.DMA_BASE + (dma.DMA_CSR_LENGTH * dma_ch_config)
#mem32[addr_dma_ch_data + dma.DMA_0_READ_ADDR]
mem32[addr_dma_ch_data + dma.DMA_0_WRITE_ADDR] = pio.PIO0_BASE + (pio.PIO_TXF0 + 4)
mem32[addr_dma_ch_data + dma.DMA_0_TRANS_COUNT] = 16
ctrl = struct(addr_dma_ch_data + dma.DMA_1_CTRL, dma.DMA_CTRL_LAYOUT)
ctrl.TREQ_SEL = dma.DREQ_PIO0_TX1
ctrl.CHAIN_TO = dma_ch_config
ctrl.INCR_WRITE = 0
ctrl.INCR_READ = 1
ctrl.DATA_SIZE = 0x2 # SIZE_WORD: 4 bytes
ctrl.EN = 1
#mem32[addr_dma_ch_config + dma.DMA_0_READ_ADDR]
mem32[addr_dma_ch_config + dma.DMA_0_WRITE_ADDR] = addr_dma_ch_data + dma.DMA_0_READ_ADDR
mem32[addr_dma_ch_config + dma.DMA_0_TRANS_COUNT] = 1
ctrl = struct(addr_dma_ch_config + dma.DMA_1_CTRL, dma.DMA_CTRL_LAYOUT)
ctrl.TREQ_SEL = 0x3f # permanent request
ctrl.CHAIN_TO = dma_ch_data
ctrl.INCR_WRITE = 0
ctrl.INCR_READ = 0
ctrl.DATA_SIZE = 0x2 # SIZE_WORD: 4 bytes
ctrl.EN = 1
from collections import deque as Queue
queue = Queue((), 50)
blank = memoryview(bytes(4 * 16))
readAddrRegister = bytearray(4)
_buffer0 = bytearray(4 * 16)
_buffer1 = bytearray(4 * 16)
readAddrRegister[:] = int.to_bytes(addressof(_buffer0), 4, byteorder)
left = blank
right = blank
@micropython.viper
def render(i:int):
global left, right
buff = ptr32(_buffer0 if i % 2 else _buffer1)
for row in range(16):
l = ptr32(left)[row]
r = ptr32(right)[row]
red = (l & uint(0xFFFF_0000)) | (r >> 16) & 0xFFFF
yg = (l << 16) | (r & 0xFFFF)
bits = (red >> (16 - i) & 0xFFFF) << 16
bits |= (yg >> (16 - i) & 0xFFFF)
buff[row] = bits
ptr32(readAddrRegister)[0] = uint(buff)
if i == 15:
left = right
right = queue.popleft() if len(queue) else blank
from machine import Timer
from micropython import schedule
def cycle(n):
while True:
yield from range(n)
def handler(timer, i = cycle(16)):
schedule(render, next(i))
tim = Timer()
tim.init(period=33, mode=Timer.PERIODIC, callback=handler)
mem32[addr_dma_ch_config + dma.DMA_3_READ_ADD_TRIG] = addressof(readAddrRegister)
_oe.low()
tx.active(1)
from time import sleep_ms
sleep_ms(100)
timer.active(1)
def glyph(p):
@micropython.viper
def glyph(fh):
dat = bytes(4 * 16)
p = ptr8(dat)
for i in range(16):
b = fh.read(2)
i = 4 * i
p[i+3] = int(b[0])
p[i+2] = int(b[1])
return dat
SIZE = 34
QTY = 6879
with open('font.dat', 'rb') as f:
head = 0
end = QTY - 1
while head <= end:
mid = (head + end) // 2
f.seek(SIZE * mid)
q = int.from_bytes(f.read(2), 'little')
if p == q:
return glyph(f)
head = head if p < q else mid + 1
end = end if p > q else mid - 1
return None
def display(s):
for c in s:
queue.append(glyph(ord(c)) or blank)
##################################
def stop():
dma.pause(dma_ch_data)
dma.abort(dma_ch_data)
tx.put(0)
class _exit:
def __enter__(_):
pass
def __exit__(self, _1, _2, _3):
stop()
print("stopped")
import sys
with _exit() as _:
while True:
display(sys.stdin.readline())
PIO0_BASE = const(0x5020_0000)
PIO1_BASE = const(0x5030_0000)
PIO_CTRL = const(0x000)
PIO_FSTAT = const(0x004)
PIO_FDEBUG = const(0x008)
PIO_FLEVEL = const(0x00c)
# tx fifo0 ... tx fifo3
PIO_TXF0 = const(0x010)
# rx fifo0 ... rx firl3
PIO_RXF0 = const(0x020)
PIO_IRQ = const(0x030)
PIO_IRQ_FORCE = const(0x034)
# sm0 ... sm3
PIO_SM_CTRL_LENGTH = const(0x018)
PIO_SM0_CLKDIV = const(0x0c8)
PIO_SM0_EXECTRL = const(0x0cc)
PIO_SM0_SHIFTCTRL = const(0x0d0)
PIO_SM0_ADDR = const(0x0d4)
PIO_SM0_INSTR = const(0x0d8)
PIO_SM0_PINCTRL = const(0x0dc)
@micropython.viper
def print_sm(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
p = ptr32(base + PIO_CTRL)
irq = ptr32(base + PIO_IRQ)
smctrl = ptr32(base + (PIO_SM_CTRL_LENGTH * sm) + PIO_SM0_CLKDIV)
instr = smctrl[4] & 0xFFFF
print(f'''\
IRQ {(irq[0] >> 4) & 0xf:04b} {irq[0] & 0xf:04b}
PIO{pio}_SM{sm} enable={(p[0] >> sm) & 1} \
stalled={smctrl[1] >> 31}
WRAP TOP={smctrl[1] >> 12 & 0b1_1111} \
BOTTOM={smctrl[1] >> 7 & 0b1_1111} \
CURRENT={smctrl[3] & 0b1_1111}
INSTR=0x{instr:04x} {instr >> 13:03b} {instr>>8&0b1_1111:05b} {instr & 0xFF:08b}
TX LEVEL={(p[3] >> (sm * 8)) & 1} \
EMPTY={(p[1] >> 24 + sm) & 1} \
FULL={(p[1] >> 16 + sm) & 1} \
STALL={(p[2] >> 24 + sm) & 1} \
OVER={(p[2] >> 16 + sm) & 1}
RX LEVEL={(p[3] >> (sm * 8) + 4) & 1} \
EMPTY={(p[1] >> 8 + sm) & 1} \
FULL={(p[1] >> sm) & 1} \
UNDER={(p[2] >> 8 + sm) & 1} \
STALL={(p[2] >> sm) & 1}\
''')
@micropython.viper
def pio_sm(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
sm = uint(stateMachineNumber % 4)
return pio, sm
@micropython.viper
def restart(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
ctrl = ptr32(base + PIO_CTRL)
ctrl[0] = 1 << (4 + sm)
@micropython.viper
def pause(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
ctrl = ptr32(base + PIO_CTRL)
ctrl[0] &= (1 << sm) ^ 0b1111
@micropython.viper
def unpause(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
ctrl = ptr32(base + PIO_CTRL)
ctrl[0] |= 1 << sm
@micropython.viper
def clear_irq(pio: int, irq: int):
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
p = ptr32(base + PIO_IRQ)
p[0] = 1 << irq
@micropython.viper
def force_irq(pio: int, irq:int):
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
p = ptr32(base + PIO_IRQ_FORCE)
p[0] = 1 << irq
@micropython.viper
def clear_fifos(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
shiftctrl = ptr32(base + (PIO_SM_CTRL_LENGTH * sm) + PIO_SM0_SHIFTCTRL)
shiftctrl[0] ^= uint(1 << 31)
shiftctrl[0] ^= uint(1 << 31)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment