1
0
Fork 0

wasp: Integrate the touch driver

At this point we are starting to bring an event model for applications
but there's still a long way to go!
This commit is contained in:
Daniel Thompson 2020-03-08 10:18:08 +00:00
parent 753a1e68f1
commit b5b96bd776
7 changed files with 193 additions and 15 deletions

View file

@ -11,12 +11,14 @@ freeze('../..',
'drivers/signal.py',
'drivers/st7789.py',
'drivers/vibrator.py',
'flashlight.py',
'fonts/clock.py',
'fonts/sans24.py',
'icons.py',
'logo.py',
'manager.py',
'shell.py',
'testapp.py',
'widgets.py',
),
opt=3

View file

@ -77,7 +77,10 @@ class CST816SSim():
raise OSError
dbuf[:] = self.regs[reg:len(dbuf)+reg]
self.regs[1] = 0
if self.regs[3]:
self.regs[3] = 0
else:
self.regs[1] = 0
def handle_key(self, key):
if key.keysym.sym == sdl2.SDLK_DOWN:
@ -88,6 +91,7 @@ class CST816SSim():
self.regs[1] = 3
elif key.keysym.sym == sdl2.SDLK_RIGHT:
self.regs[1] = 4
self.regs[3] = 0x80
self.raise_interrupt()
def handle_mousebutton(self, button):

View file

@ -50,6 +50,12 @@ class ClockApp(object):
"""De-activate the application (without losing state)."""
pass
def sleep(self):
return True
def wake(self):
self.update()
def draw(self, effect=None):
"""Redraw the display from scratch."""
display = watch.display

View file

@ -1,4 +1,14 @@
"""Hynitron CST816S touch contoller driver for MicroPython."""
"""Hynitron CST816S touch contoller driver for MicroPython.
After modifying this file we can replace the frozen driver with the
test driver with the following command::
./tools/wasptool \
--exec wasp/drivers/cst816s.py \
--eval "watch.touch = CST816S(watch.i2c)"`
"""
import array
class CST816S:
"""Hynitron CST816S I2C touch controller driver."""
@ -6,23 +16,48 @@ class CST816S:
def __init__(self, bus):
self.i2c = bus
self.dbuf = bytearray(6)
self.event = array.array('H', (0, 0, 0))
def get_event(self, queue):
def get_event(self):
"""Receive a touch event.
Check for a pending touch event and, if an event is pending,
prepare it ready to go in the event queue.
:return: True if an event is received, False otherwise.
:return: An event record if an event is received, None otherwise.
"""
dbuf = self.dbuf
event = self.event
# TODO: check the interrupt pin
try:
self.i2c.readfrom_mem_into(21, 1, dbuf)
except OSError:
return False
return None
queue[0] = dbuf[0]
queue[1] = ((dbuf[2] & 0xf) << 8) + dbuf[3]
queue[2] = ((dbuf[4] & 0xf) << 8) + dbuf[5]
return True
# Skip junk events
if dbuf[0] == 0:
return None
x = ((dbuf[2] & 0xf) << 8) + dbuf[3]
y = ((dbuf[4] & 0xf) << 8) + dbuf[5]
swipe_start = dbuf[2] & 0x80
# Skip identical events... when the I2C interface comes alive
# we can still get back stale events
if dbuf[0] == event[0] and x == event[1] and y == event[2] \
and not swipe_start:
return None
# This is a good event, lets save it
event[0] = dbuf[0]
event[1] = x
event[2] = y
# Do not forward swipe start events
if dbuf[2] & 0x80:
event[0] = 0
return None
return event

34
wasp/flashlight.py Normal file
View file

@ -0,0 +1,34 @@
import watch
import manager
from draw565 import Draw565
class FlashlightApp(object):
"""Trivial flashlight application.
Shows a pure white screen with the backlight set to maximum.
"""
def __init__(self):
self.backlight = None
def foreground(self, manager, effect=None):
"""Activate the application."""
self.on_screen = ( -1, -1, -1, -1, -1, -1 )
self.draw(effect)
manager.request_tick(1000)
def background(self):
"""De-activate the application (without losing state)."""
pass
def sleep(self):
return False
def tick(self, ticks):
pass
def draw(self, effect=None):
"""Redraw the display from scratch."""
display = watch.display
display.fill(0xffff)

View file

@ -1,30 +1,69 @@
import clock
import flashlight
import testapp
import gc
import machine
EVENT_TICK = 0x100
EVENT_KEYMASK = 0xff
DOWN = 1
UP = 2
LEFT = 3
RIGHT = 4
EVENT_TOUCH = 0x0001
EVENT_BUTTON = 0x0002
class Manager(object):
def __init__(self, watch):
self.watch = watch
self.app = None
self.switch(clock.ClockApp())
self.applications = [
clock.ClockApp(),
flashlight.FlashlightApp(),
testapp.TouchTestApp()
]
self.watch.display.poweron()
self.switch(self.applications[0])
self.watch.backlight.set(2)
self.sleep_at = watch.rtc.uptime + 90
self.charging = True
def switch(self, app):
if self.app:
self.app.background(self)
self.app.background()
# Clear out any configuration from the old application
self.event_mask = 0
self.tick_period_ms = 0
self.tick_expiry = None
self.app = app
app.foreground(self)
def navigate(self, direction=None):
"""Navigate between different applications.
Currently the direction is ignored.
"""
app_list = self.applications
if direction == DOWN:
i = app_list.index(self.app) + 1
if i >= len(app_list):
i = 0
self.switch(app_list[i])
elif direction == UP:
i = app_list.index(self.app) - 1
if i < 0:
i = len(app_list)-1
self.switch(app_list[i])
def request_event(self, event_mask):
self.event_mask |= event_mask
def request_tick(self, period_ms=None):
"""Request (and subscribe to) a periodic tick event.
@ -34,6 +73,14 @@ class Manager(object):
self.tick_period_ms = period_ms
self.tick_expiry = self.watch.rtc.get_uptime_ms() + period_ms
def handle_event(self, event):
self.sleep_at = self.watch.rtc.uptime + 15
if event[0] < 5:
self.navigate(event[0])
elif event[0] == 5 and self.event_mask & EVENT_TOUCH:
self.app.touch(event)
def tick(self):
rtc = self.watch.rtc
@ -51,8 +98,15 @@ class Manager(object):
if self.watch.button.value():
self.sleep_at = self.watch.rtc.uptime + 15
event = self.watch.touch.get_event()
if event:
self.handle_event(event)
if self.watch.rtc.uptime > self.sleep_at:
self.watch.backlight.set(0)
if not self.app.sleep():
self.switch(self.applications[0])
self.app.sleep()
self.watch.display.poweroff()
self.charging = self.watch.battery.charging()
self.sleep_at = None
@ -64,13 +118,21 @@ class Manager(object):
charging = self.watch.battery.charging()
if self.watch.button.value() or self.charging != charging:
self.watch.display.poweron()
self.app.tick(None)
self.app.wake()
self.watch.backlight.set(2)
# Discard any pending touch events
_ = self.watch.touch.get_event()
self.sleep_at = self.watch.rtc.uptime + 15
def run(self):
"""Run the system manager synchronously.
This allows all watch management activities to handle in the
normal execution context meaning any exceptions and other problems
can be observed interactively via the console.
"""
while True:
self.tick()
machine.deepsleep()

35
wasp/testapp.py Normal file
View file

@ -0,0 +1,35 @@
import watch
import widgets
import manager
from draw565 import Draw565
class TouchTestApp(object):
"""Simple application to visualize touch events.
"""
def __init__(self):
pass
def foreground(self, system, effect=None):
"""Activate the application."""
self.on_screen = ( -1, -1, -1, -1, -1, -1 )
self.draw(effect)
system.request_event(manager.EVENT_TOUCH)
def background(self):
"""De-activate the application (without losing state)."""
pass
def sleep(self):
return False
def touch(self, event):
draw = Draw565(watch.display)
draw.string('({}, {})'.format(event[1], event[2]),
0, 180, width=240)
return True
def draw(self, effect=None):
"""Redraw the display from scratch."""
watch.display.fill(0)