lcd_2002a08f.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <sys/ioctl.h>
  7. #include <linux/i2c-dev.h>
  8. #include "lcd_2002a08f.h"
  9. static int i2c_fd = -1;
  10. static uint8_t i2c_addr;
  11. /* 延时函数 - 数据手册建议指令间延时1ms以上 */
  12. static void lcd_delay_ms(int ms)
  13. {
  14. usleep(ms * 1000);
  15. }
  16. /* I2C写数据 */
  17. static int i2c_write(uint8_t *data, int len)
  18. {
  19. if (i2c_fd < 0) {
  20. return -1;
  21. }
  22. if (write(i2c_fd, data, len) != len) {
  23. perror("I2C write failed");
  24. return -1;
  25. }
  26. return 0;
  27. }
  28. /* 发送指令到LCD - 根据数据手册第7-8页的两线串行通讯格式 */
  29. static int lcd_send_command(uint8_t cmd)
  30. {
  31. uint8_t buf[3];
  32. /* 控制字节: Co=0, RS=0 (指令) */
  33. buf[0] = LCD_CONTROL_BYTE; /* 0x00 */
  34. buf[1] = cmd;
  35. if (i2c_write(buf, 2) < 0) {
  36. return -1;
  37. }
  38. lcd_delay_ms(2); /* 指令执行时间,数据手册建议1ms以上 */
  39. return 0;
  40. }
  41. /* 发送数据到LCD */
  42. static int lcd_send_data(uint8_t data)
  43. {
  44. uint8_t buf[3];
  45. /* 控制字节: Co=0, RS=1 (数据) */
  46. buf[0] = LCD_DATA_BYTE; /* 0x40 */
  47. buf[1] = data;
  48. if (i2c_write(buf, 2) < 0) {
  49. return -1;
  50. }
  51. lcd_delay_ms(1);
  52. return 0;
  53. }
  54. /* 初始化LCD - 根据数据手册的初始化流程 */
  55. int lcd_init(const char *i2c_bus, uint8_t addr)
  56. {
  57. i2c_addr = addr;
  58. /* 打开I2C设备 */
  59. i2c_fd = open(i2c_bus, O_RDWR);
  60. if (i2c_fd < 0) {
  61. perror("Failed to open I2C bus");
  62. return -1;
  63. }
  64. /* 设置I2C从机地址 */
  65. if (ioctl(i2c_fd, I2C_SLAVE, i2c_addr) < 0) {
  66. perror("Failed to set I2C address");
  67. close(i2c_fd);
  68. i2c_fd = -1;
  69. return -1;
  70. }
  71. /* 初始化延时 - 上电后需要等待 */
  72. lcd_delay_ms(50);
  73. /* 功能设置: 2行显示,5x7点阵 */
  74. /* 指令码: 0010 NFxx, N=1(2行), F=0(5x7) -> 0x28 */
  75. lcd_send_command(LCD_FUNCTION_SET | LCD_2LINE | LCD_5x8DOTS);
  76. lcd_delay_ms(5);
  77. /* 显示开/关控制: 显示开,光标关,闪烁关 */
  78. /* 指令码: 0000 1DCB, D=1, C=0, B=0 -> 0x0C */
  79. lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF);
  80. /* 清显示 */
  81. lcd_clear();
  82. /* 设置输入模式: 光标右移,显示不移位 */
  83. /* 指令码: 0000 01IS, I/D=1, S=0 -> 0x06 */
  84. lcd_send_command(LCD_ENTRY_MODE_SET | LCD_ENTRY_RIGHT | LCD_ENTRY_SHIFT_OFF);
  85. return 0;
  86. }
  87. /* 关闭LCD */
  88. void lcd_close(void)
  89. {
  90. if (i2c_fd >= 0) {
  91. /* 关闭显示 */
  92. lcd_send_command(LCD_DISPLAY_CONTROL | LCD_DISPLAY_OFF);
  93. close(i2c_fd);
  94. i2c_fd = -1;
  95. }
  96. }
  97. /* 清显示 - 指令码0x01 */
  98. int lcd_clear(void)
  99. {
  100. int ret = lcd_send_command(LCD_CLEAR_DISPLAY);
  101. lcd_delay_ms(2); /* 清显示需要1.52ms */
  102. return ret;
  103. }
  104. /* 归位 - 指令码0x02 */
  105. int lcd_home(void)
  106. {
  107. int ret = lcd_send_command(LCD_RETURN_HOME);
  108. lcd_delay_ms(2); /* 归位需要1.52ms */
  109. return ret;
  110. }
  111. /* 显示控制 */
  112. int lcd_display_control(uint8_t display, uint8_t cursor, uint8_t blink)
  113. {
  114. uint8_t cmd = LCD_DISPLAY_CONTROL | display | cursor | blink;
  115. return lcd_send_command(cmd);
  116. }
  117. /* 设置输入模式 */
  118. int lcd_entry_mode(uint8_t id, uint8_t shift)
  119. {
  120. uint8_t cmd = LCD_ENTRY_MODE_SET | id | shift;
  121. return lcd_send_command(cmd);
  122. }
  123. /* 设置光标位置 - 根据数据手册第16页DDRAM地址 */
  124. int lcd_set_cursor(uint8_t col, uint8_t row)
  125. {
  126. uint8_t addr;
  127. if (col >= LCD_COLS || row >= LCD_ROWS) {
  128. return -1;
  129. }
  130. /* 计算DDRAM地址 */
  131. if (row == 0) {
  132. addr = LCD_LINE1_ADDR + col; /* 第一行: 0x00-0x27 */
  133. } else {
  134. addr = LCD_LINE2_ADDR + col; /* 第二行: 0x40-0x67 */
  135. }
  136. return lcd_send_command(LCD_SET_DDRAM_ADDR | addr);
  137. }
  138. /* 写单个字符 */
  139. int lcd_write_char(char c)
  140. {
  141. return lcd_send_data((uint8_t)c);
  142. }
  143. /* 写字符串 */
  144. int lcd_write_string(const char *str)
  145. {
  146. while (*str) {
  147. if (lcd_write_char(*str++) < 0) {
  148. return -1;
  149. }
  150. }
  151. return 0;
  152. }
  153. /* 写入自定义字符到CGRAM - 根据数据手册第12页 */
  154. /* num: 0-7 (最多8个自定义字符) */
  155. /* data: 5x8点阵数据,8字节(每行5位有效) */
  156. int lcd_write_custom_char(uint8_t num, uint8_t *data)
  157. {
  158. uint8_t addr;
  159. int i;
  160. if (num > 7) {
  161. return -1;
  162. }
  163. /* 设置CGRAM地址 - 每个字符占8字节 */
  164. /* CGRAM地址: 0x01 ACG5-ACG0, 字符0地址=0x00, 字符1地址=0x08, ... */
  165. addr = num * 8;
  166. lcd_send_command(LCD_SET_CGRAM_ADDR | addr);
  167. /* 写入8行点阵数据 */
  168. for (i = 0; i < 8; i++) {
  169. /* 数据D4-D0有效,D7-D5必须为0 */
  170. lcd_send_data(data[i] & 0x1F);
  171. }
  172. return 0;
  173. }
  174. /* 显示自定义字符 */
  175. int lcd_show_custom_char(uint8_t num)
  176. {
  177. if (num > 7) {
  178. return -1;
  179. }
  180. /* 自定义字符的字符码为0x00-0x07 */
  181. return lcd_write_char(num);
  182. }
  183. /* 移位显示 */
  184. int lcd_shift_display(uint8_t direction)
  185. {
  186. uint8_t cmd = LCD_CURSOR_SHIFT | LCD_DISPLAY_MOVE | direction;
  187. return lcd_send_command(cmd);
  188. }
  189. /* 移位光标 */
  190. int lcd_shift_cursor(uint8_t direction)
  191. {
  192. uint8_t cmd = LCD_CURSOR_SHIFT | LCD_CURSOR_MOVE | direction;
  193. return lcd_send_command(cmd);
  194. }
  195. /* 使用DDRAM地址设置清除行 - 更高效 */
  196. int lcd_clear_line_fast(uint8_t row)
  197. {
  198. uint8_t addr;
  199. int i;
  200. if (row >= LCD_ROWS) {
  201. return -1;
  202. }
  203. /* 计算起始地址 */
  204. addr = (row == 0) ? LCD_LINE1_ADDR : LCD_LINE2_ADDR;
  205. /* 设置DDRAM地址 */
  206. lcd_send_command(LCD_SET_DDRAM_ADDR | addr);
  207. /* 连续写入20个空格(空位字符码0x20) */
  208. for (i = 0; i < LCD_COLS; i++) {
  209. lcd_send_data(0x20); /* 空格字符 */
  210. }
  211. return 0;
  212. }