Initial commit of original SC7 code
|
@ -0,0 +1,34 @@
|
|||
|
||||
from vectorscope import Vectorscope
|
||||
import vectoros
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import asyncio
|
||||
|
||||
import random_walk
|
||||
|
||||
_abort=False
|
||||
|
||||
async def random_walker(v):
|
||||
## Minimal example
|
||||
global _abort
|
||||
r = random_walk.RW(v)
|
||||
x,y = 0,0
|
||||
while _abort==False:
|
||||
x,y = r.random_walk(x,y)
|
||||
## this is important -- it yields to the key scanner
|
||||
await asyncio.sleep_ms(10)
|
||||
|
||||
## Below here is boilerplate.
|
||||
def do_abort(key):
|
||||
global _abort
|
||||
_abort=True
|
||||
|
||||
from vos_state import vos_state
|
||||
|
||||
async def slot_main(v):
|
||||
global _abort
|
||||
# watch the keys -- you can define your own callbacks here
|
||||
mykeys = keyboardcb.KeyboardCB( {keyleds.KEY_MENU: do_abort} )
|
||||
await random_walker(v)
|
||||
print("OK done")
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
import math
|
||||
import time
|
||||
|
||||
from vectorscope import Vectorscope
|
||||
|
||||
import vectoros
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import asyncio
|
||||
|
||||
|
||||
_abort=False
|
||||
_xscale=1
|
||||
_yscale=1
|
||||
|
||||
async def kminimal_example(v):
|
||||
## Minimal example with keys
|
||||
global _abort, _xscale, _yscale
|
||||
while _abort==False:
|
||||
for i in range(360):
|
||||
scale=1
|
||||
if _abort:
|
||||
print("Get out!")
|
||||
break
|
||||
v.wave.constantX(int(math.cos(i * math.pi / 180 * 5*_xscale) * 10000))
|
||||
v.wave.constantY(int(math.sin(i * math.pi / 180 * 5*_yscale)* 10000))
|
||||
await asyncio.sleep_ms(10)
|
||||
|
||||
|
||||
def do_abort(key):
|
||||
global _abort
|
||||
_abort=True
|
||||
|
||||
def do_xscale(key):
|
||||
global _xscale
|
||||
_xscale+=1
|
||||
if _xscale>6:
|
||||
_xscale=1
|
||||
|
||||
|
||||
def do_yscale(key):
|
||||
global _yscale
|
||||
_yscale+=1
|
||||
if _yscale>6:
|
||||
_yscale=1
|
||||
|
||||
|
||||
|
||||
from vos_state import vos_state
|
||||
|
||||
async def slot_main(v):
|
||||
global _abort,_continue
|
||||
# So... Press D (or whatever is configured) and note the message below. Press Range to start the demo
|
||||
# The demo will run until you press Menu. LEVEL/RANGE will change frequency of X and Y in steps
|
||||
# Note that if you don't yield occasionaly, you don't get key scanning
|
||||
|
||||
# watch the keys
|
||||
mykeys=keyboardcb.KeyboardCB({ keyleds.KEY_LEVEL: do_xscale, keyleds.KEY_RANGE: do_yscale, keyleds.KEY_MENU: do_abort})
|
||||
|
||||
|
||||
await kminimal_example(v)
|
||||
print("OK done")
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
from vectorscope import Vectorscope
|
||||
import vectoros
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import asyncio
|
||||
|
||||
import random_walk
|
||||
|
||||
_abort=False
|
||||
|
||||
async def random_walker(v):
|
||||
## Minimal example
|
||||
global _abort
|
||||
r = random_walk.RW(v)
|
||||
x,y = 0,0
|
||||
while _abort==False:
|
||||
x,y = r.random_walk(x,y)
|
||||
## this is important -- it yields to the key scanner
|
||||
await asyncio.sleep_ms(10)
|
||||
|
||||
## Below here is boilerplate.
|
||||
def do_abort(key):
|
||||
global _abort
|
||||
_abort=True
|
||||
|
||||
from vos_state import vos_state
|
||||
|
||||
async def slot_main(v):
|
||||
global _abort
|
||||
# watch the keys -- you can define your own callbacks here
|
||||
mykeys = keyboardcb.KeyboardCB( {keyleds.KEY_MENU: do_abort} )
|
||||
await random_walker(v)
|
||||
print("OK done")
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
from vectorscope import Vectorscope
|
||||
import vectoros
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import asyncio
|
||||
|
||||
import random_walk
|
||||
|
||||
_abort=False
|
||||
|
||||
async def random_walker(v):
|
||||
## Minimal example
|
||||
global _abort
|
||||
r = random_walk.RW(v)
|
||||
x,y = 0,0
|
||||
while _abort==False:
|
||||
x,y = r.random_walk(x,y)
|
||||
## this is important -- it yields to the key scanner
|
||||
await asyncio.sleep_ms(10)
|
||||
|
||||
## Below here is boilerplate.
|
||||
def do_abort(key):
|
||||
global _abort
|
||||
_abort=True
|
||||
|
||||
from vos_state import vos_state
|
||||
|
||||
async def slot_main(v):
|
||||
global _abort
|
||||
# watch the keys -- you can define your own callbacks here
|
||||
mykeys = keyboardcb.KeyboardCB( {keyleds.KEY_MENU: do_abort} )
|
||||
await random_walker(v)
|
||||
print("OK done")
|
|
@ -0,0 +1,146 @@
|
|||
from uctypes import addressof
|
||||
import array
|
||||
import rp2
|
||||
import time
|
||||
import machine
|
||||
import dma_defs
|
||||
import pio_defs
|
||||
|
||||
# read_debug = machine.Pin(26, machine.Pin.OUT)
|
||||
|
||||
num_frames = const(16)
|
||||
num_samples_per_frame = const(1024)
|
||||
bytes_per_sample = const(4)
|
||||
audio_data_length = const(num_frames * num_samples_per_frame * bytes_per_sample)
|
||||
|
||||
class ADC_Reader():
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.num_samples_per_frame = num_samples_per_frame
|
||||
## Setup storage for frames of samples
|
||||
self.audio_data = bytearray(audio_data_length)
|
||||
## and the individual frames start here
|
||||
self.frame_starts = array.array("L", [addressof(self.audio_data) + i*num_samples_per_frame*bytes_per_sample for i in range(num_frames)])
|
||||
self.current_frame = 0
|
||||
## finish up init
|
||||
self.align_frame_lookup_address()
|
||||
self.config_dmas()
|
||||
|
||||
def init(self):
|
||||
self.__init__()
|
||||
|
||||
def deinit(self):
|
||||
"""unwind all of the above"""
|
||||
self.audio_read_control.ctrl = 0
|
||||
time.sleep_ms(10) ## (@_@) replace with asyncio when necessary
|
||||
self.audio_read_transfer.ctrl = 0
|
||||
self.audio_read_control.close()
|
||||
time.sleep_ms(10) ## (@_@) replace with asyncio when necessary
|
||||
self.audio_read_transfer.close()
|
||||
|
||||
def pause(self):
|
||||
self.audio_read_transfer.ctrl &= ~(1) ## mask enable bit
|
||||
time.sleep_ms(10)
|
||||
self.audio_read_control.ctrl &= ~(1) ## mask enable bit
|
||||
|
||||
def resume(self):
|
||||
self.audio_read_control.ctrl |= 1 ## set enable bit
|
||||
time.sleep_ms(10)
|
||||
self.audio_read_transfer.ctrl |= 1 ## set enable bit
|
||||
self.audio_read_transfer.config(trigger=True)
|
||||
|
||||
@micropython.viper
|
||||
def audio_read_frame_interrupt(self, calling_dma):
|
||||
"""fires off once per frame, in parallel with audio_read_control"""
|
||||
# read_debug.high()
|
||||
self.current_frame = (int(self.current_frame)+1) & 0x0F
|
||||
## setup frames/colors and trigger pixel pusher here
|
||||
## want it to go through one cycle -- 15 frames at 15 colors.
|
||||
|
||||
# read_debug.low()
|
||||
|
||||
def config_dmas(self):
|
||||
self.audio_read_transfer = rp2.DMA()
|
||||
#print("self.audio_read_transfer")
|
||||
#print(self.audio_read_transfer)
|
||||
self.audio_read_control = rp2.DMA()
|
||||
#print("self.audio_read_control")
|
||||
#print(self.audio_read_control)
|
||||
self.audio_read_transfer.ctrl = self.audio_read_transfer.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.DREQ_PIO0_RX0,
|
||||
inc_write = 1,
|
||||
chain_to = self.audio_read_control.channel_id
|
||||
)
|
||||
self.audio_read_transfer.config(count = num_samples_per_frame,
|
||||
read = pio_defs.PIO0_BASE + pio_defs.RXF0_OFFSET)
|
||||
self.audio_read_transfer.irq(handler = self.audio_read_frame_interrupt)
|
||||
# manual trigger
|
||||
# self.audio_read_transfer.config(write = audio_data, trigger=True)
|
||||
|
||||
self.audio_read_control.ctrl = self.audio_read_control.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.TREQ_PERMANENT,
|
||||
IRQ_quiet = 1,
|
||||
chain_to = self.audio_read_transfer.channel_id,
|
||||
inc_read = 1,
|
||||
ring_size = 6) ## 16 addresses * 4 bytes
|
||||
## feed write addresses of each frame start when done with a frame
|
||||
self.audio_read_control.config(write = addressof(self.audio_read_transfer.registers[1:]),
|
||||
read = self.frame_lookup_address,
|
||||
count = 1,
|
||||
trigger = True)
|
||||
|
||||
|
||||
def align_frame_lookup_address(self):
|
||||
## Need to align this for DMA to enable ring looping over frame starts.
|
||||
## each address is 4 bytes, need 16 of them: 2+4 = 6 bits clear -- align to nearest 0b11000000 = C0
|
||||
## with 64 bytes to be stored, need 128 to guarantee clear (2x num_frames * 4 bytes)
|
||||
## Make an aligned lookup table for DMA porpoises
|
||||
self.frame_lookup = bytearray(num_frames * 4 * 2)
|
||||
self.frame_lookup_address = addressof(self.frame_lookup)
|
||||
|
||||
## spool forward to nearest /64 block : frame_lookup_address is now ready to use
|
||||
while self.frame_lookup_address != self.frame_lookup_address & (0xFFFFFFC0):
|
||||
self.frame_lookup_address = self.frame_lookup_address + 1
|
||||
|
||||
## copy frame starts over into lookup LSB first
|
||||
## could probably do this with struct.pack()?
|
||||
tempaddress = self.frame_lookup_address
|
||||
for f in self.frame_starts:
|
||||
machine.mem8[tempaddress] = f & 0xFF
|
||||
machine.mem8[tempaddress+1] = (f & 0xFF00) >> 8
|
||||
machine.mem8[tempaddress+2] = (f & 0xFF0000) >> 16
|
||||
machine.mem8[tempaddress+3] = (f & 0xFF000000) >> 24
|
||||
tempaddress = tempaddress + 4
|
||||
|
||||
|
||||
####################################
|
||||
## Residual useful debugging stuff
|
||||
####################################
|
||||
|
||||
def debug_print_frames(self,n):
|
||||
for j in range(16):
|
||||
print(f'############## {j} ################')
|
||||
for i in range(n):
|
||||
dma_defs.print_friendly(machine.mem32[self.frame_starts[j]+4*i])
|
||||
|
||||
def dma_frame_diagnostics(self):
|
||||
current_write_addr = self.audio_read_transfer.write
|
||||
current_frame_pointer = self.audio_read_control.read
|
||||
current_frame = (current_frame_pointer - self.frame_lookup_address) // 4
|
||||
current_frame_start_addr = machine.mem32[current_frame_pointer]
|
||||
print("Address of frame start pointer")
|
||||
print(hex(current_frame_pointer))
|
||||
print("Address of frame start")
|
||||
print(hex(current_frame_start_addr))
|
||||
print(f"Frame number: {current_frame}")
|
||||
print("Address of current datapoint")
|
||||
print(hex(current_write_addr))
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
# MIT license; Copyright (c) 2022 Jim Mussared
|
||||
|
||||
import micropython
|
||||
from micropython import const
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
# Import statement (needs to be global, and does not return).
|
||||
_RE_IMPORT = re.compile("^import ([^ ]+)( as ([^ ]+))?")
|
||||
_RE_FROM_IMPORT = re.compile("^from [^ ]+ import ([^ ]+)( as ([^ ]+))?")
|
||||
# Global variable assignment.
|
||||
_RE_GLOBAL = re.compile("^([a-zA-Z0-9_]+) ?=[^=]")
|
||||
# General assignment expression or import statement (does not return a value).
|
||||
_RE_ASSIGN = re.compile("[^=]=[^=]")
|
||||
|
||||
# Command hist (One reserved slot for the current command).
|
||||
_HISTORY_LIMIT = const(5 + 1)
|
||||
|
||||
|
||||
async def execute(code, g, s):
|
||||
if not code.strip():
|
||||
return
|
||||
|
||||
try:
|
||||
if "await " in code:
|
||||
# Execute the code snippet in an async context.
|
||||
if m := _RE_IMPORT.match(code) or _RE_FROM_IMPORT.match(code):
|
||||
code = "global {}\n {}".format(m.group(3) or m.group(1), code)
|
||||
elif m := _RE_GLOBAL.match(code):
|
||||
code = "global {}\n {}".format(m.group(1), code)
|
||||
elif not _RE_ASSIGN.search(code):
|
||||
code = "return {}".format(code)
|
||||
|
||||
code = """
|
||||
import uasyncio as asyncio
|
||||
async def __code():
|
||||
{}
|
||||
|
||||
__exec_task = asyncio.create_task(__code())
|
||||
""".format(
|
||||
code
|
||||
)
|
||||
|
||||
async def kbd_intr_task(exec_task, s):
|
||||
while True:
|
||||
if ord(await s.read(1)) == 0x03:
|
||||
exec_task.cancel()
|
||||
return
|
||||
|
||||
l = {"__exec_task": None}
|
||||
exec(code, g, l)
|
||||
exec_task = l["__exec_task"]
|
||||
|
||||
# Concurrently wait for either Ctrl-C from the stream or task
|
||||
# completion.
|
||||
intr_task = asyncio.create_task(kbd_intr_task(exec_task, s))
|
||||
|
||||
try:
|
||||
try:
|
||||
return await exec_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
finally:
|
||||
intr_task.cancel()
|
||||
try:
|
||||
await intr_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
else:
|
||||
# Excute code snippet directly.
|
||||
try:
|
||||
try:
|
||||
micropython.kbd_intr(3)
|
||||
try:
|
||||
return eval(code, g)
|
||||
except SyntaxError:
|
||||
# Maybe an assignment, try with exec.
|
||||
return exec(code, g)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
micropython.kbd_intr(-1)
|
||||
|
||||
except Exception as err:
|
||||
print("{}: {}".format(type(err).__name__, err))
|
||||
|
||||
|
||||
# REPL task. Invoke this with an optional mutable globals dict.
|
||||
async def task(g=None, prompt="--> "):
|
||||
print("Starting asyncio REPL...")
|
||||
if g is None:
|
||||
g = __import__("__main__").__dict__
|
||||
try:
|
||||
micropython.kbd_intr(-1)
|
||||
s = asyncio.StreamReader(sys.stdin)
|
||||
# clear = True
|
||||
hist = [None] * _HISTORY_LIMIT
|
||||
hist_i = 0 # Index of most recent entry.
|
||||
hist_n = 0 # Number of history entries.
|
||||
c = 0 # ord of most recent character.
|
||||
t = 0 # timestamp of most recent character.
|
||||
while True:
|
||||
hist_b = 0 # How far back in the history are we currently.
|
||||
sys.stdout.write(prompt)
|
||||
cmd = ""
|
||||
while True:
|
||||
b = await s.read(1)
|
||||
pc = c # save previous character
|
||||
c = ord(b)
|
||||
pt = t # save previous time
|
||||
t = time.ticks_ms()
|
||||
if c < 0x20 or c > 0x7E:
|
||||
if c == 0x0A:
|
||||
# LF
|
||||
# If the previous character was also LF, and was less
|
||||
# than 20 ms ago, this was likely due to CRLF->LFLF
|
||||
# conversion, so ignore this linefeed.
|
||||
if pc == 0x0A and time.ticks_diff(t, pt) < 20:
|
||||
continue
|
||||
sys.stdout.write("\n")
|
||||
if cmd:
|
||||
# Push current command.
|
||||
hist[hist_i] = cmd
|
||||
# Increase history length if possible, and rotate ring forward.
|
||||
hist_n = min(_HISTORY_LIMIT - 1, hist_n + 1)
|
||||
hist_i = (hist_i + 1) % _HISTORY_LIMIT
|
||||
|
||||
result = await execute(cmd, g, s)
|
||||
if result is not None:
|
||||
sys.stdout.write(repr(result))
|
||||
sys.stdout.write("\n")
|
||||
break
|
||||
elif c == 0x08 or c == 0x7F:
|
||||
# Backspace.
|
||||
if cmd:
|
||||
cmd = cmd[:-1]
|
||||
sys.stdout.write("\x08 \x08")
|
||||
elif c == 0x02:
|
||||
# Ctrl-B
|
||||
continue
|
||||
elif c == 0x03:
|
||||
# Ctrl-C
|
||||
if pc == 0x03 and time.ticks_diff(t, pt) < 120: #was 20
|
||||
# Two very quick Ctrl-C (faster than a human
|
||||
# typing) likely means mpremote trying to
|
||||
# escape.
|
||||
asyncio.new_event_loop()
|
||||
return
|
||||
sys.stdout.write("\n")
|
||||
break
|
||||
elif c == 0x04:
|
||||
# Ctrl-D
|
||||
sys.stdout.write("\n")
|
||||
# Shutdown asyncio.
|
||||
asyncio.new_event_loop()
|
||||
return
|
||||
elif c == 0x1B:
|
||||
# Start of escape sequence.
|
||||
key = await s.read(2)
|
||||
if key in ("[A", "[B"):
|
||||
# Stash the current command.
|
||||
hist[(hist_i - hist_b) % _HISTORY_LIMIT] = cmd
|
||||
# Clear current command.
|
||||
b = "\x08" * len(cmd)
|
||||
sys.stdout.write(b)
|
||||
sys.stdout.write(" " * len(cmd))
|
||||
sys.stdout.write(b)
|
||||
# Go backwards or forwards in the history.
|
||||
if key == "[A":
|
||||
hist_b = min(hist_n, hist_b + 1)
|
||||
else:
|
||||
hist_b = max(0, hist_b - 1)
|
||||
# Update current command.
|
||||
cmd = hist[(hist_i - hist_b) % _HISTORY_LIMIT]
|
||||
sys.stdout.write(cmd)
|
||||
else:
|
||||
# sys.stdout.write("\\x")
|
||||
# sys.stdout.write(hex(c))
|
||||
pass
|
||||
else:
|
||||
sys.stdout.write(b)
|
||||
cmd += b
|
||||
finally:
|
||||
micropython.kbd_intr(3)
|
|
@ -0,0 +1,64 @@
|
|||
from machine import Pin, SPI, PWM, I2C, I2S, PWM, freq
|
||||
import time
|
||||
import rp2
|
||||
import machine
|
||||
import pin_defs
|
||||
import pio_defs
|
||||
import pio_code
|
||||
|
||||
## I2C bus address
|
||||
ak4619_addr = const(0x10)
|
||||
READ_FIFO = pio_defs.PIO0_BASE + pio_defs.RXF0_OFFSET ## 0, 0
|
||||
WRITE_FIFO = pio_defs.PIO0_BASE + pio_defs.TXF1_OFFSET ## 0, 1
|
||||
|
||||
class Codec():
|
||||
def __init__(self):
|
||||
# RPi I2S doesn't have master clock -- we make one with PWM
|
||||
# 125000000 / 16 = 7.8125 MHz master clock
|
||||
# 250_000_000 / 16 = 7.8125 MHz master clock
|
||||
self.MASTER_CLOCK_HZ = freq() // 32
|
||||
# fs sample clock = MCLK / 256 = 30517.578 Hz
|
||||
self.mclk = Pin(pin_defs.mclk, Pin.OUT)
|
||||
self.mclk_pwm = PWM(self.mclk, freq=self.MASTER_CLOCK_HZ, duty_u16=32768)
|
||||
|
||||
self.bit_clock = Pin(pin_defs.bit_clock, Pin.OUT, value=0) ## fs / 32
|
||||
self.lr_clock = Pin(pin_defs.lr_clock, Pin.OUT, value=0) ## fs
|
||||
self.sd_in = Pin(pin_defs.sd_in, Pin.IN) ## In 3/4 (Y=L, X=R)
|
||||
self.sd_out = Pin(pin_defs.sd_out, Pin.OUT, value=0) ## Out 1/2 (Y=L, X=R)
|
||||
self.pdn = Pin(pin_defs.pdn, Pin.OUT, value=0) ## Power down on low
|
||||
|
||||
self.i2s_read_sm = rp2.StateMachine(0, pio_code.i2s_read_pio, freq=self.MASTER_CLOCK_HZ // 2, sideset_base=self.bit_clock, in_base=self.sd_in)
|
||||
self.i2s_write_sm = rp2.StateMachine(1, pio_code.i2s_write_pio, freq=self.MASTER_CLOCK_HZ // 2, out_base=self.sd_out)
|
||||
self.start()
|
||||
|
||||
def deinit(self):
|
||||
self.i2s_read_sm.active(0)
|
||||
self.i2s_write_sm.active(0)
|
||||
self.mclk_pwm.deinit()
|
||||
self.pdn.value(0)
|
||||
|
||||
## Manually resync the PIO state machines so they run in cycle
|
||||
def start(self):
|
||||
self.mclk_pwm = PWM(self.mclk, freq=self.MASTER_CLOCK_HZ, duty_u16=32768)
|
||||
machine.mem32[pio_defs.PIO0_BASE] = (0b1111 << pio_defs.CLKDIV_RESTART_BIT) ## reset all clock dividers
|
||||
machine.mem32[pio_defs.PIO0_BASE] = (0b1111 << pio_defs.SM_ENABLE_BIT) ## enable all simultaneously to sync up
|
||||
self.config_i2c()
|
||||
|
||||
def config_i2c(self):
|
||||
self.i2c = I2C(0, scl=Pin(pin_defs.i2c_scl), sda=Pin(pin_defs.i2c_sda), freq=400_000)
|
||||
## Initialize AK4619 registers
|
||||
## PDN starts 0, when goes high, enters config mode after 10 ms -- datasheet
|
||||
self.pdn.value(1)
|
||||
time.sleep_ms(10)
|
||||
## Config for 16-bit samples & 16-bit slots
|
||||
self.i2c.writeto(ak4619_addr, bytes([0x01, 0x3A])) # MSB Justified, 16 bit slot and read data on falling bit-clock
|
||||
self.i2c.writeto(ak4619_addr, bytes([0x02, 0x0A])) # 16 bit data on in and out
|
||||
## Config single-ended input on 3 & 4
|
||||
self.i2c.writeto(ak4619_addr, bytes([0x0B, 0x05]))
|
||||
## slow ADC filters -- need to look into this later?
|
||||
self.i2c.writeto(ak4619_addr, bytes([0x0A, 0x22]))
|
||||
self.i2c.writeto(ak4619_addr, bytes([0x0D, 0x06]))
|
||||
self.i2c.writeto(ak4619_addr, bytes([0x00, 0x37])) ## Power on, release reset bit
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from gc9a01 import color565
|
||||
import gc9a01 as lcd
|
||||
import phosphor_gradient_14
|
||||
|
||||
PHOSPHOR = phosphor_gradient_14.phosphor_gradient
|
||||
|
||||
PHOSPHOR_BRIGHT=PHOSPHOR[15]
|
||||
PHOSPHOR_DARK=PHOSPHOR[12]
|
||||
PHOSPHOR_BG=PHOSPHOR[0]
|
||||
|
||||
BLACK=lcd.BLACK
|
||||
WHITE=lcd.WHITE
|
||||
RED=lcd.RED
|
||||
GREEN=lcd.GREEN
|
||||
BLUE=lcd.BLUE
|
||||
CYAN=lcd.CYAN
|
||||
MAGENTA=lcd.MAGENTA
|
||||
YELLOW=lcd.YELLOW
|
||||
|
||||
GRAY=color565(0x80,0x80,0x80)
|
||||
|
||||
def rgb(r,g,b):
|
||||
return color565(r,g,b)
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
import array
|
||||
import generate_wavetables
|
||||
|
||||
NUM_SAMPLES = const(256)
|
||||
ACCUMULATOR_BITS = const(16)
|
||||
|
||||
X_CHANNEL = const(0)
|
||||
Y_CHANNEL = const(1)
|
||||
|
||||
WAVEFORM_SINE = const(0)
|
||||
WAVEFORM_SQUARE = const(1)
|
||||
WAVEFORM_SAWTOOTH = const(2)
|
||||
WAVEFORM_TRIANGLE = const(3)
|
||||
|
||||
|
||||
class DDS():
|
||||
|
||||
def __init__(self, vectorscope):
|
||||
""" pass it an instance of the Vectorscope module from main, it needs to write to the DMA audio registers"""
|
||||
self.wave = vectorscope.wave
|
||||
|
||||
self.waveform = ["sine", "sine"]
|
||||
self.increment = [51000, 25000]
|
||||
self.amplitude = [1, 1] ## set this infrequently? -- it's an expensive calculation
|
||||
self.phase = [0, 0]
|
||||
self.phase_increment = [0, 0]
|
||||
self.index = [0, 0]
|
||||
self.accumulator = [0, 0]
|
||||
self.samplesX = [0]*NUM_SAMPLES
|
||||
self.samplesY = [0]*NUM_SAMPLES
|
||||
self.samples = [self.samplesX, self.samplesY]
|
||||
|
||||
self.base_waveforms = {}
|
||||
self.base_waveforms["sine"] = generate_wavetables.sine()
|
||||
self.base_waveforms["square"] = generate_wavetables.square()
|
||||
self.base_waveforms["sawtooth"] = generate_wavetables.sawtooth()
|
||||
self.base_waveforms["triangle"] = generate_wavetables.triangle()
|
||||
|
||||
## init
|
||||
self.waves = [[0]*256, [0]*256]
|
||||
self.recalculate_waveforms() ## have to do this every time you change parameters.
|
||||
|
||||
def recalculate_waveforms(self): ## select 0=X, 1=Y
|
||||
for i in [0,1]:
|
||||
self.waves[i] = [int(self.amplitude[i]*x) for x in self.base_waveforms[self.waveform[i]]]
|
||||
|
||||
def initial_wait_for_buffer_sync(self):
|
||||
self.wave.outBuffer_ready = False ## mark seen
|
||||
while not self.wave.outBuffer_ready:
|
||||
pass ## (@_@) should be async wait
|
||||
|
||||
@micropython.viper
|
||||
def do_dds(self):
|
||||
for i in [0,1]: ## X , Y
|
||||
self.phase[i] = int(self.phase[i]) + int(self.phase_increment[i])
|
||||
for s in range(NUM_SAMPLES):
|
||||
self.index[i] = int(self.phase[i]) + (int(self.accumulator[i]) >> ACCUMULATOR_BITS) & 0xFF ## roll over sample bits
|
||||
self.accumulator[i] = (int(self.accumulator[i]) + int(self.increment[i])) & 0xFFFFFF ## sample bits + accumulator bits
|
||||
self.samples[i][s] = self.waves[i][self.index[i]]
|
||||
|
||||
def populate_buffer(self):
|
||||
while not self.wave.outBuffer_ready:
|
||||
pass ## (@_@) should be async wait
|
||||
self.wave.packX(self.samplesX)
|
||||
self.wave.packY(self.samplesY)
|
||||
self.wave.outBuffer_ready = False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import vectorscope
|
||||
import machine
|
||||
userbutton = machine.Pin(19, machine.Pin.IN)
|
||||
|
||||
v = vectorscope.Vectorscope()
|
||||
d = DDS(v)
|
||||
d.amplitude=[0.5, 0.5]
|
||||
d.recalculate_waveforms()
|
||||
## but you can also write them yourself directly: 16-bit signed, 256 values
|
||||
d.waves[0] = [0]*256
|
||||
## but it gets overwritten if you recalculate_waveforms()
|
||||
d.recalculate_waveforms()
|
||||
|
||||
|
||||
def go(n=200):
|
||||
for i in range(n):
|
||||
d.do_dds()
|
||||
d.populate_buffer()
|
||||
d.waves[0] = d.base_waveforms["square"]
|
||||
d.recalculate_waveforms()
|
||||
|
||||
|
||||
for i in range(n):
|
||||
d.do_dds()
|
||||
d.populate_buffer()
|
||||
|
||||
go()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
import machine
|
||||
from uctypes import BF_POS, BF_LEN, UINT32, BFUINT32, struct, addressof
|
||||
|
||||
## configure DMA
|
||||
BASE = const(0x50000000)
|
||||
CHAN_WIDTH = const(0x40)
|
||||
CHAN_COUNT = const(12)
|
||||
|
||||
def dma_num_tempy(x):
|
||||
return BASE + CHAN_WIDTH * x
|
||||
## ex: dma_num(7)
|
||||
|
||||
def hexmem(x):
|
||||
return hex(machine.mem32[x])
|
||||
|
||||
def hexaddr(x):
|
||||
return hex(addressof(x))
|
||||
|
||||
def print_friendly(u32):
|
||||
a = (u32 & 0xFF000000) >> 24
|
||||
b = (u32 & 0x00FF0000) >> 16
|
||||
c = (u32 & 0x0000FF00) >> 8
|
||||
d = (u32 & 0x000000FF)
|
||||
print(f'{a:#010b} {b:#010b} {c:#010b} {d:#010b}')
|
||||
|
||||
def dma_scan():
|
||||
used = []
|
||||
for i in range(12):
|
||||
ctrl_reg_contents = machine.mem32[dma_num_tempy(i)+0x10]
|
||||
if ctrl_reg_contents != 0:
|
||||
used.append(i)
|
||||
print(f"{i:02}: {ctrl_reg_contents:#034b}")
|
||||
print(f"Used DMAs: {used}")
|
||||
|
||||
def dma_debug(dma):
|
||||
base=addressof(dma.registers)
|
||||
print(f"READ: {dma.registers[0]:x} WRITE: {dma.registers[1]:x} COUNT next: {machine.mem32[base+0x804]} current: {machine.mem32[base+0x08]}")
|
||||
print(dma.unpack_ctrl(dma.ctrl))
|
||||
print("\n")
|
||||
|
||||
|
||||
## Control fields, packed into a nice structure
|
||||
## cribbed from rp2040_pio_dma.py
|
||||
## Thanks to https://github.com/benevpi/MicroPython_PIO_Music_DMA/blob/main/rp2040_pio_dma.py
|
||||
DMA_CTRL_FIELDS = {
|
||||
"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
|
||||
}
|
||||
|
||||
## Control register bit positions
|
||||
## Configuring DMA is essentially a matter of going through this list, making a choice for each bit, and then writing the result to the CTRL register
|
||||
BUSY_BIT = const(24) ## flag, readme
|
||||
SNIFF_ENABLE_BIT = const(23) ## flag
|
||||
BYTE_SWAP_BIT = const(22) ## flag
|
||||
IRQ_QUIET_BIT = const(21) ## flag to quiet IRQ
|
||||
TREQ_SEL_BIT = const(15) ## request source
|
||||
CHAIN_TO_BIT = const(11) ## chain dest
|
||||
RING_RW_SEL_BIT = const(10) ## ring on read (0) or write (1)
|
||||
RING_SIZE_BIT = const(6) ## if ring, how many bytes: 2**n
|
||||
INC_WRITE_BIT = const(5) ## flag
|
||||
INC_READ_BIT = const(4) ## flag
|
||||
DATA_SIZE_BIT = const(2) ## 2**n bytes (1, 2, 4)
|
||||
HIGH_PRIORITY_BIT = const(1) ## scheduling priority
|
||||
ENABLE_BIT = const(0) ## will it run
|
||||
|
||||
## For use with DATA_SIZE_BIT
|
||||
SIZE_1BYTE = const(0x0)
|
||||
SIZE_2BYTES = const(0x1)
|
||||
SIZE_4BYTES = const(0x2)
|
||||
|
||||
## DMA Config register offsets
|
||||
##
|
||||
## The aliases are very rich.
|
||||
## You can always write to one of these 4 and it will _never_ trigger
|
||||
READ_ADDR = const(0x00)
|
||||
WRITE_ADDR = const(0x04)
|
||||
TRANS_COUNT = const(0x08)
|
||||
CTRL = const(0x10)
|
||||
|
||||
## Writing to one of these will _always_ trigger
|
||||
READ_ADDR_TRIG = const(0x3C)
|
||||
WRITE_ADDR_TRIG = const(0x2C)
|
||||
TRANS_COUNT_TRIG = const(0x1C)
|
||||
CTRL_TRIG = const(0x0C)
|
||||
|
||||
## And here are the aliases listed out in order for when you need
|
||||
## to configure more than 1 byte in a row
|
||||
READ_ADDR_0 = const(0x00)
|
||||
WRITE_ADDR_0 = const(0x04)
|
||||
TRANS_COUNT_0 = const(0x08)
|
||||
CTRL_0 = const(0x0C)
|
||||
|
||||
CTRL_1 = const(0x10)
|
||||
READ_ADDR_1 = const(0x14)
|
||||
WRITE_ADDR_1 = const(0x18)
|
||||
TRANS_COUNT_1 = const(0x1C)
|
||||
|
||||
CTRL_2 = const(0x20)
|
||||
TRANS_COUNT_2 = const(0x24)
|
||||
READ_ADDR_2 = const(0x28)
|
||||
WRITE_ADDR_2 = const(0x2C)
|
||||
|
||||
CTRL_3 = const(0x30)
|
||||
WRITE_ADDR_3 = const(0x34)
|
||||
TRANS_COUNT_3 = const(0x38)
|
||||
READ_ADDR_3 = const(0x3C)
|
||||
|
||||
|
||||
## List of all Data Request and Trigger Requests
|
||||
DREQ_PIO0_TX0 = const(0x00)
|
||||
DREQ_PIO0_TX1 = const(0x01)
|
||||
DREQ_PIO0_TX2 = const(0x02)
|
||||
DREQ_PIO0_TX3 = const(0x03)
|
||||
DREQ_PIO0_RX0 = const(0x04)
|
||||
DREQ_PIO0_RX1 = const(0x05)
|
||||
DREQ_PIO0_RX2 = const(0x06)
|
||||
DREQ_PIO0_RX3 = const(0x07)
|
||||
DREQ_PIO1_TX0 = const(0x08)
|
||||
DREQ_PIO1_TX1 = const(0x09)
|
||||
DREQ_PIO1_TX2 = const(0x0A)
|
||||
DREQ_PIO1_TX3 = const(0x0B)
|
||||
DREQ_PIO1_RX0 = const(0x0C)
|
||||
DREQ_PIO1_RX1 = const(0x0D)
|
||||
DREQ_PIO1_RX2 = const(0x0E)
|
||||
DREQ_PIO1_RX3 = const(0x0F)
|
||||
DREQ_SPI0_TX = const(0x10)
|
||||
DREQ_SPI0_RX = const(0x11)
|
||||
DREQ_SPI1_TX = const(0x12)
|
||||
DREQ_SPI1_RX = const(0x13)
|
||||
DREQ_UART0_TX = const(0x14)
|
||||
DREQ_UART0_RX = const(0x15)
|
||||
DREQ_UART1_TX = const(0x16)
|
||||
DREQ_UART1_RX = const(0x17)
|
||||
DREQ_PWM_WRAP0 = const(0x18)
|
||||
DREQ_PWM_WRAP1 = const(0x19)
|
||||
DREQ_PWM_WRAP2 = const(0x1A)
|
||||
DREQ_PWM_WRAP3 = const(0x1B)
|
||||
DREQ_PWM_WRAP4 = const(0x1C)
|
||||
DREQ_PWM_WRAP5 = const(0x1D)
|
||||
DREQ_PWM_WRAP6 = const(0x1E)
|
||||
DREQ_PWM_WRAP7 = const(0x1F)
|
||||
DREQ_I2C0_TX = const(0x20)
|
||||
DREQ_I2C0_RX = const(0x21)
|
||||
DREQ_I2C1_TX = const(0x22)
|
||||
DREQ_I2C1_RX = const(0x23)
|
||||
DREQ_ADC = const(0x24)
|
||||
DREQ_XIP_STREAM = const(0x25)
|
||||
DREQ_XIP_SSITX = const(0x26)
|
||||
DREQ_XIP_SSIRX = const(0x27)
|
||||
TREQ_TMR0 = const(0x3B)
|
||||
TREQ_TMR1 = const(0x3C)
|
||||
TREQ_TMR2 = const(0x3D)
|
||||
TREQ_TMR3 = const(0x3E)
|
||||
TREQ_PERMANENT = const(0x3F)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
import math
|
||||
import time
|
||||
|
||||
from vectorscope import Vectorscope
|
||||
from random_walk import RW
|
||||
|
||||
|
||||
def minimal_example(v):
|
||||
## Minimal example
|
||||
for i in range(200):
|
||||
v.wave.constantX(int(math.cos(i * math.pi / 180 * 5) * 10000))
|
||||
v.wave.constantY(int(math.sin(i * math.pi / 180 * 5)* 10000))
|
||||
time.sleep_ms(10)
|
||||
|
||||
|
||||
def static_buffer_example(v):
|
||||
## Example of more complicated, repetitive waveform
|
||||
## v.wave has two buffers of 256 samples for putting sample-wise data into:
|
||||
## v.wave.outBufferX and outBufferY. These are packed 16 bits each, LSB first
|
||||
## To make your life easier, v.wave.packX() will put a list of 16-bit ints there for you
|
||||
|
||||
ramp = range(-2**15, 2**15, 2**8)
|
||||
v.wave.packX(ramp)
|
||||
|
||||
sine = [int(math.sin(2*x*math.pi/256)*16_000) for x in range(256)]
|
||||
v.wave.packY(sine)
|
||||
|
||||
time.sleep_ms(1000)
|
||||
|
||||
## That discontinuity and wobble is real --
|
||||
## that's what happens when you try to push around a real DAC that's bandwidth-limited.
|
||||
|
||||
|
||||
def animated_buffer_example(v):
|
||||
## To animate, you need to clear v.wave.outBuffer_ready and wait for it to go true
|
||||
## Each output buffer frame has 256 samples, so takes ~8.5 ms at 30 kHz
|
||||
|
||||
ramp = range(-2**15, 2**15, 2**8)
|
||||
v.wave.packX(ramp)
|
||||
|
||||
v.wave.outBuffer_ready = False
|
||||
for i in range(200):
|
||||
sine = [int(math.sin((50*i)+2*x*math.pi/256)*16_000) for x in range(256)]
|
||||
while not v.wave.outBuffer_ready:
|
||||
pass
|
||||
v.wave.packY(sine)
|
||||
v.wave.outBuffer_ready = False
|
||||
|
||||
## Any stuck pixels you see are a figment of your imagination. :)
|
||||
## Or a desperate call for a pull request. Your call.
|
||||
|
||||
def random_walk_example(v):
|
||||
## Example with a class, makes it tweakable on the command line
|
||||
## because half the fun here is live coding and experimentation
|
||||
|
||||
r = RW(v.wave)
|
||||
# print(dir(r))
|
||||
r.scale = 1000
|
||||
r.delay = 5
|
||||
r.go()
|
||||
|
||||
|
||||
def vos_main():
|
||||
import vos_state,vectoros,gc,vos_debug, asyncio
|
||||
from vos_debug import debug_print as debug
|
||||
vectoros.get_screen().idle()
|
||||
gc.collect()
|
||||
vos_state.gc_suspend=True
|
||||
asyncio.sleep(4)
|
||||
v = Vectorscope()
|
||||
minimal_example(v)
|
||||
await asyncio.sleep(5)
|
||||
static_buffer_example(v)
|
||||
await asyncio.sleep(5)
|
||||
animated_buffer_example(v)
|
||||
random_walk_example(v)
|
||||
debug(vos_debug.DEBUG_LEVEL_INFO,"Demo done, reboot!")
|
||||
vectoros.reset()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
v = Vectorscope()
|
||||
minimal_example(v)
|
||||
static_buffer_example(v)
|
||||
animated_buffer_example(v)
|
||||
random_walk_example(v)
|
||||
|
||||
v.deinit()
|
||||
## before you reload, you have to deinitialize all of the DMA
|
||||
## machines, else you get an error OS: 16
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
## This file generates headers with lookup tables for various waveforms
|
||||
## Add your own.
|
||||
|
||||
import math
|
||||
|
||||
## phase is in degrees: 360 is a full cycle, and this is probably what you want
|
||||
|
||||
def phaseSteps(maxPhase, length=256):
|
||||
steps = range(0, length)
|
||||
steps = [1.0*x/length * 2.0*math.pi * (maxPhase/360.0) for x in steps]
|
||||
return steps
|
||||
|
||||
def sine(maxPhase=360, length=256):
|
||||
wave = [math.sin(x) for x in phaseSteps(maxPhase, length)]
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def sawtooth(maxPhase=360, length=256):
|
||||
wave = [x for x in range(length)]
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def square(maxPhase=360, length=256):
|
||||
wave = [0]*(length//2)
|
||||
wave.extend([1]*(length//2))
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def triangle(maxPhase=360, length=256):
|
||||
wave = [x for x in range(length//2)]
|
||||
wave.extend([length//2 - x for x in range(length//2)])
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def bandlimitedSawtooth(numberPartials, maxPhase=360, length=256):
|
||||
wave = [0]*length
|
||||
sign = 1.0
|
||||
for k in range(1, numberPartials+1):
|
||||
phases = phaseSteps(maxPhase*k, length)
|
||||
for i in range(length):
|
||||
wave[i] += sign * math.sin(phases[i]) / k
|
||||
sign = sign * -1
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def bandlimitedSquare(numberPartials, maxPhase=360, length=256):
|
||||
wave = [0]*length
|
||||
for k in range(1, numberPartials*2, 2):
|
||||
phases = phaseSteps(maxPhase*k, length)
|
||||
for i in range(length):
|
||||
wave[i] += math.sin(phases[i]) / k
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def bandlimitedTriangle(numberPartials, maxPhase=360, length=256):
|
||||
wave = [0]*length
|
||||
sign = 1.0
|
||||
for k in range(1, numberPartials*2, 2):
|
||||
phases = phaseSteps(maxPhase*k, length)
|
||||
for i in range(length):
|
||||
wave[i] += sign * math.sin(phases[i]) / k**2
|
||||
sign = sign * -1
|
||||
return scaleAndRound(wave)
|
||||
|
||||
def scaleAndRound(data, scale=2**16-1, signedInt=True):
|
||||
data = [0.0+x-min(data) for x in data]
|
||||
data = [1.0*x/max(data)*scale for x in data]
|
||||
data = [int(round(x)) for x in data]
|
||||
if signedInt:
|
||||
data = [int(x-(scale+1)//2) for x in data]
|
||||
return(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sawtooth_sample = sawtooth(7)
|
|
@ -0,0 +1,42 @@
|
|||
import keyleds
|
||||
import keyboardcb
|
||||
|
||||
class Joystick(keyboardcb.KeyboardCB):
|
||||
"""
|
||||
A joystick class that inherits from KeyboardCB. It has a default filter and repeat set.
|
||||
It also knows how to replace chords like JOY_N + JOY_E to JOY_NE.
|
||||
|
||||
Attributes:
|
||||
callback (dict): A dictionary of callback functions or a single callback.
|
||||
single (bool): A flag to indicate if only single key press events should be handled.
|
||||
attach (bool): A flag to indicate if the joystick should be attached during initialization.
|
||||
"""
|
||||
|
||||
def __init__(self, callback={}, single_key_mode=False, attach=True):
|
||||
"""
|
||||
The constructor for Joystick class.
|
||||
|
||||
Args:
|
||||
callback (dict): A dictionary of callback functions. Default is an empty dictionary.
|
||||
single_key_mode (bool): A flag to indicate if only single key press events should be handled. Default is False.
|
||||
attach (bool): A flag to indicate if the joystick should be attached during initialization. Default is True.
|
||||
"""
|
||||
super().__init__(callback, keyleds.JOY_ALL, single_key_mode, attach=attach)
|
||||
|
||||
async def key(self, b):
|
||||
"""
|
||||
Function to handle key press events.
|
||||
|
||||
This function generates synthetic keys and dispatches callbacks as usual.
|
||||
|
||||
Args:
|
||||
b (int): The keycodes of the pressed keys (list).
|
||||
"""
|
||||
|
||||
# generate synthetic keys
|
||||
b1 = keyboardcb.replace_chord(b, [keyleds.JOY_N, keyleds.JOY_E], keyleds.JOY_NE)
|
||||
b1 = keyboardcb.replace_chord(b1, [keyleds.JOY_N, keyleds.JOY_W], keyleds.JOY_NW)
|
||||
b1 = keyboardcb.replace_chord(b1, [keyleds.JOY_S, keyleds.JOY_E], keyleds.JOY_SE)
|
||||
b1 = keyboardcb.replace_chord(b1, [keyleds.JOY_S, keyleds.JOY_W], keyleds.JOY_SW)
|
||||
|
||||
await super().key(b1) # dispatch callbacks as usual
|
|
@ -0,0 +1,121 @@
|
|||
import keyleds
|
||||
import keyboardio
|
||||
import asyncio
|
||||
|
||||
class KeyboardCB(keyboardio.KeyboardIO):
|
||||
"""
|
||||
A class that provides a single callback for a key or group of keys.
|
||||
It can use a single callback or a dictionary to have different callbacks for different keys.
|
||||
It can also filter repeating keys
|
||||
|
||||
Attributes:
|
||||
callback (dict or function): A dictionary of callback functions or a single callback function.
|
||||
filter (list): A list of keys to filter (default none, or use keys from callback when callback is a dictionary)
|
||||
single_key_mode (bool): A flag to indicate if only single key press events should be handled.
|
||||
attach (bool): A flag to indicate if the keyboard should be attached during initialization.
|
||||
active (bool): A flag to indicate if the keyboard is active.
|
||||
"""
|
||||
|
||||
def __init__(self, callback={}, filter=[], single_key_mode=True, attach=True):
|
||||
"""
|
||||
The constructor for KeyboardCB class.
|
||||
|
||||
Args:
|
||||
callback (dict or function): A dictionary of callback functions or a single callback function.
|
||||
Default is an empty dictionary (no callbacks).
|
||||
filter (list): A list of keys to filter. Default is an empty list.
|
||||
single_key_mode (bool): A flag to indicate if only single key press events should be handled. Default is True.
|
||||
attach (bool): A flag to indicate if the keyboard should be attached during initialization. Default is True.
|
||||
"""
|
||||
super().__init__(attach=attach)
|
||||
if isinstance(filter,list):
|
||||
self.filter=filter
|
||||
else:
|
||||
self.filter=[ filter ]
|
||||
self._cb=callback
|
||||
self.single_key_mode=single_key_mode
|
||||
self.active=True
|
||||
|
||||
def set_callback(self,func_or_dict):
|
||||
"""
|
||||
Function to set a non-default callback.
|
||||
|
||||
Args:
|
||||
func_or_dict (function or dict): The function or dictionary to set as the callback.
|
||||
"""
|
||||
self._cb=func_or_dict
|
||||
|
||||
async def _do_callback(self,cb,k):
|
||||
try:
|
||||
await cb(k) # async if possible
|
||||
except TypeError: # well, it was really a sync call, np...
|
||||
pass
|
||||
|
||||
async def key(self, b):
|
||||
"""
|
||||
Function to handle key press events.
|
||||
|
||||
This function checks if the keyboard is active and if the pressed key is in the filter.
|
||||
If it is, it calls the appropriate callback function.
|
||||
|
||||
Args:
|
||||
b (int): The keycodes of the pressed keys as a list.
|
||||
"""
|
||||
|
||||
# if you specify a filter, use it
|
||||
# if not but you use a dictionary callback, use its keys as a filter
|
||||
# otherwise, no filter
|
||||
if self.active==False:
|
||||
return
|
||||
|
||||
if self.filter!=[]:
|
||||
flt=self.filter
|
||||
else:
|
||||
if isinstance(self._cb,dict):
|
||||
flt=self._cb.keys()
|
||||
else:
|
||||
flt=[]
|
||||
|
||||
# remove all not in filter here first
|
||||
if flt!=[]:
|
||||
b1=[ item for item in b if item in flt ]
|
||||
else:
|
||||
b1=b
|
||||
|
||||
if (b1!=[] and self.single_key_mode==True): # single keypress mode
|
||||
b1=[ item for item in b1 if item not in self._prev ]
|
||||
|
||||
if b1:
|
||||
for k in b1:
|
||||
if flt!=[]:
|
||||
if k not in flt:
|
||||
continue
|
||||
|
||||
if isinstance(self._cb,dict):
|
||||
asyncio.create_task(self._do_callback(self._cb[k],k))
|
||||
else:
|
||||
asyncio.create_task(self._do_callback(self._cb,k))
|
||||
|
||||
def replace_chord(b,chord,value):
|
||||
"""
|
||||
Helper function to find chords (two or more keys) and replace them with one key.
|
||||
|
||||
Args:
|
||||
b (list): The list of pressed keys.
|
||||
chord (list): The list of keys that form a chord.
|
||||
value (int): The keycode of the key that will replace the chord.
|
||||
|
||||
Returns:
|
||||
list: The list of pressed keys with the chord replaced by the value.
|
||||
"""
|
||||
|
||||
b1=b
|
||||
|
||||
if all(item in b1 for item in chord):
|
||||
for item in chord:
|
||||
b1.remove(item)
|
||||
|
||||
b1.append(value)
|
||||
|
||||
return b1
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
import asyncio
|
||||
import keyleds
|
||||
from machine import SoftSPI, Pin
|
||||
import vos_debug
|
||||
|
||||
class KeyboardIO:
|
||||
"""
|
||||
A class that reads keys from the board and manages LEDs.
|
||||
|
||||
Attributes:
|
||||
leds (int): The LEDs to write out.
|
||||
task (Task): The task for the keyboard polling loop.
|
||||
current_keys (list): The list of currently pressed keys.
|
||||
_timeout (int): The poll rate in ms.
|
||||
_spi (SoftSPI): The SPI interface.
|
||||
_latch_load (Pin): The shift-register load pin.
|
||||
_button_sense (list): The two columns of button matrix.
|
||||
_user_sense (Pin): The user sense pin.
|
||||
_subscribers (list): The list of subscribers who want to know about keys.
|
||||
_prev (list): The list of keys that were down in the last poll.
|
||||
_capture (KeyboardIO): The subscriber that hogs all the keyboard.
|
||||
"""
|
||||
|
||||
leds=0
|
||||
task=None
|
||||
current_keys=[]
|
||||
|
||||
_timeout=100
|
||||
_spi = SoftSPI(baudrate=500_000, sck=Pin(0), mosi=Pin(1), miso=Pin(19))
|
||||
_spi.init()
|
||||
_latch_load = Pin(16, mode=Pin.OUT, value=0)
|
||||
_button_sense = [Pin(17, mode=Pin.IN), Pin(18, mode=Pin.IN)]
|
||||
_user_sense = Pin(19, mode=Pin.IN)
|
||||
_subscribers=[]
|
||||
_prev=[]
|
||||
_capture=None
|
||||
_cancel=False
|
||||
|
||||
def __init__(self, attach=True):
|
||||
"""
|
||||
The constructor for KeyboardIO class.
|
||||
|
||||
Args:
|
||||
attach (bool): A flag to indicate if the keyboard should be attached during initialization. Default is True.
|
||||
"""
|
||||
|
||||
self.subref=self.key
|
||||
if (attach):
|
||||
self._subscribers.append(self.subref);
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self._capture==self.subref:
|
||||
self._capture=None
|
||||
self.detach()
|
||||
|
||||
def capture(tf=True):
|
||||
"""
|
||||
Function to grab all keyboard events for this instance.
|
||||
|
||||
Args:
|
||||
tf (bool): A flag to indicate if the keyboard events should be captured. Default is True.
|
||||
|
||||
Returns:
|
||||
bool: True if the capture was successful, False otherwise.
|
||||
"""
|
||||
|
||||
if tf==True and self._capture!=None and self._capture!=self.subref:
|
||||
return false
|
||||
if tf==False and self._capture!=self.subref:
|
||||
return false
|
||||
if tf==True:
|
||||
self._capture=self.subref
|
||||
else:
|
||||
self._capture=None
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def _run(cls, timeout_ms=100):
|
||||
"""
|
||||
Class method to run the class level "server".
|
||||
|
||||
Args:
|
||||
timeout_ms (int): The timeout in ms. Default is 100.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
|
||||
if timeout_ms!=0:
|
||||
cls._timeout=timeout_ms
|
||||
cls.task=asyncio.create_task(cls._job())
|
||||
|
||||
@classmethod
|
||||
def run(cls,timeout_ms=100):
|
||||
"""
|
||||
Class method to launch the event loop without having to create a task yourself.
|
||||
|
||||
Args:
|
||||
timeout_ms (int): The timeout in ms. Default is 100.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
|
||||
return asyncio.create_task(cls._run(timeout_ms))
|
||||
|
||||
@classmethod
|
||||
async def _job(cls):
|
||||
"""
|
||||
Class method for the actual polling task.
|
||||
|
||||
This method runs a loop that sleeps for a specified timeout and then scans the keyboard.
|
||||
|
||||
Returns:
|
||||
Nothing
|
||||
"""
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"Starting keyboard loop")
|
||||
while cls._cancel==False:
|
||||
await asyncio.sleep_ms(cls._timeout)
|
||||
await cls._do_scan()
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"KEYIO exiting")
|
||||
|
||||
@classmethod
|
||||
def cancel(cls):
|
||||
"""
|
||||
Class method to cancel the polling task.
|
||||
"""
|
||||
cls._cancle=True
|
||||
cls.task.cancel()
|
||||
|
||||
def attach(self):
|
||||
"""
|
||||
Method to attach a subscriber to the keyboard.
|
||||
|
||||
This method adds a subscriber to the list of subscribers if it is not already there.
|
||||
"""
|
||||
|
||||
if self.subref not in self._subscribers:
|
||||
self._subscribers.append(self.subref)
|
||||
|
||||
def detach(self):
|
||||
"""
|
||||
Method to detach a subscriber from the keyboard.
|
||||
|
||||
This method removes a subscriber from the list of subscribers if it is there.
|
||||
"""
|
||||
|
||||
if self.subref in self._subscribers:
|
||||
self._subscribers.remove(self.subref)
|
||||
|
||||
@classmethod
|
||||
async def _do_scan(cls):
|
||||
"""
|
||||
Class method to scan the keyboard and process all keys.
|
||||
|
||||
This method scans the keyboard and calls the appropriate callback function for each pressed key.
|
||||
"""
|
||||
b=cls.scan()
|
||||
if b!=[]:
|
||||
cls.current_keys=b
|
||||
if cls._capture==None:
|
||||
for sub in cls._subscribers:
|
||||
try:
|
||||
await sub(b)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
await cls._capture(b)
|
||||
except TypeError:
|
||||
pass
|
||||
cls._prev=b
|
||||
else:
|
||||
cls._prev=[]
|
||||
|
||||
|
||||
@classmethod
|
||||
def scan(cls):
|
||||
"""
|
||||
Class method to scan the buttons.
|
||||
|
||||
This method scans the buttons and returns a list of pressed keys.
|
||||
|
||||
Returns:
|
||||
list: The list of pressed keys.
|
||||
"""
|
||||
|
||||
buttons = []
|
||||
button_scan = 1
|
||||
if cls._user_sense()==0:
|
||||
buttons = [ 24 ]
|
||||
|
||||
# update leds and scan buttons (if scan provided)
|
||||
for i in range(8):
|
||||
register = cls.leds + (button_scan << 8)
|
||||
cls._spi.write(register.to_bytes(2,'big'))
|
||||
cls._latch_load(1)
|
||||
cls._latch_load(0)
|
||||
|
||||
if (cls._button_sense[0]() == 0 ): # buttons are inverted
|
||||
buttons.append(i*3+1)
|
||||
|
||||
if (cls._button_sense[1]() == 0):
|
||||
buttons.append(i*3+2)
|
||||
|
||||
button_scan = button_scan<<1 ## next scan
|
||||
|
||||
return buttons
|
||||
|
||||
async def key(self,b):
|
||||
"""
|
||||
Method to handle key press events. Always overridden
|
||||
|
||||
This method prints the list of pressed keys.
|
||||
|
||||
Args:
|
||||
b (list): The list of pressed keys.
|
||||
"""
|
||||
|
||||
print(f"Keyboardio: {b}")
|
|
@ -0,0 +1,61 @@
|
|||
## MENU = 19
|
||||
## User = 24
|
||||
## SAVE = 22
|
||||
## A=8 B=7 C=1 D=4
|
||||
## Level=5 RANGE=2
|
||||
## XY=10 WAVE=16 SCOPE=13
|
||||
## J=17 JN=20 JS=11 JE=23 JW=14
|
||||
KEY_MENU=19
|
||||
KEY_USER=24
|
||||
KEY_SAVE=22
|
||||
KEY_A=8
|
||||
KEY_B=7
|
||||
KEY_C=1
|
||||
KEY_D=4
|
||||
KEY_LEVEL=5
|
||||
KEY_RANGE=2
|
||||
KEY_XY=10
|
||||
KEY_WAVE=16
|
||||
KEY_SCOPE=13
|
||||
JOY_PRESS=17
|
||||
JOY_UP=20
|
||||
JOY_DN=11
|
||||
JOY_RT=23
|
||||
JOY_LF=14
|
||||
# synonyms and synthetic joystick
|
||||
JOY_LT=JOY_LF
|
||||
JOY_N=JOY_UP
|
||||
JOY_S=JOY_DN
|
||||
JOY_E=JOY_RT
|
||||
JOY_W=JOY_LF
|
||||
JOY_NE=100
|
||||
JOY_NW=101
|
||||
JOY_SE=102
|
||||
JOY_SW=103
|
||||
|
||||
|
||||
JOY_ALL=[JOY_PRESS, JOY_UP, JOY_DN, JOY_RT, JOY_LF, JOY_NE, JOY_NW, JOY_SE, JOY_SW]
|
||||
KEY_ALL=[KEY_MENU, KEY_USER, KEY_SAVE, KEY_A, KEY_B, KEY_C, KEY_D, KEY_LEVEL, KEY_RANGE, KEY_XY, KEY_WAVE, KEY_SCOPE]
|
||||
KEY_EVERYTHING=JOY_ALL + KEY_ALL
|
||||
KEY_ABCD=[KEY_A, KEY_B, KEY_C, KEY_D]
|
||||
|
||||
## Numerically
|
||||
## C, RANGE, [3], D, Level, [6], B, A, [9], XY, JS, [12], SCOPE, JW, [15], WAVE, J, [18], Menu, JN, [21], Save, JE, User
|
||||
|
||||
## LEDs
|
||||
## 1- SIG generator
|
||||
## 2 - Scope
|
||||
## 4 - Saw tooth
|
||||
## 8 - Triangle
|
||||
## 16 - Square
|
||||
## 32 - Sine
|
||||
## 64 - Y
|
||||
## 128 - X
|
||||
LED_SIG=1
|
||||
LED_SCOPE=2
|
||||
LED_SAW=4
|
||||
LED_TRI=8
|
||||
LED_SQ=16
|
||||
LED_SINE=32
|
||||
LED_Y=64
|
||||
LED_X=128
|
|
@ -0,0 +1,147 @@
|
|||
from vectorscope import Vectorscope
|
||||
from dds import DDS
|
||||
|
||||
import gc
|
||||
from vos_debug import debug_print as debug
|
||||
import vectoros
|
||||
import vos_state
|
||||
import vos_debug
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import keyboardio
|
||||
import asyncio
|
||||
|
||||
_abort=False
|
||||
## map waveform types to LEDs
|
||||
_waves_lookup = {0:"sine", 3:"sawtooth", 1:"square", 2:"triangle"}
|
||||
_waves_reverse_lookup = {"sine":0, "sawtooth":3, "square":1, "triangle":2}
|
||||
|
||||
lissajous_state = {
|
||||
"selected_axis":0,
|
||||
"selected_waveform":0,
|
||||
"waves_leds":[0,0]
|
||||
|
||||
}
|
||||
|
||||
async def do_dds_loop(d):
|
||||
while not _abort:
|
||||
for i in range(50):
|
||||
d.do_dds()
|
||||
d.populate_buffer()
|
||||
await asyncio.sleep(0)
|
||||
|
||||
def do_abort(key):
|
||||
global _abort
|
||||
_abort=True
|
||||
|
||||
|
||||
async def vos_main():
|
||||
|
||||
vectoros.get_screen().idle()
|
||||
gc.collect()
|
||||
vos_state.gc_suspend=True
|
||||
# await asyncio.sleep(1)
|
||||
|
||||
keyboardio.KeyboardIO.leds = (1<<7)
|
||||
keyboardio.KeyboardIO.leds |= (1<<5) ## sine wave
|
||||
keyboardio.KeyboardIO.scan()
|
||||
|
||||
v = Vectorscope()
|
||||
d = DDS(v)
|
||||
d.increment = [1500, 1200]
|
||||
d.amplitude=[0.5, 0.5]
|
||||
d.recalculate_waveforms()
|
||||
|
||||
def toggle_xy(key):
|
||||
## not right yet
|
||||
global lissajous_state
|
||||
global d
|
||||
if lissajous_state["selected_axis"] == 0:
|
||||
lissajous_state["selected_axis"] = 1
|
||||
keyboardio.KeyboardIO.leds |= (1<<6)
|
||||
keyboardio.KeyboardIO.leds &= ~(1<<7)
|
||||
else:
|
||||
lissajous_state["selected_axis"] = 0
|
||||
keyboardio.KeyboardIO.leds |= (1<<7)
|
||||
keyboardio.KeyboardIO.leds &= ~(1<<6)
|
||||
|
||||
## Update leds to reflect switch
|
||||
keyboardio.KeyboardIO.leds &= (0b11000011)
|
||||
which_led = lissajous_state["waves_leds"][lissajous_state["selected_axis"]]
|
||||
keyboardio.KeyboardIO.leds |= (1<<(5-which_led))
|
||||
|
||||
def toggle_waveform(key):
|
||||
global lissajous_state
|
||||
which_led = lissajous_state["waves_leds"][lissajous_state["selected_axis"]]
|
||||
## clear leds
|
||||
keyboardio.KeyboardIO.leds &= (0b11000011)
|
||||
## update led
|
||||
which_led = ( which_led + 1) % 4
|
||||
keyboardio.KeyboardIO.leds |= (1<<(5-which_led ))
|
||||
## update storage
|
||||
lissajous_state["waves_leds"][lissajous_state["selected_axis"]] = which_led
|
||||
## update waveform
|
||||
d.waveform[lissajous_state["selected_axis"]] = _waves_lookup[which_led]
|
||||
d.recalculate_waveforms()
|
||||
|
||||
def handle_joystick_up(key):
|
||||
current_keys = keyboardcb.KeyboardCB.current_keys
|
||||
if len(current_keys) == 1: ## just joystick
|
||||
d.increment[1] = int(d.increment[1] * 1.1)
|
||||
if keyleds.KEY_RANGE in current_keys:
|
||||
d.amplitude[1] = d.amplitude[1] * 1.1
|
||||
d.recalculate_waveforms()
|
||||
## increase amplitude Y
|
||||
if keyleds.KEY_LEVEL in current_keys:
|
||||
d.phase_increment[1] = d.phase_increment[1] + 1
|
||||
## increase phase Y
|
||||
|
||||
def handle_joystick_down(key):
|
||||
current_keys = keyboardcb.KeyboardCB.current_keys
|
||||
if len(current_keys) == 1: ## just joystick
|
||||
d.increment[1] = int(d.increment[1] * 0.91)
|
||||
if keyleds.KEY_RANGE in current_keys:
|
||||
d.amplitude[1] = d.amplitude[1] * 0.91
|
||||
d.recalculate_waveforms()
|
||||
## increase amplitude Y
|
||||
if keyleds.KEY_LEVEL in current_keys:
|
||||
d.phase_increment[1] = d.phase_increment[1] - 1
|
||||
## increase phase Y
|
||||
def handle_joystick_right(key):
|
||||
current_keys = keyboardcb.KeyboardCB.current_keys
|
||||
if len(current_keys) == 1: ## just joystick
|
||||
d.increment[0] = int(d.increment[0] * 1.1)
|
||||
if keyleds.KEY_RANGE in current_keys:
|
||||
d.amplitude[0] = d.amplitude[0] * 1.1
|
||||
d.recalculate_waveforms()
|
||||
## increase amplitude Y
|
||||
if keyleds.KEY_LEVEL in current_keys:
|
||||
d.phase_increment[0] = d.phase_increment[0] + 1
|
||||
## increase phase Y
|
||||
|
||||
def handle_joystick_left(key):
|
||||
current_keys = keyboardcb.KeyboardCB.current_keys
|
||||
if len(current_keys) == 1: ## just joystick
|
||||
d.increment[0] = int(d.increment[0] * 0.91)
|
||||
if keyleds.KEY_RANGE in current_keys:
|
||||
d.amplitude[0] = d.amplitude[0] * 0.91
|
||||
d.recalculate_waveforms()
|
||||
## increase amplitude X
|
||||
if keyleds.KEY_LEVEL in current_keys:
|
||||
d.phase_increment[0] = d.phase_increment[0] - 1
|
||||
## increase phase X
|
||||
|
||||
mykeys=keyboardcb.KeyboardCB({keyleds.KEY_MENU: do_abort,
|
||||
keyleds.KEY_XY:toggle_xy,
|
||||
keyleds.KEY_WAVE: toggle_waveform,
|
||||
keyleds.JOY_UP: handle_joystick_up,
|
||||
keyleds.JOY_DN: handle_joystick_down,
|
||||
keyleds.JOY_RT: handle_joystick_right,
|
||||
keyleds.JOY_LT: handle_joystick_left
|
||||
})
|
||||
await do_dds_loop(d)
|
||||
|
||||
vectoros.reset()
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import _thread
|
||||
import vectoros
|
||||
from time import sleep
|
||||
|
||||
import machine
|
||||
user_button = machine.Pin(19, machine.Pin.IN)
|
||||
if user_button.value():
|
||||
vectoros.run()
|
||||
else:
|
||||
import vectorscope
|
||||
v = vectorscope.Vectorscope()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
import screennorm
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import joystick
|
||||
import asyncio
|
||||
import gc9a01
|
||||
import vectoros
|
||||
import vos_debug
|
||||
|
||||
|
||||
|
||||
|
||||
screen=vectoros.get_screen()
|
||||
|
||||
BACK=-1
|
||||
EXIT=1
|
||||
CONT=0
|
||||
|
||||
SUBMENU=[]
|
||||
|
||||
def m_back(arg):
|
||||
return BACK
|
||||
|
||||
def m_exit(arg):
|
||||
return EXIT
|
||||
|
||||
class Menu:
|
||||
"""
|
||||
A class that manages a menu.
|
||||
|
||||
Attributes:
|
||||
fg (int): The foreground color.
|
||||
bg (int): The background color.
|
||||
clear_after (bool): A flag to indicate if the menu should be cleared after exit.
|
||||
joycontroller (Joystick): The joystick controller.
|
||||
scanrate (int): The scan rate.
|
||||
cursor (int): The position of the screen cursor.
|
||||
dispmenu (int): The top line of the display.
|
||||
current (list): The current menu string.
|
||||
level (int): The current level in the submenus.
|
||||
stack (list): The stack of how we got here.
|
||||
clear_after (bool): A flag to indicate if the menu should be cleared after exit.
|
||||
fg (int): The foreground color.
|
||||
bg (int): The background color.
|
||||
update_callback (function): A function to call to update the menu in real time.
|
||||
"""
|
||||
|
||||
def __init__(self, fg_color=gc9a01.color565(45, 217, 80), bg_color=gc9a01.color565(0, 0, 0),cursor_fg=gc9a01.color565(120,247,180),
|
||||
cursor_bg=None,clear_after=False, joy_controller=None, scan_rate=0):
|
||||
"""
|
||||
The constructor for Menu class.
|
||||
|
||||
Args:
|
||||
fg_color (int): The foreground color. Default is gc9a01.color565(243,191,16).
|
||||
bg_color (int): The background color. Default is gc9a01.color565(26,26,26).
|
||||
clear_after (bool): A flag to indicate if the menu should be cleared after exit. Default is False.
|
||||
joycontroller (Joystick): The joystick controller. Default is None.
|
||||
scanrate (int): The scan rate. Default is 0.
|
||||
"""
|
||||
|
||||
if joy_controller != None:
|
||||
self.joy=joy_controller
|
||||
self._extjoy=True
|
||||
else:
|
||||
self.joy=None
|
||||
self._extjoy=False
|
||||
|
||||
if scan_rate!=0:
|
||||
joystick.Joystick.run(scan_rate)
|
||||
|
||||
self.cursor=0
|
||||
self.dispmenu=0
|
||||
self.current=[]
|
||||
self.level=0
|
||||
self.stack=[]
|
||||
self.clear_after=clear_after
|
||||
self.fg=fg_color
|
||||
self.bg=bg_color
|
||||
if cursor_fg==None:
|
||||
self.cfg=self.bg
|
||||
else:
|
||||
self.cfg=cursor_fg
|
||||
if cursor_bg==None:
|
||||
self.cbg=self.fg
|
||||
else:
|
||||
self.cbg=cursor_bg
|
||||
self.update_callback=None
|
||||
self.font=None
|
||||
self.scale=1
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self,exc_type,exc_val,exc_tb):
|
||||
if self.joy!=None:
|
||||
self.joy.detach()
|
||||
|
||||
def detach(self):
|
||||
"""
|
||||
Method to detach the joystick from the menu.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
|
||||
self.joy.detach()
|
||||
|
||||
def set_font(self,font,scale=1.0):
|
||||
"""
|
||||
Method to set a font and scale factor.
|
||||
Default: use default font (set to None)
|
||||
"*": use default vector font (romans)
|
||||
Or you can load a vector font and pass it
|
||||
|
||||
Args:
|
||||
font (font or string or None): See above
|
||||
scale (float): Text scale (defaults to 1.0)
|
||||
"""
|
||||
if font=="*":
|
||||
self.font=screen.get_vfont()
|
||||
else:
|
||||
self.font=font
|
||||
self.scale=scale
|
||||
|
||||
async def menu_custom(self):
|
||||
"""
|
||||
Method you can override to customize the menu in real time
|
||||
Or, if you don't want to subclass, you can use set_callback
|
||||
and the default will call that
|
||||
"""
|
||||
|
||||
if self.update_callback!=None:
|
||||
try:
|
||||
await self.update_callback(self)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
def set_callback(self,func):
|
||||
"""
|
||||
Method to set a callback function.
|
||||
|
||||
Args:
|
||||
func (function): The function to set as the callback.
|
||||
"""
|
||||
|
||||
self.update_callback=func
|
||||
|
||||
|
||||
async def menu_update(self):
|
||||
"""
|
||||
Method to update the menu with cursor, scrolling, etc.
|
||||
|
||||
This method updates the menu based on the current state of the cursor and calls the custom update function if it is set.
|
||||
"""
|
||||
|
||||
screen.clear(self.bg)
|
||||
await self.menu_custom()
|
||||
|
||||
for i in range(0,min(4,len(self.current))):
|
||||
if i==self.cursor:
|
||||
xfg=self.cfg
|
||||
xbg=self.cbg
|
||||
else:
|
||||
xfg=self.fg
|
||||
xbg=self.bg
|
||||
|
||||
# screen.tft.fill_rect(24,40*(i+1),195,40,xbg)
|
||||
if self.font==None:
|
||||
screen.text(24,40*(i+1),self.current[self.dispmenu+i][0],xfg,xbg)
|
||||
else:
|
||||
screen.text_font(self.font,24,40*(i+1)+20,self.current[self.dispmenu+i][0],xfg,self.scale)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# the controller callback for the keyboard
|
||||
# all the real work happens here
|
||||
async def _menu_control(self,key):
|
||||
"""
|
||||
Method to handle joystick events.
|
||||
|
||||
This method updates the menu based on the joystick event. It can move the cursor, select a menu item,
|
||||
go to a submenu, or exit the menu.
|
||||
|
||||
Args:
|
||||
key (int): The keycode of the joystick event.
|
||||
"""
|
||||
rv=0
|
||||
if self.level<=0:
|
||||
return # no menu
|
||||
if key==keyleds.JOY_UP:
|
||||
if self.cursor>0:
|
||||
self.cursor=self.cursor-1
|
||||
else:
|
||||
if self.dispmenu>0:
|
||||
self.dispmenu=self.dispmenu-1
|
||||
await self.menu_update()
|
||||
|
||||
if key==keyleds.JOY_DN:
|
||||
if self.cursor<min(3,len(self.current)-1):
|
||||
self.cursor=self.cursor+1
|
||||
else:
|
||||
if self.dispmenu<len(self.current)-4:
|
||||
self.dispmenu=self.dispmenu+1
|
||||
await self.menu_update()
|
||||
|
||||
if key==keyleds.JOY_PRESS or key==keyleds.JOY_RT:
|
||||
|
||||
cmd=self.current[self.cursor+self.dispmenu][1]
|
||||
arg=self.current[self.cursor+self.dispmenu][2]
|
||||
if cmd==None: # replace with ret val from built-in callback
|
||||
rv=-1
|
||||
elif cmd==[]:
|
||||
self.stack.append(self.current)
|
||||
self.current=arg
|
||||
self.cursor=0
|
||||
self.dispmenu=0
|
||||
self.level=self.level+1
|
||||
else:
|
||||
if len(self.current[self.cursor+self.dispmenu])<4:
|
||||
rv=self.current[self.cursor+self.dispmenu][1](self.current[self.cursor+self.dispmenu][2])
|
||||
else:
|
||||
rv=await self.current[self.cursor+self.dispmenu][1](self.current[self.cursor+self.dispmenu][2])
|
||||
if rv==1:
|
||||
self.stack=[]
|
||||
rv=-1 # make sure we exit
|
||||
await self.menu_update()
|
||||
|
||||
if key==keyleds.JOY_LT or rv==-1:
|
||||
self.level=self.level-1
|
||||
|
||||
if self.stack==[]:
|
||||
self.level=-1
|
||||
self.current=None
|
||||
else:
|
||||
self.current=self.stack.pop()
|
||||
self.cursor=0
|
||||
self.dispmenu=0
|
||||
await self.menu_update()
|
||||
|
||||
|
||||
# This is how you kick off the menu. The menu list is a list
|
||||
# with a sublist for each entry. The sublists have three items:
|
||||
# A text string, a function, and an argument
|
||||
# Pro tip: pass a single list, tuple, etc as an argument and you can pass as much as you want
|
||||
async def do_menu(self,menulist):
|
||||
"""
|
||||
Method to start the menu.
|
||||
|
||||
This method starts the menu and runs a loop until the level is less than 0. It also updates the menu after each event.
|
||||
|
||||
Args:
|
||||
menulist (list): The list of menu items. Each item is a sublist with three items: a text string, a function, and an argument.
|
||||
"""
|
||||
|
||||
if self._extjoy==False:
|
||||
self.joy=joystick.Joystick(self._menu_control,True)
|
||||
|
||||
self.current=menulist
|
||||
self.stack=[]
|
||||
self.dispmenu=0
|
||||
self.cursor=0
|
||||
self.level=1
|
||||
|
||||
screen.clear()
|
||||
await self.menu_update()
|
||||
|
||||
while self.level>=0:
|
||||
await asyncio.sleep(0)
|
||||
|
||||
if self.clear_after:
|
||||
screen.clear()
|
||||
|
||||
if (self._extjoy):
|
||||
self.joy.detach()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import joystick
|
||||
from menu import * # bad habit but makes our menu definition nice
|
||||
from vos_state import vos_state
|
||||
import blinker
|
||||
import vectoros
|
||||
import colors
|
||||
import gc
|
||||
|
||||
# run the sketch demo
|
||||
def runsketch(arg):
|
||||
vos_state.show_menu=False # get the menu of the way
|
||||
vectoros.launch_task('sketch')
|
||||
return EXIT
|
||||
|
||||
def gfxdemo(arg):
|
||||
vos_state.show_menu=False # get the menu of the way
|
||||
vectoros.launch_task('screen') # launch
|
||||
return EXIT
|
||||
|
||||
def testdemo(arg):
|
||||
import tester2
|
||||
vectoros.vectoros_shutdown()
|
||||
tester2.main() # this never returns but it does reboot
|
||||
|
||||
|
||||
# the main vector scope demo
|
||||
def run_demo(arg):
|
||||
vos_state.show_menu=False
|
||||
vectoros.launch_task('demo')
|
||||
# we never come back
|
||||
return EXIT
|
||||
|
||||
def reboot(arg):
|
||||
if arg==False:
|
||||
vectoros.reset()
|
||||
else:
|
||||
vectoros.soft_reset()
|
||||
|
||||
# handle slots
|
||||
def abcd(key):
|
||||
if vos_state.show_menu:
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"Menu key {key}")
|
||||
kdict={ keyleds.KEY_A: 'A', keyleds.KEY_B: 'B', keyleds.KEY_C: 'C', keyleds.KEY_D: 'D'}
|
||||
await vectoros.launch_vecslot("slot"+kdict[key])
|
||||
|
||||
|
||||
# I really didn't want this to be async but it seems like do_menu must have an await
|
||||
# and run rarely returns when you have a lot going on
|
||||
async def vos_main():
|
||||
# you do NOT have to use with here
|
||||
# but if you don't you have to worry about the menu controller's joystick instance going out of scope yourself
|
||||
# or just make everything global -- the menu is smart enough to not listen to events when it is not active
|
||||
# note: m_back and m_exit were imported from menu
|
||||
while True: # since this is the main menu, we don't really every quit
|
||||
print("creating slotkey")
|
||||
slotkey=keyboardcb.KeyboardCB(abcd,keyleds.KEY_ABCD)
|
||||
with Menu(clear_after=True,fg_color=colors.PHOSPHOR_DARK,bg_color=colors.PHOSPHOR_BG,
|
||||
cursor_bg=colors.PHOSPHOR_BG, cursor_fg=colors.PHOSPHOR_BRIGHT) as amenu:
|
||||
# submenu=[["Test", testdemo, 0],["Previous",m_back,None],["Abort",m_exit,None],["Reset CPU",reboot,False]]
|
||||
mainmenu=[["Demo",run_demo,None],["Sketch", runsketch, 0],["GFX",gfxdemo,0 ] ]
|
||||
# ["Test Menu",SUBMENU,submenu]]
|
||||
# comment next line for default font
|
||||
amenu.set_font("*") # set default vector font
|
||||
#amenu.set_callback(menu_custom)
|
||||
await amenu.do_menu(mainmenu)
|
||||
# screen.text(40,80,"menu done")
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"Menu waiting {vos_state.show_menu}")
|
||||
while vos_state.show_menu==False: # wait until we have to be seen again
|
||||
await asyncio.sleep_ms(0)
|
||||
|
||||
|
||||
def main():
|
||||
asyncio.run(vos_main())
|
||||
# this never runs
|
||||
|
||||
if __name__=="__main__":
|
||||
import vectoros
|
||||
vectoros.run()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
import math
|
||||
import time
|
||||
|
||||
from vectorscope import Vectorscope
|
||||
from random_walk import RW
|
||||
|
||||
|
||||
|
||||
def minimal_example(v):
|
||||
## Minimal example
|
||||
for i in range(200):
|
||||
v.wave.constantX(int(math.cos(i * math.pi / 180 * 5) * 10000))
|
||||
v.wave.constantY(int(math.sin(i * math.pi / 180 * 5)* 10000))
|
||||
time.sleep_ms(10)
|
||||
|
||||
async def slot_main(v):
|
||||
minimal_example(v)
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import gc9a01
|
||||
import struct
|
||||
## https://colordesigner.io/gradient-generator
|
||||
|
||||
_gradient_rgb = [ (10, 15, 10),(10, 15, 10), (15, 22, 15), (19, 31, 20), (24, 47, 26), (28, 64, 32), (31, 82, 39),
|
||||
(34, 100, 45), (37, 118, 51), (38, 138, 57), (40, 157, 63), (41, 177, 68),
|
||||
(41, 198, 74), (41, 219, 80), (41, 240, 85), (150, 255, 200) ]
|
||||
|
||||
phosphor_gradient = [gc9a01.color565(r,g,b) for r,g,b in _gradient_rgb]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
## Hardware GPIO # pin defines
|
||||
|
||||
## Screen pins
|
||||
sck = const(2)
|
||||
data = const(3)
|
||||
reset = const(4)
|
||||
dc = const(5)
|
||||
|
||||
## there are a number of libs that require pins that we don't provide
|
||||
## pass them the on-board LED. It's unlikely to hurt.
|
||||
throwaway = const(25)
|
||||
debug = const(27)
|
||||
|
||||
user_button = const(19)
|
||||
audio_shutdown = const(22)
|
||||
|
||||
## Codec pins
|
||||
## clocks
|
||||
mclk = const(7)
|
||||
bit_clock = const(8)
|
||||
lr_clock = const(9)
|
||||
## data
|
||||
sd_in = const(13) ## In 3/4 (Y=L, X=R)
|
||||
sd_out = const(10) ## Out 1/2 (Y=L, X=R)
|
||||
## config
|
||||
pdn = const(6)
|
||||
i2c_scl = const(21)
|
||||
i2c_sda = const(20)
|
||||
|
||||
|
||||
|
||||
|
||||
## some globally useful pin inits
|
||||
# debug_pin = machine.Pin(debug, machine.Pin.OUT, value=0)
|
||||
# audio_shutdown_pin = machine.Pin(audio_shutdown, machine.Pin.OUT, value=1)
|
||||
# user_button = machine.Pin(user_button, machine.Pin.IN)
|
||||
|
||||
|
||||
#sd_in=Pin(13, Pin.IN) ## In 3/4 (Y=L, X=R)
|
||||
#sd_out=Pin(10, Pin.OUT, value=0) ## Out 1/2 (Y=L, X=R)
|
|
@ -0,0 +1,124 @@
|
|||
import rp2
|
||||
|
||||
######################################################
|
||||
## Define I2S PIO machines
|
||||
## 16-bit slots, data
|
||||
## read on falling edge of bit clock
|
||||
######################################################
|
||||
|
||||
@rp2.asm_pio(sideset_init=[rp2.PIO.OUT_LOW]*2, fifo_join=rp2.PIO.JOIN_RX)
|
||||
## bitclock and LR clock on the sidesets, respectively
|
||||
def i2s_read_pio():
|
||||
nop()[3] ## delay to sync up with write PIO -- empirically determined
|
||||
wrap_target()
|
||||
|
||||
set(y,14) .side(0b00)[1] ## send 32 bits per LR read
|
||||
label("L")
|
||||
in_(pins, 1) .side(0b01)[1] ## Left, bits
|
||||
jmp(y_dec, "L") .side(0b00)[1]
|
||||
in_(pins, 1) .side(0b11)[1]
|
||||
|
||||
set(y,14) .side(0b10)[1]
|
||||
label("R")
|
||||
in_(pins,1) .side(0b11)[1] ## right, bits
|
||||
jmp(y_dec, "R") .side(0b10)[1]
|
||||
in_(pins,1) .side(0b01)
|
||||
|
||||
push(noblock) ## noblock keeps PIOs in sync
|
||||
wrap()
|
||||
|
||||
## 10
|
||||
|
||||
@rp2.asm_pio(out_init=rp2.PIO.OUT_LOW, fifo_join=rp2.PIO.JOIN_TX)
|
||||
## runs lockstep with read pio, spits out data when it's got it, zeros otherwise
|
||||
def i2s_write_pio():
|
||||
wrap_target()
|
||||
|
||||
pull(noblock) ## noblock keeps PIOs in sync
|
||||
set(y,14) ## send 16 bits per LR clock
|
||||
label("L")
|
||||
out(pins,1) [1] ## Left, bits
|
||||
jmp(y_dec, "L") [1]
|
||||
out(pins,1) [1]
|
||||
|
||||
set(y,14) [1]
|
||||
label("R")
|
||||
out(pins,1) [1]
|
||||
jmp(y_dec, "R") [1]
|
||||
out(pins,1) [1]
|
||||
wrap()
|
||||
|
||||
## 19
|
||||
|
||||
@rp2.asm_pio(in_shiftdir=1, out_shiftdir=1)
|
||||
def bit_flipper_pio():
|
||||
## takes in two 16-bit as signed/unsigned integers
|
||||
## converts them by flipping MSBit of each 16-bitter
|
||||
## tailored for reading X/Y channel from DAC and fitting
|
||||
## the result on the (unsigned 8bit) screen
|
||||
pull()
|
||||
set(y,1)
|
||||
label("twice")
|
||||
|
||||
in_(osr, 15) ## copy 15 LSB unchanged to output
|
||||
out(null, 15) ## discard 15 bits from OSR to keep pace
|
||||
|
||||
mov(osr, invert(osr)) ## inverted what's left
|
||||
in_(osr, 1) ## copy out one inverted bit
|
||||
out(null, 1) ## drop that bit from OSR
|
||||
mov(osr, invert(osr)) ## reinvert for the next word
|
||||
|
||||
jmp(y_dec, "twice")
|
||||
push()
|
||||
|
||||
## 29
|
||||
|
||||
|
||||
|
||||
########################################################
|
||||
## Pixel Pusher: Handle Screen Command
|
||||
## Takes commands and data
|
||||
## sends it to the screen pretty fast
|
||||
## Format:
|
||||
# 0 2A 0 X
|
||||
# 0 2B 0 Y
|
||||
# 0 2C C1 C2
|
||||
## Screen expects 2A 0 X 0 X 2B 0 Y 0 Y 2C C1 C2
|
||||
## with the commands framed by the DC line going low
|
||||
## This PIO ignores the first byte,
|
||||
## transmits the second, framed by DC,
|
||||
## then doubles the second pair
|
||||
## This means it actually transmits C1 C2 C1 C2, but
|
||||
## the screen doesn't seem to care (hack, hack!)
|
||||
########################################################
|
||||
|
||||
@rp2.asm_pio(out_init=rp2.PIO.OUT_LOW, set_init=rp2.PIO.OUT_HIGH,
|
||||
sideset_init=rp2.PIO.OUT_LOW, autopull=True, autopush=True)
|
||||
def handle_screen_command():
|
||||
pull(block).side(0)
|
||||
out(null, 8).side(0) ## drop initial zeros
|
||||
|
||||
set(pins, 0).side(0) ## DC set low, CMD mode
|
||||
|
||||
set(y,7).side(0) ## send 8 command bits
|
||||
label("send_cmd")
|
||||
out(pins, 1).side(0)
|
||||
jmp(y_dec, "send_cmd").side(1)
|
||||
|
||||
set(pins, 1).side(0) ## DC set high, data mode
|
||||
|
||||
mov(x, osr).side(0) ## copy next bytes over, b/c going to send them twice
|
||||
mov(osr, x).side(0) ## copy next bytes over, b/c going to send them twice
|
||||
set(y,15).side(0) ## send 0, X
|
||||
label("one_time")
|
||||
out(pins, 1).side(0)
|
||||
jmp(y_dec, "one_time").side(1)
|
||||
|
||||
mov(osr,x).side(0) ## copy bytes back
|
||||
|
||||
set(y,15).side(0) ## re-send 0, X
|
||||
label("one_more_time")
|
||||
out(pins, 1).side(0)
|
||||
jmp(y_dec, "one_more_time").side(1)
|
||||
|
||||
# 16
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
|
||||
PIO0_BASE = const(0x50200000)
|
||||
PIO1_BASE = const(0x50300000)
|
||||
|
||||
CLKDIV_RESTART_BIT = const(8)
|
||||
SM_ENABLE_BIT = const(0)
|
||||
|
||||
FSTAT_OFFSET = const(0x004)
|
||||
FDEBUG_OFFSET = const(0x008)
|
||||
|
||||
TXF0_OFFSET = const(0x010)
|
||||
TXF1_OFFSET = const(0x014)
|
||||
TXF2_OFFSET = const(0x018)
|
||||
TXF3_OFFSET = const(0x01C)
|
||||
|
||||
RXF0_OFFSET = const(0x020)
|
||||
RXF1_OFFSET = const(0x024)
|
||||
RXF2_OFFSET = const(0x028)
|
||||
RXF3_OFFSET = const(0x02C)
|
||||
|
||||
## Direct write-only access to instruction memory.
|
||||
INSTR_MEM0 = const(0x048)
|
||||
INSTR_MEM1 = const(0x04C)
|
||||
# etc.
|
||||
INSTR_MEM31 = const(0x0C4)
|
||||
|
||||
SM0_CLKDIV = const(0x0C8)
|
||||
SM1_CLKDIV = const(0x0E0)
|
||||
SM2_CLKDIV = const(0x0F8)
|
||||
SM3_CLKDIV = const(0x110)
|
||||
## Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
## pixel pusher!
|
||||
import rp2
|
||||
import array
|
||||
import machine
|
||||
import pin_defs
|
||||
import dma_defs
|
||||
import pio_code
|
||||
import pio_defs
|
||||
import micropython
|
||||
from phosphor_gradient_14 import phosphor_gradient
|
||||
from uctypes import addressof
|
||||
|
||||
## allows interrupts to throw errors
|
||||
import micropython
|
||||
micropython.alloc_emergency_exception_buf(100)
|
||||
|
||||
NOP = const(41) # 0x29 -- display on
|
||||
SET_X = const(42) # 0x2A -- column select
|
||||
SET_Y = const(43) # 0x2B -- row select
|
||||
SET_COLOR = const(44) # 0x2C -- send data
|
||||
X_MSB_OFFSET = const(1) # data from ADC comes 16 bits X | 16 bits Y, LSB order
|
||||
Y_MSB_OFFSET = const(3)
|
||||
|
||||
COLOR_MASK = const(0x0F) ## 16 colors max
|
||||
FRAME_MASK = const(0x0F) ## 16 colors max
|
||||
|
||||
_LOOPING = const(False)
|
||||
_IRQ = const(False)
|
||||
# pixel_debug = machine.Pin(27, machine.Pin.OUT)
|
||||
|
||||
class Pixel_Pusher():
|
||||
|
||||
@micropython.viper
|
||||
def boop(self, color:int, frame:int):
|
||||
"""Trigger me once per adc_reader frame update"""
|
||||
# pixel_debug.high()
|
||||
## set frame, color
|
||||
machine.mem16[self.color_storage_address] = self.phosphors[color & COLOR_MASK]
|
||||
self.stage_sample_data.registers[0] = self.frame_starts[frame & FRAME_MASK]
|
||||
## reset counter lookup
|
||||
self.pixel_frame_counter.registers[0] = self.frame_counter_lookup_address
|
||||
## and go!
|
||||
self.stage_sample_data.registers[7] = 1 ## transaction count trigger register
|
||||
# pixel_debug.low()
|
||||
|
||||
@micropython.viper
|
||||
def pixel_frame_interrupt_handler(self, dma_caller):
|
||||
## even this is too much -- my guess is that the IRQs stack up during a GC or other system stupid thing
|
||||
self.frame_done = True
|
||||
|
||||
def resume(self):
|
||||
for d in self.allDMAs:
|
||||
d.ctrl = d.ctrl | 1
|
||||
self.stage_sample_data.config(trigger=True)
|
||||
def pause(self):
|
||||
for d in self.allDMAs:
|
||||
d.ctrl = d.ctrl & ~1 ## clear enable bit
|
||||
def deinit(self):
|
||||
# print(self.allDMAs)
|
||||
self.pixel_pusher_sm.active(0)
|
||||
self.pause()
|
||||
for d in self.allDMAs:
|
||||
d.close()
|
||||
|
||||
def __init__(self, adc_reader):
|
||||
## Consider deinitializing screen here? Or should that just always happen in code: risky! (@_@)
|
||||
## Needs adc_reader b/c it needs to know where the samples are stored
|
||||
self.frame_starts = adc_reader.frame_starts
|
||||
self.adc_frame = adc_reader.current_frame
|
||||
self.phosphors = phosphor_gradient
|
||||
self.num_samples_per_frame = adc_reader.num_samples_per_frame
|
||||
self.frame_done = False
|
||||
self._init_PIO()
|
||||
|
||||
## Data storage for DMA trickery
|
||||
## These three 32-bit numbers are formatted up to pass to the pixel_pusher_pio
|
||||
## It takes care of setting the command bit when relevant, and repeating the data
|
||||
## 120s are just placeholders for the X/Y coordinates
|
||||
self.pixel_command_array = array.array("B",
|
||||
[0, SET_X, 0, 120,
|
||||
0, SET_Y, 0, 120,
|
||||
0, SET_COLOR, 0xFF,0xFF,
|
||||
0, NOP, 0, 0])
|
||||
self.pixel_command_addr = array.array("L", [addressof(self.pixel_command_array)]) ## one element, for resetter
|
||||
|
||||
self.command_x = addressof(self.pixel_command_array) + 3 ## byte position in array
|
||||
self.command_y = addressof(self.pixel_command_array) + 7
|
||||
self.command_color = addressof(self.pixel_command_array) + 10
|
||||
|
||||
## Storage for one sample from ADC
|
||||
self.one_sample_storage = bytearray(4)
|
||||
self.one_sample_storage_address = addressof(self.one_sample_storage)
|
||||
|
||||
## Storage for the color
|
||||
self.color_storage = bytearray(2)
|
||||
## One way to set it
|
||||
machine.mem16[addressof(self.color_storage)] = self.phosphors[15]
|
||||
self.color_storage_address = addressof(self.color_storage)
|
||||
|
||||
## Strange array for counting purposes
|
||||
## returns a count for stage_sample_data, num_samples_per_frame-1 times
|
||||
## and then a 0, which will trigger and IRQ @ end of frame
|
||||
## and then you can reset
|
||||
self.frame_counter_lookup = array.array("L", [1]*(self.num_samples_per_frame-1))
|
||||
self.frame_counter_lookup.append(0)
|
||||
self.frame_counter_lookup_address = addressof(self.frame_counter_lookup)
|
||||
|
||||
## And here's the world's most convoluted DMA / PIO chain!
|
||||
self.stage_sample_data = rp2.DMA() ## pulls one sample pair from memory into bit_flipper_pio
|
||||
self.store_flipped_data = rp2.DMA() ## pulls bit-flipped pair and writes to sample storage
|
||||
self.pixel_load_x = rp2.DMA() ## extracts X from sample storage, puts it in pixel command array
|
||||
self.pixel_load_y = rp2.DMA() ## extracts Y ...
|
||||
self.pixel_load_color = rp2.DMA() ## extracts color ...
|
||||
self.pixel_command_to_screen = rp2.DMA() ## writes pixel command array to screen
|
||||
self.pixel_command_resetter = rp2.DMA() ## resets command_to_screen after three commands
|
||||
self.pixel_frame_counter = rp2.DMA() ## refreshes pixel command array address, stalls stage_sample_data at end of every frame
|
||||
## by sending a zero read address. Restart by configuring these two.
|
||||
self.allDMAs = [self.stage_sample_data, self.store_flipped_data, self.pixel_load_x , self.pixel_load_y , self.pixel_load_color, self.pixel_command_to_screen,
|
||||
self.pixel_command_resetter , self.pixel_frame_counter ]
|
||||
|
||||
self.pixel_frame_counter_read_address = int(addressof(self.pixel_frame_counter.registers[0:]))
|
||||
|
||||
## Control defs:
|
||||
## {'inc_read': 0, 'high_pri': 0, 'ring_sel': 0, 'size': 0,
|
||||
## 'enable': 0, 'treq_sel': 0, 'sniff_en': 0, 'chain_to': 0,
|
||||
## 'inc_write': 0, 'ring_size': 0, 'bswap': 0, 'IRQ_quiet': 0}
|
||||
self.stage_sample_data.ctrl = self.stage_sample_data.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.DREQ_PIO0_TX2, ## pace to TX FIFO
|
||||
chain_to = self.store_flipped_data.channel_id,
|
||||
IRQ_quiet = 1,
|
||||
inc_read = 1
|
||||
)
|
||||
self.stage_sample_data.config(count = 1,
|
||||
read = self.frame_starts[0], ## initial value, set this to start of frame to begin chain
|
||||
write = pio_defs.PIO0_BASE + pio_defs.TXF2_OFFSET
|
||||
)
|
||||
if _IRQ:
|
||||
self.stage_sample_data.irq(handler=self.pixel_frame_interrupt_handler, hard=True)
|
||||
## when one-shot, don't need to interrupt itself.
|
||||
|
||||
|
||||
## The bit-flipper PIO is in the middle here.
|
||||
## It's PIO 0, 2
|
||||
|
||||
self.store_flipped_data.ctrl = self.store_flipped_data.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.DREQ_PIO0_RX2, ## on PIO
|
||||
chain_to = self.pixel_load_x.channel_id,
|
||||
IRQ_quiet = 1
|
||||
)
|
||||
self.store_flipped_data.config(count = 1,
|
||||
read = pio_defs.PIO0_BASE + pio_defs.RXF2_OFFSET,
|
||||
write = self.one_sample_storage_address)
|
||||
######
|
||||
|
||||
self.pixel_load_x.ctrl = self.pixel_load_x.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_1BYTE,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.TREQ_PERMANENT,
|
||||
chain_to = self.pixel_load_y.channel_id,
|
||||
IRQ_quiet = 1
|
||||
)
|
||||
self.pixel_load_x.config(count = 1,
|
||||
read = self.one_sample_storage_address + X_MSB_OFFSET,
|
||||
write = self.command_x)
|
||||
|
||||
self.pixel_load_y.ctrl = self.pixel_load_y.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_1BYTE,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.TREQ_PERMANENT,
|
||||
chain_to = self.pixel_load_color.channel_id,
|
||||
IRQ_quiet = 1
|
||||
)
|
||||
self.pixel_load_y.config(count = 1,
|
||||
read = self.one_sample_storage_address + Y_MSB_OFFSET,
|
||||
write = self.command_y)
|
||||
|
||||
self.pixel_load_color.ctrl = self.pixel_load_color.pack_ctrl(default = 0,
|
||||
bswap = 1,
|
||||
size = dma_defs.SIZE_2BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.TREQ_PERMANENT,
|
||||
chain_to = self.pixel_command_to_screen.channel_id,
|
||||
IRQ_quiet = 1
|
||||
)
|
||||
self.pixel_load_color.config(count = 1,
|
||||
read = self.color_storage_address,
|
||||
write = self.command_color)
|
||||
|
||||
self.pixel_command_to_screen.ctrl = self.pixel_command_to_screen.pack_ctrl(default = 0,
|
||||
bswap = 1,
|
||||
inc_read = 1,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.DREQ_PIO1_TX0,
|
||||
chain_to = self.pixel_command_resetter.channel_id,
|
||||
IRQ_quiet = 1,
|
||||
ring_sel = 0, # ring on read
|
||||
ring_size = 4 # 2**4 = 16 bytes = 4 transfers
|
||||
)
|
||||
self.pixel_command_to_screen.config(count = 3,
|
||||
read = addressof(self.pixel_command_array),
|
||||
write = pio_defs.PIO1_BASE + pio_defs.TXF0_OFFSET) ## PIO sm(4)
|
||||
|
||||
|
||||
self.pixel_command_resetter.ctrl = self.pixel_command_resetter.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.TREQ_PERMANENT,
|
||||
IRQ_quiet = 1,
|
||||
chain_to = self.pixel_frame_counter.channel_id
|
||||
)
|
||||
self.pixel_command_resetter.config(count = 1,
|
||||
write = addressof(self.pixel_command_to_screen.registers[0:]), ## set read address back to top
|
||||
read = addressof(self.pixel_command_addr))
|
||||
|
||||
self.pixel_frame_counter.ctrl = self.pixel_frame_counter.pack_ctrl(default = 0,
|
||||
inc_read = 1,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.TREQ_PERMANENT,
|
||||
IRQ_quiet = 1,
|
||||
chain_to = self.pixel_frame_counter.channel_id
|
||||
## ends the chain. reset the frame_counter_lookup to restart.
|
||||
)
|
||||
self.pixel_frame_counter.config(count = 1,
|
||||
write = addressof(self.stage_sample_data.registers[7:]), ## count trigger register
|
||||
read = addressof(self.frame_counter_lookup))
|
||||
|
||||
|
||||
def _init_PIO(self):
|
||||
## same pins shared with
|
||||
self.sck_pin = machine.Pin(pin_defs.sck, machine.Pin.OUT)
|
||||
self.data_pin = machine.Pin(pin_defs.data, machine.Pin.OUT)
|
||||
self.dc_pin = machine.Pin(pin_defs.dc, machine.Pin.OUT)
|
||||
|
||||
## PIO 1, 0
|
||||
self.pixel_pusher_sm = rp2.StateMachine(4, pio_code.handle_screen_command, freq=250_000_000,
|
||||
out_base=self.data_pin, set_base=self.dc_pin, sideset_base=self.sck_pin)
|
||||
self.pixel_pusher_sm.active(1)
|
||||
|
||||
## PIO 0, 2
|
||||
self.bit_flipper_sm = rp2.StateMachine(2, pio_code.bit_flipper_pio, freq=250_000_000)
|
||||
self.bit_flipper_sm.restart() ## flush buffers? belt and suspenders.
|
||||
self.bit_flipper_sm.active(1)
|
||||
|
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,24 @@
|
|||
import random
|
||||
import time
|
||||
|
||||
class RW():
|
||||
def __init__(self, vectorscope, scale=5000, iterations=1000, delay=10):
|
||||
self.scale = scale
|
||||
self.v = vectorscope
|
||||
self.delay = delay
|
||||
self.iterations=iterations
|
||||
|
||||
def random_walk(self, x, y):
|
||||
x = x + random.randint(-self.scale,self.scale)
|
||||
y = y + random.randint(-self.scale,self.scale)
|
||||
self.v.wave.point(x,y)
|
||||
return x,y
|
||||
|
||||
def go(self):
|
||||
x,y = 0,0
|
||||
for i in range(self.iterations):
|
||||
x,y = self.random_walk(x,y)
|
||||
time.sleep_ms(self.delay)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
WIDTH = 32
|
||||
HEIGHT = 32
|
||||
FIRST = 0x20
|
||||
LAST = 0x7f
|
||||
|
||||
_font =\
|
||||
b'\x00\x4a\x5a\x08\x4d\x57\x52\x46\x52\x54\x20\x52\x52\x59\x51'\
|
||||
b'\x5a\x52\x5b\x53\x5a\x52\x59\x05\x4a\x5a\x4e\x46\x4e\x4d\x20'\
|
||||
b'\x52\x56\x46\x56\x4d\x0b\x48\x5d\x53\x42\x4c\x62\x20\x52\x59'\
|
||||
b'\x42\x52\x62\x20\x52\x4c\x4f\x5a\x4f\x20\x52\x4b\x55\x59\x55'\
|
||||
b'\x1a\x48\x5c\x50\x42\x50\x5f\x20\x52\x54\x42\x54\x5f\x20\x52'\
|
||||
b'\x59\x49\x57\x47\x54\x46\x50\x46\x4d\x47\x4b\x49\x4b\x4b\x4c'\
|
||||
b'\x4d\x4d\x4e\x4f\x4f\x55\x51\x57\x52\x58\x53\x59\x55\x59\x58'\
|
||||
b'\x57\x5a\x54\x5b\x50\x5b\x4d\x5a\x4b\x58\x1f\x46\x5e\x5b\x46'\
|
||||
b'\x49\x5b\x20\x52\x4e\x46\x50\x48\x50\x4a\x4f\x4c\x4d\x4d\x4b'\
|
||||
b'\x4d\x49\x4b\x49\x49\x4a\x47\x4c\x46\x4e\x46\x50\x47\x53\x48'\
|
||||
b'\x56\x48\x59\x47\x5b\x46\x20\x52\x57\x54\x55\x55\x54\x57\x54'\
|
||||
b'\x59\x56\x5b\x58\x5b\x5a\x5a\x5b\x58\x5b\x56\x59\x54\x57\x54'\
|
||||
b'\x22\x45\x5f\x5c\x4f\x5c\x4e\x5b\x4d\x5a\x4d\x59\x4e\x58\x50'\
|
||||
b'\x56\x55\x54\x58\x52\x5a\x50\x5b\x4c\x5b\x4a\x5a\x49\x59\x48'\
|
||||
b'\x57\x48\x55\x49\x53\x4a\x52\x51\x4e\x52\x4d\x53\x4b\x53\x49'\
|
||||
b'\x52\x47\x50\x46\x4e\x47\x4d\x49\x4d\x4b\x4e\x4e\x50\x51\x55'\
|
||||
b'\x58\x57\x5a\x59\x5b\x5b\x5b\x5c\x5a\x5c\x59\x07\x4d\x57\x52'\
|
||||
b'\x48\x51\x47\x52\x46\x53\x47\x53\x49\x52\x4b\x51\x4c\x0a\x4b'\
|
||||
b'\x59\x56\x42\x54\x44\x52\x47\x50\x4b\x4f\x50\x4f\x54\x50\x59'\
|
||||
b'\x52\x5d\x54\x60\x56\x62\x0a\x4b\x59\x4e\x42\x50\x44\x52\x47'\
|
||||
b'\x54\x4b\x55\x50\x55\x54\x54\x59\x52\x5d\x50\x60\x4e\x62\x08'\
|
||||
b'\x4a\x5a\x52\x4c\x52\x58\x20\x52\x4d\x4f\x57\x55\x20\x52\x57'\
|
||||
b'\x4f\x4d\x55\x05\x45\x5f\x52\x49\x52\x5b\x20\x52\x49\x52\x5b'\
|
||||
b'\x52\x07\x4e\x56\x53\x57\x52\x58\x51\x57\x52\x56\x53\x57\x53'\
|
||||
b'\x59\x51\x5b\x02\x45\x5f\x49\x52\x5b\x52\x05\x4e\x56\x52\x56'\
|
||||
b'\x51\x57\x52\x58\x53\x57\x52\x56\x02\x47\x5d\x5b\x42\x49\x62'\
|
||||
b'\x11\x48\x5c\x51\x46\x4e\x47\x4c\x4a\x4b\x4f\x4b\x52\x4c\x57'\
|
||||
b'\x4e\x5a\x51\x5b\x53\x5b\x56\x5a\x58\x57\x59\x52\x59\x4f\x58'\
|
||||
b'\x4a\x56\x47\x53\x46\x51\x46\x04\x48\x5c\x4e\x4a\x50\x49\x53'\
|
||||
b'\x46\x53\x5b\x0e\x48\x5c\x4c\x4b\x4c\x4a\x4d\x48\x4e\x47\x50'\
|
||||
b'\x46\x54\x46\x56\x47\x57\x48\x58\x4a\x58\x4c\x57\x4e\x55\x51'\
|
||||
b'\x4b\x5b\x59\x5b\x0f\x48\x5c\x4d\x46\x58\x46\x52\x4e\x55\x4e'\
|
||||
b'\x57\x4f\x58\x50\x59\x53\x59\x55\x58\x58\x56\x5a\x53\x5b\x50'\
|
||||
b'\x5b\x4d\x5a\x4c\x59\x4b\x57\x06\x48\x5c\x55\x46\x4b\x54\x5a'\
|
||||
b'\x54\x20\x52\x55\x46\x55\x5b\x11\x48\x5c\x57\x46\x4d\x46\x4c'\
|
||||
b'\x4f\x4d\x4e\x50\x4d\x53\x4d\x56\x4e\x58\x50\x59\x53\x59\x55'\
|
||||
b'\x58\x58\x56\x5a\x53\x5b\x50\x5b\x4d\x5a\x4c\x59\x4b\x57\x17'\
|
||||
b'\x48\x5c\x58\x49\x57\x47\x54\x46\x52\x46\x4f\x47\x4d\x4a\x4c'\
|
||||
b'\x4f\x4c\x54\x4d\x58\x4f\x5a\x52\x5b\x53\x5b\x56\x5a\x58\x58'\
|
||||
b'\x59\x55\x59\x54\x58\x51\x56\x4f\x53\x4e\x52\x4e\x4f\x4f\x4d'\
|
||||
b'\x51\x4c\x54\x05\x48\x5c\x59\x46\x4f\x5b\x20\x52\x4b\x46\x59'\
|
||||
b'\x46\x1d\x48\x5c\x50\x46\x4d\x47\x4c\x49\x4c\x4b\x4d\x4d\x4f'\
|
||||
b'\x4e\x53\x4f\x56\x50\x58\x52\x59\x54\x59\x57\x58\x59\x57\x5a'\
|
||||
b'\x54\x5b\x50\x5b\x4d\x5a\x4c\x59\x4b\x57\x4b\x54\x4c\x52\x4e'\
|
||||
b'\x50\x51\x4f\x55\x4e\x57\x4d\x58\x4b\x58\x49\x57\x47\x54\x46'\
|
||||
b'\x50\x46\x17\x48\x5c\x58\x4d\x57\x50\x55\x52\x52\x53\x51\x53'\
|
||||
b'\x4e\x52\x4c\x50\x4b\x4d\x4b\x4c\x4c\x49\x4e\x47\x51\x46\x52'\
|
||||
b'\x46\x55\x47\x57\x49\x58\x4d\x58\x52\x57\x57\x55\x5a\x52\x5b'\
|
||||
b'\x50\x5b\x4d\x5a\x4c\x58\x0b\x4e\x56\x52\x4f\x51\x50\x52\x51'\
|
||||
b'\x53\x50\x52\x4f\x20\x52\x52\x56\x51\x57\x52\x58\x53\x57\x52'\
|
||||
b'\x56\x0d\x4e\x56\x52\x4f\x51\x50\x52\x51\x53\x50\x52\x4f\x20'\
|
||||
b'\x52\x53\x57\x52\x58\x51\x57\x52\x56\x53\x57\x53\x59\x51\x5b'\
|
||||
b'\x03\x46\x5e\x5a\x49\x4a\x52\x5a\x5b\x05\x45\x5f\x49\x4f\x5b'\
|
||||
b'\x4f\x20\x52\x49\x55\x5b\x55\x03\x46\x5e\x4a\x49\x5a\x52\x4a'\
|
||||
b'\x5b\x14\x49\x5b\x4c\x4b\x4c\x4a\x4d\x48\x4e\x47\x50\x46\x54'\
|
||||
b'\x46\x56\x47\x57\x48\x58\x4a\x58\x4c\x57\x4e\x56\x4f\x52\x51'\
|
||||
b'\x52\x54\x20\x52\x52\x59\x51\x5a\x52\x5b\x53\x5a\x52\x59\x37'\
|
||||
b'\x45\x60\x57\x4e\x56\x4c\x54\x4b\x51\x4b\x4f\x4c\x4e\x4d\x4d'\
|
||||
b'\x50\x4d\x53\x4e\x55\x50\x56\x53\x56\x55\x55\x56\x53\x20\x52'\
|
||||
b'\x51\x4b\x4f\x4d\x4e\x50\x4e\x53\x4f\x55\x50\x56\x20\x52\x57'\
|
||||
b'\x4b\x56\x53\x56\x55\x58\x56\x5a\x56\x5c\x54\x5d\x51\x5d\x4f'\
|
||||
b'\x5c\x4c\x5b\x4a\x59\x48\x57\x47\x54\x46\x51\x46\x4e\x47\x4c'\
|
||||
b'\x48\x4a\x4a\x49\x4c\x48\x4f\x48\x52\x49\x55\x4a\x57\x4c\x59'\
|
||||
b'\x4e\x5a\x51\x5b\x54\x5b\x57\x5a\x59\x59\x5a\x58\x20\x52\x58'\
|
||||
b'\x4b\x57\x53\x57\x55\x58\x56\x08\x49\x5b\x52\x46\x4a\x5b\x20'\
|
||||
b'\x52\x52\x46\x5a\x5b\x20\x52\x4d\x54\x57\x54\x17\x47\x5c\x4b'\
|
||||
b'\x46\x4b\x5b\x20\x52\x4b\x46\x54\x46\x57\x47\x58\x48\x59\x4a'\
|
||||
b'\x59\x4c\x58\x4e\x57\x4f\x54\x50\x20\x52\x4b\x50\x54\x50\x57'\
|
||||
b'\x51\x58\x52\x59\x54\x59\x57\x58\x59\x57\x5a\x54\x5b\x4b\x5b'\
|
||||
b'\x12\x48\x5d\x5a\x4b\x59\x49\x57\x47\x55\x46\x51\x46\x4f\x47'\
|
||||
b'\x4d\x49\x4c\x4b\x4b\x4e\x4b\x53\x4c\x56\x4d\x58\x4f\x5a\x51'\
|
||||
b'\x5b\x55\x5b\x57\x5a\x59\x58\x5a\x56\x0f\x47\x5c\x4b\x46\x4b'\
|
||||
b'\x5b\x20\x52\x4b\x46\x52\x46\x55\x47\x57\x49\x58\x4b\x59\x4e'\
|
||||
b'\x59\x53\x58\x56\x57\x58\x55\x5a\x52\x5b\x4b\x5b\x0b\x48\x5b'\
|
||||
b'\x4c\x46\x4c\x5b\x20\x52\x4c\x46\x59\x46\x20\x52\x4c\x50\x54'\
|
||||
b'\x50\x20\x52\x4c\x5b\x59\x5b\x08\x48\x5a\x4c\x46\x4c\x5b\x20'\
|
||||
b'\x52\x4c\x46\x59\x46\x20\x52\x4c\x50\x54\x50\x16\x48\x5d\x5a'\
|
||||
b'\x4b\x59\x49\x57\x47\x55\x46\x51\x46\x4f\x47\x4d\x49\x4c\x4b'\
|
||||
b'\x4b\x4e\x4b\x53\x4c\x56\x4d\x58\x4f\x5a\x51\x5b\x55\x5b\x57'\
|
||||
b'\x5a\x59\x58\x5a\x56\x5a\x53\x20\x52\x55\x53\x5a\x53\x08\x47'\
|
||||
b'\x5d\x4b\x46\x4b\x5b\x20\x52\x59\x46\x59\x5b\x20\x52\x4b\x50'\
|
||||
b'\x59\x50\x02\x4e\x56\x52\x46\x52\x5b\x0a\x4a\x5a\x56\x46\x56'\
|
||||
b'\x56\x55\x59\x54\x5a\x52\x5b\x50\x5b\x4e\x5a\x4d\x59\x4c\x56'\
|
||||
b'\x4c\x54\x08\x47\x5c\x4b\x46\x4b\x5b\x20\x52\x59\x46\x4b\x54'\
|
||||
b'\x20\x52\x50\x4f\x59\x5b\x05\x48\x59\x4c\x46\x4c\x5b\x20\x52'\
|
||||
b'\x4c\x5b\x58\x5b\x0b\x46\x5e\x4a\x46\x4a\x5b\x20\x52\x4a\x46'\
|
||||
b'\x52\x5b\x20\x52\x5a\x46\x52\x5b\x20\x52\x5a\x46\x5a\x5b\x08'\
|
||||
b'\x47\x5d\x4b\x46\x4b\x5b\x20\x52\x4b\x46\x59\x5b\x20\x52\x59'\
|
||||
b'\x46\x59\x5b\x15\x47\x5d\x50\x46\x4e\x47\x4c\x49\x4b\x4b\x4a'\
|
||||
b'\x4e\x4a\x53\x4b\x56\x4c\x58\x4e\x5a\x50\x5b\x54\x5b\x56\x5a'\
|
||||
b'\x58\x58\x59\x56\x5a\x53\x5a\x4e\x59\x4b\x58\x49\x56\x47\x54'\
|
||||
b'\x46\x50\x46\x0d\x47\x5c\x4b\x46\x4b\x5b\x20\x52\x4b\x46\x54'\
|
||||
b'\x46\x57\x47\x58\x48\x59\x4a\x59\x4d\x58\x4f\x57\x50\x54\x51'\
|
||||
b'\x4b\x51\x18\x47\x5d\x50\x46\x4e\x47\x4c\x49\x4b\x4b\x4a\x4e'\
|
||||
b'\x4a\x53\x4b\x56\x4c\x58\x4e\x5a\x50\x5b\x54\x5b\x56\x5a\x58'\
|
||||
b'\x58\x59\x56\x5a\x53\x5a\x4e\x59\x4b\x58\x49\x56\x47\x54\x46'\
|
||||
b'\x50\x46\x20\x52\x53\x57\x59\x5d\x10\x47\x5c\x4b\x46\x4b\x5b'\
|
||||
b'\x20\x52\x4b\x46\x54\x46\x57\x47\x58\x48\x59\x4a\x59\x4c\x58'\
|
||||
b'\x4e\x57\x4f\x54\x50\x4b\x50\x20\x52\x52\x50\x59\x5b\x14\x48'\
|
||||
b'\x5c\x59\x49\x57\x47\x54\x46\x50\x46\x4d\x47\x4b\x49\x4b\x4b'\
|
||||
b'\x4c\x4d\x4d\x4e\x4f\x4f\x55\x51\x57\x52\x58\x53\x59\x55\x59'\
|
||||
b'\x58\x57\x5a\x54\x5b\x50\x5b\x4d\x5a\x4b\x58\x05\x4a\x5a\x52'\
|
||||
b'\x46\x52\x5b\x20\x52\x4b\x46\x59\x46\x0a\x47\x5d\x4b\x46\x4b'\
|
||||
b'\x55\x4c\x58\x4e\x5a\x51\x5b\x53\x5b\x56\x5a\x58\x58\x59\x55'\
|
||||
b'\x59\x46\x05\x49\x5b\x4a\x46\x52\x5b\x20\x52\x5a\x46\x52\x5b'\
|
||||
b'\x0b\x46\x5e\x48\x46\x4d\x5b\x20\x52\x52\x46\x4d\x5b\x20\x52'\
|
||||
b'\x52\x46\x57\x5b\x20\x52\x5c\x46\x57\x5b\x05\x48\x5c\x4b\x46'\
|
||||
b'\x59\x5b\x20\x52\x59\x46\x4b\x5b\x06\x49\x5b\x4a\x46\x52\x50'\
|
||||
b'\x52\x5b\x20\x52\x5a\x46\x52\x50\x08\x48\x5c\x59\x46\x4b\x5b'\
|
||||
b'\x20\x52\x4b\x46\x59\x46\x20\x52\x4b\x5b\x59\x5b\x0b\x4b\x59'\
|
||||
b'\x4f\x42\x4f\x62\x20\x52\x50\x42\x50\x62\x20\x52\x4f\x42\x56'\
|
||||
b'\x42\x20\x52\x4f\x62\x56\x62\x02\x4b\x59\x4b\x46\x59\x5e\x0b'\
|
||||
b'\x4b\x59\x54\x42\x54\x62\x20\x52\x55\x42\x55\x62\x20\x52\x4e'\
|
||||
b'\x42\x55\x42\x20\x52\x4e\x62\x55\x62\x05\x4a\x5a\x52\x44\x4a'\
|
||||
b'\x52\x20\x52\x52\x44\x5a\x52\x02\x49\x5b\x49\x62\x5b\x62\x07'\
|
||||
b'\x4e\x56\x53\x4b\x51\x4d\x51\x4f\x52\x50\x53\x4f\x52\x4e\x51'\
|
||||
b'\x4f\x11\x49\x5c\x58\x4d\x58\x5b\x20\x52\x58\x50\x56\x4e\x54'\
|
||||
b'\x4d\x51\x4d\x4f\x4e\x4d\x50\x4c\x53\x4c\x55\x4d\x58\x4f\x5a'\
|
||||
b'\x51\x5b\x54\x5b\x56\x5a\x58\x58\x11\x48\x5b\x4c\x46\x4c\x5b'\
|
||||
b'\x20\x52\x4c\x50\x4e\x4e\x50\x4d\x53\x4d\x55\x4e\x57\x50\x58'\
|
||||
b'\x53\x58\x55\x57\x58\x55\x5a\x53\x5b\x50\x5b\x4e\x5a\x4c\x58'\
|
||||
b'\x0e\x49\x5b\x58\x50\x56\x4e\x54\x4d\x51\x4d\x4f\x4e\x4d\x50'\
|
||||
b'\x4c\x53\x4c\x55\x4d\x58\x4f\x5a\x51\x5b\x54\x5b\x56\x5a\x58'\
|
||||
b'\x58\x11\x49\x5c\x58\x46\x58\x5b\x20\x52\x58\x50\x56\x4e\x54'\
|
||||
b'\x4d\x51\x4d\x4f\x4e\x4d\x50\x4c\x53\x4c\x55\x4d\x58\x4f\x5a'\
|
||||
b'\x51\x5b\x54\x5b\x56\x5a\x58\x58\x11\x49\x5b\x4c\x53\x58\x53'\
|
||||
b'\x58\x51\x57\x4f\x56\x4e\x54\x4d\x51\x4d\x4f\x4e\x4d\x50\x4c'\
|
||||
b'\x53\x4c\x55\x4d\x58\x4f\x5a\x51\x5b\x54\x5b\x56\x5a\x58\x58'\
|
||||
b'\x08\x4d\x59\x57\x46\x55\x46\x53\x47\x52\x4a\x52\x5b\x20\x52'\
|
||||
b'\x4f\x4d\x56\x4d\x16\x49\x5c\x58\x4d\x58\x5d\x57\x60\x56\x61'\
|
||||
b'\x54\x62\x51\x62\x4f\x61\x20\x52\x58\x50\x56\x4e\x54\x4d\x51'\
|
||||
b'\x4d\x4f\x4e\x4d\x50\x4c\x53\x4c\x55\x4d\x58\x4f\x5a\x51\x5b'\
|
||||
b'\x54\x5b\x56\x5a\x58\x58\x0a\x49\x5c\x4d\x46\x4d\x5b\x20\x52'\
|
||||
b'\x4d\x51\x50\x4e\x52\x4d\x55\x4d\x57\x4e\x58\x51\x58\x5b\x08'\
|
||||
b'\x4e\x56\x51\x46\x52\x47\x53\x46\x52\x45\x51\x46\x20\x52\x52'\
|
||||
b'\x4d\x52\x5b\x0b\x4d\x57\x52\x46\x53\x47\x54\x46\x53\x45\x52'\
|
||||
b'\x46\x20\x52\x53\x4d\x53\x5e\x52\x61\x50\x62\x4e\x62\x08\x49'\
|
||||
b'\x5a\x4d\x46\x4d\x5b\x20\x52\x57\x4d\x4d\x57\x20\x52\x51\x53'\
|
||||
b'\x58\x5b\x02\x4e\x56\x52\x46\x52\x5b\x12\x43\x61\x47\x4d\x47'\
|
||||
b'\x5b\x20\x52\x47\x51\x4a\x4e\x4c\x4d\x4f\x4d\x51\x4e\x52\x51'\
|
||||
b'\x52\x5b\x20\x52\x52\x51\x55\x4e\x57\x4d\x5a\x4d\x5c\x4e\x5d'\
|
||||
b'\x51\x5d\x5b\x0a\x49\x5c\x4d\x4d\x4d\x5b\x20\x52\x4d\x51\x50'\
|
||||
b'\x4e\x52\x4d\x55\x4d\x57\x4e\x58\x51\x58\x5b\x11\x49\x5c\x51'\
|
||||
b'\x4d\x4f\x4e\x4d\x50\x4c\x53\x4c\x55\x4d\x58\x4f\x5a\x51\x5b'\
|
||||
b'\x54\x5b\x56\x5a\x58\x58\x59\x55\x59\x53\x58\x50\x56\x4e\x54'\
|
||||
b'\x4d\x51\x4d\x11\x48\x5b\x4c\x4d\x4c\x62\x20\x52\x4c\x50\x4e'\
|
||||
b'\x4e\x50\x4d\x53\x4d\x55\x4e\x57\x50\x58\x53\x58\x55\x57\x58'\
|
||||
b'\x55\x5a\x53\x5b\x50\x5b\x4e\x5a\x4c\x58\x11\x49\x5c\x58\x4d'\
|
||||
b'\x58\x62\x20\x52\x58\x50\x56\x4e\x54\x4d\x51\x4d\x4f\x4e\x4d'\
|
||||
b'\x50\x4c\x53\x4c\x55\x4d\x58\x4f\x5a\x51\x5b\x54\x5b\x56\x5a'\
|
||||
b'\x58\x58\x08\x4b\x58\x4f\x4d\x4f\x5b\x20\x52\x4f\x53\x50\x50'\
|
||||
b'\x52\x4e\x54\x4d\x57\x4d\x11\x4a\x5b\x58\x50\x57\x4e\x54\x4d'\
|
||||
b'\x51\x4d\x4e\x4e\x4d\x50\x4e\x52\x50\x53\x55\x54\x57\x55\x58'\
|
||||
b'\x57\x58\x58\x57\x5a\x54\x5b\x51\x5b\x4e\x5a\x4d\x58\x08\x4d'\
|
||||
b'\x59\x52\x46\x52\x57\x53\x5a\x55\x5b\x57\x5b\x20\x52\x4f\x4d'\
|
||||
b'\x56\x4d\x0a\x49\x5c\x4d\x4d\x4d\x57\x4e\x5a\x50\x5b\x53\x5b'\
|
||||
b'\x55\x5a\x58\x57\x20\x52\x58\x4d\x58\x5b\x05\x4a\x5a\x4c\x4d'\
|
||||
b'\x52\x5b\x20\x52\x58\x4d\x52\x5b\x0b\x47\x5d\x4a\x4d\x4e\x5b'\
|
||||
b'\x20\x52\x52\x4d\x4e\x5b\x20\x52\x52\x4d\x56\x5b\x20\x52\x5a'\
|
||||
b'\x4d\x56\x5b\x05\x4a\x5b\x4d\x4d\x58\x5b\x20\x52\x58\x4d\x4d'\
|
||||
b'\x5b\x09\x4a\x5a\x4c\x4d\x52\x5b\x20\x52\x58\x4d\x52\x5b\x50'\
|
||||
b'\x5f\x4e\x61\x4c\x62\x4b\x62\x08\x4a\x5b\x58\x4d\x4d\x5b\x20'\
|
||||
b'\x52\x4d\x4d\x58\x4d\x20\x52\x4d\x5b\x58\x5b\x27\x4b\x59\x54'\
|
||||
b'\x42\x52\x43\x51\x44\x50\x46\x50\x48\x51\x4a\x52\x4b\x53\x4d'\
|
||||
b'\x53\x4f\x51\x51\x20\x52\x52\x43\x51\x45\x51\x47\x52\x49\x53'\
|
||||
b'\x4a\x54\x4c\x54\x4e\x53\x50\x4f\x52\x53\x54\x54\x56\x54\x58'\
|
||||
b'\x53\x5a\x52\x5b\x51\x5d\x51\x5f\x52\x61\x20\x52\x51\x53\x53'\
|
||||
b'\x55\x53\x57\x52\x59\x51\x5a\x50\x5c\x50\x5e\x51\x60\x52\x61'\
|
||||
b'\x54\x62\x02\x4e\x56\x52\x42\x52\x62\x27\x4b\x59\x50\x42\x52'\
|
||||
b'\x43\x53\x44\x54\x46\x54\x48\x53\x4a\x52\x4b\x51\x4d\x51\x4f'\
|
||||
b'\x53\x51\x20\x52\x52\x43\x53\x45\x53\x47\x52\x49\x51\x4a\x50'\
|
||||
b'\x4c\x50\x4e\x51\x50\x55\x52\x51\x54\x50\x56\x50\x58\x51\x5a'\
|
||||
b'\x52\x5b\x53\x5d\x53\x5f\x52\x61\x20\x52\x53\x53\x51\x55\x51'\
|
||||
b'\x57\x52\x59\x53\x5a\x54\x5c\x54\x5e\x53\x60\x52\x61\x50\x62'\
|
||||
b'\x17\x46\x5e\x49\x55\x49\x53\x4a\x50\x4c\x4f\x4e\x4f\x50\x50'\
|
||||
b'\x54\x53\x56\x54\x58\x54\x5a\x53\x5b\x51\x20\x52\x49\x53\x4a'\
|
||||
b'\x51\x4c\x50\x4e\x50\x50\x51\x54\x54\x56\x55\x58\x55\x5a\x54'\
|
||||
b'\x5b\x51\x5b\x4f\x22\x4a\x5a\x4a\x46\x4a\x5b\x4b\x5b\x4b\x46'\
|
||||
b'\x4c\x46\x4c\x5b\x4d\x5b\x4d\x46\x4e\x46\x4e\x5b\x4f\x5b\x4f'\
|
||||
b'\x46\x50\x46\x50\x5b\x51\x5b\x51\x46\x52\x46\x52\x5b\x53\x5b'\
|
||||
b'\x53\x46\x54\x46\x54\x5b\x55\x5b\x55\x46\x56\x46\x56\x5b\x57'\
|
||||
b'\x5b\x57\x46\x58\x46\x58\x5b\x59\x5b\x59\x46\x5a\x46\x5a\x5b'\
|
||||
b''
|
||||
|
||||
_index =\
|
||||
b'\x00\x00\x03\x00\x16\x00\x23\x00\x3c\x00\x73\x00\xb4\x00\xfb'\
|
||||
b'\x00\x0c\x01\x23\x01\x3a\x01\x4d\x01\x5a\x01\x6b\x01\x72\x01'\
|
||||
b'\x7f\x01\x86\x01\xab\x01\xb6\x01\xd5\x01\xf6\x01\x05\x02\x2a'\
|
||||
b'\x02\x5b\x02\x68\x02\xa5\x02\xd6\x02\xef\x02\x0c\x03\x15\x03'\
|
||||
b'\x22\x03\x2b\x03\x56\x03\xc7\x03\xda\x03\x0b\x04\x32\x04\x53'\
|
||||
b'\x04\x6c\x04\x7f\x04\xae\x04\xc1\x04\xc8\x04\xdf\x04\xf2\x04'\
|
||||
b'\xff\x04\x18\x05\x2b\x05\x58\x05\x75\x05\xa8\x05\xcb\x05\xf6'\
|
||||
b'\x05\x03\x06\x1a\x06\x27\x06\x40\x06\x4d\x06\x5c\x06\x6f\x06'\
|
||||
b'\x88\x06\x8f\x06\xa8\x06\xb5\x06\xbc\x06\xcd\x06\xf2\x06\x17'\
|
||||
b'\x07\x36\x07\x5b\x07\x80\x07\x93\x07\xc2\x07\xd9\x07\xec\x07'\
|
||||
b'\x05\x08\x18\x08\x1f\x08\x46\x08\x5d\x08\x82\x08\xa7\x08\xcc'\
|
||||
b'\x08\xdf\x08\x04\x09\x17\x09\x2e\x09\x3b\x09\x54\x09\x61\x09'\
|
||||
b'\x76\x09\x89\x09\xda\x09\xe1\x09\x32\x0a\x63\x0a'
|
||||
|
||||
FONT = memoryview(_font)
|
||||
INDEX = memoryview(_index)
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
from machine import Pin, SPI, SoftSPI
|
||||
import pin_defs
|
||||
import gc9a01
|
||||
import gc
|
||||
import rp2
|
||||
|
||||
class Screen():
|
||||
def __init__(self, softSPI=False):
|
||||
self.softSPI = softSPI
|
||||
self.sck = Pin(pin_defs.sck, Pin.OUT)
|
||||
self.data = Pin(pin_defs.data, Pin.OUT)
|
||||
self.dc = Pin(pin_defs.dc, Pin.OUT)
|
||||
self.init()
|
||||
|
||||
def deinit(self):
|
||||
self.spi.deinit()
|
||||
## this does not appear to de-initialize the DMAs -- the enable bits are still set
|
||||
del(self.spi)
|
||||
del(self.tft)
|
||||
gc.collect()
|
||||
|
||||
def init(self):
|
||||
if self.softSPI:
|
||||
self.spi = SoftSPI(baudrate=10_000_000, sck=self.sck, mosi=self.data, miso=Pin(pin_defs.throwaway))
|
||||
else:
|
||||
self.spi = SPI(0, baudrate=40_000_000, sck=self.sck, mosi=self.data)
|
||||
|
||||
self.tft = gc9a01.GC9A01(self.spi, 240, 240,
|
||||
reset = Pin(pin_defs.reset, Pin.OUT),
|
||||
cs = Pin(pin_defs.throwaway, Pin.OUT), ## not used, grounded on board
|
||||
dc = self.dc,
|
||||
backlight = Pin(pin_defs.throwaway, Pin.OUT), ## not used, always on
|
||||
rotation = 0)
|
||||
self.tft.init()
|
||||
self.tft.fill(gc9a01.color565(10,15,10))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import time
|
||||
import random
|
||||
|
||||
## instantiate and init
|
||||
s = Screen()
|
||||
|
||||
## For everything you can do with the GC9A01 library:
|
||||
## https://github.com/russhughes/gc9a01_mpy
|
||||
|
||||
s.tft.fill(gc9a01.BLUE)
|
||||
time.sleep_ms(500)
|
||||
s.tft.fill(gc9a01.YELLOW)
|
||||
time.sleep_ms(500)
|
||||
s.tft.fill(gc9a01.BLACK)
|
||||
|
||||
phosphor_bright = gc9a01.color565(120, 247, 180)
|
||||
phosphor_dark = gc9a01.color565(45, 217, 80)
|
||||
|
||||
## better graphic demo should go here
|
||||
for i in range(200):
|
||||
x1 = random.randint(0, 240)
|
||||
x2 = random.randint(0, 240)
|
||||
y1 = random.randint(0, 240)
|
||||
y2 = random.randint(0, 240)
|
||||
|
||||
if i % 2:
|
||||
s.tft.line(x1, y1, x2, y2, phosphor_bright)
|
||||
else:
|
||||
s.tft.line(x1, y1, x2, y2, phosphor_dark)
|
||||
time.sleep_ms(20)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import gc9a01
|
||||
from machine import Pin, SPI
|
||||
import vga1_16x32 as font
|
||||
import gc
|
||||
import romans as deffont
|
||||
|
||||
class ScreenNorm:
|
||||
"""
|
||||
A light wrapper around the gc9a01 screen.
|
||||
|
||||
Attributes:
|
||||
_spi (SPI): The SPI interface.
|
||||
tft (gc9a01.GC9A01): The TFT display.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
The constructor for ScreenNorm class.
|
||||
"""
|
||||
|
||||
self._spi = SPI(0, baudrate=40000000, sck=Pin(2, Pin.OUT), mosi=Pin(3, Pin.OUT), miso=Pin(20,Pin.IN))
|
||||
self.wake()
|
||||
|
||||
def get_font(self):
|
||||
"""
|
||||
Get the normal font
|
||||
|
||||
Returns:
|
||||
font object
|
||||
"""
|
||||
return font
|
||||
|
||||
def get_vfont(self):
|
||||
"""
|
||||
Get the default vector font (romans)
|
||||
"""
|
||||
return deffont
|
||||
|
||||
def wake(self):
|
||||
"""
|
||||
Method to wake up the display.
|
||||
"""
|
||||
|
||||
self.tft = gc9a01.GC9A01(self._spi, 240, 240,
|
||||
reset=Pin(4, Pin.OUT),
|
||||
cs=Pin(26, Pin.OUT),
|
||||
dc=Pin(5, Pin.OUT),
|
||||
backlight=Pin(27, Pin.OUT),
|
||||
rotation=0)
|
||||
|
||||
self.tft.init()
|
||||
|
||||
def idle(self):
|
||||
"""
|
||||
Method to get the display out of the way for another screen helper.
|
||||
"""
|
||||
|
||||
self._spi.deinit()
|
||||
self.tft=None
|
||||
|
||||
def jpg(self,filename):
|
||||
"""
|
||||
Method to show a jpg on the display.
|
||||
|
||||
Args:
|
||||
filename (str): The name of the jpg file.
|
||||
"""
|
||||
|
||||
if self.tft!=None:
|
||||
gc.collect()
|
||||
self.tft.jpg(filename,0,0,1)
|
||||
|
||||
def text(self,x,y,txt,fg_color=gc9a01.color565(45, 217, 80),bg_color=gc9a01.color565(10,15,10)):
|
||||
"""
|
||||
Method to draw text on the display.
|
||||
|
||||
Args:
|
||||
x (int): The x-coordinate of the top left corner of the text.
|
||||
y (int): The y-coordinate of the top left corner of the text.
|
||||
txt (str): The text to draw.
|
||||
fg_color (int): The foreground color. Default is gc9a01.color565(243,191,16).
|
||||
bg_color (int): The background color. Default is gc9a01.color565(26,26,26).
|
||||
"""
|
||||
|
||||
if self.tft!=None:
|
||||
self.tft.text(font,txt,x,y,fg_color,bg_color)
|
||||
|
||||
def text_font(self,font,x,y,txt,fg_color=gc9a01.color565(45, 217, 80),scale=1.0):
|
||||
"""
|
||||
Method to draw text with a font.
|
||||
|
||||
Args:
|
||||
font (font or None): The font to use (or use default font)
|
||||
x,y (int): X and Y coordinate
|
||||
txt (str): String
|
||||
fg_color (int) Foreground color (note: no background color -- always transparent)
|
||||
"""
|
||||
if self.tft!=None:
|
||||
if font==None:
|
||||
font=deffont
|
||||
self.tft.draw(font,txt,x,y,fg_color,scale)
|
||||
|
||||
def clear(self,color=0):
|
||||
"""
|
||||
Method to clear the display with a color.
|
||||
|
||||
Args:
|
||||
color (int): The background color. Default is 0.
|
||||
"""
|
||||
|
||||
if self.tft!=None:
|
||||
self.tft.fill_rect(0,0,240,240,color)
|
||||
|
||||
def pixel(self,x,y,color):
|
||||
"""
|
||||
Method to set a pixel on the display.
|
||||
|
||||
Args:
|
||||
x (int): The x-coordinate of the pixel.
|
||||
y (int): The y-coordinate of the pixel.
|
||||
color (int): The color of the pixel.
|
||||
"""
|
||||
|
||||
if self.tft!=None:
|
||||
self.tft.pixel(x,y,color)
|
|
@ -0,0 +1,60 @@
|
|||
import screennorm
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import vectoros
|
||||
import gc
|
||||
import asyncio
|
||||
|
||||
screen=screennorm.ScreenNorm()
|
||||
|
||||
|
||||
exit_flag=False
|
||||
|
||||
def text_overlay():
|
||||
screen.text(60,25,"SUPERCON")
|
||||
screen.text(85,190,"2023")
|
||||
|
||||
def back(key):
|
||||
screen.jpg("bluemarble.jpg") # button A globe
|
||||
text_overlay()
|
||||
|
||||
def fwd(key):
|
||||
screen.jpg("wrencher.jpg") # button B wrencher
|
||||
text_overlay()
|
||||
|
||||
def menu(key): # menu -bail out
|
||||
global exit_flag
|
||||
exit_flag=True
|
||||
|
||||
def startlcd(key): # button D - start LCD
|
||||
if screen.tft==None:
|
||||
screen.wake()
|
||||
back(None)
|
||||
|
||||
def stoplcd(key): # button C stop LCD
|
||||
if screen.tft!=None:
|
||||
screen.clear()
|
||||
screen.idle()
|
||||
|
||||
async def vos_main():
|
||||
global exit_flag
|
||||
keys=keyboardcb.KeyboardCB({keyleds.KEY_A: back, keyleds.KEY_B: fwd, keyleds.KEY_C: stoplcd, keyleds.KEY_D: startlcd,
|
||||
keyleds.KEY_MENU: menu})
|
||||
back(None)
|
||||
while exit_flag==False:
|
||||
await asyncio.sleep_ms(500)
|
||||
if vectoros.vectoros_active==False:
|
||||
gc.collect()
|
||||
# stop listening for keys
|
||||
keys.detach()
|
||||
exit_flag=False # next time
|
||||
from vos_state import vos_state
|
||||
vos_state.show_menu=True
|
||||
|
||||
def main():
|
||||
asyncio.run(vos_main())
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
keyboardcb.KeyboardCB.run(100)
|
||||
main()
|
|
@ -0,0 +1,169 @@
|
|||
import screennorm
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import joystick
|
||||
import vectoros
|
||||
from vos_state import vos_state
|
||||
import gc9a01
|
||||
|
||||
# little etch-a-sketch demo
|
||||
|
||||
pendown = True # start off with pen down
|
||||
color = gc9a01.BLACK # start off black
|
||||
|
||||
# To maintain the cursor we need a bit map which is slow
|
||||
# so less is more. Also pixels are tiny
|
||||
# so 40 is a good compromise (40x40 drawing area)
|
||||
SIZE=40
|
||||
PIXSIZE=240//SIZE # size of each "pixel"
|
||||
|
||||
# start cursor in the middle
|
||||
cursor_x = (SIZE+1)//2
|
||||
cursor_y = (SIZE+1)//2
|
||||
|
||||
# This will be the backing store for the screen
|
||||
model=[]
|
||||
|
||||
stopflag=False # stop when true
|
||||
|
||||
# clear the mdoel with color c (does not draw; see cursor)
|
||||
def fill_model(c):
|
||||
global model
|
||||
model=[[c for i in range(SIZE)] for j in range(SIZE)]
|
||||
|
||||
|
||||
|
||||
# update screen and overlay cursor (or just cursor area if blit==False)
|
||||
def cursor(blit=True):
|
||||
global model
|
||||
global cursor_x
|
||||
global cursor_y
|
||||
global PIXSIZE
|
||||
global pendown
|
||||
ccolor=gc9a01.color565(0xC0,0xFF,0x80)
|
||||
if pendown:
|
||||
ccolor=gc9a01.color565(0xFF,0x80,0x80)
|
||||
if blit:
|
||||
for x in range(SIZE):
|
||||
for y in range(SIZE):
|
||||
screen.tft.fill_rect(x*PIXSIZE,y*PIXSIZE,PIXSIZE,PIXSIZE,model[x][y])
|
||||
else: # only draw around the cursor
|
||||
for x in range(-1,2):
|
||||
for y in range(-1,2):
|
||||
nx=cursor_x+x
|
||||
ny=cursor_y+y
|
||||
screen.tft.fill_rect(nx*PIXSIZE,ny*PIXSIZE,PIXSIZE,PIXSIZE,model[nx][ny])
|
||||
screen.tft.fill_rect(cursor_x*PIXSIZE,cursor_y*PIXSIZE,PIXSIZE,PIXSIZE,ccolor)
|
||||
|
||||
# flip pen/up down on joystick button (non-repeating)
|
||||
def joybtn(key):
|
||||
global pendown
|
||||
pendown=not pendown
|
||||
cursor(False) # update cursor color
|
||||
|
||||
# normal joystick commands
|
||||
def joycmd(key):
|
||||
global pendown, cursor_x, cursor_y
|
||||
x=0
|
||||
y=0
|
||||
if key==keyleds.JOY_N:
|
||||
y=-1
|
||||
elif key==keyleds.JOY_S:
|
||||
y=1
|
||||
elif key==keyleds.JOY_E:
|
||||
x=1
|
||||
elif key==keyleds.JOY_W:
|
||||
x=-1
|
||||
elif key==keyleds.JOY_NW:
|
||||
y=-1
|
||||
x=-1
|
||||
elif key==keyleds.JOY_SW:
|
||||
y=1
|
||||
x=-1
|
||||
elif key==keyleds.JOY_NE:
|
||||
y=-1
|
||||
x=1
|
||||
elif key==keyleds.JOY_SE:
|
||||
y=1
|
||||
x=1
|
||||
if x!=0 or y!=0:
|
||||
newx=cursor_x+x
|
||||
if newx>=0 and newx<SIZE-1:
|
||||
cursor_x=newx
|
||||
newy=cursor_y+y
|
||||
if newy>=0 and newy<SIZE-1:
|
||||
cursor_y=newy
|
||||
if pendown:
|
||||
model[cursor_x][cursor_y]=color
|
||||
cursor(False)
|
||||
|
||||
# command keys (A=Black, B=Red, C=Green, D=Blue, User=Clear)
|
||||
def red(key):
|
||||
global color
|
||||
color=gc9a01.RED
|
||||
|
||||
def green(key):
|
||||
global color
|
||||
color=gc9a01.GREEN
|
||||
|
||||
def blue(key):
|
||||
global color
|
||||
color=gc9a01.BLUE
|
||||
|
||||
def black(key):
|
||||
global color
|
||||
color=gc9a01.BLACK
|
||||
def white(key): # erase
|
||||
global color
|
||||
color=gc9a01.WHITE
|
||||
|
||||
def cls(key):
|
||||
fill_model(gc9a01.WHITE)
|
||||
cursor()
|
||||
|
||||
def menu(key): # exit and return to menu
|
||||
global stopflag
|
||||
print("menu")
|
||||
if vos_state.active:
|
||||
stopflag=True
|
||||
joy.detach()
|
||||
btn.detach()
|
||||
csel.detach()
|
||||
|
||||
# create our screen and keyboard/joystick
|
||||
screen=screennorm.ScreenNorm()
|
||||
joy=joystick.Joystick(joycmd,attach=False)
|
||||
btn=keyboardcb.KeyboardCB(joybtn,keyleds.JOY_PRESS,attach=False) # no repeat
|
||||
csel=keyboardcb.KeyboardCB({ keyleds.KEY_LEVEL: white, keyleds.KEY_A: black,
|
||||
keyleds.KEY_B: red, keyleds.KEY_C: green, keyleds.KEY_D: blue, keyleds.KEY_USER: cls,
|
||||
keyleds.KEY_MENU: menu},
|
||||
attach=False)
|
||||
|
||||
|
||||
import asyncio
|
||||
import gc
|
||||
|
||||
async def vos_main():
|
||||
global stopflag
|
||||
if keyboardcb.KeyboardCB.task==None:
|
||||
keyboardcb.KeyboardCB.run(100) # start keyboard service
|
||||
cls(None) # zero out model and draw
|
||||
joy.attach()
|
||||
btn.attach()
|
||||
csel.attach()
|
||||
|
||||
while stopflag==False:
|
||||
await asyncio.sleep(5)
|
||||
if vos_state.active==False:
|
||||
gc.collect()
|
||||
stopflag=False # ready for next time
|
||||
vos_state.show_menu=True
|
||||
vectoros.remove_task('sketch')
|
||||
print("Exiting")
|
||||
|
||||
|
||||
def main():
|
||||
asyncio.run(vos_main())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,60 @@
|
|||
import screennorm
|
||||
import keyboardcb
|
||||
import keyleds
|
||||
import vectoros
|
||||
import gc
|
||||
import asyncio
|
||||
|
||||
screen=screennorm.ScreenNorm()
|
||||
|
||||
|
||||
exit_flag=False
|
||||
|
||||
def text_overlay():
|
||||
screen.text(60,25,"SUPERCON")
|
||||
screen.text(85,190,"2023")
|
||||
|
||||
def back(key):
|
||||
screen.jpg("bluemarble.jpg") # button A globe
|
||||
text_overlay()
|
||||
|
||||
def fwd(key):
|
||||
screen.jpg("wrencher.jpg") # button B wrencher
|
||||
text_overlay()
|
||||
|
||||
def menu(key): # menu -bail out
|
||||
global exit_flag
|
||||
exit_flag=True
|
||||
|
||||
def startlcd(key): # button D - start LCD
|
||||
if screen.tft==None:
|
||||
screen.wake()
|
||||
back(None)
|
||||
|
||||
def stoplcd(key): # button C stop LCD
|
||||
if screen.tft!=None:
|
||||
screen.clear()
|
||||
screen.idle()
|
||||
|
||||
async def vos_main():
|
||||
global exit_flag
|
||||
keys=keyboardcb.KeyboardCB({keyleds.KEY_A: back, keyleds.KEY_B: fwd, keyleds.KEY_C: stoplcd, keyleds.KEY_D: startlcd,
|
||||
keyleds.KEY_MENU: menu})
|
||||
back(None)
|
||||
while exit_flag==False:
|
||||
await asyncio.sleep_ms(500)
|
||||
if vectoros.vectoros_active==False:
|
||||
gc.collect()
|
||||
# stop listening for keys
|
||||
keys.detach()
|
||||
exit_flag=False # next time
|
||||
from vos_state import vos_state
|
||||
vos_state.show_menu=True
|
||||
|
||||
def main():
|
||||
asyncio.run(vos_main())
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
keyboardcb.KeyboardCB.run(100)
|
||||
main()
|
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,25 @@
|
|||
import math
|
||||
import time
|
||||
|
||||
from vectorscope import Vectorscope
|
||||
from random_walk import RW
|
||||
|
||||
|
||||
|
||||
def static_buffer_example(v):
|
||||
## Example of more complicated, repetitive waveform
|
||||
## v.wave has two buffers of 256 samples for putting sample-wise data into:
|
||||
## v.wave.outBufferX and outBufferY. These are packed 16 bits each, LSB first
|
||||
## To make your life easier, v.wave.packX() will put a list of 16-bit ints there for you
|
||||
|
||||
ramp = range(-2**15, 2**15, 2**8)
|
||||
v.wave.packX(ramp)
|
||||
sine = [int(math.sin(2*x*math.pi/256)*16_000) for x in range(256)]
|
||||
v.wave.packY(sine)
|
||||
time.sleep(5)
|
||||
## That discontinuity and wobble is real --
|
||||
## that's what happens when you try to push around a real DAC that's bandwidth-limited.
|
||||
|
||||
async def slot_main(v):
|
||||
static_buffer_example(v)
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
import machine
|
||||
import joystick
|
||||
from menu import * # bad habit but makes our menu definition nice
|
||||
from vos_state import vos_state
|
||||
import vectoros
|
||||
import colors
|
||||
import gc
|
||||
import random
|
||||
|
||||
|
||||
# run the sketch demo
|
||||
def runsketch(arg):
|
||||
vos_state.show_menu=False # get the menu of the way
|
||||
vectoros.launch_task('sketch')
|
||||
return EXIT
|
||||
|
||||
def planets(arg):
|
||||
vos_state.show_menu=False # get the menu of the way
|
||||
vectoros.launch_task('planets') # launch
|
||||
return EXIT
|
||||
|
||||
def menu_custom(the_menu):
|
||||
if the_menu.level==1:
|
||||
if machine.Pin(22).value():
|
||||
the_menu.current[2]=[" Sound Off",toggle_sound,0]
|
||||
else:
|
||||
the_menu.current[2]=[" Sound On",toggle_sound,1]
|
||||
|
||||
def toggle_sound(arg):
|
||||
## toggle sound here
|
||||
machine.Pin(22, machine.Pin.OUT).toggle()
|
||||
return CONT
|
||||
|
||||
|
||||
# the main vector scope demo
|
||||
def run_lissajous(arg):
|
||||
vos_state.show_menu=False
|
||||
vectoros.launch_task('lissajous')
|
||||
# we never come back, vectorscope
|
||||
return EXIT
|
||||
|
||||
def reboot(arg):
|
||||
if arg==False:
|
||||
vectoros.reset()
|
||||
else:
|
||||
vectoros.soft_reset()
|
||||
|
||||
# handle slots
|
||||
def abcd(key):
|
||||
if vos_state.show_menu:
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"Menu key {key}")
|
||||
kdict={ keyleds.KEY_A: 'A', keyleds.KEY_B: 'B', keyleds.KEY_C: 'C', keyleds.KEY_D: 'D'}
|
||||
await vectoros.launch_vecslot("slot"+kdict[key])
|
||||
|
||||
|
||||
# I really didn't want this to be async but it seems like do_menu must have an await
|
||||
# and run rarely returns when you have a lot going on
|
||||
async def vos_main():
|
||||
# you do NOT have to use with here
|
||||
# but if you don't you have to worry about the menu controller's joystick instance going out of scope yourself
|
||||
# or just make everything global -- the menu is smart enough to not listen to events when it is not active
|
||||
# note: m_back and m_exit were imported from menu
|
||||
## Start with sound off
|
||||
machine.Pin(22, machine.Pin.OUT).toggle()
|
||||
|
||||
screen=vectoros.get_screen()
|
||||
splashes = ["splash_x.jpg", "splash_7.jpg", "splash_wrencher.jpg"]
|
||||
screen.jpg(random.choice(splashes))
|
||||
await asyncio.sleep_ms(1000)
|
||||
|
||||
|
||||
|
||||
while True: # since this is the main menu, we don't really every quit
|
||||
print("creating slotkey")
|
||||
slotkey=keyboardcb.KeyboardCB(abcd,keyleds.KEY_ABCD)
|
||||
with Menu(clear_after=True,fg_color=colors.PHOSPHOR_DARK,bg_color=colors.PHOSPHOR_BG,
|
||||
cursor_bg=colors.PHOSPHOR_BG, cursor_fg=colors.PHOSPHOR_BRIGHT) as amenu:
|
||||
## name in menu, command to run, return value?
|
||||
submenu=[[" Planets", planets, 0],[" Sketch",runsketch,0],[" Back",m_exit,None]]
|
||||
mainmenu=[[" Lissajous", run_lissajous,None],
|
||||
[" Demos", SUBMENU, submenu] ,
|
||||
[" Sound", toggle_sound, None],
|
||||
[" Reboot",reboot,False],
|
||||
]
|
||||
|
||||
# comment next line for default font
|
||||
amenu.set_font("*") # set default vector font
|
||||
amenu.set_callback(menu_custom)
|
||||
await amenu.do_menu(mainmenu)
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"Menu waiting {vos_state.show_menu}")
|
||||
while vos_state.show_menu==False: # wait until we have to be seen again
|
||||
await asyncio.sleep_ms(0)
|
||||
|
||||
|
||||
def main():
|
||||
asyncio.run(vos_main())
|
||||
# this never runs
|
||||
|
||||
if __name__=="__main__":
|
||||
import vectoros
|
||||
vectoros.run()
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
import vos_launch
|
||||
import asyncio
|
||||
import vos_debug
|
||||
|
||||
|
||||
# if you don't care about the asyncio aspect of this
|
||||
# you can try using machine.Timer
|
||||
|
||||
|
||||
class Timer:
|
||||
"""
|
||||
A class that manages a timer for VectorOS.
|
||||
|
||||
Attributes:
|
||||
clients (dict): Each item is [reset value, value, callback, sync/async, oneshot].
|
||||
baserate (int): The base rate in milliseconds.
|
||||
_next_id (int): The next id to use.
|
||||
current_id (int): The current id.
|
||||
task (Task): The task for the timer.
|
||||
gc_delay (int): The delay for garbage collection.
|
||||
"""
|
||||
|
||||
clients={}
|
||||
baserate=vos_launch.timer_base_rate
|
||||
_next_id=1
|
||||
current_id=None
|
||||
task=None
|
||||
gc_delay=0
|
||||
_cancel=False
|
||||
|
||||
@classmethod
|
||||
async def _run(cls):
|
||||
"""
|
||||
Class method to run the class level "server".
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
|
||||
cls.task=asyncio.create_task(cls._job())
|
||||
|
||||
@classmethod
|
||||
def run(cls):
|
||||
"""
|
||||
Class method to launch the event loop without having to create a task yourself.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
|
||||
return asyncio.create_task(cls._run())
|
||||
|
||||
@classmethod
|
||||
async def _job(cls):
|
||||
"""
|
||||
Class method for the actual polling task.
|
||||
|
||||
This method runs a loop that sleeps for a specified timeout and then ticks the timer.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
|
||||
ctr=0
|
||||
while cls._cancel==False:
|
||||
await asyncio.sleep_ms(cls.baserate)
|
||||
await cls._tick()
|
||||
ctr+=1
|
||||
if cls.gc_delay!=0 and ctr>cls.gc_delay:
|
||||
ctr=0
|
||||
gc.collect()
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"Timer exit")
|
||||
|
||||
@classmethod
|
||||
def cancel(cls):
|
||||
"""
|
||||
Class method to cancel the polling task.
|
||||
"""
|
||||
cls._cancel=True
|
||||
cls.task.cancel()
|
||||
|
||||
@classmethod
|
||||
async def _tick(cls):
|
||||
"""
|
||||
Class method to tick the timer.
|
||||
|
||||
This method ticks the timer and calls the appropriate callback function for each timer event.
|
||||
"""
|
||||
|
||||
for item in cls.clients:
|
||||
cls.clients[item][1]+=1
|
||||
if cls.clients[item][0]<=cls.clients[item][1]:
|
||||
cls.clients[item][1]=0
|
||||
cls.current_id=item
|
||||
try:
|
||||
await cls.clients[item][2]()
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
if cls.clients[item][3]:
|
||||
del cls.clients[item]
|
||||
except Exception:
|
||||
pass
|
||||
cls.current_id=None
|
||||
|
||||
@classmethod
|
||||
def remove_timer(cls,id):
|
||||
"""
|
||||
Class method to remove a timer.
|
||||
|
||||
Args:
|
||||
id (int): The id of the timer to remove.
|
||||
"""
|
||||
if id in cls.clients:
|
||||
cls._next_id=id # try to recycle timer IDs (note we search "up" if one is in use already)
|
||||
del cls.clients[id]
|
||||
|
||||
@classmethod
|
||||
def add_timer(cls,ticks, callback, oneshot=False):
|
||||
"""
|
||||
Class method to add a timer.
|
||||
|
||||
Args:
|
||||
ticks (int): The number of ticks before the timer triggers.
|
||||
callback (function): The function to call when the timer triggers.
|
||||
oneshot (bool): A flag to indicate if the timer should only trigger once. Default is False.
|
||||
|
||||
Returns:
|
||||
int: The id of the added timer.
|
||||
"""
|
||||
|
||||
# we search for a free timerID starting from _next_id
|
||||
# normally that means we get an ascending number
|
||||
# but if you delete a timer ID we will reuse it unless
|
||||
# you delete two or more without creating any
|
||||
# then you may "leak" lower number IDs but you
|
||||
# should run out of resources before you run out of IDs
|
||||
# anyway This is just a hedge for the case where you
|
||||
# constantly create/remove a timer
|
||||
while cls._next_id in cls.clients:
|
||||
cls._next_id+=1
|
||||
|
||||
rv=cls._next_id
|
||||
cls.clients[cls._next_id]=[ticks,0,callback,oneshot]
|
||||
cls._next_id+=1
|
||||
|
||||
return rv
|
||||
|
||||
def __init__(self,ticks,paused=False,oneshot=False):
|
||||
"""
|
||||
The constructor for Timer class.
|
||||
|
||||
Args:
|
||||
ticks (int): The number of ticks before the timer triggers.
|
||||
paused (bool): A flag to indicate if the timer should start paused. Default is False.
|
||||
oneshot (bool): A flag to indicate if the timer should only trigger once. Default is False.
|
||||
"""
|
||||
|
||||
self.ticks=ticks
|
||||
self.paused=paused
|
||||
self.oneshot=oneshot
|
||||
|
||||
if paused:
|
||||
self.id=None
|
||||
else:
|
||||
self.id=self.add_timer(ticks,self.action,oneshot)
|
||||
|
||||
def __enter__(self):
|
||||
return
|
||||
|
||||
def __exit__(self,exc_type,exc_val,exc_tb):
|
||||
if self.id!=None:
|
||||
self.remove_timer(id)
|
||||
|
||||
def action(self):
|
||||
print("unhandled action")
|
||||
|
||||
def pause(self):
|
||||
if self.id==None:
|
||||
return
|
||||
|
||||
try:
|
||||
self.remove_timer(self.id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
finally:
|
||||
self.id=None
|
||||
|
||||
def resume(self):
|
||||
try:
|
||||
self.remove_timer(self.id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.id=self.add_timer(self.ticks,self.self.action,True,self.oneshot)
|
||||
|
||||
return self.id
|
||||
|
||||
if __name__=="__main__":
|
||||
import gc
|
||||
onesecid=0
|
||||
|
||||
Timer.gc_delay=500
|
||||
|
||||
class Test(Timer):
|
||||
def __init__(self):
|
||||
super().__init__(20)
|
||||
self.ct=0
|
||||
|
||||
def action(self):
|
||||
self.ct=self.ct+1
|
||||
print("**",self.ct)
|
||||
if (self.ct>=10):
|
||||
self.pause()
|
||||
|
||||
|
||||
twosec = Test()
|
||||
|
||||
|
||||
def callback1sec():
|
||||
"""
|
||||
Function to print a message every second.
|
||||
"""
|
||||
|
||||
print("Tick 1")
|
||||
|
||||
def callback5sec():
|
||||
"""
|
||||
Function to print a message and the amount of free memory every 5 seconds.
|
||||
"""
|
||||
|
||||
print("Tick 5",gc.mem_free())
|
||||
|
||||
def once():
|
||||
"""
|
||||
Function to print a message and remove the 1 second tick. This function only runs once.
|
||||
"""
|
||||
|
||||
global onesecid
|
||||
print("I only run once",onesecid)
|
||||
Timer.remove_timer(onesecid)
|
||||
|
||||
async def acallback_worker():
|
||||
"""
|
||||
Asynchronous function to print a start and end message with a delay of 5 seconds.
|
||||
"""
|
||||
|
||||
print("async start")
|
||||
await asyncio.sleep(5)
|
||||
print("async done")
|
||||
|
||||
# This is an async callback. However, the timer will stall until it completes
|
||||
# so we just kick off a new task to run in the background and then the timer does its thing
|
||||
async def acallback():
|
||||
asyncio.create_task(acallback_worker())
|
||||
|
||||
async def amain():
|
||||
"""
|
||||
Asynchronous main function to run the timer and add several tasks to it.
|
||||
"""
|
||||
|
||||
global onesecid
|
||||
Timer.run()
|
||||
|
||||
|
||||
onesecid=Timer.add_timer(10,callback1sec) # every one second
|
||||
Timer.add_timer(50,callback5sec) # 5 second synchronous repeating
|
||||
Timer.add_timer(100,once,True) # one shot and remove the 1 second tick
|
||||
Timer.add_timer(55,acallback,True) # one shot
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(0)
|
||||
|
||||
asyncio.run(amain())
|
||||
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
import aiorepl
|
||||
import asyncio
|
||||
import keyboardio
|
||||
import screennorm
|
||||
import timer
|
||||
from vos_launch import launch_list, auto_launch_list, auto_launch_repl, key_scan_rate, gc_thread_rate, timer_base_rate, vectorscope_slots
|
||||
from vos_state import vos_state
|
||||
import gc
|
||||
from machine import RTC
|
||||
from machine import reset as m_reset, soft_reset as m_soft_reset
|
||||
import vos_debug
|
||||
from vectorscope import Vectorscope
|
||||
|
||||
|
||||
_VERSION=20231001
|
||||
|
||||
_screen=screennorm.ScreenNorm()
|
||||
|
||||
|
||||
async def _sleeper():
|
||||
"""
|
||||
Asynchronous function to sleep forever.
|
||||
|
||||
This function runs a loop that sleeps for 2 seconds and then collects garbage.
|
||||
"""
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(2)
|
||||
if vos_state.gc_suspend==False:
|
||||
gc.collect()
|
||||
|
||||
def reset():
|
||||
m_reset()
|
||||
|
||||
def soft_reset():
|
||||
m_soft_reset()
|
||||
|
||||
|
||||
def sleep_forever():
|
||||
"""
|
||||
Function to let non async function wait forever.
|
||||
"""
|
||||
|
||||
asyncio.run(_sleeper())
|
||||
|
||||
async def _delayer(n):
|
||||
"""
|
||||
Asynchronous function to delay for n seconds.
|
||||
|
||||
Args:
|
||||
n (int): The number of milliseconds to delay.
|
||||
"""
|
||||
|
||||
await asyncio.sleep_ms(n)
|
||||
|
||||
def sleep(n):
|
||||
"""
|
||||
Function to delay for n seconds.
|
||||
|
||||
Args:
|
||||
n (int): The number of milliseconds to delay.
|
||||
"""
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_WARNING,"sleep called which uses asyncio.run")
|
||||
asyncio.run(_delayer(n))
|
||||
|
||||
def get_screen():
|
||||
"""
|
||||
Function to get the one screen object.
|
||||
|
||||
Returns:
|
||||
ScreenNorm: The screen object.
|
||||
"""
|
||||
|
||||
global _screen
|
||||
return _screen
|
||||
|
||||
async def launch_repl():
|
||||
"""
|
||||
Asynchronous function to start a repl.
|
||||
"""
|
||||
|
||||
vos_state.task_dict['$repl']=asyncio.create_task(aiorepl.task())
|
||||
|
||||
async def launch(task_tag):
|
||||
"""
|
||||
Asynchronous function to launch a program by its tag.
|
||||
|
||||
Args:
|
||||
task_tag (str): The tag of the program to launch.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
if vos_state.gc_suspend==False:
|
||||
gc.collect()
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"launching {task_tag}")
|
||||
mod=__import__(launch_list[task_tag])
|
||||
try:
|
||||
fn=getattr(mod,'vos_main')
|
||||
except Exception:
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_WARNING,f"launch could not find vos_main for {task_tag}. Trying main()")
|
||||
fn=getattr(mod,'main')
|
||||
try:
|
||||
await fn()
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def launch_task(task_tag):
|
||||
"""
|
||||
Function to create async task to launch.
|
||||
|
||||
Args:
|
||||
task_tag (str): The tag of the program to launch.
|
||||
|
||||
Returns:
|
||||
Task: The created task.
|
||||
"""
|
||||
vos_state.task_dict[task_tag]=asyncio.create_task(launch(task_tag))
|
||||
|
||||
|
||||
async def launch_vecslot(slot):
|
||||
import vectorscope
|
||||
get_screen().idle()
|
||||
gc.collect()
|
||||
vos_state.gc_suspend=True
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"Launching slot {slot}:{vectorscope_slots[slot]}")
|
||||
mod=__import__(vectorscope_slots[slot])
|
||||
fn=getattr(mod,'slot_main')
|
||||
v = Vectorscope(screen_running = True)
|
||||
try:
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"launching")
|
||||
await fn(v)
|
||||
except TypeError:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("launch exception",e)
|
||||
finally:
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"Slot done, reboot!")
|
||||
# await asyncio.sleep(5)
|
||||
reset()
|
||||
|
||||
|
||||
def remove_task(t):
|
||||
"""
|
||||
Function to remove a task. Note that this does not stop the task, just takes it out of the list
|
||||
|
||||
Args:
|
||||
t (str): The tag of the task to remove.
|
||||
"""
|
||||
|
||||
try:
|
||||
vos_state.task_dict.pop(t)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_gc_exit=False
|
||||
|
||||
async def _gc_thread(ms):
|
||||
"""
|
||||
Asynchronous function for optional thread to garbage collect occasionally.
|
||||
|
||||
Args:
|
||||
ms (int): The number of milliseconds to sleep between garbage collections.
|
||||
"""
|
||||
global _gc_exit
|
||||
while _gc_exit==False:
|
||||
await asyncio.sleep_ms(ms)
|
||||
if vos_state.gc_suspend:
|
||||
continue
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"Garbage collection starting")
|
||||
gc.collect()
|
||||
gc.threshold(gc.mem_free() // 4 + gc.mem_alloc())
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"Garbage collection thread exit")
|
||||
|
||||
|
||||
def vectoros_active():
|
||||
"""
|
||||
Function to test if we are active.
|
||||
|
||||
Returns:
|
||||
bool: True if active, False otherwise.
|
||||
"""
|
||||
|
||||
return vos_state.active
|
||||
|
||||
_vectoros_runafter=None
|
||||
|
||||
def set_global_exception():
|
||||
"""
|
||||
Function to catch exceptions.
|
||||
"""
|
||||
def handle_exception(loop,context):
|
||||
import sys
|
||||
if context["exception"].args[0]=="__vectoros-exit__":
|
||||
vos_state.run_after=context["exception"].args[1]
|
||||
else:
|
||||
sys.print_exception(context["exception"])
|
||||
sys.exit()
|
||||
|
||||
|
||||
loop=asyncio.get_event_loop()
|
||||
loop.set_exception_handler(handle_exception)
|
||||
|
||||
async def vectoros_startup(autolaunch=True):
|
||||
"""
|
||||
Function to start services (called by main)
|
||||
|
||||
Args:
|
||||
autolaunch (bool): True to launch autostart programs (default)
|
||||
"""
|
||||
global _VERSION
|
||||
vos_state.version=_VERSION
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"VectorOS {_VERSION} starting",RTC().datetime())
|
||||
# honor gc thread request
|
||||
if gc_thread_rate!=0:
|
||||
gc.disable()
|
||||
vos_state.task_dict['$gc']=asyncio.create_task(_gc_thread(gc_thread_rate))
|
||||
# spin up system-level keyboard polling (see keyboardcb and joystick)
|
||||
if key_scan_rate!=0:
|
||||
vos_state.task_dict['$key']=keyboardio.KeyboardIO.run(key_scan_rate)
|
||||
# spin up timer infrastructure
|
||||
if timer_base_rate!=0:
|
||||
vos_state.task_dict['$timer']=timer.Timer.run()
|
||||
# at this point, we consider ourselves running
|
||||
vos_state.active=True
|
||||
# launch repl if requested
|
||||
if autolaunch:
|
||||
if (auto_launch_repl):
|
||||
await launch_repl()
|
||||
# run auto launch stuff from vos_launch
|
||||
for t in auto_launch_list:
|
||||
vos_state.task_dict[t]=asyncio.create_task(launch(t))
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"VectorOS started",RTC().datetime())
|
||||
|
||||
|
||||
# shut down our service (but not apps)
|
||||
def vectoros_shutdown(deactivate=True):
|
||||
"""
|
||||
Function to shut down vectoros services
|
||||
"""
|
||||
global _screen, _gc_exit
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"VectorOS shutting down",RTC().datetime())
|
||||
_screen.idle()
|
||||
_screen=None
|
||||
keyboardio.KeyboardIO.cancel()
|
||||
del vos_state.task_dict['$key']
|
||||
timer.Timer.cancel()
|
||||
del vos_state.task_dict['$timer']
|
||||
_gc_exit=True
|
||||
# vos_state.task_dict['$gc'].cancel()
|
||||
del vos_state.task_dict['$gc']
|
||||
for x in vos_state.task_dict:
|
||||
vos_state.task_dict[x].cancel()
|
||||
if (deactivate):
|
||||
vos_state.active=False
|
||||
asyncio.get_event_loop().set_exception_handler(None)
|
||||
gc.collect()
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"VectorOS shut down",RTC().datetime())
|
||||
|
||||
def ext_run(cmd):
|
||||
"""
|
||||
Function to exit VectorOS and run a program
|
||||
"""
|
||||
gc.collect()
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,f"external run {cmd}")
|
||||
raise Exception("__vectoros-exit__",cmd)
|
||||
|
||||
|
||||
|
||||
# get all set up
|
||||
async def main():
|
||||
"""
|
||||
Asynchronous main function to set up the system.
|
||||
|
||||
This function sets up exception handling, starts the garbage collection thread, keyboard polling, and timer infrastructure,
|
||||
launches the repl if requested, imports modules from vos_launch, and runs auto launch stuff from vos_launch.
|
||||
"""
|
||||
# catch any strange exceptions
|
||||
set_global_exception()
|
||||
await vectoros_startup()
|
||||
await _sleeper()
|
||||
|
||||
# This is the main entry point that starts everything
|
||||
def run():
|
||||
"""
|
||||
Function to run the main function.
|
||||
|
||||
This function runs the main function and resets the event loop.
|
||||
"""
|
||||
global _vectoros_runafter, _vectoros_runaftermod
|
||||
cmd=None
|
||||
while True:
|
||||
try:
|
||||
asyncio.run(main()) # do not create tasks before this point!
|
||||
except SystemExit as se:
|
||||
break # catch sys.exit
|
||||
except Exception as e:
|
||||
print(e) # shouldn't really run
|
||||
print(hasattr(e,'arg'))
|
||||
if hasattr(e,'arg'):
|
||||
if e.arg[0]=='__vectoros-exit__':
|
||||
_vectoros_runafter=e.args[1]
|
||||
else:
|
||||
print(e)
|
||||
else:
|
||||
print(e)
|
||||
# This code launches a program after vectoros shutdown
|
||||
# The "single core" method works more reliably but if it is
|
||||
# in the second core causes stack errors (vos_state._xthreading==0)
|
||||
# The vos_state._xthreading==1 case works for multicore but
|
||||
# requrires a small server on the main core
|
||||
#This mostly works in single core mode
|
||||
finally:
|
||||
if vos_state._xthreading==0: # single core mode
|
||||
if vos_state.run_after != None: # as we exit, set up any pending command
|
||||
cmd=vos_state.run_after
|
||||
vos_state.run_after=None
|
||||
else:
|
||||
vos_debug.debug_print(vos_debug.DEBUG_LEVEL_INFO,"reset event loop")
|
||||
asyncio.new_event_loop()
|
||||
if cmd!=None:
|
||||
vectoros_shutdown()
|
||||
vos_state.gc_suspend=True
|
||||
asyncio.sleep_ms(250) # hope the gc thread dies if it hasn't already
|
||||
import sys
|
||||
del sys.modules['screennorm']
|
||||
del sys.modules['aiorepl']
|
||||
del sys.modules['romans']
|
||||
del sys.modules['vga1_16x32']
|
||||
gc.collect()
|
||||
exec(cmd)
|
||||
else: # multicore mode (requires start w/split_vos.py)
|
||||
if vos_state.run_after != None: # as we exit, set up any pending command
|
||||
print("core 1 passing back to zero")
|
||||
import sys
|
||||
del sys.modules['screennorm']
|
||||
del sys.modules['aiorepl']
|
||||
del sys.modules['romans']
|
||||
del sys.modules['vga1_16x32']
|
||||
del sys.modules['keyboardio']
|
||||
del sys.modules['timer']
|
||||
vos_state.gc_suspend=True
|
||||
asyncio.sleep_ms(250) # hope the gc thread dies if it hasn't already
|
||||
gc.collect()
|
||||
vectoros_shutdown(False)
|
||||
gc.collect()
|
||||
else:
|
||||
print("Resetting event loop")
|
||||
asyncio.new_event_loop()
|
||||
if vos_state._xthreading==1:
|
||||
gc.collect()
|
||||
vos_state.active=False
|
||||
else:
|
||||
pass
|
||||
|
||||
if __name__=="__main__":
|
||||
run()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
import gc
|
||||
import time
|
||||
import gc9a01
|
||||
import uctypes
|
||||
import machine
|
||||
import _thread
|
||||
|
||||
## Badge-specific classes
|
||||
from codec import Codec
|
||||
from screen import Screen
|
||||
from waveform import Waveform
|
||||
from adc_reader import ADC_Reader
|
||||
from pixel_pusher import Pixel_Pusher
|
||||
|
||||
## Misc helpers and defines
|
||||
import dma_defs
|
||||
import pin_defs
|
||||
|
||||
class Vectorscope():
|
||||
|
||||
def __init__(self, screen_running = False):
|
||||
|
||||
## Couple buttons if you want to play with them
|
||||
# self.audio_shutdown_pin = machine.Pin(pin_defs.audio_shutdown, machine.Pin.OUT, value=1)
|
||||
self.user_button = machine.Pin(pin_defs.user_button, machine.Pin.IN)
|
||||
|
||||
## Turn up the heat!
|
||||
machine.freq(250_000_000)
|
||||
|
||||
if not screen_running:
|
||||
## We actually only use the raster screen for the init routine.
|
||||
## So don't need to re-init if coming from the main menu
|
||||
self.screen = Screen()
|
||||
self.screen.tft.fill(gc9a01.BLACK)
|
||||
## deinit here since we don't need screen around
|
||||
self.screen.deinit()
|
||||
gc.collect()
|
||||
|
||||
## start up I2S state machines
|
||||
self.codec = Codec()
|
||||
gc.collect()
|
||||
|
||||
## Fire up the I2S output feeder
|
||||
self.wave = Waveform()
|
||||
gc.collect()
|
||||
|
||||
## sets up memory, DMAs to continuously read in ADC data into a 16-stage buffer
|
||||
self.adc_reader = ADC_Reader()
|
||||
gc.collect()
|
||||
|
||||
## automatically blits memory out to screen
|
||||
## needs adc_reader b/c needs to know where samples go
|
||||
## this is the real house of cards...
|
||||
self.pixel_pusher = Pixel_Pusher(self.adc_reader)
|
||||
gc.collect()
|
||||
|
||||
## start up the phosphor effect feeder on the other core,
|
||||
## b/c it's a ridiculous CPU hog with precise timing requirements
|
||||
self.kill_phosphor = False
|
||||
_thread.start_new_thread(self.phosphor, [()])
|
||||
|
||||
|
||||
|
||||
def phosphor(self, thread_callback_stuff):
|
||||
previous_frame = self.adc_reader.current_frame
|
||||
## end of frame counter for pixel pusher, used to trigger next frame
|
||||
end_of_pixel_counter = uctypes.addressof(self.pixel_pusher.frame_counter_lookup)+1024*4
|
||||
while not self.kill_phosphor:
|
||||
## wait for ADC frame change to sync up
|
||||
while self.adc_reader.current_frame == previous_frame:
|
||||
pass ## (@_@) this could be an async wait: should have ~10 ms of CPU time
|
||||
previous_frame = self.adc_reader.current_frame
|
||||
|
||||
## While reading this ADC frame, push out all the others to the screen
|
||||
## starting with the oldest frame,
|
||||
## i.e. the next one in line. (circular buffer and all that.)
|
||||
frame_counter = (self.adc_reader.current_frame + 1) & 0x0F
|
||||
phosphor_counter = 1 ## this is the dimmest / off phosphor level
|
||||
for i in range(15):
|
||||
## Start off a frame's worth of pixels off to the screen
|
||||
self.pixel_pusher.boop(phosphor_counter, (frame_counter+i) & 0x0F)
|
||||
## next brighter color up next
|
||||
phosphor_counter = phosphor_counter + 1
|
||||
## wait for pixel frame to finish -- this is happening in a DMA/PIO chain asynchronously
|
||||
while self.pixel_pusher.pixel_frame_counter.read != end_of_pixel_counter:
|
||||
pass ## this one should be a busy-wait.
|
||||
## It's on the order of microseconds, depending on screen speed.
|
||||
|
||||
gc.collect() ## doing this preemptively here where we have time prevents it from happening when we don't want
|
||||
## One gc.collect per 35 ms is excessive. But it makes the beast happy, and we're stalling anyway.
|
||||
_thread.exit()
|
||||
|
||||
def deinit(self):
|
||||
|
||||
self.kill_phosphor = True
|
||||
machine.freq(125_000_000)
|
||||
|
||||
self.pixel_pusher.deinit()
|
||||
self.adc_reader.deinit()
|
||||
self.wave.deinit()
|
||||
self.codec.deinit()
|
||||
|
||||
## doesn't seem to work.
|
||||
## Get OS Error 16 on next run
|
||||
## brute force... grrr...
|
||||
machine.reset()
|
||||
|
||||
|
||||
def call_out(self):
|
||||
pass
|
||||
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
WIDTH = 16
|
||||
HEIGHT = 32
|
||||
FIRST = 0x20
|
||||
LAST = 0x7f
|
||||
_FONT = \
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x7f\xfe\x7f\xfe\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x7f\xfe\x7f\xfe\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x01\x80\x01\x80\x0f\xf0\x0f\xf0\x39\x9c\x39\x9c\x71\x8e\x71\x8e\x71\x80\x71\x80\x39\x80\x39\x80\x0f\xf0\x0f\xf0\x01\x9c\x01\x9c\x01\x8e\x01\x8e\x71\x8e\x71\x8e\x39\x9c\x39\x9c\x0f\xf0\x0f\xf0\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x1c\x1e\x1c\x1e\x38\x1e\x38\x00\x70\x00\x70\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x0e\x3c\x0e\x3c\x1c\x3c\x1c\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xc0\x07\xc0\x1c\x70\x1c\x70\x38\x38\x38\x38\x1c\x70\x1c\x70\x07\xc0\x07\xc0\x0f\xce\x0f\xce\x38\xfc\x38\xfc\x70\x78\x70\x78\x70\x78\x70\x78\x38\xfc\x38\xfc\x0f\xce\x0f\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xe0\x00\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x07\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x38\x0e\x38\x03\xe0\x03\xe0\x3f\xfe\x3f\xfe\x03\xe0\x03\xe0\x0e\x38\x0e\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x3f\xfe\x3f\xfe\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x03\xc0\x03\xc0\x03\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfe\x3f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x03\xc0\x03\xc0\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x1c\x00\x38\x00\x38\x00\x70\x00\x70\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x0e\x00\x0e\x00\x1c\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xe0\x07\xe0\x1c\x38\x1c\x38\x38\x3c\x38\x3c\x38\x7c\x38\x7c\x38\xdc\x38\xdc\x39\x9c\x39\x9c\x3b\x1c\x3b\x1c\x3e\x1c\x3e\x1c\x3c\x1c\x3c\x1c\x1c\x38\x1c\x38\x07\xe0\x07\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x03\xc0\x03\xc0\x0f\xc0\x0f\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x0f\xf0\x38\x1c\x38\x1c\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x1c\x00\x1c\x00\x70\x00\x70\x01\xc0\x01\xc0\x07\x00\x07\x00\x1c\x00\x1c\x00\x38\x00\x38\x00\x3f\xfe\x3f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x0f\xf0\x38\x1c\x38\x1c\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x1c\x00\x1c\x01\xf0\x01\xf0\x00\x1c\x00\x1c\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x38\x1c\x38\x1c\x0f\xf0\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x03\xf0\x03\xf0\x07\x70\x07\x70\x0e\x70\x0e\x70\x1c\x70\x1c\x70\x38\x70\x38\x70\x3f\xfc\x3f\xfc\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfe\x3f\xfe\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xf0\x3f\xf0\x00\x1c\x00\x1c\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x38\x1c\x38\x1c\x0f\xf0\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x00\x1c\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf8\x3f\xf8\x00\x38\x00\x38\x00\x38\x00\x38\x00\x70\x00\x70\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x0e\x1c\x0e\x07\xfe\x07\xfe\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x1c\x00\x1c\x0f\xf0\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x80\x03\x80\x03\x80\x03\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x80\x03\x80\x03\x80\x03\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x80\x03\x80\x03\x80\x03\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x80\x03\x80\x03\x80\x03\x80\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x0e\x00\x0e\x00\x1c\x00\x1c\x00\x0e\x00\x0e\x00\x07\x00\x07\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xe0\x00\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfc\x3f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfc\x3f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x07\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xe0\x00\xe0\x00\x70\x00\x70\x00\x38\x00\x38\x00\x70\x00\x70\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xe0\x07\xe0\x1c\x38\x1c\x38\x38\x1c\x38\x1c\x00\x38\x00\x38\x00\x70\x00\x70\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x0f\xf0\x38\x1c\x38\x1c\x70\x0e\x70\x0e\x71\xfe\x71\xfe\x73\x8e\x73\x8e\x73\x8e\x73\x8e\x73\x8e\x73\x8e\x71\xfc\x71\xfc\x70\x00\x70\x00\x38\x00\x38\x00\x0f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x03\xc0\x07\xe0\x07\xe0\x0e\x70\x0e\x70\x1c\x38\x1c\x38\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x3f\xfc\x3f\xfc\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x1c\x38\x1c\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x1c\x38\x1c\x3f\xf0\x3f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x1c\x38\x1c\x3f\xf0\x3f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfc\x3f\xfc\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xe0\x3f\xe0\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xfc\x3f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfc\x3f\xfc\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xe0\x3f\xe0\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x3e\x38\x3e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x3f\xfe\x3f\xfe\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x00\x1c\x1c\x1c\x1c\x1c\x0e\x38\x0e\x38\x03\xe0\x03\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x38\x1c\x38\x1c\x70\x1c\x70\x1c\xe0\x1c\xe0\x1d\xc0\x1d\xc0\x1f\x80\x1f\x80\x1f\x80\x1f\x80\x1d\xc0\x1d\xc0\x1c\xe0\x1c\xe0\x1c\x70\x1c\x70\x1c\x38\x1c\x38\x1c\x1c\x1c\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xfc\x3f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x78\x1e\x78\x1e\x7c\x3e\x7c\x3e\x7e\x7e\x7e\x7e\x77\xee\x77\xee\x73\xce\x73\xce\x71\x8e\x71\x8e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x0e\x38\x0e\x3c\x0e\x3c\x0e\x3e\x0e\x3e\x0e\x3f\x0e\x3f\x0e\x3b\x8e\x3b\x8e\x39\xce\x39\xce\x38\xee\x38\xee\x38\x7e\x38\x7e\x38\x3e\x38\x3e\x38\x1e\x38\x1e\x38\x0e\x38\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x1c\x38\x1c\x3f\xf0\x3f\xf0\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\xee\x38\xee\x1c\x7c\x1c\x7c\x07\xf8\x07\xf8\x00\x1c\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x1c\x38\x1c\x3f\xf0\x3f\xf0\x38\xe0\x38\xe0\x38\x70\x38\x70\x38\x38\x38\x38\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x0f\xf0\x38\x1c\x38\x1c\x70\x0e\x70\x0e\x70\x00\x70\x00\x38\x00\x38\x00\x0f\xf0\x0f\xf0\x00\x1c\x00\x1c\x00\x0e\x00\x0e\x70\x0e\x70\x0e\x38\x1c\x38\x1c\x0f\xf0\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfe\x3f\xfe\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x1c\x38\x1c\x38\x0e\x70\x0e\x70\x07\xe0\x07\xe0\x03\xc0\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x70\x0e\x71\x8e\x71\x8e\x73\xce\x73\xce\x77\xee\x77\xee\x3e\x7c\x3e\x7c\x1c\x38\x1c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x1c\x38\x1c\x38\x0e\x70\x0e\x70\x07\xe0\x07\xe0\x03\xc0\x03\xc0\x07\xe0\x07\xe0\x0e\x70\x0e\x70\x1c\x38\x1c\x38\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x0e\x38\x0e\x38\x07\x70\x07\x70\x03\xe0\x03\xe0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfe\x3f\xfe\x00\x1c\x00\x1c\x00\x38\x00\x38\x00\x70\x00\x70\x00\xe0\x00\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x0e\x00\x0e\x00\x1c\x00\x1c\x00\x3f\xfe\x3f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\x00\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x1c\x00\x0e\x00\x0e\x00\x07\x00\x07\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xe0\x00\xe0\x00\x70\x00\x70\x00\x38\x00\x38\x00\x1c\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x03\xc0\x03\xc0\x07\xe0\x07\xe0\x0e\x70\x0e\x70\x1c\x38\x1c\x38\x38\x1c\x38\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x07\x00\x07\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xe0\x00\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf8\x0f\xf8\x00\x0e\x00\x0e\x0f\xfe\x0f\xfe\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x0f\xfe\x0f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x3f\xf8\x3f\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf8\x0f\xf8\x38\x0e\x38\x0e\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x0e\x38\x0e\x0f\xf8\x0f\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x07\xfe\x07\xfe\x1c\x0e\x1c\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x0f\xfe\x0f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf8\x0f\xf8\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x3f\xfe\x3f\xfe\x38\x00\x38\x00\x38\x00\x38\x00\x0f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\xf8\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x0f\xf0\x0f\xf0\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf8\x0f\xf8\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x0f\xfe\x0f\xfe\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x1f\xf8\x1f\xf8\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x3b\xf8\x3b\xf8\x3c\x0e\x3c\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x00\x70\x00\x70\x00\x70\x00\x00\x00\x00\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\x70\x00\xe0\x00\xe0\x0f\x80\x0f\x80\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x38\x0e\x38\x0e\x70\x0e\x70\x0e\xe0\x0e\xe0\x0f\xc0\x0f\xc0\x0e\xe0\x0e\xe0\x0e\x70\x0e\x70\x0e\x38\x0e\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3e\x78\x3e\x78\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x39\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xe0\x3f\xe0\x38\x38\x38\x38\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x07\xf0\x1c\x1c\x1c\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x07\xf0\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x1c\x38\x1c\x3f\xf0\x3f\xf0\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\xfe\x07\xfe\x1c\x0e\x1c\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x1c\x0e\x1c\x0e\x07\xfe\x07\xfe\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xf0\x3f\xf0\x38\x1c\x38\x1c\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x0f\xfc\x38\x00\x38\x00\x38\x00\x38\x00\x0f\xf8\x0f\xf8\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x1f\xf8\x1f\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x1f\xfc\x1f\xfc\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x38\x1c\x0f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x0e\x70\x0e\x38\x1c\x38\x1c\x1c\x38\x1c\x38\x0e\x70\x0e\x70\x07\xe0\x07\xe0\x03\xc0\x03\xc0\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x38\x0e\x39\xce\x39\xce\x3b\xee\x3b\xee\x1f\x7c\x1f\x7c\x0e\x38\x0e\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x38\x1c\x38\x0e\x70\x0e\x70\x07\xe0\x07\xe0\x03\xc0\x03\xc0\x07\xe0\x07\xe0\x0e\x70\x0e\x70\x1c\x38\x1c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x0e\x38\x0e\x1c\x1c\x1c\x1c\x0e\x38\x0e\x38\x07\x70\x07\x70\x03\xe0\x03\xe0\x01\xc0\x01\xc0\x03\x80\x03\x80\x07\x00\x07\x00\x0e\x00\x0e\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\xfe\x3f\xfe\x00\x1c\x00\x1c\x00\x70\x00\x70\x01\xc0\x01\xc0\x07\x00\x07\x00\x1c\x00\x1c\x00\x3f\xfe\x3f\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\xf8\x01\xc0\x01\xc0\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x1e\x00\x1e\x00\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x03\x80\x01\xc0\x01\xc0\x00\xf8\x00\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x1f\x00\x03\x80\x03\x80\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x00\x78\x00\x78\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x01\xc0\x03\x80\x03\x80\x1f\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\x9e\x07\x9e\x3c\xf0\x3c\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\x01\xc0\x07\x70\x07\x70\x1c\x1c\x1c\x1c\x70\x07\x70\x07\x70\x07\x70\x07\x7f\xff\x7f\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
|
||||
|
||||
FONT = memoryview(_FONT)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
|
||||
DEBUG_LEVEL_SILENT=-1
|
||||
DEBUG_LEVEL_SEVERE=0
|
||||
DEBUG_LEVEL_ERROR=10
|
||||
DEBUG_LEVEL_WARNING=20
|
||||
DEBUG_LEVEL_INFO=30
|
||||
|
||||
from vos_launch import debug_level
|
||||
|
||||
def debug_print(level,*args):
|
||||
"""
|
||||
Function to print debug messages that are lower than debug level (set in vos_launch.py)
|
||||
"""
|
||||
if level<=debug_level:
|
||||
return print(*args)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# list of things to launch from vectoros
|
||||
|
||||
# dictionary of tags to imports (will look for .vos_main() or .main() here)
|
||||
launch_list={ "menu": "supercon_menu", 'sketch': 'sketch', "demo": "examples", "planets":"planets", "lissajous":"lissajous"}
|
||||
|
||||
# list what you want to start auto (maybe just one thing?) need tag
|
||||
auto_launch_list=["menu"]
|
||||
|
||||
vectorscope_slots={"slotA": "A", "slotB": "B", "slotC": "C", "slotD": "D"}
|
||||
|
||||
auto_launch_repl=False # to get out: import sys followed by sys.exit()
|
||||
|
||||
key_scan_rate = 100 # how often to scan the keyboard globally (ms; 0 to do it yourself)
|
||||
|
||||
# how often to garbage collect
|
||||
# if you set this to zero and do nothing else
|
||||
# garbage collection will be automatic as usual and before new tasks launch
|
||||
gc_thread_rate = 5000
|
||||
|
||||
# Base rate for the timer (ms)
|
||||
timer_base_rate=100
|
||||
|
||||
# Debug level (messages must be < this level to print)
|
||||
# That is, at level 0 only level 0 messages print
|
||||
# at level 1 then level 1 and level 0 messages print
|
||||
# Set level to -1 to stop all messages (assuming you only call debug_print with positive values)
|
||||
|
||||
# if you want to use symbols for debug level, these are defined in vos_debug:
|
||||
DEBUG_LEVEL_SILENT=-1
|
||||
DEBUG_LEVEL_SEVERE=0
|
||||
DEBUG_LEVEL_ERROR=10
|
||||
DEBUG_LEVEL_WARNING=20
|
||||
DEBUG_LEVEL_INFO=30
|
||||
|
||||
debug_level=DEBUG_LEVEL_INFO
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
import vectoros
|
||||
vectoros.run()
|
|
@ -0,0 +1,18 @@
|
|||
# state for vector os
|
||||
|
||||
class vos_state:
|
||||
task_dict={} # tasks created (not removed, though, unless you do it yourself
|
||||
active=False # are we active?
|
||||
gc_suspend=False # do you want to suspend the gc?
|
||||
show_menu=True # Used to restart main menu
|
||||
run_after=None # Run after OS is done
|
||||
version=None # Version (filled in on init)
|
||||
_xthreading=0
|
||||
|
||||
|
||||
# put any globals you want for your application here (nice to put them "in" something like a dictionary
|
||||
|
||||
# global my_task_vars={"exit_flag": False, "rate": 200}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
import dma_defs
|
||||
import pin_defs
|
||||
import codec
|
||||
import math
|
||||
import struct
|
||||
import time
|
||||
import array
|
||||
import rp2
|
||||
import machine
|
||||
from uctypes import addressof
|
||||
|
||||
|
||||
## Set up sample memory arrays
|
||||
|
||||
_DEBUG = const(False)
|
||||
|
||||
class Waveform():
|
||||
def __init__(self, num_samples_per_frame=256):
|
||||
|
||||
self.outBuffer_ready = False ## flag raised when a sample gets stored and the outBuffers can be written to
|
||||
## your code needs to clear this from outside, then wait for next True to sync up
|
||||
## at 30 kHz it's about every 38 ms.
|
||||
self.num_samples = num_samples_per_frame
|
||||
self.outBufferX = bytearray(self.num_samples * 2) ## 2 bytes, 1 channel
|
||||
self.outBufferY = bytearray(self.num_samples * 2)
|
||||
self.outBuffer = bytearray(self.num_samples * 4) ## interleave
|
||||
self.outBuffer_addr = array.array("L", [addressof(self.outBuffer)])
|
||||
|
||||
## This DMA takes the whole block of samples and feeds them off to
|
||||
## the codec's write PIO state machine
|
||||
## It fires off an IRQ (feed_dac_irq_handler) when it's done with a round
|
||||
## feed_dac_irq_handler interleaves the two sample channels and queues them up
|
||||
self.feed_dac_transfer = rp2.DMA()
|
||||
## And this DMA is chained to the above, resets it back to the beginning
|
||||
self.feed_dac_control = rp2.DMA()
|
||||
## Control defs:
|
||||
## {'inc_read': 0, 'high_pri': 0, 'ring_sel': 0, 'size': 0,
|
||||
## 'enable': 0, 'treq_sel': 0, 'sniff_en': 0, 'chain_to': 0,
|
||||
## 'inc_write': 0, 'ring_size': 0, 'bswap': 0, 'IRQ_quiet': 0}
|
||||
self.feed_dac_transfer.ctrl = self.feed_dac_transfer.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES,
|
||||
enable = 1,
|
||||
treq_sel = dma_defs.DREQ_PIO0_TX1,
|
||||
chain_to = self.feed_dac_control.channel_id,
|
||||
bswap = 1,
|
||||
IRQ_quiet = 0,
|
||||
inc_read = 1)
|
||||
self.feed_dac_transfer.config(count = self.num_samples, write = codec.WRITE_FIFO)
|
||||
self.feed_dac_transfer.irq(handler=self.feed_dac_irq_handler, hard=False)
|
||||
|
||||
self.feed_dac_control.ctrl = self.feed_dac_control.pack_ctrl(default = 0,
|
||||
size = dma_defs.SIZE_4BYTES, ## addresses
|
||||
enable = 1,
|
||||
chain_to = self.feed_dac_control.channel_id, ## no chain
|
||||
treq_sel = dma_defs.TREQ_PERMANENT, ## always on
|
||||
IRQ_quiet = 1)
|
||||
self.feed_dac_control.config(count = 1,
|
||||
write = self.feed_dac_transfer.registers[15:],
|
||||
read = addressof(self.outBuffer_addr),
|
||||
trigger = True)
|
||||
if _DEBUG:
|
||||
self.debug_pin = machine.Pin(28, machine.Pin.OUT, value=0)
|
||||
|
||||
def init(self):
|
||||
self.__init__()
|
||||
|
||||
def deinit(self):
|
||||
"""unwind all of the above"""
|
||||
print("(@_@) TODO: verify DMA shuts down")
|
||||
self.feed_dac_control.ctrl = 0
|
||||
time.sleep_ms(10) ## (@_@) replace with asyncio when necessary
|
||||
self.feed_dac_transfer.ctrl = 0
|
||||
self.feed_dac_control.close()
|
||||
time.sleep_ms(10) ## (@_@) replace with asyncio when necessary
|
||||
self.feed_dac_transfer.close()
|
||||
|
||||
## 175 us? not horrible -- roughly 6 sample frames, have 256
|
||||
@micropython.viper
|
||||
def interleave_buffers(self):
|
||||
bufX_p = ptr16(self.outBufferX)
|
||||
bufY_p = ptr16(self.outBufferY)
|
||||
out_buffer_p = ptr32(self.outBuffer)
|
||||
num_samples = int(self.num_samples)
|
||||
for i in range(num_samples):
|
||||
out_buffer_p[i] = (bufY_p[i] << 16) + bufX_p[i]
|
||||
|
||||
@micropython.viper
|
||||
def feed_dac_irq_handler(self, calling_dma):
|
||||
## this is where the magic happens
|
||||
## roughly every 8.5 ms @ 256 samples
|
||||
if _DEBUG:
|
||||
self.debug_pin.high()
|
||||
self.interleave_buffers()
|
||||
self.outBuffer_ready = True
|
||||
## Ping the user to load up their data
|
||||
if _DEBUG:
|
||||
self.debug_pin.low()
|
||||
|
||||
## Get data into the buffers
|
||||
@micropython.viper
|
||||
def _constant(self, value:int, buffer):
|
||||
buffer_p = ptr8(buffer)
|
||||
num_samples = int(self.num_samples)
|
||||
for i in range(num_samples):
|
||||
buffer_p[2*i+0] = (value & 0xFF00) >> 8
|
||||
buffer_p[2*i+1] = value & 0x00FF
|
||||
|
||||
@micropython.viper
|
||||
def _pack_wave(self, wavelist, buffer):
|
||||
"""takes a list of integers, +/- 15 bits worth, and packs it into a buffer"""
|
||||
buffer_p = ptr8(buffer)
|
||||
num_samples = int(self.num_samples)
|
||||
for i in range(num_samples):
|
||||
sample = int(wavelist[i])
|
||||
buffer_p[2*i+0] = (sample & 0xFF00) >> 8
|
||||
buffer_p[2*i+1] = sample & 0x00FF
|
||||
|
||||
## Convenience functions
|
||||
def packX(self, wavelist):
|
||||
self._pack_wave(wavelist, self.outBufferX)
|
||||
def packY(self, wavelist):
|
||||
self._pack_wave(wavelist, self.outBufferY)
|
||||
def constantX(self, value:int):
|
||||
self._constant(value, self.outBufferX)
|
||||
def constantY(self, value:int):
|
||||
self._constant(value, self.outBufferY)
|
||||
def point(self, x:int, y:int):
|
||||
self._constant(x, self.outBufferX)
|
||||
self._constant(y, self.outBufferY)
|
||||
|
||||
|
||||
|