lcd.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * 4-bit HD44780 LCD support
  3. *
  4. * Copyright (C) 2007-2012 Michael Buesch <m@bues.ch>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include "lcd.h"
  17. #include "util.h"
  18. #include <avr/io.h>
  19. #include <string.h>
  20. #include <stdio.h>
  21. #define LCD_NR_CHARS (LCD_NR_LINES * LCD_NR_COLUMNS)
  22. #define LCD_BUFFER_SIZE LCD_NR_CHARS
  23. static uint8_t lcd_buffer[LCD_BUFFER_SIZE];
  24. uint8_t lcd_cursor_pos;
  25. static const uint8_t PROGMEM sad_smiley[] = {
  26. 0x00, 0x0A, 0x00, 0x00, 0x0E, 0x11, 0x00, 0x00,
  27. };
  28. /** lcd_enable_pulse - Send an E-pulse */
  29. static void lcd_enable_pulse(void)
  30. {
  31. LCD_PORT |= LCD_PIN_E;
  32. __asm__ __volatile__("nop\n\t"
  33. "nop\n\t"
  34. : : : "memory");
  35. LCD_PORT &= ~LCD_PIN_E;
  36. }
  37. /** lcd_write - Write one byte to the LCD. */
  38. static void lcd_write(uint8_t data)
  39. {
  40. LCD_PORT = (LCD_PORT & ~(0xF << LCD_DATA_SHIFT)) |
  41. (((data & 0xF0) >> 4) << LCD_DATA_SHIFT);
  42. lcd_enable_pulse();
  43. LCD_PORT = (LCD_PORT & ~(0xF << LCD_DATA_SHIFT)) |
  44. ((data & 0x0F) << LCD_DATA_SHIFT);
  45. lcd_enable_pulse();
  46. _delay_us(50);
  47. }
  48. /** lcd_data - Send data to the LCD. */
  49. static void lcd_data(uint8_t data)
  50. {
  51. LCD_PORT |= LCD_PIN_RS;
  52. lcd_write(data);
  53. }
  54. /** lcd_command - Send command to the LCD. */
  55. static void lcd_command(uint8_t command)
  56. {
  57. LCD_PORT &= ~LCD_PIN_RS;
  58. lcd_write(command);
  59. }
  60. /** lcd_cmd_clear - Clear LCD and return cursor to home position. */
  61. static void lcd_cmd_clear(void)
  62. {
  63. lcd_command(0x01);
  64. _delay_ms(2);
  65. }
  66. /** lcd_cmd_home - Move cursor to home position. */
  67. static void lcd_cmd_home(void)
  68. {
  69. lcd_command(0x02);
  70. _delay_ms(2);
  71. }
  72. /** lcd_cmd_entrymode - Set entry mode.
  73. * @cursor_inc: 1 = increment cursor, 0 = decrement cursor.
  74. * @display_shift: 1 = shift display, 0 = don't shift display.
  75. */
  76. static void lcd_cmd_entrymode(uint8_t cursor_inc,
  77. uint8_t display_shift)
  78. {
  79. lcd_command(0x04 |
  80. (cursor_inc ? 0x02 : 0x00) |
  81. (display_shift ? 0x01 : 0x00));
  82. }
  83. /** lcd_cmd_dispctl - Display control.
  84. * @display_on: 1 = turn display on, 0 = turn display off.
  85. * @cursor_on: 1 = turn cursor on, 0 = turn cursor off.
  86. * @cursor_blink: 1 = blink cursor, 0 = don't blink cursor.
  87. */
  88. void lcd_cmd_dispctl(uint8_t display_on,
  89. uint8_t cursor_on,
  90. uint8_t cursor_blink)
  91. {
  92. lcd_command(0x08 |
  93. (display_on ? 0x04 : 0x00) |
  94. (cursor_on ? 0x02 : 0x00) |
  95. (cursor_blink ? 0x01 : 0x00));
  96. }
  97. /** lcd_cmd_shiftctl - Display shift control.
  98. * @shift_display: 1 = move display, 0 = move cursor.
  99. * @shift_right: 1 = right direction, 0 = left direction.
  100. */
  101. static void lcd_cmd_shiftctl(uint8_t shift_display,
  102. uint8_t shift_right)
  103. {
  104. lcd_command(0x10 |
  105. (shift_display ? 0x08 : 0x00) |
  106. (shift_right ? 0x04 : 0x00));
  107. }
  108. /** lcd_cmd_funcset - Set basic display function.
  109. * @eight_bit: 1 = 8bit mode, 0 = 4bit mode.
  110. * @two_lines: 1 = two rows, 0 = one row.
  111. * @font_5x10: 1 = 5x10 font, 0 = 5x8 font.
  112. */
  113. static void lcd_cmd_funcset(uint8_t eight_bit,
  114. uint8_t two_lines,
  115. uint8_t font_5x10)
  116. {
  117. lcd_command(0x20 |
  118. (eight_bit ? 0x10 : 0x00) |
  119. (two_lines ? 0x08 : 0x00) |
  120. (font_5x10 ? 0x04 : 0x00));
  121. }
  122. /** lcd_cmd_cgram_addr_set - Move CGRAM address pointer.
  123. * @address: The address to move to.
  124. */
  125. static void lcd_cmd_cgram_addr_set(uint8_t address)
  126. {
  127. lcd_command(0x40 | (address & 0x3F));
  128. }
  129. /** lcd_cmd_cursor - Move cursor (DDRAM address).
  130. * @line: Line number. 0 - 1.
  131. * @column: Column number. 0 - 15.
  132. */
  133. void lcd_cmd_cursor(uint8_t line, uint8_t column)
  134. {
  135. lcd_command(0x80 |
  136. ((line & (LCD_NR_LINES - 1)) << 6) |
  137. (column & (LCD_NR_COLUMNS - 1)));
  138. }
  139. /** lcd_clear_buffer - Clear the software buffer. */
  140. void lcd_clear_buffer(void)
  141. {
  142. memset(lcd_buffer, ' ', LCD_BUFFER_SIZE);
  143. lcd_cursor_pos = 0;
  144. }
  145. /** lcd_commit - Write the software buffer to the display. */
  146. void lcd_commit(void)
  147. {
  148. uint8_t line, col;
  149. const uint8_t *buf = lcd_buffer;
  150. for (line = 0; line < LCD_NR_LINES; line++) {
  151. lcd_cmd_cursor(line, 0);
  152. for (col = 0; col < LCD_NR_COLUMNS; col++)
  153. lcd_data(*buf++);
  154. }
  155. lcd_cmd_cursor(lcd_getline(), lcd_getcolumn());
  156. }
  157. /** lcd_put_char - Put one character into software buffer. */
  158. void lcd_put_char(char c)
  159. {
  160. uint8_t line, column;
  161. if (c == '\r') {
  162. lcd_cursor(lcd_getline(), 0);
  163. } else if (c == '\n') {
  164. line = lcd_getline() + 1;
  165. lcd_cursor(line & (LCD_NR_LINES - 1), 0);
  166. } else {
  167. lcd_buffer[lcd_cursor_pos] = c;
  168. column = (lcd_getcolumn() + 1) & (LCD_NR_COLUMNS - 1);
  169. lcd_cursor(lcd_getline(), column);
  170. }
  171. }
  172. static int lcd_stream_putchar(char c, FILE *unused)
  173. {
  174. lcd_put_char(c);
  175. return 0;
  176. }
  177. static FILE lcd_fstream = FDEV_SETUP_STREAM(lcd_stream_putchar, NULL,
  178. _FDEV_SETUP_WRITE);
  179. void _lcd_printf(const char PROGPTR *_fmt, ...)
  180. {
  181. char fmt[64];
  182. va_list args;
  183. strlcpy_P(fmt, _fmt, sizeof(fmt));
  184. va_start(args, _fmt);
  185. vfprintf(&lcd_fstream, fmt, args);
  186. va_end(args);
  187. }
  188. void lcd_put_pstr(const char PROGPTR *str)
  189. {
  190. uint8_t c;
  191. for ( ; ; str++) {
  192. c = pgm_read_byte(str);
  193. if (c == '\0')
  194. break;
  195. lcd_put_char(c);
  196. }
  197. }
  198. void lcd_put_mstr(const char *str)
  199. {
  200. uint8_t c;
  201. for ( ; ; str++) {
  202. c = *str;
  203. if (c == '\0')
  204. break;
  205. lcd_put_char(c);
  206. }
  207. }
  208. void lcd_shift(int8_t count)
  209. {
  210. int8_t i, new_pos;
  211. if (!count)
  212. return;
  213. if (count < 0) { /* shift left */
  214. count = -count;
  215. if (count >= LCD_BUFFER_SIZE) {
  216. lcd_clear_buffer();
  217. return;
  218. }
  219. for (i = count; i < LCD_BUFFER_SIZE; i++) {
  220. new_pos = i - count;
  221. lcd_buffer[new_pos] = lcd_buffer[i];
  222. lcd_buffer[i] = ' ';
  223. }
  224. if (count > lcd_cursor_pos)
  225. lcd_cursor_pos = 0;
  226. else
  227. lcd_cursor_pos -= count;
  228. } else { /* shift right */
  229. if (count >= LCD_BUFFER_SIZE) {
  230. lcd_clear_buffer();
  231. lcd_cursor_pos = LCD_BUFFER_SIZE - 1;
  232. }
  233. for (i = LCD_BUFFER_SIZE - count - 1; i >= 0; i--) {
  234. new_pos = i + count;
  235. lcd_buffer[new_pos] = lcd_buffer[i];
  236. lcd_buffer[i] = ' ';
  237. }
  238. lcd_cursor_pos += count;
  239. if (lcd_cursor_pos >= LCD_BUFFER_SIZE)
  240. lcd_cursor_pos = LCD_BUFFER_SIZE - 1;
  241. }
  242. }
  243. /** lcd_upload_char - Upload a user defined character to CGRAM.
  244. * @char_code: The character code to use.
  245. * @char_tab: The character bitmap. The bitmap has got one byte
  246. * per character pixel row. The upper 3 bits of each byte
  247. * are unused. For 5x10 char-pixel displays, the char_tab
  248. * is 10 bytes long. For 5x8 char-pixel displays, it's 8 bytes.
  249. */
  250. void lcd_upload_char(uint8_t char_code,
  251. const uint8_t PROGPTR *char_tab)
  252. {
  253. uint8_t i, c, address;
  254. address = char_code << (LCD_FONT_5x10 ? 4 : 3);
  255. for (i = 0; i < (LCD_FONT_5x10 ? 10 : 8); i++) {
  256. lcd_cmd_cgram_addr_set(address);
  257. c = pgm_read_byte(char_tab);
  258. lcd_data(c);
  259. address++;
  260. char_tab++;
  261. }
  262. lcd_cmd_cursor(lcd_getline(), lcd_getcolumn());
  263. }
  264. /** lcd_init - Initialize the LCD. */
  265. void lcd_init(void)
  266. {
  267. uint8_t i;
  268. LCD_DDR |= (0xF << LCD_DATA_SHIFT) |
  269. LCD_PIN_E | LCD_PIN_RS;
  270. /* Force it into 8-bit mode first */
  271. LCD_PORT &= ~(LCD_PIN_E | LCD_PIN_RS |
  272. (0xF << LCD_DATA_SHIFT));
  273. LCD_PORT |= (0x03 << LCD_DATA_SHIFT);
  274. _delay_ms(200);
  275. for (i = 3; i; i--) {
  276. lcd_enable_pulse();
  277. _delay_ms(5);
  278. }
  279. /* We're in a known state. Enable 4-bit mode. */
  280. LCD_PORT = (LCD_PORT & ~(0xF << LCD_DATA_SHIFT)) |
  281. (0x02 << LCD_DATA_SHIFT);
  282. lcd_enable_pulse();
  283. _delay_ms(10);
  284. lcd_cmd_funcset(0,
  285. (LCD_NR_LINES > 1) ? 1 : 0,
  286. LCD_FONT_5x10 ? 1 : 0);
  287. lcd_cmd_dispctl(0, 0, 0);
  288. lcd_cmd_clear();
  289. lcd_cmd_entrymode(1, 0);
  290. lcd_cmd_shiftctl(0, 0);
  291. lcd_cmd_dispctl(1, 0, 0);
  292. lcd_cmd_home();
  293. lcd_cursor_pos = 0;
  294. lcd_clear_buffer();
  295. lcd_commit();
  296. lcd_upload_char(0x01, sad_smiley);
  297. }