drv_lcd.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. * Copyright (c) 2006-2018, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2019-03-12 ZYH first version
  9. */
  10. #include <rtthread.h>
  11. #ifdef BSP_USING_LCD
  12. #include <rtdevice.h>
  13. #include "drv_lcd.h"
  14. #include <board.h>
  15. #include <gpiohs.h>
  16. #include <spi.h>
  17. #include <drv_io_config.h>
  18. #include <rthw.h>
  19. #define DBG_ENABLE
  20. #define DBG_SECTION_NAME "LCD"
  21. #define DBG_LEVEL DBG_WARNING
  22. #define DBG_COLOR
  23. #include <rtdbg.h>
  24. #define NO_OPERATION 0x00
  25. #define SOFTWARE_RESET 0x01
  26. #define READ_ID 0x04
  27. #define READ_STATUS 0x09
  28. #define READ_POWER_MODE 0x0A
  29. #define READ_MADCTL 0x0B
  30. #define READ_PIXEL_FORMAT 0x0C
  31. #define READ_IMAGE_FORMAT 0x0D
  32. #define READ_SIGNAL_MODE 0x0E
  33. #define READ_SELT_DIAG_RESULT 0x0F
  34. #define SLEEP_ON 0x10
  35. #define SLEEP_OFF 0x11
  36. #define PARTIAL_DISPALY_ON 0x12
  37. #define NORMAL_DISPALY_ON 0x13
  38. #define INVERSION_DISPALY_OFF 0x20
  39. #define INVERSION_DISPALY_ON 0x21
  40. #define GAMMA_SET 0x26
  41. #define DISPALY_OFF 0x28
  42. #define DISPALY_ON 0x29
  43. #define HORIZONTAL_ADDRESS_SET 0x2A
  44. #define VERTICAL_ADDRESS_SET 0x2B
  45. #define MEMORY_WRITE 0x2C
  46. #define COLOR_SET 0x2D
  47. #define MEMORY_READ 0x2E
  48. #define PARTIAL_AREA 0x30
  49. #define VERTICAL_SCROL_DEFINE 0x33
  50. #define TEAR_EFFECT_LINE_OFF 0x34
  51. #define TEAR_EFFECT_LINE_ON 0x35
  52. #define MEMORY_ACCESS_CTL 0x36
  53. #define VERTICAL_SCROL_S_ADD 0x37
  54. #define IDLE_MODE_OFF 0x38
  55. #define IDLE_MODE_ON 0x39
  56. #define PIXEL_FORMAT_SET 0x3A
  57. #define WRITE_MEMORY_CONTINUE 0x3C
  58. #define READ_MEMORY_CONTINUE 0x3E
  59. #define SET_TEAR_SCANLINE 0x44
  60. #define GET_SCANLINE 0x45
  61. #define WRITE_BRIGHTNESS 0x51
  62. #define READ_BRIGHTNESS 0x52
  63. #define WRITE_CTRL_DISPALY 0x53
  64. #define READ_CTRL_DISPALY 0x54
  65. #define WRITE_BRIGHTNESS_CTL 0x55
  66. #define READ_BRIGHTNESS_CTL 0x56
  67. #define WRITE_MIN_BRIGHTNESS 0x5E
  68. #define READ_MIN_BRIGHTNESS 0x5F
  69. #define READ_ID1 0xDA
  70. #define READ_ID2 0xDB
  71. #define READ_ID3 0xDC
  72. #define RGB_IF_SIGNAL_CTL 0xB0
  73. #define NORMAL_FRAME_CTL 0xB1
  74. #define IDLE_FRAME_CTL 0xB2
  75. #define PARTIAL_FRAME_CTL 0xB3
  76. #define INVERSION_CTL 0xB4
  77. #define BLANK_PORCH_CTL 0xB5
  78. #define DISPALY_FUNCTION_CTL 0xB6
  79. #define ENTRY_MODE_SET 0xB7
  80. #define BACKLIGHT_CTL1 0xB8
  81. #define BACKLIGHT_CTL2 0xB9
  82. #define BACKLIGHT_CTL3 0xBA
  83. #define BACKLIGHT_CTL4 0xBB
  84. #define BACKLIGHT_CTL5 0xBC
  85. #define BACKLIGHT_CTL7 0xBE
  86. #define BACKLIGHT_CTL8 0xBF
  87. #define POWER_CTL1 0xC0
  88. #define POWER_CTL2 0xC1
  89. #define VCOM_CTL1 0xC5
  90. #define VCOM_CTL2 0xC7
  91. #define NV_MEMORY_WRITE 0xD0
  92. #define NV_MEMORY_PROTECT_KEY 0xD1
  93. #define NV_MEMORY_STATUS_READ 0xD2
  94. #define READ_ID4 0xD3
  95. #define POSITIVE_GAMMA_CORRECT 0xE0
  96. #define NEGATIVE_GAMMA_CORRECT 0xE1
  97. #define DIGITAL_GAMMA_CTL1 0xE2
  98. #define DIGITAL_GAMMA_CTL2 0xE3
  99. #define INTERFACE_CTL 0xF6
  100. typedef enum _lcd_dir
  101. {
  102. DIR_XY_RLUD = 0x00,
  103. DIR_YX_RLUD = 0x20,
  104. DIR_XY_LRUD = 0x40,
  105. DIR_YX_LRUD = 0x60,
  106. DIR_XY_RLDU = 0x80,
  107. DIR_YX_RLDU = 0xA0,
  108. DIR_XY_LRDU = 0xC0,
  109. DIR_YX_LRDU = 0xE0,
  110. DIR_XY_MASK = 0x20,
  111. DIR_MASK = 0xE0,
  112. } lcd_dir_t;
  113. #define LCD_SPI_CHANNEL SPI_DEVICE_0
  114. #define LCD_SPI_CHIP_SELECT SPI_CHIP_SELECT_0
  115. typedef struct lcd_8080_device
  116. {
  117. struct rt_device parent;
  118. struct rt_device_graphic_info lcd_info;
  119. int spi_channel;
  120. int cs;
  121. int dc_pin;
  122. int dma_channel;
  123. } * lcd_8080_device_t;
  124. static void drv_lcd_cmd(lcd_8080_device_t lcd, rt_uint8_t cmd)
  125. {
  126. gpiohs_set_pin(lcd->dc_pin, GPIO_PV_LOW);
  127. spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
  128. spi_init_non_standard(lcd->spi_channel, 8 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
  129. SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
  130. spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, &cmd, 1, SPI_TRANS_CHAR);
  131. }
  132. static void drv_lcd_data_byte(lcd_8080_device_t lcd, rt_uint8_t *data_buf, rt_uint32_t length)
  133. {
  134. gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
  135. spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
  136. spi_init_non_standard(lcd->spi_channel, 8 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
  137. SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
  138. spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, data_buf, length, SPI_TRANS_CHAR);
  139. }
  140. static void drv_lcd_data_half_word(lcd_8080_device_t lcd, rt_uint16_t *data_buf, rt_uint32_t length)
  141. {
  142. gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
  143. spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 16, 0);
  144. spi_init_non_standard(lcd->spi_channel, 16 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
  145. SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
  146. spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, data_buf, length, SPI_TRANS_SHORT);
  147. }
  148. static void drv_lcd_data_word(lcd_8080_device_t lcd, rt_uint32_t *data_buf, rt_uint32_t length)
  149. {
  150. gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
  151. spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 32, 0);
  152. spi_init_non_standard(lcd->spi_channel, 0 /*instrction length*/, 32 /*address length*/, 0 /*wait cycles*/,
  153. SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
  154. spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, data_buf, length, SPI_TRANS_INT);
  155. }
  156. static void drv_lcd_hw_init(lcd_8080_device_t lcd)
  157. {
  158. gpiohs_set_drive_mode(lcd->dc_pin, GPIO_DM_OUTPUT);
  159. gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
  160. spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
  161. spi_set_clk_rate(lcd->spi_channel, 25000000);
  162. }
  163. static void drv_lcd_set_direction(lcd_8080_device_t lcd, lcd_dir_t dir)
  164. {
  165. #if !BOARD_LICHEEDAN
  166. dir |= 0x08;
  167. #endif
  168. if (dir & DIR_XY_MASK)
  169. {
  170. lcd->lcd_info.width = BSP_LCD_Y_MAX;
  171. lcd->lcd_info.height = BSP_LCD_X_MAX;
  172. }
  173. else
  174. {
  175. lcd->lcd_info.width = BSP_LCD_X_MAX;
  176. lcd->lcd_info.height = BSP_LCD_Y_MAX;
  177. }
  178. drv_lcd_cmd(lcd, MEMORY_ACCESS_CTL);
  179. drv_lcd_data_byte(lcd, (rt_uint8_t *)&dir, 1);
  180. }
  181. static void drv_lcd_set_area(lcd_8080_device_t lcd, rt_uint16_t x1, rt_uint16_t y1, rt_uint16_t x2, rt_uint16_t y2)
  182. {
  183. rt_uint8_t data[4] = {0};
  184. data[0] = (rt_uint8_t)(x1 >> 8);
  185. data[1] = (rt_uint8_t)(x1);
  186. data[2] = (rt_uint8_t)(x2 >> 8);
  187. data[3] = (rt_uint8_t)(x2);
  188. drv_lcd_cmd(lcd, HORIZONTAL_ADDRESS_SET);
  189. drv_lcd_data_byte(lcd, data, 4);
  190. data[0] = (rt_uint8_t)(y1 >> 8);
  191. data[1] = (rt_uint8_t)(y1);
  192. data[2] = (rt_uint8_t)(y2 >> 8);
  193. data[3] = (rt_uint8_t)(y2);
  194. drv_lcd_cmd(lcd, VERTICAL_ADDRESS_SET);
  195. drv_lcd_data_byte(lcd, data, 4);
  196. drv_lcd_cmd(lcd, MEMORY_WRITE);
  197. }
  198. static void drv_lcd_set_pixel(lcd_8080_device_t lcd, uint16_t x, uint16_t y, uint16_t color)
  199. {
  200. drv_lcd_set_area(lcd, x, y, x, y);
  201. drv_lcd_data_half_word(lcd, &color, 1);
  202. }
  203. static void drv_lcd_clear(lcd_8080_device_t lcd, uint16_t color)
  204. {
  205. uint32_t data = ((uint32_t)color << 16) | (uint32_t)color;
  206. drv_lcd_set_area(lcd, 0, 0, lcd->lcd_info.width - 1, lcd->lcd_info.height - 1);
  207. gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
  208. spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 32, 0);
  209. spi_init_non_standard(lcd->spi_channel, 0 /*instrction length*/, 32 /*address length*/, 0 /*wait cycles*/,
  210. SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
  211. spi_fill_data_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, (const uint32_t *)&data, lcd->lcd_info.width * lcd->lcd_info.height / 2);
  212. }
  213. static void rt_bitblt(rt_uint16_t * dest, int dest_segment, int dest_common, int dest_x, int dest_y, int width, int height,
  214. rt_uint16_t *src, int src_segment, int src_common, int src_x, int src_y)
  215. {
  216. int sx0, sx1, sy0, sy1;
  217. int dx0, dx1, dy0, dy1;
  218. rt_uint16_t *buff_src;
  219. rt_uint16_t *buff_dest;
  220. int x, y;
  221. if (width <= 0) {
  222. return;
  223. }
  224. if (height <= 0) {
  225. return;
  226. }
  227. sx0 = src_x;
  228. sy0 = src_y;
  229. sx1 = sx0 + width - 1;
  230. sy1 = sy0 + height - 1;
  231. dx0 = dest_x;
  232. dy0 = dest_y;
  233. dx1 = dx0 + width - 1;
  234. dy1 = dy0 + height - 1;
  235. if (sx0 < 0) {
  236. dx0 -= sx0;
  237. sx0 = 0;
  238. }
  239. if (sy0 < 0) {
  240. dy0 -= sy0;
  241. sy0 = 0;
  242. }
  243. if (sx1 >= src_segment) {
  244. dx1 -= (sx1 - src_segment + 1);
  245. sx1 = src_segment - 1;
  246. }
  247. if (sy1 >= src_common) {
  248. dy1 -= (sy1 - src_common + 1);
  249. sy1 = src_common - 1;
  250. }
  251. if (dx0 < 0) {
  252. sx0 -= dx0;
  253. dx0 = 0;
  254. }
  255. if (dy0 < 0) {
  256. sy0 -= dy0;
  257. dy0 = 0;
  258. }
  259. if (dx1 >= dest_segment) {
  260. sx1 -= (dx1 - dest_segment + 1);
  261. dx1 = dest_segment - 1;
  262. }
  263. if (dy1 >= dest_common) {
  264. sy1 -= (dy1 - dest_common + 1);
  265. dy1 = dest_common - 1;
  266. }
  267. if (sx1 < 0 || sx0 >= src_segment) {
  268. return;
  269. }
  270. if (sy1 < 0 || sy0 >= src_common) {
  271. return;
  272. }
  273. if (dx1 < 0 || dx0 >= dest_segment) {
  274. return;
  275. }
  276. if (dy1 < 0 || dy0 >= dest_common) {
  277. return;
  278. }
  279. if ((rt_ubase_t)dest < (rt_ubase_t)src) {
  280. buff_src = src + (sy0 * src_segment) + sx0;
  281. buff_dest = dest + (dy0 * dest_segment) + dx0;
  282. for (y = sy0; y <= sy1; y++) {
  283. src = buff_src;
  284. dest = buff_dest;
  285. for (x = sx0; x <= sx1; x++) {
  286. *dest++ = *src++;
  287. }
  288. buff_src += src_segment;
  289. buff_dest += dest_segment;
  290. }
  291. } else {
  292. buff_src = src + (sy1 * src_segment) + sx1;
  293. buff_dest = dest + (dy1 * dest_segment) + dx1;
  294. for (y = sy1; y >= sy0; y--) {
  295. src = buff_src;
  296. dest = buff_dest;
  297. for (x = sx1; x >= sx0; x--) {
  298. *dest-- = *src--;
  299. }
  300. buff_src -= src_segment;
  301. buff_dest -= dest_segment;
  302. }
  303. }
  304. }
  305. static void drv_lcd_rect_update(lcd_8080_device_t lcd, uint16_t x1, uint16_t y1, uint16_t width, uint16_t height)
  306. {
  307. static rt_uint16_t * rect_buffer = RT_NULL;
  308. if(!rect_buffer)
  309. {
  310. rect_buffer = rt_malloc_align(lcd->lcd_info.height * lcd->lcd_info.width * (lcd->lcd_info.bits_per_pixel / 8), 64);
  311. if(!rect_buffer)
  312. {
  313. return;
  314. }
  315. }
  316. if(x1 == 0 && y1 == 0 && width == lcd->lcd_info.width && height == lcd->lcd_info.height)
  317. {
  318. drv_lcd_set_area(lcd, x1, y1, x1 + width - 1, y1 + height - 1);
  319. drv_lcd_data_word(lcd, (rt_uint32_t *)lcd->lcd_info.framebuffer, width * height / (lcd->lcd_info.bits_per_pixel / 8));
  320. }
  321. else
  322. {
  323. rt_bitblt(rect_buffer, width, height, 0, 0, width, height,
  324. (rt_uint16_t *)lcd->lcd_info.framebuffer, lcd->lcd_info.width, lcd->lcd_info.height, x1, y1);
  325. drv_lcd_set_area(lcd, x1, y1, x1 + width - 1, y1 + height - 1);
  326. drv_lcd_data_word(lcd, (rt_uint32_t *)rect_buffer, width * height / 2);
  327. }
  328. }
  329. static rt_err_t drv_lcd_init(rt_device_t dev)
  330. {
  331. rt_err_t ret = RT_EOK;
  332. lcd_8080_device_t lcd = (lcd_8080_device_t)dev;
  333. rt_uint8_t data = 0;
  334. if(!lcd)
  335. {
  336. return RT_ERROR;
  337. }
  338. drv_lcd_hw_init(lcd);
  339. /* reset LCD */
  340. drv_lcd_cmd(lcd, SOFTWARE_RESET);
  341. rt_thread_mdelay(100);
  342. /* Enter normal status */
  343. drv_lcd_cmd(lcd, SLEEP_OFF);
  344. rt_thread_mdelay(100);
  345. /* pixel format rgb565 */
  346. drv_lcd_cmd(lcd, PIXEL_FORMAT_SET);
  347. data = 0x55;
  348. drv_lcd_data_byte(lcd, &data, 1);
  349. /* set direction */
  350. drv_lcd_set_direction(lcd, DIR_YX_RLUD);
  351. lcd->lcd_info.framebuffer = rt_malloc_align(lcd->lcd_info.height * lcd->lcd_info.width * (lcd->lcd_info.bits_per_pixel / 8), 64);
  352. RT_ASSERT(lcd->lcd_info.framebuffer);
  353. /*display on*/
  354. drv_lcd_cmd(lcd, DISPALY_ON);
  355. /* set to black */
  356. drv_lcd_clear(lcd, 0x0000);
  357. return ret;
  358. }
  359. static rt_err_t drv_lcd_open(rt_device_t dev, rt_uint16_t oflag)
  360. {
  361. /* Not need */
  362. return RT_EOK;
  363. }
  364. static rt_err_t drv_lcd_close(rt_device_t dev)
  365. {
  366. /* Not need */
  367. return RT_EOK;
  368. }
  369. static rt_size_t drv_lcd_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
  370. {
  371. /* Not need */
  372. return 0;
  373. }
  374. static rt_size_t drv_lcd_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
  375. {
  376. /* Not need */
  377. return 0;
  378. }
  379. static rt_err_t drv_lcd_control(rt_device_t dev, int cmd, void *args)
  380. {
  381. rt_err_t ret = RT_EOK;
  382. lcd_8080_device_t lcd = (lcd_8080_device_t)dev;
  383. rt_base_t level;
  384. struct rt_device_rect_info* rect_info = (struct rt_device_rect_info*)args;
  385. RT_ASSERT(dev != RT_NULL);
  386. switch (cmd)
  387. {
  388. case RTGRAPHIC_CTRL_RECT_UPDATE:
  389. if(!rect_info)
  390. {
  391. LOG_E("RTGRAPHIC_CTRL_RECT_UPDATE error args");
  392. return -RT_ERROR;
  393. }
  394. drv_lcd_rect_update(lcd, rect_info->x, rect_info->y, rect_info->width, rect_info->height);
  395. break;
  396. case RTGRAPHIC_CTRL_POWERON:
  397. /* Todo: power on */
  398. ret = -RT_ENOSYS;
  399. break;
  400. case RTGRAPHIC_CTRL_POWEROFF:
  401. /* Todo: power off */
  402. ret = -RT_ENOSYS;
  403. break;
  404. case RTGRAPHIC_CTRL_GET_INFO:
  405. *(struct rt_device_graphic_info *)args = lcd->lcd_info;
  406. break;
  407. case RTGRAPHIC_CTRL_SET_MODE:
  408. ret = -RT_ENOSYS;
  409. break;
  410. case RTGRAPHIC_CTRL_GET_EXT:
  411. ret = -RT_ENOSYS;
  412. break;
  413. default:
  414. LOG_E("drv_lcd_control cmd: %d", cmd);
  415. break;
  416. }
  417. return ret;
  418. }
  419. #ifdef RT_USING_DEVICE_OPS
  420. const static struct rt_device_ops drv_lcd_ops =
  421. {
  422. drv_lcd_init,
  423. drv_lcd_open,
  424. drv_lcd_close,
  425. drv_lcd_read,
  426. drv_lcd_write,
  427. drv_lcd_control
  428. };
  429. #endif
  430. int rt_hw_lcd_init(void)
  431. {
  432. rt_err_t ret = RT_EOK;
  433. lcd_8080_device_t lcd_dev = (lcd_8080_device_t)rt_malloc(sizeof(struct lcd_8080_device));
  434. if(!lcd_dev)
  435. {
  436. return -1;
  437. }
  438. lcd_dev->cs = SPI_CHIP_SELECT_0;
  439. lcd_dev->dc_pin = LCD_DC_PIN;
  440. lcd_dev->dma_channel = DMAC_CHANNEL0;
  441. lcd_dev->spi_channel = SPI_DEVICE_0;
  442. lcd_dev->lcd_info.bits_per_pixel = 16;
  443. lcd_dev->lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565;
  444. lcd_dev->parent.type = RT_Device_Class_Graphic;
  445. lcd_dev->parent.rx_indicate = RT_NULL;
  446. lcd_dev->parent.tx_complete = RT_NULL;
  447. #ifdef RT_USING_DEVICE_OPS
  448. lcd_dev->parent.ops = &drv_lcd_ops;
  449. #else
  450. lcd_dev->parent.init = drv_lcd_init;
  451. lcd_dev->parent.open = drv_lcd_open;
  452. lcd_dev->parent.close = drv_lcd_close;
  453. lcd_dev->parent.read = drv_lcd_read;
  454. lcd_dev->parent.write = drv_lcd_write;
  455. lcd_dev->parent.control = drv_lcd_control;
  456. #endif
  457. lcd_dev->parent.user_data = RT_NULL;
  458. ret = rt_device_register(&lcd_dev->parent, "lcd", RT_DEVICE_FLAG_RDWR);
  459. return ret;
  460. }
  461. INIT_DEVICE_EXPORT(rt_hw_lcd_init);
  462. #endif