Q1 display and ux
This commit is contained in:
parent
394d1c0590
commit
3781f7a08d
@ -46,20 +46,20 @@ def crunch(n):
|
||||
|
||||
return a[0]
|
||||
|
||||
# LCD Display wants RGB565 values, but wrong endian from us, so green gets split weird.
|
||||
# LCD Display wants RGB565 values, but big endian, so green gets split weird.
|
||||
def swizzle(r,g,b):
|
||||
# from 0-255 per component => two bytes
|
||||
b = (b >> 3)
|
||||
g = (g >> 3) # should be >> 2 for 6 bits; but looks trash?
|
||||
r = (r >> 3)
|
||||
|
||||
return pack('<H', ((r<<11) | (g<<6) | b))
|
||||
return pack('>H', ((r<<11) | (g<<6) | b))
|
||||
|
||||
# these values tested on real hardware
|
||||
assert swizzle(255, 0, 0) == b'\x00\xf8' # red
|
||||
#assert swizzle(0, 255, 0) == b'\xc0\x0f' # green (6 bits)
|
||||
assert swizzle(0, 255, 0) == b'\xc0\x07' # green (5 bits)
|
||||
assert swizzle(0, 0, 255) == b'\x1f\x00' # blue
|
||||
assert swizzle(255, 0, 0) == b'\xf8\x00' # red
|
||||
##assert swizzle(0, 255, 0) == b'\xc0\x0f' # green (6 bits)
|
||||
assert swizzle(0, 255, 0) == b'\x07\xc0' # green (5 bits)
|
||||
assert swizzle(0, 0, 255) == b'\x00\x1f' # blue
|
||||
|
||||
|
||||
def into_bgr565(img):
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -9,8 +9,8 @@ GlyphInfo = namedtuple('GlyphInfo', 'w h bits')
|
||||
|
||||
|
||||
#FONT_SHADES = [0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 223, 239, 255]
|
||||
TEXT_PALETTE = b'\x00\x00a\x08\xe3\x18e)\xe79iJ\xebZmk\xef{q\x8c\xf3\x9cu\xad\xf7\xbd\xfb\xde}\xef\xff\xff'
|
||||
TEXT_PALETTE_INV = b'\xff\xff}\xef\xfb\xdey\xce\xf7\xbdu\xad\xf3\x9cq\x8c\xef{mk\xebZiJ\xe79\xe3\x18a\x08\x00\x00'
|
||||
TEXT_PALETTE = b'\x00\x00\x08a\x18\xe3)e9\xe7JiZ\xebkm{\xef\x8cq\x9c\xf3\xadu\xbd\xf7\xde\xfb\xef}\xff\xff'
|
||||
TEXT_PALETTE_INV = b'\xff\xff\xef}\xde\xfb\xcey\xbd\xf7\xadu\x9c\xf3\x8cq{\xefkmZ\xebJi9\xe7\x18\xe3\x08a\x00\x00'
|
||||
|
||||
# same, but w/o byte swapping, packing (useful for simulator)
|
||||
#TEXT_PALETTE = [0x0000, 0x0861, 0x18e3, 0x2965, 0x39e7, 0x4a69, 0x5aeb, 0x6b6d, 0x7bef, 0x8c71, 0x9cf3, 0xad75, 0xbdf7, 0xdefb, 0xef7d, 0xffff]
|
||||
|
||||
@ -83,7 +83,7 @@ def make_palette(shades, col):
|
||||
assert len(shades) == NUM_GREYS
|
||||
vals = [remap(col, s) for s in shades]
|
||||
txt = ', '.join('0x%04x'% i for i in vals)
|
||||
return txt, pack('<%dH' % NUM_GREYS, *vals)
|
||||
return txt, pack('>%dH' % NUM_GREYS, *vals)
|
||||
|
||||
def doit(out_fname='font_iosevka.py', cls_name='FontIosevka'):
|
||||
font = ImageFont.truetype(FONT, FONT_SIZE)
|
||||
|
||||
@ -363,39 +363,6 @@ Press 6 to prove you read to the end of this message.''', title='WARNING', escap
|
||||
from flow import EmptyWallet
|
||||
return MenuSystem(EmptyWallet)
|
||||
|
||||
async def login_countdown(sec):
|
||||
# Show a countdown, which may need to
|
||||
# run for multiple **days**
|
||||
from glob import dis
|
||||
from display import FontSmall, FontLarge
|
||||
from utime import ticks_ms, ticks_diff
|
||||
|
||||
# pre-render fixed parts
|
||||
dis.clear()
|
||||
y = 0
|
||||
dis.text(None, y, 'Login countdown in', font=FontSmall); y += 14
|
||||
dis.text(None, y, 'effect. Must wait:', font=FontSmall); y += 14
|
||||
y += 5
|
||||
dis.save()
|
||||
|
||||
st = ticks_ms()
|
||||
while sec > 0:
|
||||
dis.restore()
|
||||
dis.text(None, y, pretty_short_delay(sec), font=FontLarge)
|
||||
|
||||
dis.show()
|
||||
dis.busy_bar(1)
|
||||
|
||||
# this should be more accurate, errors were accumulating
|
||||
now = ticks_ms()
|
||||
dt = 1000 - ticks_diff(now, st)
|
||||
await sleep_ms(dt)
|
||||
st = ticks_ms()
|
||||
|
||||
sec -= 1
|
||||
|
||||
dis.busy_bar(0)
|
||||
|
||||
async def block_until_login():
|
||||
#
|
||||
# Force user to enter a valid PIN.
|
||||
@ -428,17 +395,22 @@ async def show_nickname(nick):
|
||||
# Show a nickname for this coldcard (as a personalization)
|
||||
# - no keys here, just show it until they press anything
|
||||
from glob import dis
|
||||
from display import FontLarge, FontTiny, FontSmall
|
||||
from ux import ux_wait_keyup
|
||||
|
||||
dis.clear()
|
||||
|
||||
if dis.width(nick, FontLarge) <= dis.WIDTH:
|
||||
dis.text(None, 21, nick, font=FontLarge)
|
||||
if dis.has_lcd:
|
||||
from lcd_display import CHARS_H
|
||||
dis.text(None, CHARS_H//3, nick)
|
||||
else:
|
||||
dis.text(None, 27, nick, font=FontSmall)
|
||||
from display import FontLarge, FontSmall
|
||||
|
||||
dis.show()
|
||||
if dis.width(nick, FontLarge) <= dis.WIDTH:
|
||||
dis.text(None, 21, nick, font=FontLarge)
|
||||
else:
|
||||
dis.text(None, 27, nick, font=FontSmall)
|
||||
|
||||
dis.show()
|
||||
|
||||
await ux_wait_keyup()
|
||||
|
||||
@ -788,7 +760,7 @@ async def start_login_sequence():
|
||||
#
|
||||
# - easy to brick units here, so catch and ignore errors where possible/appropriate
|
||||
#
|
||||
from ux import idle_logout
|
||||
from ux import idle_logout, ux_login_countdown
|
||||
from glob import dis
|
||||
import callgate
|
||||
|
||||
@ -841,7 +813,7 @@ async def start_login_sequence():
|
||||
if delay:
|
||||
# kill some time, with countdown, and get "the" PIN again for real login
|
||||
pa.reset()
|
||||
await login_countdown(delay * (60 if not version.is_devmode else 1))
|
||||
await ux_login_countdown(delay * (60 if not version.is_devmode else 1))
|
||||
|
||||
# keep it simple for Mk4+: just challenge again for any PIN
|
||||
# - if it's the same countdown pin, it will be accepted and they
|
||||
|
||||
@ -12,7 +12,6 @@ from mk4 import dev_enable_repl
|
||||
from multisig import make_multisig_menu, import_multisig_nfc
|
||||
from seed import make_ephemeral_seed_menu, make_seed_vault_menu
|
||||
from address_explorer import address_explore
|
||||
from users import make_users_menu
|
||||
from drv_entro import drv_entro_start, password_entry
|
||||
from backups import clone_start, clone_write_data
|
||||
from xor_seed import xor_split_start, xor_restore_start
|
||||
@ -23,13 +22,15 @@ from trick_pins import TrickPinMenu
|
||||
|
||||
|
||||
# Optional feature: HSM, depends on hardware
|
||||
# - code for HSM support wont exist on other version, so dont call it
|
||||
# - code for HSM support wont exist on some platforms, so dont call it
|
||||
if version.supports_hsm:
|
||||
from hsm import hsm_policy_available
|
||||
from users import make_users_menu
|
||||
hsm_feature = lambda: True
|
||||
else:
|
||||
hsm_policy_available = lambda: False
|
||||
hsm_feature = lambda: False
|
||||
make_users_menu = lambda: []
|
||||
|
||||
trick_pin_menu = TrickPinMenu.make_menu
|
||||
|
||||
|
||||
@ -11,19 +11,16 @@ from graphics import Graphics as obsoleteGraphics
|
||||
import sram2
|
||||
from st7788 import ST7788
|
||||
|
||||
# we support 4 fonts
|
||||
from zevvpeep import FontSmall, FontLarge, FontTiny
|
||||
FontFixed = object() # ugly 8x8 PET font
|
||||
|
||||
# the one font: fixed-width (except for a few double-width chars)
|
||||
from font_iosevka import CELL_W, CELL_H, TEXT_PALETTE, TEXT_PALETTE_INV
|
||||
from font_iosevka import FontIosevka
|
||||
|
||||
# free unused screen buffers, we will make bigger ones
|
||||
# free unused screen buffers, we don't work that way
|
||||
del sram2.display_buf
|
||||
del sram2.display2_buf
|
||||
|
||||
# one byte per pixel; fixed palette maps to BGR565 in C code
|
||||
display2_buf = bytearray(320 * 240)
|
||||
#display2_buf = bytearray(320 * 240)
|
||||
|
||||
#WIDTH = const(320)
|
||||
#HEIGHT = const(240)
|
||||
@ -106,7 +103,7 @@ class Display:
|
||||
def draw_status(self, full=False, **kws):
|
||||
if full:
|
||||
y = TOP_MARGIN
|
||||
self.dis.fill_rect(0, 0, WIDTH, y-2, 0x0)
|
||||
self.dis.fill_rect(0, 0, WIDTH, y-1, 0x0)
|
||||
self.dis.fill_rect(0, y-1, WIDTH, 1, grey_level(0.25))
|
||||
kws = get_sys_status()
|
||||
|
||||
@ -132,10 +129,7 @@ class Display:
|
||||
self.image(x, 0, '%s_%d' % (meta, kws[meta]))
|
||||
|
||||
def width(self, msg, font):
|
||||
if font == FontFixed:
|
||||
return len(msg) * 8
|
||||
else:
|
||||
return sum(font.lookup(ord(ch)).w for ch in msg)
|
||||
return sum(font.lookup(ord(ch)).w for ch in msg)
|
||||
|
||||
def image(self, x, y, name):
|
||||
# display a graphics image, immediately
|
||||
@ -148,45 +142,6 @@ class Display:
|
||||
# XXX plan is these are chars or images
|
||||
return 10, 10
|
||||
|
||||
def XXX_text(self, x,y, msg, font=FontSmall, invert=0):
|
||||
# Draw at x,y (top left corner of first letter)
|
||||
# using font. Use invert=1 to get reverse video
|
||||
|
||||
if x is None or x < 0:
|
||||
# center/rjust
|
||||
w = self.width(msg, font)
|
||||
if x == None:
|
||||
x = max(0, (WIDTH - w) // 2)
|
||||
else:
|
||||
# measure from right edge (right justify)
|
||||
x = max(0, WIDTH - w + 1 + x)
|
||||
|
||||
if y < 0:
|
||||
# measure up from bottom edge
|
||||
y = HEIGHT - font.height + 1 + y
|
||||
|
||||
if font == FontFixed:
|
||||
# use font provided by Micropython: 8x8
|
||||
self.dis.text(msg, x, y)
|
||||
|
||||
return x + (len(msg) * 8)
|
||||
|
||||
for ch in msg:
|
||||
fn = font.lookup(ord(ch))
|
||||
if fn is None:
|
||||
# use last char in font as error char for junk we don't
|
||||
# know how to render
|
||||
fn = font.lookup(font.code_range.stop)
|
||||
bits = bytearray(fn.w * fn.h)
|
||||
bits[0:len(fn.bits)] = fn.bits
|
||||
if invert:
|
||||
bits = bytearray(i^0xff for i in bits)
|
||||
gly = framebuf.FrameBuffer(bits, fn.w, fn.h, framebuf.MONO_HLSB)
|
||||
self.dis.blit(gly, x, y, invert)
|
||||
x += fn.w
|
||||
|
||||
return x
|
||||
|
||||
def text(self, x,y, msg, font=None, invert=0):
|
||||
# Draw at x,y (in cell positions, not pixels)
|
||||
# Use invert=1 to get reverse video
|
||||
@ -228,8 +183,7 @@ class Display:
|
||||
if x >= WIDTH: break
|
||||
|
||||
def clear(self):
|
||||
# fill to black, but only text area
|
||||
# - not status bar
|
||||
# fill to black, but only text area, not status bar
|
||||
self.dis.fill_rect(0, TOP_MARGIN, WIDTH, HEIGHT-TOP_MARGIN, 0x0)
|
||||
|
||||
def clear_rect(self, x,y, w,h):
|
||||
@ -242,10 +196,11 @@ class Display:
|
||||
pass
|
||||
|
||||
# rather than clearing and redrawing, use this buffer w/ fixed parts of screen
|
||||
# - obsolete concept
|
||||
def save(self):
|
||||
display2_buf[:] = self.dis.buffer
|
||||
pass
|
||||
def restore(self):
|
||||
self.dis.buffer[:] = display2_buf
|
||||
pass
|
||||
|
||||
def hline(self, y):
|
||||
self.dis.fill_rect(0,y, WIDTH, 1, 0xffff)
|
||||
@ -435,7 +390,7 @@ class Display:
|
||||
# centered text under that
|
||||
y = CHARS_H - num_lines
|
||||
for line in parts:
|
||||
self.text(None, y, line, FontTiny)
|
||||
self.text(None, y, line)
|
||||
y += 1
|
||||
|
||||
if idx_hint:
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#
|
||||
import pincodes, version, random
|
||||
from glob import dis
|
||||
from display import FontLarge, FontTiny
|
||||
from ux import PressRelease, ux_wait_keyup, ux_show_story, ux_show_pin
|
||||
from callgate import show_logout
|
||||
from pincodes import pa
|
||||
@ -70,6 +69,7 @@ class LoginUX:
|
||||
dis.text(None, -1, "CANCEL or SELECT to continue")
|
||||
else:
|
||||
# Old style
|
||||
from display import FontLarge, FontTiny
|
||||
y = 15
|
||||
x = 18
|
||||
dis.text(x, y, words[0], FontLarge)
|
||||
|
||||
@ -16,7 +16,7 @@ assert not glob.dis, "main reimport"
|
||||
# this makes the GC run when larger objects are free in an attempt to reduce fragmentation.
|
||||
gc.threshold(4096)
|
||||
|
||||
if 0:
|
||||
if 1: #XXX
|
||||
# useful for debug: keep this stub!
|
||||
import ckcc
|
||||
ckcc.vcp_enabled(True)
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
# menu.py - Implement an interactive menu system.
|
||||
#
|
||||
import gc
|
||||
from display import FontLarge, FontTiny, Display
|
||||
from ux import PressRelease, the_ux
|
||||
from uasyncio import sleep_ms
|
||||
from charcodes import (KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME,
|
||||
|
||||
@ -253,9 +253,9 @@ individual words if you wish.''')
|
||||
|
||||
def late_draw(self, dis):
|
||||
# add an overlay with "word N" in small text, top right.
|
||||
from display import FontTiny
|
||||
if dis.has_lcd: return # unreachable anyway?
|
||||
|
||||
if dis.has_lcd: return # unreachable?
|
||||
from display import FontTiny
|
||||
|
||||
count = len(self.words)
|
||||
if count >= self.target_words:
|
||||
@ -303,6 +303,7 @@ async def show_words(words, prompt=None, escape=None, extra='', ephemeral=False)
|
||||
|
||||
async def add_dice_rolls(count, seed, judge_them, nwords=None, enforce=False):
|
||||
from glob import dis
|
||||
# XXX q1 support
|
||||
from display import FontTiny, FontLarge
|
||||
|
||||
low_entropy_msg = "You only provided %d dice rolls, and each roll adds only 2.585 bits of entropy."
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
import ckcc
|
||||
from uasyncio import sleep_ms
|
||||
from glob import dis
|
||||
from display import FontLarge
|
||||
from ux import ux_wait_keyup, ux_clear_keys, ux_poll_key
|
||||
from ux import ux_show_story
|
||||
from callgate import get_is_bricked, get_genuine, clear_genuine
|
||||
@ -14,6 +13,11 @@ import version
|
||||
from glob import settings
|
||||
from charcodes import KEY_SELECT, KEY_CANCEL
|
||||
|
||||
try:
|
||||
from display import FontLarge
|
||||
except ImportError:
|
||||
FontLarge = None
|
||||
|
||||
async def wait_ok():
|
||||
k = await ux_wait_keyup('xy' + KEY_SELECT + KEY_CANCEL)
|
||||
if k not in 'y' + KEY_SELECT:
|
||||
@ -51,6 +55,12 @@ async def test_keyboard():
|
||||
# XXX
|
||||
pass
|
||||
|
||||
async def test_qr_scanner():
|
||||
# QR Scanner module: assume pretested, just testing connection
|
||||
from glob import SCAN
|
||||
assert SCAN
|
||||
assert glob.SCAN.version.startswith('V2.3.')
|
||||
|
||||
def set_genuine():
|
||||
# PIN must be blank for this to work
|
||||
# - or logged in already as main
|
||||
@ -309,6 +319,8 @@ async def start_selftest():
|
||||
await test_keyboard()
|
||||
else:
|
||||
await test_numpad()
|
||||
if version.has_qr:
|
||||
await test_qr_scanner()
|
||||
await test_secure_element()
|
||||
await test_sd_active()
|
||||
await test_usb_light()
|
||||
|
||||
@ -22,7 +22,8 @@ RAMWR = const(0x2c)
|
||||
# - maybe: with QR module expansion?
|
||||
# - clear to pixel value
|
||||
# - palette + xy/wh + nible-packed palette lookup (for font)
|
||||
from ckcc import lcd_blast
|
||||
# - see stm32/COLDCARD_Q1/modlcd.c for code
|
||||
import lcd
|
||||
|
||||
class ST7788():
|
||||
def __init__(self):
|
||||
@ -32,9 +33,9 @@ class ST7788():
|
||||
from pyb import Timer # not from machine
|
||||
|
||||
self.spi = machine.SPI(1, baudrate=60_000_000, polarity=0, phase=0)
|
||||
#reset_pin = Pin('PA6', Pin.OUT) # not using
|
||||
self.dc = Pin('PA8', Pin.OUT, value=0)
|
||||
self.cs = Pin('PA4', Pin.OUT, value=1)
|
||||
#reset_pin = Pin('LCD_RESET', Pin.OUT) # not using
|
||||
self.dc = Pin('LCD_DATA_CMD', Pin.OUT, value=0)
|
||||
self.cs = Pin('LCD_CS', Pin.OUT, value=1)
|
||||
|
||||
if 0:
|
||||
# BUST - just fades away
|
||||
@ -47,9 +48,6 @@ class ST7788():
|
||||
# for framebuf.FrameBuffer
|
||||
self.width = 320
|
||||
self.height = 240
|
||||
#self.buffer = bytearray(320*240)
|
||||
|
||||
#super().__init__(self.buffer, self.width, self.height, framebuf.GS8)
|
||||
|
||||
def write_cmd(self, cmd, args=None):
|
||||
# send a command byte and a number of arguments
|
||||
@ -72,18 +70,6 @@ class ST7788():
|
||||
self.spi.write(buf)
|
||||
self.cs(1)
|
||||
|
||||
def write_pixel_data(self, buf):
|
||||
# lcd_blast expands 1-byte per pixel to BGR565
|
||||
self.cs(1)
|
||||
self.dc(1)
|
||||
self.cs(0)
|
||||
try:
|
||||
lcd_blast(self.spi, buf)
|
||||
except:
|
||||
print('lcd_blast fail')
|
||||
self.cs(1)
|
||||
|
||||
|
||||
def _set_window(self, x, y, w=320, h=240):
|
||||
#self.write_cmd(0x2a, 0, LCD_WIDTH-1) # CASET - Column address set range (x)
|
||||
#self.write_cmd(0x2b, y, LCD_HEIGHT-1) # RASET - Row address set range (y)
|
||||
@ -97,33 +83,52 @@ class ST7788():
|
||||
|
||||
# .. follow with w*h*2 bytes of pixel data
|
||||
|
||||
def show_partial(self, y, h):
|
||||
# update just a few rows of the display
|
||||
assert h >= 1
|
||||
self._set_window(0, y, h=h)
|
||||
rows = memoryview(self.buffer)[320*y:320*(y+h)]
|
||||
self.write_pixel_data(rows)
|
||||
def fill_screen(self, pixel=0x0000):
|
||||
# clear ENTIRE screen to indicated pixel value
|
||||
self.fill_rect(0,0, 320, 240, pixel)
|
||||
|
||||
def show_zpixels(self, x, y, w, h, zpixels):
|
||||
# display compressed pixel data
|
||||
print('st7788.show_zpixels ... write me')
|
||||
# display compressed pixel data, used for images/icons
|
||||
# - keeping in mpy since C version would be same speed
|
||||
data = uzlib.decompress(zpixels, -10)
|
||||
self._set_window(x, y, w, h)
|
||||
self.write_data(data)
|
||||
|
||||
def show_pal_pixels(self, x, y, w, h, palette, pixels):
|
||||
# show 4-bit packed paletted lookup pixels; used for fonts, icons
|
||||
# show 4-bit packed paletted lookup pixels; used for fonts
|
||||
assert len(palette) == 2 * 16
|
||||
if 0:
|
||||
buf = bytearray()
|
||||
for here in pixels:
|
||||
px1 = (here >> 4) * 2
|
||||
px2 = (here & 0xf) * 2
|
||||
buf.extend(palette[px1:px1+2])
|
||||
buf.extend(palette[px2:px2+2])
|
||||
|
||||
def show(self):
|
||||
# send entire frame buffer
|
||||
self._set_window(0, 0)
|
||||
self.write_pixel_data(self.buffer)
|
||||
if (w*h) % 2 == 1:
|
||||
buf = memoryview(buf[0:-2])
|
||||
|
||||
self._set_window(x, y, w, h)
|
||||
self.write_data(buf)
|
||||
else:
|
||||
lcd.send_packed(self.spi, x, y, w, h, palette, pixels)
|
||||
|
||||
def show_qr_data(self, x, y, w, expand, scan_w, packed_data):
|
||||
# 8-bit packed QR data, and where to draw it, expanded by 'expand'
|
||||
assert len(packed_data) == (scan_w*w) // 8
|
||||
# XXX write me
|
||||
|
||||
def fill_rect(self, x,y, w,h, pixel=0x0000):
|
||||
# need C code
|
||||
pass
|
||||
|
||||
def fill_screen(self, pixel=0x0000):
|
||||
# clear screen to indicated pixel value
|
||||
# XXX need C code
|
||||
pass
|
||||
# set a rectangle to a single colour
|
||||
if not w or not h: return
|
||||
if 0:
|
||||
assert h >= 1 and w >= 1
|
||||
pixel = struct.pack('>H', pixel)
|
||||
ln = pixel * w
|
||||
self._set_window(x, y, w, h)
|
||||
for y in range(h):
|
||||
self.write_data(ln)
|
||||
else:
|
||||
lcd.fill_rect(self.spi, x, y, w, h, pixel)
|
||||
|
||||
# EOF
|
||||
|
||||
@ -18,12 +18,14 @@ if version.has_qwerty:
|
||||
CH_PER_W = CHARS_W
|
||||
STORY_H = CHARS_H
|
||||
from ux_q1 import PressRelease, ux_enter_number, ux_input_numbers, ux_input_text, ux_show_pin
|
||||
from ux_q1 import ux_login_countdown
|
||||
else:
|
||||
# How many characters can we fit on each line? How many lines?
|
||||
# (using FontSmall)
|
||||
CH_PER_W = 17
|
||||
STORY_H = 5
|
||||
from ux_mk4 import PressRelease, ux_enter_number, ux_input_numbers, ux_input_text, ux_show_pin
|
||||
from ux_mk4 import ux_login_countdown
|
||||
|
||||
class UserInteraction:
|
||||
def __init__(self):
|
||||
|
||||
@ -425,4 +425,37 @@ def ux_show_pin(dis, pin, subtitle, is_first_part, is_confirmation, force_draw,
|
||||
|
||||
dis.show()
|
||||
|
||||
async def ux_login_countdown(sec):
|
||||
# Show a countdown, which may need to
|
||||
# run for multiple **days**
|
||||
from glob import dis
|
||||
from display import FontSmall, FontLarge
|
||||
from utime import ticks_ms, ticks_diff
|
||||
|
||||
# pre-render fixed parts
|
||||
dis.clear()
|
||||
y = 0
|
||||
dis.text(None, y, 'Login countdown in', font=FontSmall); y += 14
|
||||
dis.text(None, y, 'effect. Must wait:', font=FontSmall); y += 14
|
||||
y += 5
|
||||
dis.save()
|
||||
|
||||
st = ticks_ms()
|
||||
while sec > 0:
|
||||
dis.restore()
|
||||
dis.text(None, y, pretty_short_delay(sec), font=FontLarge)
|
||||
|
||||
dis.show()
|
||||
dis.busy_bar(1)
|
||||
|
||||
# this should be more accurate, errors were accumulating
|
||||
now = ticks_ms()
|
||||
dt = 1000 - ticks_diff(now, st)
|
||||
await sleep_ms(dt)
|
||||
st = ticks_ms()
|
||||
|
||||
sec -= 1
|
||||
|
||||
dis.busy_bar(0)
|
||||
|
||||
# EOF
|
||||
|
||||
@ -219,5 +219,31 @@ def ux_show_pin(dis, pin, subtitle, is_first_part, is_confirmation, force_draw,
|
||||
dis.text(10, y+2, msg)
|
||||
dis.show()
|
||||
|
||||
async def ux_login_countdown(sec):
|
||||
# Show a countdown, which may need to
|
||||
# run for multiple **days**
|
||||
# XXX untested
|
||||
from glob import dis
|
||||
from utime import ticks_ms, ticks_diff
|
||||
|
||||
y = 4
|
||||
dis.clear()
|
||||
dis.text(None, y-2, "Login countdown in effect.", invert=1)
|
||||
dis.text(None, y-1, "Must wait:")
|
||||
|
||||
st = ticks_ms()
|
||||
while sec > 0:
|
||||
dis.text(None, y, pretty_short_delay(sec))
|
||||
dis.busy_bar(1)
|
||||
|
||||
# this should be more accurate, errors were accumulating
|
||||
now = ticks_ms()
|
||||
dt = 1000 - ticks_diff(now, st)
|
||||
await sleep_ms(dt)
|
||||
st = ticks_ms()
|
||||
|
||||
sec -= 1
|
||||
|
||||
dis.busy_bar(0)
|
||||
|
||||
# EOF
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
//
|
||||
// AUTO-generated.
|
||||
//
|
||||
// built: 2023-09-08
|
||||
// version: 5.1.4
|
||||
// built: 2023-06-05
|
||||
// version: 6.0.1
|
||||
//
|
||||
#include <stdint.h>
|
||||
|
||||
// this overrides ports/stm32/fatfs_port.c
|
||||
uint32_t get_fattime(void) {
|
||||
return 0x57282820UL;
|
||||
return 0x56c53000UL;
|
||||
}
|
||||
|
||||
@ -249,68 +249,6 @@ STATIC mp_obj_t watchpoint(volatile mp_obj_t arg1)
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(watchpoint_obj, watchpoint);
|
||||
|
||||
#define SWAB16(n) (( ((n)>>8) | ((n) << 8) )&0xffff)
|
||||
#define GREY(n) SWAB16( (n<<11) | (n<<5) | n)
|
||||
|
||||
// BGR565 values, but wrong endian, so green split weird
|
||||
static uint16_t palette[16] = {
|
||||
0x0000, // 0 => black
|
||||
0xffff, // 1 => white
|
||||
|
||||
SWAB16(0xf800), // 2 => red
|
||||
SWAB16(0x07e0), // 3 => green
|
||||
SWAB16(0x001f), // 4 => blue
|
||||
|
||||
// some greys: 5 .. 12
|
||||
GREY(5), GREY(9), GREY(13), GREY(17), GREY(21), GREY(25), GREY(29),
|
||||
|
||||
0x2000, // tbd
|
||||
0x4000, // tbd
|
||||
0x8000, // tbd
|
||||
|
||||
// Coinkite brand colour? ie. orange rgb(241, 100, 34) => rgb(.945, .392, .133)
|
||||
// XXX needs work -- too red
|
||||
SWAB16((29 << 11) | (25<<5) | 4),
|
||||
};
|
||||
|
||||
STATIC mp_obj_t lcd_blast(mp_obj_t spi_arg, mp_obj_t buf_arg)
|
||||
{
|
||||
// Just send a bunch of bytes, expanded via fixed palette to the LCD
|
||||
// over SPI. CS/CMD_vs_DATA must already be set correctly, and cleared
|
||||
// at end... we are just reformatting and sending the pixel data.
|
||||
mp_buffer_info_t buf;
|
||||
mp_get_buffer_raise(buf_arg, &buf, MP_BUFFER_READ);
|
||||
|
||||
const spi_t *spi = spi_from_mp_obj(spi_arg);
|
||||
|
||||
int len = buf.len;
|
||||
if((len < 1) || (len > 320*240)) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
|
||||
const uint8_t *pixels = buf.buf;
|
||||
|
||||
// working buffer
|
||||
const int max_rows = 30;
|
||||
uint16_t fb[max_rows * 320]; // largish: 19.2k
|
||||
|
||||
while(len) {
|
||||
uint32_t here = 0;
|
||||
|
||||
for(; len && here < (sizeof(fb)/2); len--, here++, pixels++) {
|
||||
fb[here] = palette[(*pixels) & 0xf];
|
||||
}
|
||||
|
||||
if(!here) break;
|
||||
|
||||
// send what we have
|
||||
spi_transfer(spi, here*2, (const uint8_t *)fb, NULL, SPI_TRANSFER_TIMEOUT(here*2));
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(lcd_blast_obj, lcd_blast);
|
||||
|
||||
// See psram.c
|
||||
extern const mp_obj_type_t psram_type;
|
||||
|
||||
@ -330,7 +268,6 @@ STATIC const mp_rom_map_elem_t ckcc_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_stack_limit), MP_ROM_PTR(&stack_limit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_usb_active), MP_ROM_PTR(&usb_active_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PSRAM), MP_ROM_PTR(&psram_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_lcd_blast), MP_ROM_PTR(&lcd_blast_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(ckcc_module_globals, ckcc_module_globals_table);
|
||||
|
||||
173
stm32/COLDCARD_Q1/modlcd.c
Normal file
173
stm32/COLDCARD_Q1/modlcd.c
Normal file
@ -0,0 +1,173 @@
|
||||
//
|
||||
// (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
//
|
||||
// modlcd.c - module for driving the Q1 LCD fastly.
|
||||
//
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "bufhelper.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/mpstate.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "boardctrl.h"
|
||||
#include "spi.h"
|
||||
|
||||
#include "extint.h"
|
||||
|
||||
#define PIN_LCD_TEAR pin_B11
|
||||
#define PIN_LCD_CS pin_A4
|
||||
#define PIN_LCD_SCLK pin_A5
|
||||
#define PIN_LCD_RESET pin_A6
|
||||
#define PIN_LCD_MOSI pin_A7
|
||||
#define PIN_LCD_DATA_CMD pin_A8
|
||||
|
||||
// few key commands for this display
|
||||
#define CASET 0x2a
|
||||
#define RASET 0x2b
|
||||
#define RAMWR 0x2c
|
||||
|
||||
|
||||
#define SWAB16(n) (( ((n)>>8) | ((n) << 8) )&0xffff)
|
||||
|
||||
static inline void write_cmd(const spi_t *spi, uint8_t cmd)
|
||||
{
|
||||
// write a command byte
|
||||
mp_hal_pin_write(PIN_LCD_CS, 1);
|
||||
mp_hal_pin_write(PIN_LCD_DATA_CMD, 0);
|
||||
mp_hal_pin_write(PIN_LCD_CS, 0);
|
||||
|
||||
spi_transfer(spi, 1, (const uint8_t *)&cmd, NULL, SPI_TRANSFER_TIMEOUT(1));
|
||||
|
||||
mp_hal_pin_write(PIN_LCD_CS, 1);
|
||||
}
|
||||
|
||||
static inline void write_cmd2(const spi_t *spi, uint8_t cmd, uint16_t arg1, uint16_t arg2)
|
||||
{
|
||||
// Write a command byte, followed by 2 big-endian 16 bit arguments.
|
||||
uint16_t args[2] = { SWAB16(arg1), SWAB16(arg2)};
|
||||
|
||||
mp_hal_pin_write(PIN_LCD_CS, 1);
|
||||
mp_hal_pin_write(PIN_LCD_DATA_CMD, 0);
|
||||
mp_hal_pin_write(PIN_LCD_CS, 0);
|
||||
|
||||
//spi_transfer(spi, 1, (const uint8_t *)&cmd, NULL, SPI_TRANSFER_TIMEOUT(1));
|
||||
HAL_SPI_Transmit(spi->spi, (uint8_t *)&cmd, 1, SPI_TRANSFER_TIMEOUT(1));
|
||||
|
||||
mp_hal_pin_write(PIN_LCD_DATA_CMD, 1);
|
||||
|
||||
// faster to avoid DMA for little transfers, so do that
|
||||
//spi_transfer(spi, 4, (const uint8_t *)&args, NULL, SPI_TRANSFER_TIMEOUT(4));
|
||||
HAL_SPI_Transmit(spi->spi, (uint8_t *)&args, 4, SPI_TRANSFER_TIMEOUT(4));
|
||||
|
||||
mp_hal_pin_write(PIN_LCD_CS, 1);
|
||||
}
|
||||
|
||||
|
||||
static void write_data(const spi_t *spi, int len, const uint8_t *data)
|
||||
{
|
||||
// Send a bunch of data, like pixel data.
|
||||
mp_hal_pin_write(PIN_LCD_CS, 1);
|
||||
mp_hal_pin_write(PIN_LCD_DATA_CMD, 1);
|
||||
mp_hal_pin_write(PIN_LCD_CS, 0);
|
||||
|
||||
spi_transfer(spi, len, data, NULL, SPI_TRANSFER_TIMEOUT(len));
|
||||
|
||||
mp_hal_pin_write(PIN_LCD_CS, 1);
|
||||
}
|
||||
|
||||
static void set_window(const spi_t *spi, int x, int y, int w, int h)
|
||||
{
|
||||
// set active window; controls where pixel data will show up on screen
|
||||
write_cmd2(spi, CASET, x, x+w-1);
|
||||
write_cmd2(spi, RASET, y, y+h-1);
|
||||
write_cmd(spi, RAMWR); // RAMWR - memory write, implies data to follow
|
||||
}
|
||||
|
||||
|
||||
STATIC mp_obj_t send_packed(size_t n_args, const mp_obj_t *args)
|
||||
{
|
||||
// take 4-bit packed palette-ized data, unpack and send
|
||||
// signature: spi, x, y, w, h, pal, pixels
|
||||
|
||||
const spi_t *spi = spi_from_mp_obj(args[0]);
|
||||
|
||||
mp_int_t x = mp_obj_get_int(args[1]);
|
||||
mp_int_t y = mp_obj_get_int(args[2]);
|
||||
mp_int_t w = mp_obj_get_int(args[3]);
|
||||
mp_int_t h = mp_obj_get_int(args[4]);
|
||||
|
||||
mp_buffer_info_t palette;
|
||||
mp_get_buffer_raise(args[5], &palette, MP_BUFFER_READ);
|
||||
mp_buffer_info_t pixels;
|
||||
mp_get_buffer_raise(args[6], &pixels, MP_BUFFER_READ);
|
||||
|
||||
if(palette.len != 16*2) mp_raise_ValueError(NULL);
|
||||
const uint8_t *pal = palette.buf;
|
||||
|
||||
// working buffer
|
||||
uint8_t fb[(w * h * 2) + 4]; // may write one extra bogus pixel for odd w*h cases
|
||||
const uint8_t *p = pixels.buf;
|
||||
uint8_t *o = fb;
|
||||
for(int i=0; i<pixels.len; i++, p++, o+=4) {
|
||||
uint8_t px1 = (*p >> 4) * 2;
|
||||
uint8_t px2 = (*p & 0xf) * 2;
|
||||
o[0] = pal[px1];
|
||||
o[1] = pal[px1+1];
|
||||
o[2] = pal[px2];
|
||||
o[3] = pal[px2+1];
|
||||
}
|
||||
|
||||
set_window(spi, x, y, w, h);
|
||||
write_data(spi, w*h*2, (const uint8_t *)fb);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(send_packed_obj, 7, 7, send_packed);
|
||||
|
||||
STATIC mp_obj_t fill_rect(size_t n_args, const mp_obj_t *args)
|
||||
{
|
||||
// write the same pixel value to a region
|
||||
// signature: spi, x, y, w, h, pixel_value
|
||||
const spi_t *spi = spi_from_mp_obj(args[0]);
|
||||
|
||||
mp_int_t x = mp_obj_get_int(args[1]);
|
||||
mp_int_t y = mp_obj_get_int(args[2]);
|
||||
mp_int_t w = mp_obj_get_int(args[3]);
|
||||
mp_int_t h = mp_obj_get_int(args[4]);
|
||||
mp_int_t pixel = mp_obj_get_int(args[5]);
|
||||
|
||||
uint16_t line[w];
|
||||
for(int i=0; i<w; i++) {
|
||||
line[i] = SWAB16(pixel);
|
||||
}
|
||||
|
||||
set_window(spi, x, y, w, h);
|
||||
for(int y=0; y<h; y++) {
|
||||
write_data(spi, w*2, (const uint8_t *)line);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fill_rect_obj, 6, 6, fill_rect);
|
||||
|
||||
STATIC const mp_rom_map_elem_t lcd_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_lcd) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_send_packed), MP_ROM_PTR(&send_packed_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&fill_rect_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(lcd_module_globals, lcd_module_globals_table);
|
||||
|
||||
const mp_obj_module_t lcd_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&lcd_module_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_lcd, lcd_module, 1);
|
||||
|
||||
|
||||
// EOF
|
||||
@ -26,7 +26,7 @@ USER_C_MODULES = boards/$(BOARD)/c-modules
|
||||
# - do not want contents of stm32/boards/manifest.py
|
||||
FROZEN_MANIFEST = \
|
||||
boards/$(BOARD)/shared/manifest.py \
|
||||
boards/$(BOARD)/shared/manifest_mk4.py
|
||||
boards/$(BOARD)/shared/manifest_q1.py
|
||||
|
||||
# This will relocate things up by 128k=0x2_0000
|
||||
# see also ./layout.ld
|
||||
@ -73,18 +73,18 @@ checksum:
|
||||
COPT += -g
|
||||
|
||||
# bugfix IIRC
|
||||
build-COLDCARD_MK4/boards/COLDCARD_MK4/modckcc.o: COPT = -O0 -DNDEBUG
|
||||
build-COLDCARD_Q1/boards/COLDCARD_Q1/modckcc.o: COPT = -O0 -DNDEBUG
|
||||
|
||||
# pickiness
|
||||
build-COLDCARD_MK4/dma.o: COPT=-Werror=unused-const-variable=0
|
||||
build-COLDCARD_MK4/boards/COLDCARD_MK4/psramdisk.o: COPT=-Werror=unused-const-variable=0
|
||||
build-COLDCARD_Q1/dma.o: COPT=-Werror=unused-const-variable=0
|
||||
build-COLDCARD_Q1/boards/COLDCARD_Q1/psramdisk.o: COPT=-Werror=unused-const-variable=0
|
||||
|
||||
# bugfix: remove unwanted setup code called from ports/stm32/resethandler.s
|
||||
build-COLDCARD_MK4/lib/stm32lib/CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.o: \
|
||||
build-COLDCARD_Q1/lib/stm32lib/CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.o: \
|
||||
CFLAGS += -DSystemInit=SystemInit_OMIT
|
||||
|
||||
# bugfix: replace keyboard interrupt handling
|
||||
build-COLDCARD_MK4/lib/utils/interrupt_char.o: \
|
||||
build-COLDCARD_Q1/lib/utils/interrupt_char.o: \
|
||||
CFLAGS += -Dmp_hal_set_interrupt_char=mp_hal_set_interrupt_char_OMIT
|
||||
|
||||
|
||||
|
||||
1
stm32/COLDCARD_Q1/shared
Symbolic link
1
stm32/COLDCARD_Q1/shared
Symbolic link
@ -0,0 +1 @@
|
||||
../../shared
|
||||
@ -2,10 +2,13 @@
|
||||
#
|
||||
# Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images.
|
||||
#
|
||||
# XXX obsolete, not supported but kept as reference?
|
||||
#
|
||||
include version.mk
|
||||
|
||||
BOARD = COLDCARD
|
||||
MK_NUM = 3
|
||||
HW_MODEL = mk3
|
||||
PARENT_MKFILE = MK3-Makefile
|
||||
|
||||
# These values used to make .DFU files. Flash memory locations.
|
||||
FIRMWARE_BASE = 0x08008000
|
||||
|
||||
@ -9,7 +9,8 @@ include version.mk
|
||||
BOARD = COLDCARD_MK4
|
||||
FIRMWARE_BASE = 0x08020000
|
||||
BOOTLOADER_BASE = 0x08000000
|
||||
MK_NUM = 4
|
||||
HW_MODEL = mk4
|
||||
PARENT_MKFILE = MK4-Makefile
|
||||
|
||||
# This is release of the bootloader that will be built into the release firmware.
|
||||
BOOTLOADER_VERSION = 3.1.4
|
||||
|
||||
@ -1,25 +1,21 @@
|
||||
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Placeholder for lazy devs. Builds debug version of Mk4 by default. For a few specific
|
||||
# goals, will do it for both Mk3 and Mk4.
|
||||
# Placeholder for lazy devs. Builds debug version of Q1 by default. For a few specific
|
||||
# goals, will do it for all supported platforms.
|
||||
#
|
||||
# Normally you should use:
|
||||
#
|
||||
# make -f MK4-Makefile
|
||||
# or
|
||||
# make -f MK3-Makefile
|
||||
# make -f Q1-Makefile
|
||||
#
|
||||
|
||||
.DEFAULT:
|
||||
$(MAKE) DEBUG_BUILD=1 -f MK4-Makefile $(MAKECMDGOALS)
|
||||
$(MAKE) DEBUG_BUILD=1 -f Q1-Makefile $(MAKECMDGOALS)
|
||||
|
||||
|
||||
clean clobber rc1:
|
||||
clean clobber rc1 release repro:
|
||||
$(MAKE) -f Q1-Makefile $(MAKECMDGOALS)
|
||||
$(MAKE) -f MK4-Makefile $(MAKECMDGOALS)
|
||||
$(MAKE) -f MK3-Makefile $(MAKECMDGOALS)
|
||||
|
||||
release repro:
|
||||
$(MAKE) -f MK4-Makefile $(MAKECMDGOALS)
|
||||
#NOTYET#$(MAKE) -f MK3-Makefile $(MAKECMDGOALS)
|
||||
|
||||
# EOF
|
||||
|
||||
55
stm32/Q1-Makefile
Normal file
55
stm32/Q1-Makefile
Normal file
@ -0,0 +1,55 @@
|
||||
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images.
|
||||
#
|
||||
# Q1 .. mostly same as Mk4
|
||||
#
|
||||
include version.mk
|
||||
|
||||
BOARD = COLDCARD_Q1
|
||||
FIRMWARE_BASE = 0x08020000
|
||||
BOOTLOADER_BASE = 0x08000000
|
||||
HW_MODEL = q1
|
||||
PARENT_MKFILE = Q1-Makefile
|
||||
|
||||
# This is release of the bootloader that will be built into the release firmware.
|
||||
BOOTLOADER_VERSION = 1.0.0
|
||||
BOOTLOADER_DIR = q1-bootloader
|
||||
|
||||
LATEST_RELEASE = $(shell ls -t1 ../releases/*-q1-*.dfu | head -1)
|
||||
|
||||
# keep near top, because defined default target (all)
|
||||
include shared.mk
|
||||
|
||||
# This is fast for Coinkite devs, but no DFU support in the wild.
|
||||
dfu-up: dev.dfu
|
||||
echo 'dfu' | nc localhost 4444
|
||||
$(PYTHON_DO_DFU) -u dev.dfu
|
||||
|
||||
dfu-up2:
|
||||
$(PYTHON_DO_DFU) -u dev.dfu
|
||||
|
||||
# Super fast, assumes Coldcard already attached and unlock on this Mac.
|
||||
up: dev.dfu
|
||||
cp dev.dfu /Volumes/COLDCARD/.
|
||||
diskutil eject /Volumes/COLDCARD
|
||||
|
||||
# Fairly fast, assumes openocd already running, and its current directory is here.
|
||||
ocp-up: dev.dfu
|
||||
echo "load_image dev.dfu $(FIRMWARE_BASE) bin; reset run" | nc localhost 4444
|
||||
|
||||
# In another window:
|
||||
#
|
||||
# openocd -f openocd-q1.cfg
|
||||
#
|
||||
# Can do:
|
||||
# - "load" which writes the flash (medium speed, lots of output on st-util)
|
||||
# - "cont" starts/continues system
|
||||
# - "br main" sets breakpoints
|
||||
# - "mon reset" to reset micro
|
||||
# - and so on
|
||||
#
|
||||
debug:
|
||||
arm-none-eabi-gdb $(BUILD_DIR)/firmware.elf -x gogo-q1.gdb
|
||||
|
||||
# EOF
|
||||
1
stm32/openocd-q1.cfg
Symbolic link
1
stm32/openocd-q1.cfg
Symbolic link
@ -0,0 +1 @@
|
||||
openocd-mk4.cfg
|
||||
@ -10,7 +10,8 @@ include version.mk
|
||||
#FIRMWARE_BASE = 0x08020000
|
||||
#BOOTLOADER_BASE = 0x08000000
|
||||
#BOOTLOADER_DIR = mk4-bootloader vs bootloader
|
||||
#MK_NUM = 4
|
||||
#HW_MODEL = mk4
|
||||
#PARENT_MKFILE = xx-Makefile
|
||||
|
||||
MPY_TOP = ../external/micropython
|
||||
PORT_TOP = $(MPY_TOP)/ports/stm32
|
||||
@ -26,7 +27,7 @@ PROD_KEYNUM = -k 1
|
||||
BUILD_DIR = l-port/build-$(BOARD)
|
||||
MAKE_ARGS = BOARD=$(BOARD) -j 4 EXCLUDE_NGU_TESTS=1 DEBUG_BUILD=$(DEBUG_BUILD)
|
||||
|
||||
all: $(BOARD)/file_time.c sigheader.py
|
||||
all: $(BOARD)/file_time.c
|
||||
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS)
|
||||
|
||||
clean:
|
||||
@ -48,7 +49,7 @@ firmware.elf: $(BUILD_DIR)/firmware.elf
|
||||
# Sign and merge various parts
|
||||
#
|
||||
firmware-signed.bin: $(BUILD_DIR)/firmware0.bin $(BUILD_DIR)/firmware1.bin
|
||||
$(SIGNIT) sign -b $(BUILD_DIR) -m $(MK_NUM) $(VERSION_STRING) -o $@
|
||||
$(SIGNIT) sign -b $(BUILD_DIR) -m $(HW_MODEL) $(VERSION_STRING) -o $@
|
||||
firmware-signed.dfu: firmware-signed.bin
|
||||
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):$< $@
|
||||
|
||||
@ -60,7 +61,7 @@ dfu: firmware-signed.dfu
|
||||
.PHONY: dev.dfu
|
||||
dev.dfu: $(BUILD_DIR)/firmware0.bin
|
||||
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS)
|
||||
$(SIGNIT) sign -b $(BUILD_DIR) -m $(MK_NUM) $(VERSION_STRING) $(PROD_KEYNUM) -o dev.bin
|
||||
$(SIGNIT) sign -b $(BUILD_DIR) -m $(HW_MODEL) $(VERSION_STRING) $(PROD_KEYNUM) -o dev.bin
|
||||
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):dev.bin dev.dfu
|
||||
|
||||
.PHONY: relink
|
||||
@ -81,18 +82,13 @@ $(BOARD)/file_time.c: make_filetime.py version.mk
|
||||
./make_filetime.py $(BOARD)/file_time.c $(VERSION_STRING)
|
||||
cp $(BOARD)/file_time.c .
|
||||
|
||||
# Makes the .py from a shared header file
|
||||
# - used by q1/mk4/earlier bootroms, and also signit
|
||||
sigheader.py: make-sigheader.py sigheader.h
|
||||
python3 make-sigheader.py
|
||||
|
||||
# Make a factory release: using key #1
|
||||
# - when executed in a repro w/o the required key, it defaults to key zero
|
||||
# - and that's what happens inside the Docker build
|
||||
production.bin: firmware-signed.bin Makefile
|
||||
$(SIGNIT) sign -m $(MK_NUM) $(VERSION_STRING) -r firmware-signed.bin $(PROD_KEYNUM) -o $@
|
||||
$(SIGNIT) sign -m $(HW_MODEL) $(VERSION_STRING) -r firmware-signed.bin $(PROD_KEYNUM) -o $@
|
||||
|
||||
SUBMAKE = $(MAKE) -f MK$(MK_NUM)-Makefile
|
||||
SUBMAKE = $(MAKE) -f $(PARENT_MKFILE)
|
||||
|
||||
.PHONY: release
|
||||
release: code-committed
|
||||
@ -107,27 +103,25 @@ release: code-committed
|
||||
rc1:
|
||||
$(SUBMAKE) clean # critical, or else you get a mix of debug/not
|
||||
$(SUBMAKE) DEBUG_BUILD=0 all
|
||||
$(SIGNIT) sign -b $(BUILD_DIR) -m $(MK_NUM) $(VERSION_STRING) $(PROD_KEYNUM) -o rc1.bin
|
||||
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):rc1.bin \
|
||||
`signit version rc1.bin`-mk$(MK_NUM)-RC1-coldcard.dfu
|
||||
$(SIGNIT) sign -b $(BUILD_DIR) -m $(HW_MODEL) $(VERSION_STRING) $(PROD_KEYNUM) -o rc1.bin
|
||||
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):rc1.bin \
|
||||
-b $(BOOTLOADER_BASE):$(BOOTLOADER_DIR)/releases/$(BOOTLOADER_VERSION)/bootloader.bin \
|
||||
`signit version rc1.bin`-mk$(MK_NUM)-RC1-coldcard-factory.dfu
|
||||
`signit version rc1.bin`-$(HW_MODEL)-RC1-coldcard.dfu
|
||||
ls -1 *-RC1-*.dfu
|
||||
|
||||
# This target just combines latest version of production firmware with bootrom into a DFU
|
||||
# file, stored in ../releases with appropriately dated file name.
|
||||
.PHONY: release-products
|
||||
release-products: NEW_VERSION = $(shell $(SIGNIT) version built/production.bin)
|
||||
release-products: RELEASE_FNAME = ../releases/$(NEW_VERSION)-mk$(MK_NUM)-coldcard.dfu
|
||||
release-products: RELEASE_FNAME = ../releases/$(NEW_VERSION)-$(HW_MODEL)-coldcard.dfu
|
||||
release-products: built/production.bin
|
||||
test ! -f $(RELEASE_FNAME)
|
||||
cp built/file_time.c $(BOARD)/file_time.c
|
||||
$(SIGNIT) sign -m $(MK_NUM) $(VERSION_STRING) -r built/production.bin $(PROD_KEYNUM) -o built/production.bin
|
||||
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin $(RELEASE_FNAME)
|
||||
-git commit $(BOARD)/file_time.c -m "For $(NEW_VERSION)"
|
||||
$(SIGNIT) sign -m $(HW_MODEL) $(VERSION_STRING) -r built/production.bin $(PROD_KEYNUM) -o built/production.bin
|
||||
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin \
|
||||
-b $(BOOTLOADER_BASE):$(BOOTLOADER_DIR)/releases/$(BOOTLOADER_VERSION)/bootloader.bin \
|
||||
$(RELEASE_FNAME:%.dfu=%-factory.dfu)
|
||||
$(RELEASE_FNAME)
|
||||
@echo
|
||||
@echo 'Made release: ' $(RELEASE_FNAME)
|
||||
@echo
|
||||
@ -148,7 +142,7 @@ latest:
|
||||
code-committed:
|
||||
@echo ""
|
||||
@echo "Are all changes commited already?"
|
||||
git diff --stat --ignore-submodules=dirty --exit-code
|
||||
git diff --stat --exit-code .
|
||||
@echo '... yes'
|
||||
|
||||
# Sign a message with the contents of ../releases on the developer's machine
|
||||
@ -156,6 +150,7 @@ code-committed:
|
||||
sign-release:
|
||||
(cd ../releases; shasum -a 256 *.dfu *.md | sort -rk 2 | \
|
||||
gpg --clearsign -u A3A31BAD5A2A5B10 --digest-algo SHA256 --output signatures.txt --yes - )
|
||||
git commit -m "Signed for release." ../releases/signatures.txt
|
||||
|
||||
# Tag source code associate with built release version.
|
||||
# - do "make release" before this step!
|
||||
@ -163,8 +158,8 @@ sign-release:
|
||||
# - update & sign signatures file
|
||||
# - and tag everything
|
||||
tag-source: PUBLIC_VERSION = $(shell $(SIGNIT) version built/production.bin)
|
||||
tag-source: sign-release
|
||||
git commit -m "New release: "$(PUBLIC_VERSION) ../releases/signatures.txt $(BOARD)/file_time.c
|
||||
tag-source: sign-release code-committed
|
||||
git commit --allow-empty -am "New release: "$(PUBLIC_VERSION)
|
||||
echo "Tagging version: " $(PUBLIC_VERSION)
|
||||
git tag -a $(PUBLIC_VERSION) -m "Release "$(PUBLIC_VERSION)
|
||||
git push
|
||||
@ -232,6 +227,8 @@ setup:
|
||||
ln -s ../../../../../stm32/COLDCARD COLDCARD; fi
|
||||
cd $(PORT_TOP)/boards; if [ ! -L COLDCARD_MK4 ]; then \
|
||||
ln -s ../../../../../stm32/COLDCARD_MK4 COLDCARD_MK4; fi
|
||||
cd $(PORT_TOP)/boards; if [ ! -L COLDCARD_Q1 ]; then \
|
||||
ln -s ../../../../../stm32/COLDCARD_Q1 COLDCARD_Q1; fi
|
||||
|
||||
|
||||
# Caution: docker container has read access to your source tree
|
||||
@ -240,11 +237,11 @@ setup:
|
||||
# - works from this repo, but starts with copy of HEAD
|
||||
DOCK_RUN_ARGS = -v $(realpath ..):/work/src:ro \
|
||||
-v $(realpath built):/work/built:rw \
|
||||
-u $$(id -u):$$(id -g) coldcard-build
|
||||
--privileged coldcard-build
|
||||
repro: code-committed
|
||||
repro:
|
||||
docker build -t coldcard-build - < dockerfile.build
|
||||
(cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh $(VERSION_STRING) $(MK_NUM))
|
||||
(cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh $(VERSION_STRING) $(HW_MODEL))
|
||||
|
||||
# debug: shell into docker container
|
||||
shell:
|
||||
@ -253,7 +250,7 @@ shell:
|
||||
# debug: allow docker to write into source tree
|
||||
#DOCK_RUN_ARGS := -v $(realpath ..):/work/src:rw --privileged coldcard-build
|
||||
|
||||
PUBLISHED_BIN ?= $(wildcard ../releases/*-v$(VERSION_STRING)-mk$(MK_NUM)-coldcard.dfu)
|
||||
PUBLISHED_BIN ?= $(wildcard ../releases/*-v$(VERSION_STRING)-$(HW_MODEL)-coldcard.dfu)
|
||||
|
||||
# final step in repro-building: check you got the right bytes
|
||||
# - but you don't have the production signing key, so that section is removed
|
||||
|
||||
@ -79,6 +79,7 @@ class SimulatedScreen:
|
||||
|
||||
class LCDSimulator(SimulatedScreen):
|
||||
# Simulate the LCD found on the Q1: 320x240xRGB565
|
||||
# - written with little-endian (16 bit) data
|
||||
|
||||
background_img = 'q1-images/background.png'
|
||||
|
||||
@ -189,7 +190,7 @@ class LCDSimulator(SimulatedScreen):
|
||||
for x in range(X, X+w):
|
||||
#val = (raw[pos] << 8) + raw[pos+1]
|
||||
#val = raw[pos+1] + (raw[pos] << 8)
|
||||
val, = struct.unpack('<H', raw[pos:pos+2])
|
||||
val, = struct.unpack('>H', raw[pos:pos+2])
|
||||
self.mv[x][y] = val
|
||||
pos += 2
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user