]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blob - displayio/i2cdisplay.py
a29422eaba61c744069181174f459c7fa109d85e
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / i2cdisplay.py
1 # SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
2 # SPDX-FileCopyrightText: 2020 Erik Tollerud
3 # SPDX-FileCopyrightText: 2021 Jim Morris
4 # SPDX-FileCopyrightText: 2021 James Carr
5 #
6 # SPDX-License-Identifier: MIT
7
8 """
9 `displayio.i2cdisplay`
10 ================================================================================
11
12 displayio for Blinka
13
14 **Software and Dependencies:**
15
16 * Adafruit Blinka:
17   https://github.com/adafruit/Adafruit_Blinka/releases
18
19 * Author(s): Melissa LeBlanc-Williams, Erik Tollerud, James Carr
20
21 """
22
23 __version__ = "0.0.0-auto.0"
24 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
25
26
27 import time
28 import busio
29 import digitalio
30 import microcontroller
31
32 try:
33     from typing import Optional
34 except ImportError:
35     pass
36 try:
37     from _typing import ReadableBuffer
38 except ImportError:
39     pass
40
41
42 class I2CDisplay:
43     """Manage updating a display over I2C in the background while Python code runs.
44     It doesn’t handle display initialization.
45     """
46
47     def __init__(
48         self,
49         i2c_bus: busio.I2C,
50         *,
51         device_address: int,
52         reset: Optional[microcontroller.Pin] = None
53     ):
54         """Create a I2CDisplay object associated with the given I2C bus and reset pin.
55
56         The I2C bus and pins are then in use by the display until displayio.release_displays() is
57         called even after a reload. (It does this so CircuitPython can use the display after your
58         code is done.) So, the first time you initialize a display bus in code.py you should call
59         :py:func`displayio.release_displays` first, otherwise it will error after the first
60         code.py run.
61         """
62
63         if reset is not None:
64             self._reset = digitalio.DigitalInOut(reset)
65             self._reset.switch_to_output(value=True)
66         else:
67             self._reset = None
68         self._i2c = i2c_bus
69         self._dev_addr = device_address
70
71     def _release(self):
72         self.reset()
73         self._i2c.deinit()
74         if self._reset is not None:
75             self._reset.deinit()
76
77     def reset(self) -> None:
78         """
79         Performs a hardware reset via the reset pin if one is present.
80         """
81
82         if self._reset is None:
83             return
84
85         self._reset.value = False
86         time.sleep(0.0001)
87         self._reset.value = True
88
89     def begin_transaction(self) -> None:
90         """
91         Lock the bus before sending data.
92         """
93         while not self._i2c.try_lock():
94             pass
95
96     def send(self, command: bool, data: ReadableBuffer) -> None:
97         """
98         Sends the given command value followed by the full set of data. Display state,
99         such as vertical scroll, set via ``send`` may or may not be reset once the code is
100         done.
101         """
102
103         if command:
104             n = len(data)
105             if n > 0:
106                 command_bytes = bytearray(n * 2)
107                 for i in range(n):
108                     command_bytes[2 * i] = 0x80
109                     command_bytes[2 * i + 1] = data[i]
110
111                 self._i2c.writeto(self._dev_addr, buffer=command_bytes, stop=True)
112
113         else:
114             data_bytes = bytearray(len(data) + 1)
115             data_bytes[0] = 0x40
116             data_bytes[1:] = data
117             self._i2c.writeto(self._dev_addr, buffer=data_bytes, stop=True)
118
119     def end_transaction(self) -> None:
120         """
121         Release the bus after sending data.
122         """
123         self._i2c.unlock()