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/signal.py',
'drivers/st7789.py', 'drivers/st7789.py',
'drivers/vibrator.py', 'drivers/vibrator.py',
'flashlight.py',
'fonts/clock.py', 'fonts/clock.py',
'fonts/sans24.py', 'fonts/sans24.py',
'icons.py', 'icons.py',
'logo.py', 'logo.py',
'manager.py', 'manager.py',
'shell.py', 'shell.py',
'testapp.py',
'widgets.py', 'widgets.py',
), ),
opt=3 opt=3

View file

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

View file

@ -50,6 +50,12 @@ class ClockApp(object):
"""De-activate the application (without losing state).""" """De-activate the application (without losing state)."""
pass pass
def sleep(self):
return True
def wake(self):
self.update()
def draw(self, effect=None): def draw(self, effect=None):
"""Redraw the display from scratch.""" """Redraw the display from scratch."""
display = watch.display 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: class CST816S:
"""Hynitron CST816S I2C touch controller driver.""" """Hynitron CST816S I2C touch controller driver."""
@ -6,23 +16,48 @@ class CST816S:
def __init__(self, bus): def __init__(self, bus):
self.i2c = bus self.i2c = bus
self.dbuf = bytearray(6) 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. """Receive a touch event.
Check for a pending touch event and, if an event is pending, Check for a pending touch event and, if an event is pending,
prepare it ready to go in the event queue. 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 dbuf = self.dbuf
event = self.event
# TODO: check the interrupt pin
try: try:
self.i2c.readfrom_mem_into(21, 1, dbuf) self.i2c.readfrom_mem_into(21, 1, dbuf)
except OSError: except OSError:
return False return None
queue[0] = dbuf[0] # Skip junk events
queue[1] = ((dbuf[2] & 0xf) << 8) + dbuf[3] if dbuf[0] == 0:
queue[2] = ((dbuf[4] & 0xf) << 8) + dbuf[5] return None
return True
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 clock
import flashlight
import testapp
import gc import gc
import machine import machine
EVENT_TICK = 0x100 DOWN = 1
EVENT_KEYMASK = 0xff UP = 2
LEFT = 3
RIGHT = 4
EVENT_TOUCH = 0x0001
EVENT_BUTTON = 0x0002
class Manager(object): class Manager(object):
def __init__(self, watch): def __init__(self, watch):
self.watch = watch self.watch = watch
self.app = None 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.sleep_at = watch.rtc.uptime + 90
self.charging = True self.charging = True
def switch(self, app): def switch(self, app):
if self.app: if self.app:
self.app.background(self) self.app.background()
# Clear out any configuration from the old application # Clear out any configuration from the old application
self.event_mask = 0
self.tick_period_ms = 0 self.tick_period_ms = 0
self.tick_expiry = None self.tick_expiry = None
self.app = app self.app = app
app.foreground(self) 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): def request_tick(self, period_ms=None):
"""Request (and subscribe to) a periodic tick event. """Request (and subscribe to) a periodic tick event.
@ -34,6 +73,14 @@ class Manager(object):
self.tick_period_ms = period_ms self.tick_period_ms = period_ms
self.tick_expiry = self.watch.rtc.get_uptime_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): def tick(self):
rtc = self.watch.rtc rtc = self.watch.rtc
@ -51,8 +98,15 @@ class Manager(object):
if self.watch.button.value(): if self.watch.button.value():
self.sleep_at = self.watch.rtc.uptime + 15 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: if self.watch.rtc.uptime > self.sleep_at:
self.watch.backlight.set(0) self.watch.backlight.set(0)
if not self.app.sleep():
self.switch(self.applications[0])
self.app.sleep()
self.watch.display.poweroff() self.watch.display.poweroff()
self.charging = self.watch.battery.charging() self.charging = self.watch.battery.charging()
self.sleep_at = None self.sleep_at = None
@ -64,13 +118,21 @@ class Manager(object):
charging = self.watch.battery.charging() charging = self.watch.battery.charging()
if self.watch.button.value() or self.charging != charging: if self.watch.button.value() or self.charging != charging:
self.watch.display.poweron() self.watch.display.poweron()
self.app.tick(None) self.app.wake()
self.watch.backlight.set(2) self.watch.backlight.set(2)
# Discard any pending touch events
_ = self.watch.touch.get_event()
self.sleep_at = self.watch.rtc.uptime + 15 self.sleep_at = self.watch.rtc.uptime + 15
def run(self): 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: while True:
self.tick() self.tick()
machine.deepsleep() 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)