Skip to content

Commit f95766a

Browse files
committed
adds a display driver and fixes the io expanders
1 parent e29be7a commit f95766a

File tree

8 files changed

+1494
-80
lines changed

8 files changed

+1494
-80
lines changed

api_drivers/common_api_drivers/display/spd2010/_spd2010_init.py

+1,239
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Copyright (c) 2024 - 2025 Kevin G. Schlosser
2+
3+
import display_driver_framework
4+
import rgb_display_framework # NOQA
5+
from micropython import const # NOQA
6+
import lcd_bus
7+
import lvgl as lv
8+
import gc
9+
10+
11+
STATE_HIGH = display_driver_framework.STATE_HIGH
12+
STATE_LOW = display_driver_framework.STATE_LOW
13+
STATE_PWM = display_driver_framework.STATE_PWM
14+
15+
BYTE_ORDER_RGB = display_driver_framework.BYTE_ORDER_RGB
16+
BYTE_ORDER_BGR = display_driver_framework.BYTE_ORDER_BGR
17+
18+
_WRITE_CMD = const(0x02)
19+
_WRITE_COLOR = const(0x32)
20+
21+
_RASET = const(0x2B)
22+
_CASET = const(0x2A)
23+
_RAMWR = const(0x2C)
24+
25+
26+
class SPD2010(display_driver_framework.DisplayDriver):
27+
28+
_ORIENTATION_TABLE = ()
29+
30+
def _madctl(self, colormode, rotations, rotation=None):
31+
if rotation is None:
32+
rotation = ~self._rotation
33+
34+
rotation = ~rotation
35+
value = colormode
36+
37+
if rotation:
38+
value |= 0x03
39+
40+
return value
41+
42+
def set_rotation(self, value):
43+
44+
if value not in (lv.DISPLAY_ROTATION._0, lv.DISPLAY_ROTATION._180): # NOQA
45+
print('This rotation is not supported, only 0 and 180 is supported')
46+
return
47+
48+
self._disp_drv.set_rotation(value)
49+
50+
@staticmethod
51+
def __quad_spi_cmd_modifier(cmd):
52+
cmd <<= 8
53+
cmd |= _WRITE_CMD << 24
54+
return cmd
55+
56+
@staticmethod
57+
def __quad_spi_color_cmd_modifier(cmd):
58+
cmd <<= 8
59+
cmd |= _WRITE_COLOR << 24
60+
return cmd
61+
62+
@staticmethod
63+
def __dummy_cmd_modifier(cmd):
64+
return cmd
65+
66+
def __init__(
67+
self,
68+
data_bus,
69+
display_width,
70+
display_height,
71+
frame_buffer1=None,
72+
frame_buffer2=None,
73+
reset_pin=None,
74+
reset_state=STATE_HIGH,
75+
power_pin=None,
76+
power_on_state=STATE_HIGH,
77+
backlight_pin=None,
78+
backlight_on_state=STATE_HIGH,
79+
offset_x=0,
80+
offset_y=0,
81+
color_byte_order=BYTE_ORDER_RGB,
82+
color_space=lv.COLOR_FORMAT.RGB888, # NOQA
83+
rgb565_byte_swap=False, # NOQA
84+
):
85+
num_lanes = data_bus.get_lane_count()
86+
87+
if num_lanes == 4:
88+
self.__cmd_modifier = self.__quad_spi_cmd_modifier
89+
self.__color_cmd_modifier = self.__quad_spi_color_cmd_modifier
90+
_cmd_bits = 32
91+
92+
# we need to override the default handling for creating the frame
93+
# buffer is using a quad spi bus. we don't want it to create
94+
# partial buffers for the quad SPI display
95+
96+
buf_size = display_width * display_height * lv.color_format_get_size(color_space)
97+
98+
if frame_buffer1 is None:
99+
gc.collect()
100+
101+
for flags in (
102+
lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_DMA,
103+
lcd_bus.MEMORY_SPIRAM | lcd_bus.MEMORY_DMA,
104+
lcd_bus.MEMORY_INTERNAL,
105+
lcd_bus.MEMORY_SPIRAM
106+
):
107+
try:
108+
frame_buffer1 = (
109+
data_bus.allocate_framebuffer(buf_size, flags)
110+
)
111+
112+
if (flags | lcd_bus.MEMORY_DMA) == flags:
113+
frame_buffer2 = (
114+
data_bus.allocate_framebuffer(buf_size, flags)
115+
)
116+
117+
break
118+
except MemoryError:
119+
frame_buffer1 = data_bus.free_framebuffer(frame_buffer1) # NOQA
120+
121+
if frame_buffer1 is None:
122+
raise MemoryError(
123+
f'Unable to allocate memory for frame buffer ({buf_size})' # NOQA
124+
)
125+
126+
if len(frame_buffer1) != buf_size:
127+
raise ValueError('incorrect framebuffer size')
128+
else:
129+
self.__cmd_modifier = self.__dummy_cmd_modifier
130+
self.__color_cmd_modifier = self.__dummy_cmd_modifier
131+
_cmd_bits = 8
132+
133+
# store these so we do not have to keep on converting them
134+
self.__ramwr = self.__color_cmd_modifier(_RAMWR)
135+
self.__caset = self.__cmd_modifier(_CASET)
136+
self.__raset = self.__cmd_modifier(_RASET)
137+
138+
super().__init__(
139+
data_bus,
140+
display_width,
141+
display_height,
142+
frame_buffer1,
143+
frame_buffer2,
144+
reset_pin,
145+
reset_state,
146+
power_pin,
147+
power_on_state,
148+
backlight_pin,
149+
backlight_on_state,
150+
offset_x,
151+
offset_y,
152+
color_byte_order,
153+
color_space, # NOQA
154+
# we don't need to sue RGB565 byte swap so we override it
155+
rgb565_byte_swap=False,
156+
_cmd_bits=_cmd_bits,
157+
_param_bits=8,
158+
_init_bus=True
159+
)
160+
161+
def _flush_ready_cb(self, *_):
162+
# since there are 2 transfers that take place we need to
163+
# make sure that flush ready is only called after the second part
164+
# of the buffer has sent.
165+
self._disp_drv.flush_ready()
166+
167+
def set_params(self, cmd, params=None):
168+
cmd = self.__cmd_modifier(cmd)
169+
self._data_bus.tx_param(cmd, params)
170+
171+
def _set_memory_location(self, x1: int, y1: int, x2: int, y2: int):
172+
param_buf = self._param_buf # NOQA
173+
174+
param_buf[0] = (x1 >> 8) & 0xFF
175+
param_buf[1] = x1 & 0xFF
176+
param_buf[2] = (x2 >> 8) & 0xFF
177+
param_buf[3] = x2 & 0xFF
178+
179+
self._data_bus.tx_param(self.__caset, self._param_mv)
180+
181+
param_buf[0] = (y1 >> 8) & 0xFF
182+
param_buf[1] = y1 & 0xFF
183+
param_buf[2] = (y2 >> 8) & 0xFF
184+
param_buf[3] = y2 & 0xFF
185+
186+
self._data_bus.tx_param(self.__raset, self._param_mv)
187+
188+
def _flush_cb(self, _, area, color_p):
189+
x1 = area.x1 + self._offset_x
190+
x2 = area.x2 + self._offset_x
191+
192+
y1 = area.y1 + self._offset_y
193+
y2 = area.y2 + self._offset_y
194+
195+
self._set_memory_location(x1, y1, x2, y2)
196+
197+
width = x2 - x1 + 1
198+
height = y2 - y1 + 1
199+
size = width * height * lv.color_format_get_size(self._color_space)
200+
201+
data_view = color_p.__dereference__(size)
202+
self._data_bus.tx_color(self.__ramwr, data_view, x1, y1, x2, y2, self._rotation, False)

api_drivers/common_api_drivers/io_expander/ch422g.py

+12-9
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import i2c
44

55

6-
EXIO1 = io_expander_framework.EXIO1
7-
EXIO2 = io_expander_framework.EXIO2
8-
EXIO3 = io_expander_framework.EXIO3
9-
EXIO4 = io_expander_framework.EXIO4
10-
EXIO5 = io_expander_framework.EXIO5
11-
EXIO6 = io_expander_framework.EXIO6
12-
EXIO7 = io_expander_framework.EXIO7
13-
EXIO8 = io_expander_framework.EXIO8
6+
EXIO1 = 0x01
7+
EXIO2 = 0x02
8+
EXIO3 = 0x03
9+
EXIO4 = 0x04
10+
EXIO5 = 0x05
11+
EXIO6 = 0x06
12+
EXIO7 = 0x07
13+
EXIO8 = 0x08
1414

1515

1616
I2C_ADDR = 0x24 # 36 00100100
@@ -33,7 +33,10 @@ class Pin(io_expander_framework.Pin):
3333

3434
@classmethod
3535
def set_device(cls, device):
36-
io_expander_framework.Pin.set_device.__func__(cls, device)
36+
if cls._device is not None:
37+
raise ValueError('device has already been set')
38+
39+
cls._device = device
3740

3841
cls._reg_in = i2c.I2C.Device(
3942
bus=device._bus, # NOQA

api_drivers/common_api_drivers/io_expander/ht8574a.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
import machine
55

66

7-
EXIO1 = io_expander_framework.EXIO1
8-
EXIO2 = io_expander_framework.EXIO2
9-
EXIO3 = io_expander_framework.EXIO3
10-
EXIO4 = io_expander_framework.EXIO4
11-
EXIO5 = io_expander_framework.EXIO5
12-
EXIO6 = io_expander_framework.EXIO6
13-
EXIO7 = io_expander_framework.EXIO7
14-
EXIO8 = io_expander_framework.EXIO8
7+
EXIO1 = 0x01
8+
EXIO2 = 0x02
9+
EXIO3 = 0x03
10+
EXIO4 = 0x04
11+
EXIO5 = 0x05
12+
EXIO6 = 0x06
13+
EXIO7 = 0x07
14+
EXIO8 = 0x08
1515

1616

1717
# 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E

api_drivers/common_api_drivers/io_expander/ht8574b.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import io_expander_framework
44

55

6-
EXIO1 = io_expander_framework.EXIO1
7-
EXIO2 = io_expander_framework.EXIO2
8-
EXIO3 = io_expander_framework.EXIO3
9-
EXIO4 = io_expander_framework.EXIO4
10-
EXIO5 = io_expander_framework.EXIO5
11-
EXIO6 = io_expander_framework.EXIO6
12-
EXIO7 = io_expander_framework.EXIO7
13-
EXIO8 = io_expander_framework.EXIO8
6+
EXIO1 = 0x01
7+
EXIO2 = 0x02
8+
EXIO3 = 0x03
9+
EXIO4 = 0x04
10+
EXIO5 = 0x05
11+
EXIO6 = 0x06
12+
EXIO7 = 0x07
13+
EXIO8 = 0x08
1414

1515

1616
# 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E

api_drivers/common_api_drivers/io_expander/tca9535.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@
44
import io_expander_framework
55

66

7-
EXIO1 = io_expander_framework.EXIO1
8-
EXIO2 = io_expander_framework.EXIO2
9-
EXIO3 = io_expander_framework.EXIO3
10-
EXIO4 = io_expander_framework.EXIO4
11-
EXIO5 = io_expander_framework.EXIO5
12-
EXIO6 = io_expander_framework.EXIO6
13-
EXIO7 = io_expander_framework.EXIO7
14-
EXIO8 = io_expander_framework.EXIO8
15-
EXIO9 = io_expander_framework.EXIO9
16-
EXIO10 = io_expander_framework.EXIO10
17-
EXIO11 = io_expander_framework.EXIO11
18-
EXIO12 = io_expander_framework.EXIO12
19-
EXIO13 = io_expander_framework.EXIO13
20-
EXIO14 = io_expander_framework.EXIO14
21-
EXIO15 = io_expander_framework.EXIO15
22-
EXIO16 = io_expander_framework.EXIO16
7+
EXIO1 = 0x01
8+
EXIO2 = 0x02
9+
EXIO3 = 0x03
10+
EXIO4 = 0x04
11+
EXIO5 = 0x05
12+
EXIO6 = 0x06
13+
EXIO7 = 0x07
14+
EXIO8 = 0x08
15+
EXIO9 = 0x09
16+
EXIO10 = 0x0A
17+
EXIO11 = 0x0B
18+
EXIO12 = 0x0C
19+
EXIO13 = 0x0D
20+
EXIO14 = 0x0E
21+
EXIO15 = 0x0F
22+
EXIO16 = 0x10
2323

2424
_INPUT_PORT_REG = const(0x00)
2525
_OUTPUT_PORT_REG = const(0x02)

api_drivers/common_api_drivers/io_expander/tca9554.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
import io_expander_framework
55

66

7-
EXIO1 = io_expander_framework.EXIO1
8-
EXIO2 = io_expander_framework.EXIO2
9-
EXIO3 = io_expander_framework.EXIO3
10-
EXIO4 = io_expander_framework.EXIO4
11-
EXIO5 = io_expander_framework.EXIO5
12-
EXIO6 = io_expander_framework.EXIO6
13-
EXIO7 = io_expander_framework.EXIO7
14-
EXIO8 = io_expander_framework.EXIO8
7+
EXIO1 = 0x01
8+
EXIO2 = 0x02
9+
EXIO3 = 0x03
10+
EXIO4 = 0x04
11+
EXIO5 = 0x05
12+
EXIO6 = 0x06
13+
EXIO7 = 0x07
14+
EXIO8 = 0x08
1515

1616

1717
_INPUT_PORT_REG = const(0x00)
@@ -36,7 +36,7 @@ class Pin(io_expander_framework.Pin):
3636

3737
@property
3838
def __bit(self):
39-
return 1 << self._id
39+
return 1 << (self._id - 1)
4040

4141
def __read_reg(self, reg):
4242
self._buf[0] = 0

api_drivers/py_api_drivers/frozen/io_expander/io_expander_framework.py

-30
Original file line numberDiff line numberDiff line change
@@ -306,33 +306,3 @@ def read(self):
306306

307307
def _read(self):
308308
raise NotImplementedError
309-
310-
311-
# this class allows for dynamically pulling the EXIO constants. We do not
312-
# want to have a mess of int objects loaded into memory so instead we have
313-
# this one class that acts like a module and will return the integer value
314-
# of the ESIO constant that is being requested.
315-
class DynamicModule(object):
316-
317-
def __init__(self):
318-
import sys
319-
320-
self.__mod__ = sys.modules[__name__]
321-
self.__name__ = __name__
322-
323-
sys.modules[__name__] = self
324-
325-
def __getattr__(self, item):
326-
if item in self.__dict__:
327-
return self.__dict__[item]
328-
329-
if hasattr(self.__mod__, item):
330-
return getattr(self.__mod__, item)
331-
332-
if item.startswith('EXIO') and item[4:].isdigit():
333-
return int(item[4:])
334-
335-
raise AttributeError(item)
336-
337-
338-
__dm = DynamicModule()

0 commit comments

Comments
 (0)