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