drv_lcd.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. /*
  2. * Copyright (c) 2006-2025, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2025-05-23 godmial Refactor to conform to RT-Thread coding style.
  9. */
  10. #include <board.h>
  11. #include "gd32f4xx.h"
  12. #include "gd32f4xx_tli.h"
  13. #include "drv_lcd.h"
  14. #include "font.h"
  15. #if defined(__CC_ARM) || defined(__ARMCC_VERSION)
  16. /* Keil MDK Compiler */
  17. uint16_t ltdc_lcd_framebuf0[800][480] __attribute__((at(LCD_FRAME_BUF_ADDR)));
  18. #elif defined(__GNUC__)
  19. /* GCC Compiler (used by RT-Thread) */
  20. uint16_t ltdc_lcd_framebuf0[10][10];
  21. #endif
  22. static void tli_gpio_config(void);
  23. static void tli_config(void);
  24. static void ipa_config(void);
  25. static void lcd_disp_en_config(void);
  26. static void lcd_disp_off(void);
  27. static void lcd_disp_on(void);
  28. /**
  29. * @brief Configure and initialize the LCD display.
  30. *
  31. * @note This function enables display power, initializes TLI GPIO and IPA,
  32. * and sets up TLI and its layer configuration.
  33. *
  34. * @param None
  35. *
  36. * @return None
  37. *
  38. * @warning Should be called during system initialization phase.
  39. */
  40. void lcd_disp_config(void)
  41. {
  42. lcd_disp_en_config();
  43. lcd_disp_off();
  44. /* configure the GPIO of TLI */
  45. tli_gpio_config();
  46. lcd_disp_on();
  47. tli_config();
  48. tli_layer_enable(LAYER0);
  49. tli_reload_config(TLI_FRAME_BLANK_RELOAD_EN);
  50. tli_enable();
  51. ipa_config();
  52. }
  53. /**
  54. * @brief Initialize the TLI peripheral and layer configuration.
  55. *
  56. * @note This function sets up the TLI clock, background color, timing parameters,
  57. * and layer 0 settings including framebuffer and blending configuration.
  58. *
  59. * @param None
  60. *
  61. * @return None
  62. *
  63. * @warning Must be called after clock and GPIO initialization.
  64. */
  65. static void tli_config(void)
  66. {
  67. tli_parameter_struct tli_init_struct;
  68. tli_layer_parameter_struct tli_layer_init_struct;
  69. rcu_periph_clock_enable(RCU_TLI);
  70. tli_gpio_config();
  71. /* configure PLLSAI to generate TLI clock */
  72. // if(ERROR == rcu_pllsai_config(216, 2, 3)){
  73. if (ERROR == rcu_pllsai_config(192, 2, 3))
  74. {
  75. while (1);
  76. }
  77. rcu_tli_clock_div_config(RCU_PLLSAIR_DIV2);
  78. rcu_osci_on(RCU_PLLSAI_CK);
  79. if (ERROR == rcu_osci_stab_wait(RCU_PLLSAI_CK))
  80. {
  81. while (1);
  82. }
  83. /* configure TLI parameter struct */
  84. tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
  85. tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
  86. tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
  87. tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI;
  88. /* LCD display timing configuration */
  89. tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;
  90. tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;
  91. tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;
  92. tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;
  93. tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1;
  94. tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1;
  95. tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1;
  96. tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1;
  97. /* configure LCD background R,G,B values */
  98. tli_init_struct.backcolor_red = 0xFF;
  99. tli_init_struct.backcolor_green = 0xFF;
  100. tli_init_struct.backcolor_blue = 0xFF;
  101. tli_init(&tli_init_struct);
  102. #if 1
  103. /* TLI layer0 configuration */
  104. /* TLI window size configuration */
  105. tli_layer_init_struct.layer_window_leftpos = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH;
  106. tli_layer_init_struct.layer_window_rightpos = (ACTIVE_WIDTH + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
  107. tli_layer_init_struct.layer_window_toppos = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
  108. tli_layer_init_struct.layer_window_bottompos = (ACTIVE_HEIGHT + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
  109. /* TLI window pixel format configuration */
  110. tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
  111. /* TLI window specified alpha configuration */
  112. tli_layer_init_struct.layer_sa = 255;
  113. /* TLI layer default alpha R,G,B value configuration */
  114. tli_layer_init_struct.layer_default_blue = 0x00;
  115. tli_layer_init_struct.layer_default_green = 0x00;
  116. tli_layer_init_struct.layer_default_red = 0x00;
  117. tli_layer_init_struct.layer_default_alpha = 0x00;
  118. /* TLI window blend configuration */
  119. tli_layer_init_struct.layer_acf1 = LAYER_ACF1_SA;
  120. tli_layer_init_struct.layer_acf2 = LAYER_ACF2_SA;
  121. /* TLI layer frame buffer base address configuration */
  122. tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)ltdc_lcd_framebuf0;
  123. tli_layer_init_struct.layer_frame_line_length = ((ACTIVE_WIDTH * 2) + 3);
  124. tli_layer_init_struct.layer_frame_buf_stride_offset = (ACTIVE_WIDTH * 2);
  125. tli_layer_init_struct.layer_frame_total_line_number = ACTIVE_HEIGHT;
  126. tli_layer_init(LAYER0, &tli_layer_init_struct);
  127. tli_dither_config(TLI_DITHER_ENABLE);
  128. #endif
  129. }
  130. /**
  131. * @brief Initialize the IPA peripheral.
  132. *
  133. * @note Enable clock and interrupt for the IPA module.
  134. *
  135. * @param None
  136. *
  137. * @return None
  138. */
  139. static void ipa_config(void)
  140. {
  141. rcu_periph_clock_enable(RCU_IPA);
  142. nvic_irq_enable(IPA_IRQn, 0, 2);
  143. }
  144. /**
  145. * @brief Configure the GPIO pins used for TLI display.
  146. *
  147. * @note Includes alternate function mapping and output mode setup
  148. * for RGB and sync signals.
  149. *
  150. * @param None
  151. *
  152. * @return None
  153. */
  154. static void tli_gpio_config(void)
  155. {
  156. /* enable the periphral clock */
  157. rcu_periph_clock_enable(RCU_GPIOA);
  158. rcu_periph_clock_enable(RCU_GPIOB);
  159. rcu_periph_clock_enable(RCU_GPIOC);
  160. rcu_periph_clock_enable(RCU_GPIOD);
  161. rcu_periph_clock_enable(RCU_GPIOF);
  162. rcu_periph_clock_enable(RCU_GPIOG);
  163. /* configure HSYNC(PC6), VSYNC(PA4), PCLK(PG7), DE(PF10) */
  164. /* configure LCD_R7(PG6), LCD_R6(PA8), LCD_R5(PA12), LCD_R4(PA11), LCD_R3(PB0),
  165. LCD_G7(PD3), LCD_G6(PC7), LCD_G5(PB11), LCD_G4(PB10), LCD_G3(PG10), LCD_G2(PA6),
  166. LCD_B7(PB9), LCD_B6(PB8), LCD_B5(PA3), LCD_B4(PG12), LCD_B3(PG11) */
  167. gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_3);
  168. gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_4);
  169. gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_6);
  170. gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_8);
  171. gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_11);
  172. gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_12);
  173. gpio_af_set(GPIOB, GPIO_AF_9, GPIO_PIN_0);
  174. gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_8);
  175. gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_9);
  176. gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_10);
  177. gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_11);
  178. gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_6);
  179. gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_7);
  180. gpio_af_set(GPIOD, GPIO_AF_14, GPIO_PIN_3);
  181. gpio_af_set(GPIOF, GPIO_AF_14, GPIO_PIN_10);
  182. gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_6);
  183. gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_7);
  184. gpio_af_set(GPIOG, GPIO_AF_9, GPIO_PIN_10);
  185. gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_11);
  186. gpio_af_set(GPIOG, GPIO_AF_9, GPIO_PIN_12);
  187. gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_6 | GPIO_PIN_8 | GPIO_PIN_11 | GPIO_PIN_12);
  188. gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_6 | GPIO_PIN_8 | GPIO_PIN_11 | GPIO_PIN_12);
  189. gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11);
  190. gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11);
  191. gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6 | GPIO_PIN_7);
  192. gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
  193. gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
  194. gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
  195. gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
  196. gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
  197. gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
  198. gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
  199. }
  200. /**
  201. * @brief Configure the GPIO pin for DISP enable control.
  202. *
  203. * @note This function enables the clock for GPIOD and sets up pin 13
  204. * as push-pull output to control the display on/off signal.
  205. *
  206. * @param None
  207. *
  208. * @return None
  209. *
  210. * @warning Must be called before attempting to control the LCD display.
  211. */
  212. static void lcd_disp_en_config(void)
  213. {
  214. /* enable the periphral clock */
  215. rcu_periph_clock_enable(RCU_GPIOD);
  216. gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_13);
  217. gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
  218. }
  219. /**
  220. * @brief Turn off the LCD display by resetting the DISP GPIO.
  221. *
  222. * @note This function clears the DISP control pin to disable
  223. * the LCD backlight or power depending on hardware design.
  224. *
  225. * @param None
  226. *
  227. * @return None
  228. *
  229. * @warning Only valid if the DISP GPIO has been previously configured.
  230. */
  231. static void lcd_disp_off(void)
  232. {
  233. gpio_bit_reset(GPIOD, GPIO_PIN_13);
  234. }
  235. /**
  236. * @brief Turn on the LCD display by setting the DISP GPIO.
  237. *
  238. * @note This function sets the DISP control pin to enable
  239. * the LCD backlight or power depending on hardware design.
  240. *
  241. * @param None
  242. *
  243. * @return None
  244. *
  245. * @warning Only valid if the DISP GPIO has been previously configured.
  246. */
  247. static void lcd_disp_on(void)
  248. {
  249. gpio_bit_set(GPIOD, GPIO_PIN_13);
  250. }
  251. /**
  252. * @brief Draw a pixel on the LCD at specified coordinates.
  253. *
  254. * @note This function writes a color value directly to the framebuffer.
  255. * Coordinates must be within the valid LCD dimensions.
  256. *
  257. * @param x Horizontal coordinate of the pixel.
  258. * @param y Vertical coordinate of the pixel.
  259. * @param color Color of the pixel in RGB565 format.
  260. *
  261. * @return None
  262. */
  263. void tli_draw_point(uint16_t x, uint16_t y, uint16_t color)
  264. {
  265. *(ltdc_lcd_framebuf0[0] + (LCD_WIDTH * y + x)) = color;
  266. }
  267. /**
  268. * @brief Draw a straight line between two points.
  269. *
  270. * @note Uses Bresenham's line drawing algorithm to connect two points.
  271. * Coordinates should be within LCD bounds.
  272. *
  273. * @param sx Start point X coordinate.
  274. * @param sy Start point Y coordinate.
  275. * @param ex End point X coordinate.
  276. * @param ey End point Y coordinate.
  277. * @param color Line color in RGB565 format.
  278. *
  279. * @return None
  280. */
  281. void tli_draw_line(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color)
  282. {
  283. uint16_t t;
  284. int xerr = 0, yerr = 0, delta_x, delta_y, distance;
  285. int incx, incy, uRow, uCol;
  286. delta_x = ex - sx;
  287. delta_y = ey - sy;
  288. uRow = sx;
  289. uCol = sy;
  290. if (delta_x > 0)
  291. incx = 1;
  292. else if (delta_x == 0)
  293. incx = 0;
  294. else
  295. {
  296. incx = -1;
  297. delta_x = -delta_x;
  298. }
  299. if (delta_y > 0)
  300. incy = 1;
  301. else if (delta_y == 0)
  302. incy = 0;
  303. else
  304. {
  305. incy = -1;
  306. delta_y = -delta_y;
  307. }
  308. if (delta_x > delta_y)
  309. distance = delta_x;
  310. else
  311. distance = delta_y;
  312. for (t = 0; t <= distance + 1; t++)
  313. {
  314. tli_draw_point(uRow, uCol, color);
  315. xerr += delta_x;
  316. yerr += delta_y;
  317. if (xerr > distance)
  318. {
  319. xerr -= distance;
  320. uRow += incx;
  321. }
  322. if (yerr > distance)
  323. {
  324. yerr -= distance;
  325. uCol += incy;
  326. }
  327. }
  328. }
  329. /**
  330. * @brief Draw a rectangle on the screen, optionally filled.
  331. *
  332. * @note The rectangle is defined by top-left and bottom-right corners.
  333. * If fill is set to 1, the rectangle will be filled with the color.
  334. *
  335. * @param sx X coordinate of the top-left corner.
  336. * @param sy Y coordinate of the top-left corner.
  337. * @param ex X coordinate of the bottom-right corner.
  338. * @param ey Y coordinate of the bottom-right corner.
  339. * @param color Color of the rectangle in RGB565 format.
  340. * @param fill Fill mode: 1 = filled, 0 = only borders.
  341. *
  342. * @return None
  343. */
  344. void tli_draw_rectangle(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color, uint16_t fill)
  345. {
  346. int i = 0, j = 0;
  347. if (fill)
  348. {
  349. for (i = sx; i < ex; i++)
  350. {
  351. for (j = sy; j < ey; j++)
  352. {
  353. tli_draw_point(i, j, color);
  354. }
  355. }
  356. }
  357. else
  358. {
  359. tli_draw_line(sx, sy, ex, sy, color);
  360. tli_draw_line(sx, sy, sx, ey, color);
  361. tli_draw_line(sx, ey, ex, ey, color);
  362. tli_draw_line(ex, sy, ex, ey, color);
  363. }
  364. }
  365. /**
  366. * @brief Draw eight symmetric points of a circle.
  367. *
  368. * @note This function is used internally to draw points in all eight
  369. * symmetrical positions around a circle center.
  370. *
  371. * @param xc X coordinate of the circle center.
  372. * @param yc Y coordinate of the circle center.
  373. * @param x Relative X offset from the center.
  374. * @param y Relative Y offset from the center.
  375. * @param c Color of the point in RGB565 format.
  376. *
  377. * @return None
  378. */
  379. static void _draw_circle_8(int xc, int yc, int x, int y, uint16_t c)
  380. {
  381. tli_draw_point(xc + x, yc + y, c);
  382. tli_draw_point(xc - x, yc + y, c);
  383. tli_draw_point(xc + x, yc - y, c);
  384. tli_draw_point(xc - x, yc - y, c);
  385. tli_draw_point(xc + y, yc + x, c);
  386. tli_draw_point(xc - y, yc + x, c);
  387. tli_draw_point(xc + y, yc - x, c);
  388. tli_draw_point(xc - y, yc - x, c);
  389. }
  390. /**
  391. * @brief Draw a circle on the screen with optional fill.
  392. *
  393. * @note Uses the midpoint circle algorithm to render the circle.
  394. * When fill is enabled, the circle is drawn as a filled disk.
  395. *
  396. * @param xc X coordinate of the circle center.
  397. * @param yc Y coordinate of the circle center.
  398. * @param c Color of the circle in RGB565 format.
  399. * @param r Radius of the circle.
  400. * @param fill Fill mode: 1 = filled, 0 = outline only.
  401. *
  402. * @return None
  403. */
  404. void tli_draw_circle(int xc, int yc, uint16_t c, int r, int fill)
  405. {
  406. int x = 0, y = r, yi, d;
  407. d = 3 - 2 * r;
  408. if (fill)
  409. {
  410. while (x <= y)
  411. {
  412. for (yi = x; yi <= y; yi++)
  413. _draw_circle_8(xc, yc, x, yi, c);
  414. if (d < 0)
  415. {
  416. d = d + 4 * x + 6;
  417. }
  418. else
  419. {
  420. d = d + 4 * (x - y) + 10;
  421. y--;
  422. }
  423. x++;
  424. }
  425. }
  426. else
  427. {
  428. while (x <= y)
  429. {
  430. _draw_circle_8(xc, yc, x, y, c);
  431. if (d < 0)
  432. {
  433. d = d + 4 * x + 6;
  434. }
  435. else
  436. {
  437. d = d + 4 * (x - y) + 10;
  438. y--;
  439. }
  440. x++;
  441. }
  442. }
  443. }
  444. /**
  445. * @brief Swap the values of two 16-bit unsigned integers.
  446. *
  447. * @note This utility function is used internally to simplify sorting
  448. * or reordering logic, such as coordinate alignment.
  449. *
  450. * @param a Pointer to the first variable.
  451. * @param b Pointer to the second variable.
  452. *
  453. * @return None
  454. */
  455. static void _swap(uint16_t *a, uint16_t *b)
  456. {
  457. uint16_t tmp;
  458. tmp = *a;
  459. *a = *b;
  460. *b = tmp;
  461. }
  462. /**
  463. * @brief Draw a triangle with optional fill.
  464. *
  465. * @note The triangle is defined by three vertex points. When fill is set,
  466. * it is rendered as a filled triangle using scan-line rasterization.
  467. *
  468. * @param x0 X coordinate of the first vertex.
  469. * @param y0 Y coordinate of the first vertex.
  470. * @param x1 X coordinate of the second vertex (typically the top).
  471. * @param y1 Y coordinate of the second vertex.
  472. * @param x2 X coordinate of the third vertex.
  473. * @param y2 Y coordinate of the third vertex.
  474. * @param color Color of the triangle in RGB565 format.
  475. * @param fill Fill mode: 1 = filled, 0 = outline only.
  476. *
  477. * @return None
  478. */
  479. void tli_draw_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color, uint16_t fill)
  480. {
  481. uint16_t a, b, y, last;
  482. int dx01, dy01, dx02, dy02, dx12, dy12;
  483. long sa = 0;
  484. long sb = 0;
  485. if (fill == 0)
  486. {
  487. tli_draw_line(x0, y0, x1, y1, color);
  488. tli_draw_line(x1, y1, x2, y2, color);
  489. tli_draw_line(x2, y2, x0, y0, color);
  490. }
  491. else
  492. {
  493. if (y0 > y1)
  494. {
  495. _swap(&y0, &y1);
  496. _swap(&x0, &x1);
  497. }
  498. if (y1 > y2)
  499. {
  500. _swap(&y2, &y1);
  501. _swap(&x2, &x1);
  502. }
  503. if (y0 > y1)
  504. {
  505. _swap(&y0, &y1);
  506. _swap(&x0, &x1);
  507. }
  508. if (y0 == y2)
  509. {
  510. a = b = x0;
  511. if (x1 < a)
  512. {
  513. a = x1;
  514. }
  515. else if (x1 > b)
  516. {
  517. b = x1;
  518. }
  519. if (x2 < a)
  520. {
  521. a = x2;
  522. }
  523. else if (x2 > b)
  524. {
  525. b = x2;
  526. }
  527. tli_draw_rectangle(a, y0, b, y0, color, 0);
  528. return;
  529. }
  530. dx01 = x1 - x0;
  531. dy01 = y1 - y0;
  532. dx02 = x2 - x0;
  533. dy02 = y2 - y0;
  534. dx12 = x2 - x1;
  535. dy12 = y2 - y1;
  536. if (y1 == y2)
  537. {
  538. last = y1;
  539. }
  540. else
  541. {
  542. last = y1 - 1;
  543. }
  544. for (y = y0; y <= last; y++)
  545. {
  546. a = x0 + sa / dy01;
  547. b = x0 + sb / dy02;
  548. sa += dx01;
  549. sb += dx02;
  550. if (a > b)
  551. {
  552. _swap(&a, &b);
  553. }
  554. tli_draw_rectangle(a, y, b, y, color, 0);
  555. }
  556. sa = dx12 * (y - y1);
  557. sb = dx02 * (y - y0);
  558. for (; y <= y2; y++)
  559. {
  560. a = x1 + sa / dy12;
  561. b = x0 + sb / dy02;
  562. sa += dx12;
  563. sb += dx02;
  564. if (a > b)
  565. {
  566. _swap(&a, &b);
  567. }
  568. tli_draw_rectangle(a, y, b, y, color, 0);
  569. }
  570. }
  571. }
  572. /**
  573. * @brief Enlarge a single point by drawing a square block.
  574. *
  575. * @note The enlarged point is drawn as a 2D cross pattern made of four
  576. * rectangles centered at (x, y) with specified magnification.
  577. *
  578. * @param x X coordinate of the center point.
  579. * @param y Y coordinate of the center point.
  580. * @param color Color to fill the enlarged point.
  581. * @param magnify Enlargement factor (minimum value is 1).
  582. *
  583. * @return None
  584. */
  585. void tli_point_enlarge(uint16_t x, uint16_t y, uint16_t color, char magnify)
  586. {
  587. tli_draw_rectangle(x - magnify, y - magnify, x, y, color, 1);
  588. tli_draw_rectangle(x, y - magnify, x + magnify, y, color, 1);
  589. tli_draw_rectangle(x - magnify, y, x, y + magnify, color, 1);
  590. tli_draw_rectangle(x, y, x + magnify, y + magnify, color, 1);
  591. }
  592. /**
  593. * @brief Display a single ASCII character at the specified position.
  594. *
  595. * @note Uses a fixed-size 16x8 font to render the character with optional
  596. * enlargement and overlay mode.
  597. *
  598. * @param x X coordinate of the character start position.
  599. * @param y Y coordinate of the character start position.
  600. * @param fc Foreground color.
  601. * @param bc Background color.
  602. * @param num ASCII character to display.
  603. * @param size Enlargement factor (minimum 1).
  604. * @param mode Overlay mode: 0 = non-overlay, 1 = overlay.
  605. *
  606. * @return None
  607. */
  608. void tli_show_char(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t num, uint8_t size, uint8_t mode)
  609. {
  610. uint8_t temp;
  611. uint8_t pos, t;
  612. uint16_t x0 = 0;
  613. uint16_t y0 = 0;
  614. num = num - ' ';
  615. for (pos = 0; pos < 16; pos++)
  616. {
  617. temp = asc2_1608[num][pos];
  618. for (t = 0; t < 16 / 2; t++)
  619. {
  620. if (!mode)
  621. {
  622. if (temp & 0x01)
  623. tli_point_enlarge(x + x0, y + y0, fc, size);
  624. else
  625. tli_point_enlarge(x + x0, y + y0, bc, size);
  626. }
  627. else
  628. {
  629. if (temp & 0x01) tli_point_enlarge(x + x0, y + y0, fc, size);
  630. }
  631. temp >>= 1;
  632. x0 = x0 + size;
  633. }
  634. x0 = 0;
  635. y0 = y0 + size;
  636. }
  637. }
  638. /**
  639. * @brief Display a null-terminated ASCII string on the screen.
  640. *
  641. * @note Automatically handles line boundaries and skips illegal characters.
  642. *
  643. * @param x X coordinate of the start position.
  644. * @param y Y coordinate of the start position.
  645. * @param fc Foreground color.
  646. * @param bc Background color.
  647. * @param size Font enlargement factor (base size is 16x8).
  648. * @param p Pointer to the string.
  649. * @param mode Overlay mode: 0 = non-overlay, 1 = overlay.
  650. *
  651. * @return None
  652. */
  653. void tli_show_string(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t size, uint8_t *p, uint8_t mode)
  654. {
  655. while ((*p <= '~') && (*p >= ' '))
  656. {
  657. if (x > (LCD_WIDTH - 1) || y > (LCD_HEIGHT - 1)) return;
  658. tli_show_char(x, y, fc, bc, *p, size, mode);
  659. x += 16 * size / 2;
  660. p++;
  661. }
  662. }
  663. /**
  664. * @brief Display a picture at a specified position on the screen.
  665. *
  666. * @note The image data is expected to be in RGB565 format, with each pixel
  667. * occupying two consecutive bytes in the array.
  668. *
  669. * @param x X coordinate of the top-left corner.
  670. * @param y Y coordinate of the top-left corner.
  671. * @param w Width of the image in pixels.
  672. * @param h Height of the image in pixels.
  673. * @param pic Pointer to the image data array.
  674. *
  675. * @return None
  676. */
  677. void tli_show_picture(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const unsigned char pic[])
  678. {
  679. uint16_t i, j;
  680. uint32_t k = 0;
  681. uint16_t x0 = x, y0 = y;
  682. for (i = y; i < h + y0; i++)
  683. {
  684. for (j = x; j < w + x0; j++)
  685. {
  686. tli_draw_point(j, i, pic[k * 2] << 8 | pic[k * 2 + 1]);
  687. k++;
  688. }
  689. }
  690. }
  691. /**
  692. * @brief Draw a filled rounded rectangle button.
  693. *
  694. * @note This function draws a button with four rounded corners and fills
  695. * the background. The radius must be less than half of the height.
  696. *
  697. * @param x X coordinate of the top-left corner.
  698. * @param y Y coordinate of the top-left corner.
  699. * @param width Width of the button.
  700. * @param height Height of the button.
  701. * @param radius Radius of the corners.
  702. * @param color Fill color of the button.
  703. *
  704. * @return None
  705. */
  706. void tli_show_button(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t radius, uint16_t color)
  707. {
  708. int i = 0;
  709. int j = 0;
  710. if (radius >= (height / 2)) return;
  711. tli_draw_circle(x + radius, y + radius, color, radius, 1);
  712. tli_draw_circle(x + width - radius - 1, y + radius, color, radius, 1);
  713. tli_draw_circle(x + radius, y + height - radius - 1, color, radius, 1);
  714. tli_draw_circle(x + width - radius - 1, y + height - radius - 1, color, radius, 1);
  715. tli_draw_line(x + radius, y, x + width - radius - 1, y, color);
  716. tli_draw_line(x + radius, y + height - 1, x + width - radius - 1, y + height - 1, color);
  717. tli_draw_line(x, y + radius, x, y + height - radius - 1, color);
  718. tli_draw_line(x + width - 1, y + radius, x + width - 1, y + height - radius - 1, color);
  719. tli_draw_rectangle(x + radius, y + radius, x + width - radius, y + height - radius, color, 1);
  720. tli_draw_rectangle(x + radius, y, x + width - radius, y + radius, color, 1);
  721. tli_draw_rectangle(x, y + radius, x + width, y + height - radius, color, 1);
  722. tli_draw_rectangle(x + radius, y + height - radius, x + width - radius, y + height, color, 1);
  723. }
  724. /**
  725. * @brief Draw a switch (toggle) component with visual state.
  726. *
  727. * @note The switch has a rounded rectangle base with a circular knob
  728. * indicating on or off state. Automatically validates layout rules.
  729. *
  730. * @param x X coordinate of the top-left corner.
  731. * @param y Y coordinate of the top-left corner.
  732. * @param width Total width of the switch.
  733. * @param height Total height of the switch.
  734. * @param on_color Color of the switch when turned on.
  735. * @param off_color Color of the switch when turned off.
  736. * @param sw Switch state: 1 = on, 0 = off.
  737. *
  738. * @return None
  739. */
  740. void tli_show_switch(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t on_color, uint16_t off_color, char sw)
  741. {
  742. int radius = height / 2 - 1;
  743. if (width < 2 * (radius + 1)) return;
  744. if (sw == 0)
  745. {
  746. tli_show_button(x, y, width, height, radius, off_color);
  747. tli_draw_circle(x + radius, y + radius, WHITE, radius / 2, 1);
  748. }
  749. else
  750. {
  751. tli_show_button(x, y, width, height, radius, on_color);
  752. tli_draw_circle(x + width - radius, y + radius, WHITE, radius / 2, 1);
  753. }
  754. }