Last active
January 10, 2023 00:40
-
-
Save azechi/13e27f9c00814699520d62fde7e6d6a1 to your computer and use it in GitHub Desktop.
LEDマトリクスに流れる文字を表示する
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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