You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
7.0 KiB

  1. /*
  2. Copyright 2022
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include "hd44780.h"
  15. #include "gpio.h"
  16. #include "progmem.h"
  17. #include "wait.h"
  18. #ifndef HD44780_DATA_PINS
  19. # error hd44780: no data pins defined!
  20. #endif
  21. #ifndef HD44780_RS_PIN
  22. # error hd44780: no RS pin defined!
  23. #endif
  24. #ifndef HD44780_RW_PIN
  25. # error hd44780: no R/W pin defined!
  26. #endif
  27. #ifndef HD44780_E_PIN
  28. # error hd44780: no E pin defined!
  29. #endif
  30. static const pin_t data_pins[4] = HD44780_DATA_PINS;
  31. #ifndef HD44780_DISPLAY_COLS
  32. # define HD44780_DISPLAY_COLS 16
  33. #endif
  34. #ifndef HD44780_DISPLAY_LINES
  35. # define HD44780_DISPLAY_LINES 2
  36. #endif
  37. #ifndef HD44780_DDRAM_LINE0_ADDR
  38. # define HD44780_DDRAM_LINE0_ADDR 0x00
  39. #endif
  40. #ifndef HD44780_DDRAM_LINE1_ADDR
  41. # define HD44780_DDRAM_LINE1_ADDR 0x40
  42. #endif
  43. #define HD44780_INIT_DELAY_MS 16
  44. #define HD44780_ENABLE_DELAY_US 1
  45. static void hd44780_latch(void) {
  46. gpio_write_pin_high(HD44780_E_PIN);
  47. wait_us(HD44780_ENABLE_DELAY_US);
  48. gpio_write_pin_low(HD44780_E_PIN);
  49. }
  50. void hd44780_write(uint8_t data, bool isData) {
  51. gpio_write_pin(HD44780_RS_PIN, isData);
  52. gpio_write_pin_low(HD44780_RW_PIN);
  53. for (int i = 0; i < 4; i++) {
  54. gpio_set_pin_output(data_pins[i]);
  55. }
  56. // Write high nibble
  57. for (int i = 0; i < 4; i++) {
  58. gpio_write_pin(data_pins[i], (data >> 4) & (1 << i));
  59. }
  60. hd44780_latch();
  61. // Write low nibble
  62. for (int i = 0; i < 4; i++) {
  63. gpio_write_pin(data_pins[i], data & (1 << i));
  64. }
  65. hd44780_latch();
  66. for (int i = 0; i < 4; i++) {
  67. gpio_write_pin_high(data_pins[i]);
  68. }
  69. }
  70. uint8_t hd44780_read(bool isData) {
  71. uint8_t data = 0;
  72. gpio_write_pin(HD44780_RS_PIN, isData);
  73. gpio_write_pin_high(HD44780_RW_PIN);
  74. for (int i = 0; i < 4; i++) {
  75. gpio_set_pin_input(data_pins[i]);
  76. }
  77. gpio_write_pin_high(HD44780_E_PIN);
  78. wait_us(HD44780_ENABLE_DELAY_US);
  79. // Read high nibble
  80. for (int i = 0; i < 4; i++) {
  81. data |= (gpio_read_pin(data_pins[i]) << i);
  82. }
  83. data <<= 4;
  84. gpio_write_pin_low(HD44780_E_PIN);
  85. wait_us(HD44780_ENABLE_DELAY_US);
  86. gpio_write_pin_high(HD44780_E_PIN);
  87. wait_us(HD44780_ENABLE_DELAY_US);
  88. // Read low nibble
  89. for (int i = 0; i < 4; i++) {
  90. data |= (gpio_read_pin(data_pins[i]) << i);
  91. }
  92. gpio_write_pin_low(HD44780_E_PIN);
  93. return data;
  94. }
  95. bool hd44780_busy(void) {
  96. return hd44780_read(false) & HD44780_BUSY_FLAG;
  97. }
  98. void hd44780_command(uint8_t command) {
  99. while (hd44780_busy())
  100. ;
  101. hd44780_write(command, false);
  102. }
  103. void hd44780_data(uint8_t data) {
  104. while (hd44780_busy())
  105. ;
  106. hd44780_write(data, true);
  107. }
  108. void hd44780_clear(void) {
  109. hd44780_command(HD44780_CMD_CLEAR_DISPLAY);
  110. }
  111. void hd44780_home(void) {
  112. hd44780_command(HD44780_CMD_RETURN_HOME);
  113. }
  114. void hd44780_on(bool cursor, bool blink) {
  115. if (cursor) {
  116. if (blink) {
  117. hd44780_command(HD44780_CMD_DISPLAY | HD44780_DISPLAY_ON | HD44780_DISPLAY_CURSOR | HD44780_DISPLAY_BLINK);
  118. } else {
  119. hd44780_command(HD44780_CMD_DISPLAY | HD44780_DISPLAY_ON | HD44780_DISPLAY_CURSOR);
  120. }
  121. } else {
  122. hd44780_command(HD44780_CMD_DISPLAY | HD44780_DISPLAY_ON);
  123. }
  124. }
  125. void hd44780_off(void) {
  126. hd44780_command(HD44780_CMD_DISPLAY);
  127. }
  128. void hd44780_set_cgram_address(uint8_t address) {
  129. hd44780_command(HD44780_CMD_SET_CGRAM_ADDRESS + (address & 0x3F));
  130. }
  131. void hd44780_set_ddram_address(uint8_t address) {
  132. hd44780_command(HD44780_CMD_SET_DDRAM_ADDRESS + (address & 0x7F));
  133. }
  134. void hd44780_init(bool cursor, bool blink) {
  135. gpio_set_pin_output(HD44780_RS_PIN);
  136. gpio_set_pin_output(HD44780_RW_PIN);
  137. gpio_set_pin_output(HD44780_E_PIN);
  138. for (int i = 0; i < 4; i++) {
  139. gpio_set_pin_output(data_pins[i]);
  140. }
  141. wait_ms(HD44780_INIT_DELAY_MS);
  142. // Manually configure for 4-bit mode - can't use hd44780_command() yet
  143. // HD44780U datasheet, Fig. 24 (p46)
  144. gpio_write_pin_high(data_pins[0]); // Function set
  145. gpio_write_pin_high(data_pins[1]); // DL = 1
  146. hd44780_latch();
  147. wait_ms(5);
  148. // Send again
  149. hd44780_latch();
  150. wait_us(64);
  151. // And again (?)
  152. hd44780_latch();
  153. wait_us(64);
  154. gpio_write_pin_low(data_pins[0]); // DL = 0
  155. hd44780_latch();
  156. wait_us(64);
  157. #if HD44780_DISPLAY_LINES == 1
  158. hd44780_command(HD44780_CMD_FUNCTION); // 4 bit, 1 line, 5x8 dots
  159. #else
  160. hd44780_command(HD44780_CMD_FUNCTION | HD44780_FUNCTION_2_LINES); // 4 bit, 2 lines, 5x8 dots
  161. #endif
  162. hd44780_on(cursor, blink);
  163. hd44780_clear();
  164. hd44780_home();
  165. hd44780_command(HD44780_CMD_ENTRY_MODE | HD44780_ENTRY_MODE_INC);
  166. }
  167. void hd44780_set_cursor(uint8_t col, uint8_t line) {
  168. register uint8_t address = col;
  169. #if HD44780_DISPLAY_LINES == 1
  170. address += HD44780_DDRAM_LINE0_ADDR;
  171. #elif HD44780_DISPLAY_LINES == 2
  172. if (line == 0) {
  173. address += HD44780_DDRAM_LINE0_ADDR;
  174. } else {
  175. address += HD44780_DDRAM_LINE1_ADDR;
  176. }
  177. #endif
  178. hd44780_set_ddram_address(address);
  179. }
  180. void hd44780_define_char(uint8_t index, uint8_t *data) {
  181. hd44780_set_cgram_address((index & 0x7) << 3);
  182. for (uint8_t i = 0; i < 8; i++) {
  183. hd44780_data(data[i]);
  184. }
  185. }
  186. void hd44780_putc(char c) {
  187. while (hd44780_busy())
  188. ;
  189. uint8_t current_position = hd44780_read(false);
  190. if (c == '\n') {
  191. hd44780_set_cursor(0, current_position < HD44780_DDRAM_LINE1_ADDR ? 1 : 0);
  192. } else {
  193. #if defined(HD44780_WRAP_LINES)
  194. # if HD44780_DISPLAY_LINES == 1
  195. if (current_position == HD44780_DDRAM_LINE0_ADDR + HD44780_DISPLAY_COLS) {
  196. // Go to start of line
  197. hd44780_set_cursor(0, 0);
  198. }
  199. # elif HD44780_DISPLAY_LINES == 2
  200. if (current_position == HD44780_DDRAM_LINE0_ADDR + HD44780_DISPLAY_COLS) {
  201. // Go to start of second line
  202. hd44780_set_cursor(0, 1);
  203. } else if (current_position == HD44780_DDRAM_LINE1_ADDR + HD44780_DISPLAY_COLS) {
  204. // Go to start of first line
  205. hd44780_set_cursor(0, 0);
  206. }
  207. # endif
  208. #endif
  209. hd44780_data(c);
  210. }
  211. }
  212. void hd44780_puts(const char *s) {
  213. register char c;
  214. while ((c = *s++)) {
  215. hd44780_putc(c);
  216. }
  217. }
  218. #if defined(__AVR__)
  219. void hd44780_define_char_P(uint8_t index, const uint8_t *data) {
  220. hd44780_set_cgram_address(index << 3);
  221. for (uint8_t i = 0; i < 8; i++) {
  222. hd44780_data(pgm_read_byte(data++));
  223. }
  224. }
  225. void hd44780_puts_P(const char *s) {
  226. register char c;
  227. while ((c = pgm_read_byte(s++))) {
  228. hd44780_putc(c);
  229. }
  230. }
  231. #endif