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:
parent
753a1e68f1
commit
b5b96bd776
7 changed files with 193 additions and 15 deletions
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
34
wasp/flashlight.py
Normal 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)
|
|
@ -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
35
wasp/testapp.py
Normal 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)
|
Loading…
Add table
Reference in a new issue