__init__.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. """
  2. Author: Tobias Müller
  3. Date: 13.09.2023
  4. Version: 1.0
  5. This file contains a class for a LCD.
  6. """
  7. ############################## Modules ##############################
  8. from machine import Pin, PWM, I2C
  9. ############################## Class ##############################
  10. class LCD(object):
  11. """
  12. Class to control a DOGS104-A LC-Display.
  13. """
  14. ############################## Attributes ##############################
  15. ROM_A = "ROM A"
  16. ROM_B = "ROM B"
  17. ROM_C = "ROM C"
  18. TWO_LINES = "TWO LINES"
  19. THREE_LINES_TOP = "THREE LINES TOP"
  20. THREE_LINES_MIDDLE = "THREE LINES MIDDLE"
  21. THREE_LINES_BOTTOM = "THREE LINES BOTTOM"
  22. FOUR_LINES = "FOUR LINES"
  23. STANDARD_MODE = "STANDARD MODE"
  24. FAST_MODE = "FAST MODE"
  25. __lcd_setting_para = {"LCD_PARA_EXT_FUNC": 0x09, # Extended Function (4 line mode) # type: ignore
  26. "LCD_PARA_BIAS_SET": 0x12, # Bias Setting (BS1 = 1) # type: ignore
  27. "LCD_PARA_INT_OSC": 0x1B, # Internal OSC (BS0 = 1, Bias = 1/6)
  28. "LCD_PARA_FOL_CON": 0x6E, # Follower Control (Devider on, Set Value)
  29. "LCD_PARA_DIS_CON": 0x0C} # Display on/off Control (Cursor off, Blink off)
  30. __lcd_contrast_para = {"LCD_PARA_POW_CON": 0x54, # Power Control (Booster on) and Contrast (C5, C4)
  31. "LCD_PARA_CONTRAST": 0x70} # Contrast (C3 - C0)
  32. __lcd_line_mode_para = {"LCD_PARA_LINES": 0x12} # Line Mode
  33. __lcd_direction_para = {"LCD_PARA_ENTRY_MODE": 0x04} # Display direction / orientation
  34. __lcd_control_para_priv = {"CONTROL_BYTE": 0x00, # Control Byte
  35. "CONTROL_BYTE_C0": 0x80, # Control Byte (C0 = 1)
  36. "CONTROL_BYTE_RS": 0x40, # Control Byte (RS = 1)
  37. "CONTROL_BYTE_C0_RS": 0xC0, # Control Byte (C0 = 1, RS = 1)
  38. "FUNCTION_SET": 0x38, # Function Set
  39. "FUNCTION_SET_RE": 0x3A, # Function Set (RE = 1)
  40. "FUNCTION_SET_IS": 0x39, # Function Set (IS = 1)
  41. "CLEAR_DISPLAY": 0x01, # Clear Display
  42. "DISPLAY_ROW_1": 0x80, # Display Row 1
  43. "DISPLAY_ROW_2": 0xA0, # Display Row 2
  44. "DISPLAY_ROW_3": 0xC0, # Display Row 3
  45. "DISPLAY_ROW_4": 0xE0, # Display Row 4
  46. "DISPLAY_COLUMN": 0x0A} # Columns per Display Row
  47. __lcd_rom_para = {"LCD_PARA_ROM_SEL": 0x72}
  48. __lcd_roms = {ROM_A: 0, ROM_B: 1, ROM_C: 2,
  49. ROM_A[-1]: 0, ROM_B[-1]: 1, ROM_C[-1]: 2,
  50. 1: 0, 2: 1, 3: 2}
  51. __lcd_line_modes = {TWO_LINES: 3, FOUR_LINES: 0, THREE_LINES_TOP: 4,
  52. THREE_LINES_MIDDLE: 2, THREE_LINES_BOTTOM: 1}
  53. __i2c_ports = {"I2C0": {"SDA_PINS": [Pin(0), Pin(4) ,Pin(8) ,Pin(12) ,Pin(16) ,Pin(20)],
  54. "SCL_PINS": [Pin(1), Pin(5), Pin(9), Pin(13), Pin(17), Pin(21)]},
  55. "I2C1": {"SDA_PINS": [Pin(2), Pin(6), Pin(10), Pin(14), Pin(18), Pin(26)],
  56. "SCL_PINS": [Pin(3), Pin(7), Pin(11), Pin(15), Pin(19), Pin(27)]}}
  57. __i2c_frequencies = {STANDARD_MODE: 100000,
  58. FAST_MODE: 400000}
  59. __i2c_address = 0x3c
  60. ############################## Constructor ##############################
  61. def __init__(self,
  62. i2c_sda_pin: Pin = Pin(2),
  63. i2c_scl_pin: Pin = Pin(3),
  64. i2c_frequency: int|str = "STANDARD MODE",
  65. lcd_backlight_pin: Pin = Pin(28),
  66. lcd_brightness: int = 100,
  67. lcd_contrast: int = 60,
  68. lcd_rom: int|str = "ROM B",
  69. lcd_line_mode: str = "FOUR LINES",
  70. lcd_direction_top: int|bool = False):
  71. """
  72. Initialize LCD. Alls arguments can be changed later as a property.
  73. Args:
  74. i2c_sda_pin (Pin, optional): Set SDA-Pin for I2C-Bus. Defaults to Pin(2).
  75. i2c_scl_pin (Pin, optional): Set SCL-Pin for I2C-Bus. Defaults to Pin(3).
  76. i2c_frequency (int | str, optional): Set I2C frequency. Defaults to "STANDARD MODE".
  77. lcd_backlight_pin (Pin, optional): Set LCD backlight pin. Defaults to Pin(28).
  78. lcd_brightness (int, optional): Set LCD brightness in percent. Defaults to 100.
  79. lcd_contrast (int, optional): Set LCD contrast in percent. Defaults to 60.
  80. lcd_rom (int | str, optional): Set ASCII-ROM. Defaults to "ROM B".
  81. lcd_line_mode (str, optional): Set LCD line mode for 2-, 3- or 4-lines. Defaults to "FOUR LINES".
  82. lcd_direction_top (int | bool, optional): Set LCD direction to top. Defaults to False.
  83. """
  84. # Check and store input values
  85. self.__i2c_bus, self.__i2c_sda_pin, self.__i2c_scl_pin = self.__check_i2c_ports(i2c_sda_pin,
  86. i2c_scl_pin)
  87. self.__i2c_frequency = self.__check_i2c_frequency(i2c_frequency)
  88. self.__lcd_backlight_pin = self.__check_lcd_backlight_pin(lcd_backlight_pin)
  89. self.__lcd_brightness = self.__check_lcd_brightness(lcd_brightness)
  90. self.__lcd_contrast = self.__check_lcd_contrast(lcd_contrast)
  91. self.__lcd_rom = self.__check_lcd_rom(lcd_rom)
  92. self.__lcd_line_mode = self.__check_lcd_line_mode(lcd_line_mode)
  93. self.__lcd_direction_top = self.__check_lcd_direction_top(lcd_direction_top)
  94. # init i2c and backlight
  95. self.__init_i2c()
  96. self.__init_backlight_pwm()
  97. ############################## Properties private ##############################
  98. @property
  99. def __lcd_control_para(self):
  100. lcd_control_para = self.__lcd_control_para_priv.copy()
  101. if self.__lcd_line_modes[self.__lcd_line_mode]:
  102. lcd_control_para["FUNCTION_SET"] = 0x3C
  103. if self.__lcd_direction_top:
  104. lcd_control_para["DISPLAY_ROW_1"] = 0x8A
  105. lcd_control_para["DISPLAY_ROW_2"] = 0xAA
  106. lcd_control_para["DISPLAY_ROW_3"] = 0xCA
  107. lcd_control_para["DISPLAY_ROW_4"] = 0xEA
  108. return lcd_control_para
  109. ############################## Properties ##############################
  110. @property
  111. def lcd_brightness(self):
  112. return self.__lcd_brightness
  113. @lcd_brightness.setter
  114. def lcd_brightness(self, lcd_brightness: int):
  115. """
  116. lcd_brightness: Set LCD brightness in percent.
  117. Args:
  118. lcd_brightness (int): Value from 0-100.
  119. """
  120. self.__lcd_brightness = self.__check_lcd_brightness(lcd_brightness)
  121. self.__set_lcd_brightness()
  122. @property
  123. def lcd_rom(self):
  124. return self.__lcd_rom
  125. @lcd_rom.setter
  126. def lcd_rom(self, lcd_rom: int|str):
  127. """
  128. lcd_rom: Set ROM for ASCII-Table. Check available ROMs at:
  129. https://www.lcd-module.com/fileadmin/eng/pdf/doma/dogs104e.pdf
  130. Args:
  131. lcd_rom (int | str): 1 or "A" or "ROM A" for ROM A,
  132. 2 or "B" or "ROM B" for ROM B,
  133. 3 or "C" or "ROM C" for ROM C.
  134. """
  135. self.__lcd_rom = self.__check_lcd_rom(lcd_rom)
  136. self.__set_lcd_rom()
  137. @property
  138. def lcd_contrast(self):
  139. return self.__lcd_contrast
  140. @lcd_contrast.setter
  141. def lcd_contrast(self, lcd_contrast: int):
  142. """
  143. lcd_contrast: Set LCD contrast in percent.
  144. Args:
  145. lcd_contrast (int): Value from 0-100.
  146. """
  147. self.__lcd_contrast = self.__check_lcd_contrast(lcd_contrast)
  148. self.__set_lcd_contrast()
  149. @property
  150. def lcd_direction_top(self):
  151. return self.__lcd_direction_top
  152. @lcd_direction_top.setter
  153. def lcd_direction_top(self, lcd_direction_top: int|bool):
  154. """
  155. lcd_direction_top: Set LCD direction to top.
  156. Args:
  157. lcd_direction_top (int | bool): 0 or False for LCD direction to bottom,
  158. 1 or True for LCD direction to top.
  159. """
  160. buffer = self.read()
  161. self.__lcd_direction_top = self.__check_lcd_direction_top(lcd_direction_top)
  162. self.clear()
  163. self.__set_lcd_direction()
  164. self.clear()
  165. self.print(str(buffer))
  166. @property
  167. def lcd_line_mode(self):
  168. return self.__lcd_line_mode
  169. @lcd_line_mode.setter
  170. def lcd_line_mode(self, lcd_line_mode: str):
  171. """
  172. lcd_line_mode: Set LCD line mode to 2-, 3- or 4-lines. For 3-lines are three additional modes available.
  173. Args:
  174. lcd_line_mode (str): "TWO LINES" to set the display in two line mode,
  175. "THREE LINES TOP" to set the display in three line mode with one bigger line on top position,
  176. "THREE LINES MIDDLE" to set the display in three line mode with one bigger line on middle position,
  177. "THREE LINES BOTTOM" to set the display in three line mode with one bigger line on bottom position,
  178. "FOUR LINES" to set the display in four line mode.
  179. """
  180. self.__check_lcd_line_mode(lcd_line_mode)
  181. if (self.__lcd_line_modes[lcd_line_mode] and
  182. self.__lcd_line_modes[self.__lcd_line_mode] == 0):
  183. self.clear(row=4)
  184. if (self.__lcd_line_modes[lcd_line_mode] == 3 and
  185. self.__lcd_line_modes[self.__lcd_line_mode] != 3):
  186. self.clear(row=3)
  187. self.__lcd_line_mode = lcd_line_mode
  188. self.__set_lcd_line_mode()
  189. @property
  190. def lcd_backlight_pin(self):
  191. return self.__lcd_backlight_pin
  192. @lcd_backlight_pin.setter
  193. def lcd_backlight_pin(self, lcd_backlight_pin: Pin):
  194. """
  195. lcd_backlight_pin: Set LCD backlight pin.
  196. Args:
  197. lcd_backlight_pin (Pin): Value of class Pin.
  198. """
  199. self.__check_lcd_backlight_pin(lcd_backlight_pin)
  200. self.__deinit_backlight_pwm()
  201. self.lcd_backlight_pin = lcd_backlight_pin
  202. self.__init_backlight_pwm()
  203. @property
  204. def i2c_frequency(self):
  205. return self.__i2c_frequency
  206. @i2c_frequency.setter
  207. def i2c_frequency(self, i2c_frequency: int|str):
  208. """
  209. i2c_frequency: Set I2C frequency.
  210. Args:
  211. i2c_frequency (int | str): "STANDARD MODE" or 100000 for a clock frequency of 100 kHz,
  212. "FAST MODE" or 400000 for a clock frequency of 400 kHz.
  213. """
  214. self.__check_i2c_frequency(i2c_frequency)
  215. self.__deinit_i2c()
  216. self.__i2c_frequency = i2c_frequency
  217. self.__init_i2c()
  218. @property
  219. def i2c_sda_pin(self):
  220. return self.__i2c_sda_pin
  221. @i2c_sda_pin.setter
  222. def i2c_sda_pin(self, i2c_sda_pin: Pin):
  223. """
  224. i2c_sda_pin: Set SDA-Pin for I2C-Bus.
  225. Args:
  226. i2c_sda_pin (Pin): Value of class Pin.
  227. """
  228. self.change_i2c_port(i2c_sda_pin, self.__i2c_scl_pin)
  229. @property
  230. def i2c_scl_pin(self):
  231. return self.__i2c_scl_pin
  232. @i2c_scl_pin.setter
  233. def i2c_scl_pin(self, i2c_scl_pin: Pin):
  234. """
  235. i2c_scl_pin: Set SCL-Pin for I2C-Bus.
  236. Args:
  237. i2c_scl_pin (Pin): Value of class Pin.
  238. """
  239. self.change_i2c_port(self.__i2c_sda_pin, i2c_scl_pin)
  240. ############################## Methods private ##############################
  241. @staticmethod
  242. def __reset_pin(pin: Pin):
  243. pin.init(mode=Pin.IN)
  244. @staticmethod
  245. def __check_lcd_brightness(lcd_brightness: int):
  246. if not isinstance(lcd_brightness, int):
  247. raise ValueError(f"Value \"{lcd_brightness}\" of \"lcd_brightness\" is not type \"int\"!")
  248. elif lcd_brightness < 0 or lcd_brightness > 100:
  249. raise ValueError(f"Value \"{lcd_brightness}\" of \"lcd_brightness\" is out of range!")
  250. else:
  251. return lcd_brightness
  252. @staticmethod
  253. def __check_lcd_backlight_pin(lcd_backlight_pin: Pin):
  254. if not isinstance(lcd_backlight_pin, Pin):
  255. raise ValueError(f"Value \"{lcd_backlight_pin}\" of \"lcd_backlight_pin\" is not type \"Pin\"!")
  256. else:
  257. return lcd_backlight_pin
  258. def __check_lcd_line_mode(self, lcd_line_mode: str):
  259. if not isinstance(lcd_line_mode, str):
  260. raise ValueError(f"Value \"{lcd_line_mode}\" of \"lcd_line_mode\" is not type \"str\"!")
  261. elif lcd_line_mode.upper() not in self.__lcd_line_modes:
  262. raise ValueError(f"Value \"{lcd_line_mode}\" of \"lcd_line_mode\" is not supported!")
  263. else:
  264. return lcd_line_mode
  265. @staticmethod
  266. def __check_lcd_direction_top(lcd_direction_top: int|bool):
  267. if not isinstance(lcd_direction_top, int) and not isinstance(lcd_direction_top, bool):
  268. raise ValueError(f"Value \"{lcd_direction_top}\" of \"lcd_direction_top\" is not type" +
  269. " \"int\" or \"bool\"!")
  270. elif isinstance(lcd_direction_top, int) and (lcd_direction_top < 0 or lcd_direction_top > 1):
  271. raise ValueError(f"Value \"{lcd_direction_top}\" of \"lcd_direction_top\" is out of range!")
  272. else:
  273. return lcd_direction_top
  274. @staticmethod
  275. def __check_lcd_contrast(lcd_contrast: int):
  276. if not isinstance(lcd_contrast, int):
  277. raise ValueError(f"Value \"{lcd_contrast}\" of \"lcd_contrast\" is not type \"int\"!")
  278. elif lcd_contrast < 0 or lcd_contrast > 100:
  279. raise ValueError(f"Value \"{lcd_contrast}\" of \"lcd_contrast\" is out of range!")
  280. else:
  281. return lcd_contrast
  282. def __check_lcd_rom(self, lcd_rom: int|str):
  283. if not isinstance(lcd_rom, int) and not isinstance(lcd_rom, str):
  284. raise ValueError(f"Value \"{lcd_rom}\" of \"lcd_rom\" is not type" +
  285. " \"int\" or \"str\"!")
  286. elif((isinstance(lcd_rom, int) and lcd_rom not in self.__lcd_roms.keys()) or
  287. (isinstance(lcd_rom, str) and lcd_rom.upper() not in self.__lcd_roms.keys())):
  288. raise ValueError(f"Value \"{lcd_rom}\" of \"lcd_rom\" is not supported!")
  289. else:
  290. return lcd_rom
  291. def __check_i2c_ports(self, i2c_sda_pin: Pin, i2c_scl_pin: Pin):
  292. if not isinstance(i2c_sda_pin, Pin):
  293. raise ValueError(f"Value \"{i2c_sda_pin}\" of \"i2c_sda_pin\" is not type \"Pin\"!")
  294. elif not isinstance(i2c_scl_pin, Pin):
  295. raise ValueError(f"Value \"{i2c_scl_pin}\" of \"i2c_scl_pin\" is not type \"Pin\"!")
  296. elif (not i2c_sda_pin in self.__i2c_ports["I2C0"]["SDA_PINS"] and
  297. not i2c_sda_pin in self.__i2c_ports["I2C1"]["SDA_PINS"]):
  298. raise ValueError(f"Value \"{i2c_sda_pin}\" of \"i2c_sda_pin\" is not an I2C SDA-Pin!")
  299. elif (not i2c_scl_pin in self.__i2c_ports["I2C0"]["SCL_PINS"] and
  300. not i2c_scl_pin in self.__i2c_ports["I2C1"]["SCL_PINS"]):
  301. raise ValueError(f"Value \"{i2c_scl_pin}\" of \"i2c_scl_pin\" is not an I2C SCL-Pin!")
  302. elif ((i2c_sda_pin in self.__i2c_ports["I2C0"]["SDA_PINS"] and not
  303. i2c_scl_pin in self.__i2c_ports["I2C0"]["SCL_PINS"]) or
  304. (i2c_sda_pin in self.__i2c_ports["I2C1"]["SDA_PINS"] and not
  305. i2c_scl_pin in self.__i2c_ports["I2C1"]["SCL_PINS"])):
  306. raise ValueError(f"Value \"{i2c_scl_pin}\" of \"i2c_scl_pin\" and " +
  307. f"Value \"{i2c_sda_pin}\" of \"i2c_sda_pin\" " +
  308. "are not part of the same I2C Bus!")
  309. else:
  310. if i2c_sda_pin in self.__i2c_ports["I2C0"]["SDA_PINS"]:
  311. return 0, i2c_sda_pin, i2c_scl_pin
  312. else:
  313. return 1, i2c_sda_pin, i2c_scl_pin
  314. def __check_i2c_frequency(self, i2c_frequency: int|str):
  315. if not isinstance(i2c_frequency, int) and not isinstance(i2c_frequency, str):
  316. raise ValueError(f"Value \"{i2c_frequency}\" of \"i2c_frequency\" is not type \"int\"" +
  317. " or \"str\"!")
  318. elif (isinstance(i2c_frequency, int) and not i2c_frequency in self.__i2c_frequencies.values() or
  319. isinstance(i2c_frequency, str) and not i2c_frequency in self.__i2c_frequencies.keys()):
  320. raise ValueError(f"Value \"{i2c_frequency}\" of \"i2c_frequency\" is not supported!")
  321. else:
  322. return i2c_frequency
  323. def __init_i2c(self):
  324. self.__i2c = I2C(self.__i2c_bus,
  325. sda=self.__i2c_sda_pin,
  326. scl=self.__i2c_scl_pin,
  327. freq=(self.__i2c_frequency if isinstance(self.__i2c_frequency, int) else
  328. self.__i2c_frequencies[self.__i2c_frequency]))
  329. buffer = (self.__lcd_control_para["CONTROL_BYTE"],
  330. self.__lcd_control_para["FUNCTION_SET_RE"],
  331. self.__lcd_setting_para["LCD_PARA_EXT_FUNC"],
  332. self.__lcd_setting_para["LCD_PARA_BIAS_SET"],
  333. self.__lcd_control_para["FUNCTION_SET_IS"],
  334. self.__lcd_setting_para["LCD_PARA_INT_OSC"],
  335. self.__lcd_setting_para["LCD_PARA_FOL_CON"],
  336. self.__lcd_control_para["FUNCTION_SET"],
  337. self.__lcd_setting_para["LCD_PARA_DIS_CON"])
  338. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  339. self.__set_lcd_line_mode()
  340. self.__set_lcd_rom()
  341. self.__set_lcd_contrast()
  342. self.__set_lcd_direction()
  343. self.clear()
  344. def __deinit_i2c(self):
  345. self.__reset_pin(self.__i2c_scl_pin)
  346. self.__reset_pin(self.__i2c_sda_pin)
  347. def __init_backlight_pwm(self):
  348. self.__backlight_pwm = PWM(self.__lcd_backlight_pin)
  349. self.__backlight_pwm.freq(1000)
  350. self.__set_lcd_brightness()
  351. def __deinit_backlight_pwm(self):
  352. self.__backlight_pwm.deinit()
  353. self.__reset_pin(self.__lcd_backlight_pin)
  354. def __set_lcd_brightness(self):
  355. self.__backlight_pwm.duty_u16(round(65635 * (self.__lcd_brightness / 100)))
  356. def __set_lcd_rom(self):
  357. if isinstance(self.__lcd_rom, str):
  358. lcd_rom = self.__lcd_rom.upper()
  359. else:
  360. lcd_rom = self.__lcd_rom
  361. buffer = (self.__lcd_control_para["CONTROL_BYTE_C0"],
  362. self.__lcd_control_para["FUNCTION_SET_RE"],
  363. self.__lcd_control_para["CONTROL_BYTE_C0"],
  364. self.__lcd_rom_para["LCD_PARA_ROM_SEL"],
  365. self.__lcd_control_para["CONTROL_BYTE_C0_RS"],
  366. self.__lcd_roms[lcd_rom] << 0x02,
  367. self.__lcd_control_para["CONTROL_BYTE"],
  368. self.__lcd_control_para["FUNCTION_SET"])
  369. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  370. def __set_lcd_contrast(self):
  371. contrast = round(63 * (self.__lcd_contrast / 100))
  372. buffer = (self.__lcd_control_para["CONTROL_BYTE"],
  373. self.__lcd_control_para["FUNCTION_SET_IS"],
  374. self.__lcd_contrast_para["LCD_PARA_POW_CON"] | (contrast >> 0x04),
  375. self.__lcd_contrast_para["LCD_PARA_CONTRAST"] | (contrast & 0x0F),
  376. self.__lcd_control_para["FUNCTION_SET"])
  377. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  378. def __set_lcd_direction(self):
  379. buffer = [self.__lcd_control_para["CONTROL_BYTE"],
  380. self.__lcd_control_para["FUNCTION_SET_RE"]]
  381. if self.__lcd_direction_top:
  382. buffer.append(self.__lcd_direction_para["LCD_PARA_ENTRY_MODE"] | 0x01)
  383. else:
  384. buffer.append(self.__lcd_direction_para["LCD_PARA_ENTRY_MODE"] | 0x02)
  385. buffer.append(self.__lcd_control_para["FUNCTION_SET"])
  386. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  387. def __set_lcd_line_mode(self):
  388. buffer = [self.__lcd_control_para["CONTROL_BYTE"],
  389. self.__lcd_control_para["FUNCTION_SET_RE"]]
  390. lcd_line_mode = self.__lcd_line_modes[self.__lcd_line_mode]
  391. if lcd_line_mode:
  392. buffer.append(self.__lcd_line_mode_para["LCD_PARA_LINES"] |
  393. ((lcd_line_mode - 1) << 0x02))
  394. else:
  395. buffer.append(self.__lcd_line_mode_para["LCD_PARA_LINES"])
  396. buffer.append(self.__lcd_control_para["FUNCTION_SET"])
  397. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  398. def __check_keywords(self, keywords: dict[str,int|bool], keys: list[str]):
  399. lcd_line_mode = self.__lcd_line_modes[self.__lcd_line_mode]
  400. rows = 0x04 if lcd_line_mode == 0x00 else 0x02 if lcd_line_mode == 0x03 else 0x03
  401. output = {"rows": rows, "row": 0x01, "column": 0x01, "auto_line_break": True, "separate": False}
  402. for key, val in keywords.items():
  403. if key not in keys:
  404. raise KeyError(f"Keyword \"{key}\" is unknown!")
  405. elif key == "row":
  406. if not isinstance(val, int):
  407. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is not type \"int\"!")
  408. elif val > rows or val < 1:
  409. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is out of range!")
  410. else:
  411. output.update({key: val})
  412. elif key == "column":
  413. if not isinstance(val, int):
  414. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is not type \"int\"!")
  415. elif val > self.__lcd_control_para["DISPLAY_COLUMN"] or val < 0x01:
  416. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is out of range!")
  417. else:
  418. output.update({key: val})
  419. elif key == "auto_line_break":
  420. if not isinstance(val, int) and not isinstance(val, bool):
  421. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is not type" +
  422. " \"int\" or \"bool\"!")
  423. elif isinstance(val, int) and (val < 0 or val > 1):
  424. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is out of range!")
  425. else:
  426. output.update({key: val})
  427. elif key == "separate":
  428. if not isinstance(val, int) and not isinstance(val, bool):
  429. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is not type" +
  430. " \"int\" or \"bool\"!")
  431. elif isinstance(val, int) and (val < 0 or val > 1):
  432. raise ValueError(f"Value \"{val}\" of keyword \"{key}\" is out of range!")
  433. else:
  434. output.update({key: val})
  435. return output.copy()
  436. ############################## Methods public ##############################
  437. def change_i2c_port(self, i2c_sda_pin: Pin, i2c_scl_pin: Pin):
  438. """
  439. Used to change I2C port.
  440. Args:
  441. i2c_sda_pin (int | str): SDA-Pin of I2C Port
  442. i2c_scl_pin (int | str): SCL-Pin of I2C Port
  443. """
  444. self.__check_i2c_ports(i2c_sda_pin, i2c_scl_pin)
  445. self.__deinit_i2c()
  446. (self.__i2c_bus,
  447. self.__i2c_sda_pin,
  448. self.__i2c_scl_pin) = self.__check_i2c_ports(i2c_sda_pin, i2c_scl_pin)
  449. self.__init_i2c()
  450. def print(self, text: str, **keywords):
  451. """
  452. Used to print text on the LCD.
  453. Args:
  454. text (str): Text, which is printed on the LCD.
  455. Keywords:
  456. row (int): Row to start printing. Defaults to 1.
  457. column (int): Column to start printing. Defaults to 1.
  458. auto_line_break (int | bool): Breaks the line automatically. Defaults to True.
  459. """
  460. def check_text(text: str):
  461. if not isinstance(text, str):
  462. raise ValueError(f"Value \"{text}\" of \"text\" is not type \"str\"!")
  463. elif len(text) == 0:
  464. raise ValueError(f"Value \"{text}\" of \"text\" is out of range!")
  465. keys = ["row", "column", "auto_line_break"]
  466. keys = self.__check_keywords(keywords, keys)
  467. rows = keys["rows"]
  468. row = keys["row"]
  469. column = keys["column"]
  470. auto_line_break = keys["auto_line_break"]
  471. check_text(text)
  472. linefeed = ("\n","\r")
  473. buffer = []
  474. for line in range(row,rows + 0x01):
  475. if len(text) == 0x00 or (not auto_line_break and line > row):
  476. break
  477. buffer.append(self.__lcd_control_para["CONTROL_BYTE_C0"])
  478. buffer.append(self.__lcd_control_para["DISPLAY_ROW_" + str(line)] + (column - 0x01))
  479. for char in range(0x00, (self.__lcd_control_para["DISPLAY_COLUMN"] - (column - 0x01)) if
  480. len(text) > (self.__lcd_control_para["DISPLAY_COLUMN"] - (column - 0x01)) else
  481. len(text)):
  482. if text[char] in linefeed:
  483. text = text[char + 0x01:]
  484. for _ in range(char, (self.__lcd_control_para["DISPLAY_COLUMN"] - (column - 0x01)) if
  485. len(text) > (self.__lcd_control_para["DISPLAY_COLUMN"] - (column - 0x01)) else
  486. len(text)):
  487. buffer.append(self.__lcd_control_para["CONTROL_BYTE_C0_RS"])
  488. buffer.append(ord(" "))
  489. break
  490. buffer.append(self.__lcd_control_para["CONTROL_BYTE_C0_RS"])
  491. buffer.append(ord(text[char]))
  492. else:
  493. text = text[self.__lcd_control_para["DISPLAY_COLUMN"] - (column - 0x01):]
  494. column = 0x01
  495. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  496. def clear(self, **keywords):
  497. """
  498. Used to clear the LCD. If the keywords row or column are not used,
  499. the method will clear the whole display.
  500. Keywords:
  501. row (int): Row to start printing. Defaults to 1.
  502. column (int): Column to start printing. Defaults to 1.
  503. """
  504. buffer = []
  505. if len(keywords) == 0:
  506. buffer.append(self.__lcd_control_para["CONTROL_BYTE"])
  507. buffer.append(self.__lcd_control_para["CLEAR_DISPLAY"])
  508. else:
  509. keys = ["row", "column"]
  510. keys = self.__check_keywords(keywords, keys)
  511. row = keys["row"]
  512. column = keys["column"]
  513. buffer.append(self.__lcd_control_para["CONTROL_BYTE_C0"])
  514. buffer.append(self.__lcd_control_para["DISPLAY_ROW_" + str(row)] + (column - 0x01))
  515. for _ in range(0x00, self.__lcd_control_para["DISPLAY_COLUMN"]):
  516. buffer.append(self.__lcd_control_para["CONTROL_BYTE_C0_RS"])
  517. buffer.append(ord(" "))
  518. if "column" in keywords.keys():
  519. break
  520. self.__i2c.writeto(self.__i2c_address, bytes(buffer))
  521. def read(self, **keywords):
  522. """
  523. Used to read from the LCD.
  524. Keywords:
  525. row (int): Row to start reading from. Defaults to 1.
  526. column (int): Column to start reading from. Defaults to 1.
  527. separate (int | bool): Separate each line into a list. Defaults to False.
  528. Returns:
  529. list: (separate=True) Return each line into a list of separated strings.
  530. str: (separate=False) Return all lines as one string.
  531. """
  532. def remove_whitespace(text: str):
  533. textlen = len(text)
  534. endpos = textlen
  535. for char in range(0x00, textlen):
  536. if text[textlen - char - 0x01] == " ":
  537. endpos -= 0x01
  538. else:
  539. break
  540. return text[:endpos]
  541. keys = ["row", "column", "separate"]
  542. keys = self.__check_keywords(keywords, keys)
  543. rows = keys["rows"]
  544. row = keys["row"]
  545. column = keys["column"]
  546. separate = keys["separate"]
  547. buffer = []
  548. for line in range(0x01, rows + 0x01):
  549. tmp = (self.__lcd_control_para["CONTROL_BYTE_C0"],
  550. self.__lcd_control_para["DISPLAY_ROW_" + str(line)],
  551. self.__lcd_control_para["CONTROL_BYTE_RS"])
  552. self.__i2c.writeto(self.__i2c_address,bytes(tmp))
  553. tmp = self.__i2c.readfrom(self.__i2c_address,11)
  554. tmp = list(tmp[1:])
  555. tmp = [chr(tmp[i]) for i in range(0, len(tmp))]
  556. if ("row" in keywords.keys() or "column" in keywords.keys()) and line != row:
  557. tmp = [""]
  558. if "column" in keywords.keys() and line == row:
  559. tmp = tmp[column - 0x01]
  560. buffer.append("".join(tmp))
  561. if not separate:
  562. return remove_whitespace("".join(buffer))
  563. else:
  564. for pos in range(0, len(buffer)):
  565. buffer[pos] = remove_whitespace(buffer[pos])
  566. return buffer
  567. if __name__ == "__main__":
  568. lcd = LCD(lcd_line_mode=LCD.TWO_LINES)
  569. lcd.print("Hochschule Anhalt")
  570. lcd.lcd_brightness = 50