Skip to content

Commit a289fba

Browse files
committed
ili9341: add optional madctl kwarg, remove hardcoded writes
1 parent 230cff0 commit a289fba

File tree

5 files changed

+189
-7
lines changed

5 files changed

+189
-7
lines changed

README.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,36 @@ Usage Example
6464
while True:
6565
pass
6666
67+
Custom MADCTL Example
68+
---------------------
69+
70+
The driver accepts an optional ``madctl`` keyword argument. If provided, the driver writes
71+
this value directly to the MADCTL register (0x36) during initialization. If omitted, the
72+
driver leaves MADCTL unchanged.
73+
74+
This allows advanced users to apply hardware-level rotations or control BGR/RGB order
75+
without relying on displayio’s software rotation. Using MADCTL can improve performance
76+
when displaying large bitmaps (e.g. with ``OnDiskBitmap``).
77+
78+
79+
.. code-block:: python
80+
81+
import board
82+
import displayio
83+
import fourwire
84+
import adafruit_ili9341
85+
86+
spi = board.SPI()
87+
tft_cs = board.D9
88+
tft_dc = board.D10
89+
90+
displayio.release_displays()
91+
display_bus = fourwire.FourWire(spi, command=tft_dc, chip_select=tft_cs)
92+
93+
# Pass a custom MADCTL value (example: 0b01011000)
94+
# Note: if you don't pass madctl, the driver does not modify the register.
95+
display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240, madctl=0b01011000)
96+
6797
Documentation
6898
=============
6999

adafruit_ili9341.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,39 @@ class ILI9341(BusDisplay):
9292
ILI9341 display driver
9393
9494
:param FourWire bus: bus that the display is connected to
95+
:param bool bgr: Use BGR color order instead of RGB
96+
:param bool invert: Invert the display
97+
:keyword int madctl: (optional) Raw value to write to the MADCTL register (0x36). If provided, the driver emits a MADCTL write during init; if omitted, the driver leaves MADCTL unchanged.
98+
9599
"""
96100

97-
def __init__(self, bus: FourWire, *, bgr: bool = False, invert: bool = False, **kwargs: Any):
98-
init_sequence = _INIT_SEQUENCE
99-
if bgr:
100-
init_sequence += b"\x36\x01\x30" # _MADCTL Default rotation plus BGR encoding
101-
else:
102-
init_sequence += b"\x36\x01\x38" # _MADCTL Default rotation plus RGB encoding
101+
def __init__(
102+
self,
103+
bus: FourWire,
104+
*,
105+
bgr: bool = False,
106+
invert: bool = False,
107+
**kwargs: Any,
108+
):
109+
110+
# Accept optional raw MADCTL via kwargs (requested by maintainers)
111+
madctl = kwargs.pop("madctl", None)
112+
113+
# Start with base init sequence
114+
init_sequence = bytearray(_INIT_SEQUENCE)
115+
116+
# Only touch MADCTL if caller explicitly provided it via kwargs
117+
if madctl is not None:
118+
try:
119+
idx = init_sequence.index(0x36) # MADCTL command
120+
init_sequence[idx + 1] = 0x01 # data length
121+
init_sequence[idx + 2] = madctl & 0xFF
122+
except ValueError:
123+
# Not present — append a single-byte MADCTL write
124+
init_sequence += bytes((0x36, 0x01, madctl & 0xFF))
125+
126+
# Inversion
103127
if invert:
104-
init_sequence += b"\x21\x00" # _INVON
128+
init_sequence += b"\x21\x00"
105129

106130
super().__init__(bus, init_sequence, **kwargs)

madctl_kwargs.patch

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
diff --git a/adafruit_ili9341.py b/adafruit_ili9341.py
2+
--- a/adafruit_ili9341.py
3+
+++ b/adafruit_ili9341.py
4+
@@ -131,11 +131,12 @@ class ILI9341(BusDisplay):
5+
:param FourWire bus: bus that the display is connected to
6+
:param bool bgr: Use BGR color order instead of RGB
7+
:param bool invert: Invert the display
8+
- :param int madctl: Optional custom MADCTL value to override rotation/scan order bits
9+
+ :keyword int madctl: (optional) Raw value to write to the MADCTL register (0x36).
10+
+ If provided, the driver emits a MADCTL write during init. If omitted,
11+
+ the driver will not alter MADCTL.
12+
"""
13+
14+
def __init__(
15+
self,
16+
bus: FourWire,
17+
*,
18+
bgr: bool = False,
19+
invert: bool = False,
20+
- madctl: int | None = None,
21+
**kwargs: Any,
22+
):
23+
- # Start with base init sequence
24+
+ # Accept optional raw MADCTL via kwargs (requested by maintainers)
25+
+ madctl = kwargs.pop("madctl", None)
26+
+ # Start with base init sequence
27+
init_sequence = bytearray(_INIT_SEQUENCE)
28+
29+
- # --- Replace existing MADCTL instead of appending ---
30+
- # Find MADCTL (0x36) in init sequence
31+
- try:
32+
- idx = init_sequence.index(0x36)
33+
- # Overwrite the length (always 1) + value
34+
- if madctl is not None:
35+
- init_sequence[idx + 1] = 0x01
36+
- init_sequence[idx + 2] = madctl
37+
- elif bgr:
38+
- init_sequence[idx + 1] = 0x01
39+
- init_sequence[idx + 2] = 0x30 # BGR encoding
40+
- else:
41+
- init_sequence[idx + 1] = 0x01
42+
- init_sequence[idx + 2] = 0x38 # RGB encoding
43+
- except ValueError:
44+
- # If no MADCTL present, append it
45+
- if madctl is not None:
46+
- init_sequence += bytes([0x36, 0x01, madctl])
47+
- elif bgr:
48+
- init_sequence += b"\x36\x01\x30"
49+
- else:
50+
- init_sequence += b"\x36\x01\x38"
51+
+ # Only touch MADCTL if caller explicitly provided it via kwargs
52+
+ if madctl is not None:
53+
+ # If MADCTL (0x36) already exists, overwrite its single data byte
54+
+ try:
55+
+ idx = init_sequence.index(0x36)
56+
+ init_sequence[idx + 1] = 0x01 # data length
57+
+ init_sequence[idx + 2] = madctl & 0xFF
58+
+ except ValueError:
59+
+ # Not present — append a single-byte MADCTL write
60+
+ init_sequence += bytes((0x36, 0x01, madctl & 0xFF))
61+
62+
# Inversion
63+
if invert:
64+
init_sequence += b"\x21\x00"
65+
66+
super().__init__(bus, init_sequence, **kwargs)

pyproject.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,16 @@ py-modules = ["adafruit_ili9341"]
4646
[tool.setuptools.dynamic]
4747
dependencies = {file = ["requirements.txt"]}
4848
optional-dependencies = {optional = {file = ["optional_requirements.txt"]}}
49+
50+
[tool.ruff]
51+
# Enable default linting rules
52+
select = ["E", "F"]
53+
54+
# Ignore specific rules if needed
55+
ignore = []
56+
57+
# Allow fixing issues automatically with `ruff check --fix`
58+
fix = true
59+
60+
# Formatting (instead of Black)
61+
line-length = 88

test_madctl.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# SPDX-FileCopyrightText: 2025 Ritesh
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import pytest
6+
7+
from adafruit_ili9341 import ILI9341
8+
9+
10+
class DummyBus:
11+
"""A fake bus to avoid hardware dependency."""
12+
13+
def __init__(self):
14+
self.commands = []
15+
16+
def send(self, *args, **kwargs):
17+
self.commands.append((args, kwargs))
18+
19+
20+
def extract_madctl_bytes(init_seq: bytes) -> bytes | None:
21+
"""
22+
Helper to find MADCTL command (0x36) in the init sequence.
23+
Returns 2 bytes: [0x36, value] or None if not present.
24+
"""
25+
for i in range(len(init_seq) - 2):
26+
if init_seq[i] == 0x36:
27+
return init_seq[i : i + 3]
28+
return None
29+
30+
31+
def test_default_rgb_encoding():
32+
bus = DummyBus()
33+
disp = ILI9341(bus)
34+
madctl = extract_madctl_bytes(disp.init_sequence)
35+
assert madctl == b"\x36\x01\x38" # Default RGB
36+
37+
38+
def test_bgr_encoding():
39+
bus = DummyBus()
40+
disp = ILI9341(bus, bgr=True)
41+
madctl = extract_madctl_bytes(disp.init_sequence)
42+
assert madctl == b"\x36\x01\x30" # BGR mode
43+
44+
45+
def test_custom_madctl():
46+
bus = DummyBus()
47+
disp = ILI9341(bus, madctl=0b01011000)
48+
madctl = extract_madctl_bytes(disp.init_sequence)
49+
assert madctl == bytes([0x36, 0x01, 0b01011000]) # Custom override

0 commit comments

Comments
 (0)