drv_st7796.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2023-05-23 Chushicheng the first version
  9. */
  10. #include "drv_spi.h"
  11. #include "drv_st7796.h"
  12. #define LOG_TAG "drv.st7796"
  13. #include <drv_log.h>
  14. #ifndef ST7796_LCD_IPS_PANEL
  15. #define ST7796_LCD_IPS_PANEL 1
  16. #else
  17. #define ST7796_LCD_IPS_PANEL 0
  18. #endif
  19. #define ST7796_INIT_SEQ_NAME(x) st7796_init_seq_##x##_480_320
  20. #if ST7796_LCD_IPS_PANEL
  21. #define ST7796_LCD_INIT_SEQ ST7796_INIT_SEQ_NAME(ips)
  22. #else
  23. #define ST7796_LCD_INIT_SEQ ST7796_INIT_SEQ_NAME(tft)
  24. #endif
  25. static st7796_t lcd_spi_obj;
  26. static rt_err_t _st7796_init_seq(st7796_lcd_t *lcd);
  27. static rt_err_t _st7796_window(st7796_lcd_t *lcd, rt_uint16_t x_start, rt_uint16_t x_end, rt_uint16_t y_start, rt_uint16_t y_end);
  28. static rt_err_t _st7796_reset(st7796_lcd_t *lcd);
  29. static rt_err_t st7796_lcd_load(st7796_lcd_t *lcd, void *data, rt_uint16_t x_start, rt_uint16_t x_end, rt_uint16_t y_start, rt_uint16_t y_end);
  30. static rt_err_t st7796_lcd_sleep(st7796_lcd_t *lcd, rt_uint8_t sleep_mode);
  31. static rt_err_t st7796_lcd_display(st7796_lcd_t *lcd, rt_uint8_t display_on);
  32. static rt_err_t st7796_lcd_config(st7796_lcd_t *lcd, st7796_config_t *config);
  33. void lcd_load(rt_uint16_t x_start, rt_uint16_t x_end, rt_uint16_t y_start, rt_uint16_t y_end, void *data);
  34. static rt_uint8_t st7796_init_seq_tft_480_320[] =
  35. {
  36. 0x01, 0xF0, 0xC3, /* Enable command part 1 */
  37. 0x01, 0xF0, 0x96, /* Enable command part 2*/
  38. 0x08, 0xE8, 0x40, 0x82, 0x07, 0x18, 0x27, 0x0A, 0xB6, 0x33, /* DOCA */
  39. 0x01, 0xC5, 0x27, /* VCOM control */
  40. 0x01, 0xC2, 0xA7, /* Power control 3 */
  41. 0x0E, 0xE0, 0xF0, 0x01, 0x06, 0x0F, 0x12, 0x1D, 0x36, 0x54, 0x44, 0x0C, 0x18, 0x16, 0x13, 0x15, /* PGC */
  42. 0x0E, 0xE1, 0xF0, 0x01, 0x05, 0x0A, 0x0B, 0x07, 0x32, 0x44, 0x44, 0x0C, 0x18, 0x17, 0x13, 0x16, /* NGC */
  43. 0x01, 0xF0, 0x3C, /* Disable command part 1 */
  44. 0x01, 0xF0, 0x69, /* Disable command part 2 */
  45. };
  46. static rt_uint8_t st7796_init_seq_ips_480_320[] =
  47. {
  48. 0x01, 0xF0, 0xC3, /* Enable command part 1 */
  49. 0x01, 0xF0, 0x96, /* Enable command part 2 */
  50. 0x01, 0xB4, 0x01, /* Display inversion */
  51. 0x02, 0xB1, 0x80, 0x10, /* Frame rate control 1 */
  52. 0x04, 0xB5, 0x1F, 0x50, 0x00, 0x20, /* Blanking porch control */
  53. 0x03, 0xB6, 0x8A, 0x07, 0x3B, /* Display function control */
  54. 0x02, 0xC0, 0x80, 0x64, /* Power control 1 */
  55. 0x01, 0xC1, 0x13, /* Power control 2 */
  56. 0x01, 0xC2, 0xA7, /* Power control 3 */
  57. 0x01, 0xC5, 0x09, /* VCOM control */
  58. 0x08, 0xE8, 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33, /* DOCA */
  59. 0x0E, 0xE0, 0xF0, 0x06, 0x0B, 0x07, 0x06, 0x05, 0x2E, 0x33, 0x47, 0x3A, 0x17, 0x16, 0x2E, 0x31, /* PGC */
  60. 0x0E, 0xE1, 0xF0, 0x09, 0x0D, 0x09, 0x08, 0x23, 0x2E, 0x33, 0x46, 0x38, 0x13, 0x13, 0x2C, 0x32, /* NGC */
  61. 0x01, 0xF0, 0x3C, /* Disable command part 1 */
  62. 0x01, 0xF0, 0x69, /* Disable command part 2 */
  63. };
  64. static rt_err_t _st7796_init_seq(st7796_lcd_t *lcd)
  65. {
  66. rt_uint16_t i = 0;
  67. while (i < sizeof(ST7796_LCD_INIT_SEQ))
  68. {
  69. if (lcd->cb.write_cmd_cb(lcd->user_data, &ST7796_LCD_INIT_SEQ[i + 1], ST7796_LCD_INIT_SEQ[i] + 1) != RT_EOK)
  70. {
  71. return -RT_ERROR;
  72. };
  73. i += ST7796_LCD_INIT_SEQ[i] + 2;
  74. }
  75. return RT_EOK;
  76. }
  77. static rt_err_t _st7796_window(st7796_lcd_t *lcd, rt_uint16_t x_start, rt_uint16_t x_end, rt_uint16_t y_start, rt_uint16_t y_end)
  78. {
  79. rt_uint16_t real_x_start, real_x_end, real_y_start, real_y_end;
  80. rt_uint16_t x_offset, y_offset;
  81. switch (lcd->config.direction)
  82. {
  83. case ST7796_DIR_0:
  84. x_offset = 0;
  85. y_offset = 0;
  86. break;
  87. case ST7796_DIR_90:
  88. x_offset = 0;
  89. y_offset = 0;
  90. break;
  91. case ST7796_DIR_180:
  92. x_offset = 320;
  93. y_offset = 480;
  94. break;
  95. case ST7796_DIR_270:
  96. x_offset = 480;
  97. y_offset = 320;
  98. break;
  99. default:
  100. x_offset = 0;
  101. y_offset = 0;
  102. }
  103. real_x_start = x_start + x_offset;
  104. real_x_end = x_end + x_offset;
  105. real_y_start = y_start + y_offset;
  106. real_y_end = y_end + y_offset;
  107. rt_uint8_t tx_buf[5] = {0x2A, ((rt_uint8_t)(real_x_start >> 0x08U) & 0xFFU), (real_x_start & 0xFFU),
  108. ((rt_uint8_t)(real_x_end >> 0x08U) & 0xFFU), (real_x_end & 0xFFU)};
  109. if (lcd->cb.write_cmd_cb(lcd->user_data, tx_buf, 0x05) != RT_EOK)
  110. {
  111. return -RT_ERROR;
  112. }
  113. tx_buf[0] = 0x2B;
  114. tx_buf[1] = ((rt_uint8_t)(real_y_start >> 0x08U) & 0xFFU);
  115. tx_buf[2] = (real_y_start & 0xFFU);
  116. tx_buf[3] = ((rt_uint8_t)(real_y_end >> 0x08U) & 0xFFU);
  117. tx_buf[4] = (real_y_end & 0xFFU);
  118. if (lcd->cb.write_cmd_cb(lcd->user_data, tx_buf, 0x05) != RT_EOK)
  119. {
  120. return -RT_ERROR;
  121. }
  122. return RT_EOK;
  123. }
  124. static rt_err_t _st7796_reset(st7796_lcd_t *lcd)
  125. {
  126. return lcd->cb.reset_cb(lcd->user_data);
  127. }
  128. static rt_err_t lcd_impl_reset(void *handle)
  129. {
  130. rt_pin_write(BSP_LCD_RST_PIN, PIN_LOW);
  131. rt_thread_mdelay(50);
  132. rt_pin_write(BSP_LCD_RST_PIN, PIN_HIGH);
  133. rt_thread_mdelay(50);
  134. return RT_EOK;
  135. }
  136. static rt_err_t st7796_lcd_init(st7796_lcd_t *lcd)
  137. {
  138. if (_st7796_reset(lcd) != RT_EOK) return -RT_ERROR;
  139. if (_st7796_init_seq(lcd) != RT_EOK) return -RT_ERROR;
  140. if (st7796_lcd_config(lcd, &lcd->config) != RT_EOK) return -RT_ERROR;
  141. if (st7796_lcd_sleep(lcd, 0) != RT_EOK) return -RT_ERROR;
  142. if (st7796_lcd_display(lcd, 1) != RT_EOK) return -RT_ERROR;
  143. return RT_EOK;
  144. }
  145. static rt_err_t st7796_lcd_load(st7796_lcd_t *lcd, void *data, rt_uint16_t x_start, rt_uint16_t x_end, rt_uint16_t y_start, rt_uint16_t y_end)
  146. {
  147. rt_uint32_t pixel_count = (y_end - y_start + 1) * (x_end - x_start + 1);
  148. rt_uint32_t data_len = 0;
  149. switch (lcd->config.pix_fmt)
  150. {
  151. case ST7796_RGB444:
  152. data_len = pixel_count * 3 / 2;
  153. break;
  154. case ST7796_RGB565:
  155. data_len = pixel_count * 2;
  156. break;
  157. case ST7796_RGB666:
  158. case ST7796_RGB888:
  159. data_len = pixel_count * 3;
  160. break;
  161. default:
  162. data_len = pixel_count;
  163. break;
  164. }
  165. /* Set cursor */
  166. if (_st7796_window(lcd, x_start, x_end, y_start, y_end) != RT_EOK)
  167. {
  168. return -RT_ERROR;
  169. }
  170. rt_uint8_t command = 0x2C; /* Memory Write */
  171. if (lcd->cb.write_cmd_cb(lcd->user_data, &command, 0x01) != RT_EOK)
  172. {
  173. return -RT_ERROR;
  174. }
  175. /* Write pixel data */
  176. if (lcd->cb.write_data_cb(lcd->user_data, data, data_len) != RT_EOK)
  177. {
  178. return -RT_ERROR;
  179. }
  180. return RT_EOK;
  181. }
  182. void lcd_load(rt_uint16_t x_start, rt_uint16_t x_end, rt_uint16_t y_start, rt_uint16_t y_end, void *data)
  183. {
  184. st7796_t *lcd_obj = (st7796_t *)rt_device_find("lcd");
  185. st7796_lcd_load(&lcd_obj->st7796, data, x_start, x_end, y_start, y_end);
  186. }
  187. static rt_err_t st7796_lcd_sleep(st7796_lcd_t *lcd, rt_uint8_t sleep_mode)
  188. {
  189. /* Write SLPIN or SLPOUT command */
  190. rt_uint8_t command = sleep_mode ? 0x10 : 0x11;
  191. return lcd->cb.write_cmd_cb(lcd->user_data, &command, 0x01);
  192. }
  193. static rt_err_t st7796_lcd_display(st7796_lcd_t *lcd, rt_uint8_t display_on)
  194. {
  195. /* write display_on command */
  196. rt_uint8_t command = display_on ? 0x29 : 0x28;
  197. if (lcd->cb.write_cmd_cb(lcd->user_data, &command, 0x01) != RT_EOK)
  198. {
  199. return -RT_ERROR;
  200. }
  201. if ((lcd->cb.backlight_cb != NULL) && (lcd->cb.backlight_cb(lcd->user_data, display_on) != RT_EOK))
  202. {
  203. return -RT_ERROR;
  204. }
  205. return RT_EOK;
  206. }
  207. static rt_err_t st7796_lcd_config(st7796_lcd_t *lcd, st7796_config_t *config)
  208. {
  209. lcd->config.direction = config->direction;
  210. /* Write inversion command */
  211. rt_uint8_t command[2] = {config->inversion ? 0x20 : 0x21, 0x00};
  212. if (lcd->cb.write_cmd_cb(lcd->user_data, command, 0x01) != RT_EOK)
  213. {
  214. return -RT_ERROR;
  215. }
  216. lcd->config.inversion = config->inversion;
  217. command[0] = 0x3A;
  218. command[1] = config->pix_fmt;
  219. if (lcd->cb.write_cmd_cb(lcd->user_data, command, 0x02) != RT_EOK)
  220. {
  221. return -RT_ERROR;
  222. }
  223. lcd->config.pix_fmt = config->pix_fmt;
  224. command[0] = 0x36;
  225. command[1] = config->direction;
  226. if (!config->bgr_mode)
  227. {
  228. command[1] &= ~0x08U;
  229. }
  230. if (config->mirrored)
  231. {
  232. /* Invert X or Y bit */
  233. if (config->direction == ST7796_DIR_90 || config->direction == ST7796_DIR_270)
  234. {
  235. command[1] ^= 0x80U;
  236. }
  237. else
  238. {
  239. command[1] ^= 0x40U;
  240. }
  241. }
  242. return lcd->cb.write_cmd_cb(lcd->user_data, command, 0x02);
  243. }
  244. static rt_err_t lcd_impl_write_cmd(void *handle, rt_uint8_t *cmd, rt_uint8_t len)
  245. {
  246. st7796_t *nxp_lcd = (st7796_t*)handle;
  247. rt_pin_write(BSP_LCD_DC_PIN, PIN_LOW);
  248. rt_spi_send(nxp_lcd->spi_dev, cmd, 1);
  249. if (len > 1)
  250. {
  251. rt_pin_write(BSP_LCD_DC_PIN, PIN_HIGH);
  252. rt_spi_send(nxp_lcd->spi_dev, &cmd[1], len-1);
  253. }
  254. return RT_EOK;
  255. }
  256. static rt_err_t lcd_impl_write_data(void *handle, void *data, rt_uint32_t len)
  257. {
  258. st7796_t *nxp_lcd = (st7796_t*)handle;
  259. rt_pin_write(BSP_LCD_DC_PIN, PIN_HIGH);
  260. rt_spi_send(nxp_lcd->spi_dev, data, len);
  261. return RT_EOK;
  262. }
  263. int drv_st7796_init(void)
  264. {
  265. rt_pin_mode(BSP_LCD_RST_PIN, PIN_MODE_OUTPUT);
  266. rt_pin_mode(BSP_LCD_DC_PIN, PIN_MODE_OUTPUT);
  267. rt_pin_write(BSP_LCD_RST_PIN, PIN_HIGH);
  268. lcd_spi_obj.st7796.config.direction = ST7796_DIR_90;
  269. lcd_spi_obj.st7796.config.pix_fmt = ST7796_RGB565;
  270. lcd_spi_obj.st7796.config.bgr_mode = 1;
  271. lcd_spi_obj.st7796.config.inversion = 0;
  272. lcd_spi_obj.st7796.config.mirrored = 1;
  273. lcd_spi_obj.st7796.cb.reset_cb = lcd_impl_reset;
  274. lcd_spi_obj.st7796.cb.write_cmd_cb = lcd_impl_write_cmd;
  275. lcd_spi_obj.st7796.cb.write_data_cb = lcd_impl_write_data;
  276. lcd_spi_obj.st7796.user_data = lcd_spi_obj.parent.user_data = &lcd_spi_obj;
  277. rt_hw_spi_device_attach(BSP_LCD_SPI_BUS, LCD_DEVICE_NAME, BSP_LCD_CS_PIN);
  278. lcd_spi_obj.spi_dev = (struct rt_spi_device *)rt_device_find(LCD_DEVICE_NAME);
  279. if (!lcd_spi_obj.spi_dev)
  280. {
  281. LOG_E("lcd init run failed! can't find %s device!", LCD_DEVICE_NAME);
  282. return -RT_ERROR;
  283. }
  284. struct rt_spi_configuration cfg;
  285. cfg.data_width = 8;
  286. cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */
  287. cfg.max_hz = 10 * 1000 * 1000; /* 10M */
  288. rt_spi_configure(lcd_spi_obj.spi_dev, &cfg);
  289. st7796_lcd_init(&lcd_spi_obj.st7796);
  290. rt_device_register(&lcd_spi_obj.parent, "lcd", RT_DEVICE_FLAG_RDWR);
  291. return RT_EOK;
  292. }
  293. INIT_ENV_EXPORT(drv_st7796_init);