一、GuiLite 6 N/ B# _3 Q2 G, x) u2 O3 ^4 I
GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。( I6 @ T4 F5 a% q% Z. R
2 X# d% w4 q3 x9 W9 }- u) X二、创建工程/ a) y/ R+ @: X7 G; G8 ?
8 ?0 _' C/ w7 f 实现了LCD屏幕点亮功能。# ~2 c7 g' s. b& W2 S- ~ S1 l$ ]
$ T" X; J) m5 Q9 O+ g 【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。5 U) e9 h' T% Y- o( ]
; I/ ^$ _2 n8 a4 Z2 B6 n0 |
在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。; o" K/ {* d' }) Z' U$ a5 O
+ I+ E+ |0 y" {' r9 w
1 d5 \8 w9 C' w6 l b
8 P, X+ v# ^2 [
【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容:
4 D' I9 [* k; {% ]/ S- volatile uint8_t one_frame_done;( \' @+ }7 ] } X" |
- void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
( F* Y+ o2 M$ s - {: D: E E1 }; e) J( H; V+ ^
- one_frame_done = 1;+ _- f) C" d( \- G; }: K% [
- }
复制代码
% T$ C& M! w. [' x
2 `" _: e2 n, x" S6 e9 c2 K8 }1 Z( e& Y+ K% T, p" H
5 r# q1 x* p" E; E9 T三、GuiLite库及Hello3D样例移植
2 g* C( V" G/ C, q0 z9 r9 v 在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。
2 I- ?; b1 ?, B: `1 g+ e% m! ~, C
【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。3 {% i5 M9 j' d( B! w$ c0 C- D
6 K' d5 Q9 P U" V% F delay.h& ?+ I5 y1 J# f7 Q, U: U
- #ifndef DELAY_DELAY_H_8 m! e2 q1 Z5 I- m# h4 G# ]; S( I
- #define DELAY_DELAY_H_
. C! \0 _4 i/ J2 ~ - #ifdef __cplusplus& G" e' P* `5 o. n2 v9 i
- extern "C" {
2 T1 |: s! s, p# |# b$ u2 i. X - #endif: ~* z9 y6 o# r7 ]
- & Q* |/ E+ p9 b3 ^1 N$ {0 n
- #include "stm32l4xx_hal.h" //HAL库文件声明' { a e" F3 e9 ]% b6 L; e, ~
- void delay_us(uint32_t us); //C文件中的函数声明& I, n7 U3 j) i# ]' g/ O
-
5 } t) ], S# b8 _. ^ - #ifdef __cplusplus6 H: t5 D1 i2 [9 N6 U
- }7 ~* E& Y4 u1 |( R; }
- #endif
" {/ x! F( N2 C- e - #endif /* DELAY_DELAY_H_ */
复制代码
a) y8 e' b1 Y5 c6 P; \, ?0 G
8 K" N, a! i% [0 q, {2 t delay.c
$ E3 u5 r7 x+ e b, a- #include "delay.h"
' b0 d+ w' G' ?" S- _ - 5 i q+ [2 i* C$ B0 W* d
- void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
6 Q2 ?$ y1 P8 k: ?5 A - {$ x9 A; z/ W) v9 X3 R1 K/ G0 V' O
- uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数6 Q0 x+ W; }1 Z# n# S/ ~( |. E
- while (delay--); //循环delay次,达到1微秒延时
6 h5 g( ^6 ^4 K - }
复制代码 / i! n; i) }% [# @9 ?
由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。
6 D9 Q( A% r; S5 A9 b! q- #ifdef __cplusplus5 w" D7 I% z7 ?% }/ Q9 }9 C8 T
- extern "C" {
9 j% g( E/ u5 Z" V - #endif
# Z4 Y) D: t- C5 E2 I -
- P% s c1 o: @3 M9 w6 g% D - //you code- L8 J1 e1 i+ J0 i& @$ w1 {6 q
- : M7 E$ v7 T$ [+ D2 r/ o, h& ~& ~
- #ifdef __cplusplus
, z7 V* j2 \; t/ P! Z; ?, w9 S - }
复制代码 ) }6 W4 h( u# R5 H$ W1 G- b' u
【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下) X6 t5 U6 P J# S" z+ K
- #ifndef GUILITE_H_. o' e6 ]7 \* ?3 @
- #define GUILITE_H_
I, Z3 W3 J) Z - #include "../delay/delay.h"
复制代码 ; \+ D0 L q8 u7 s
在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。- F/ j7 e+ g! Z7 l; r9 L
- extern "C" void delay_ms(unsigned short nms);
" }9 Q( E* v: p7 r - void thread_sleep(unsigned int milli_seconds)
' G( ^) r- `& T - {//MCU alway implemnet driver code in APP.
. T: ]6 U; X' b3 R) i! } - delay_ms(milli_seconds);
; R9 R6 o$ F& q% m - }
复制代码 7 Q" r$ k, u5 g. F0 Q/ }/ `
调整为0 R5 O( t) u# Z/ m5 T7 I
- //extern "C" void delay_ms(unsigned short nms);
& ~: h v8 y& V: b( v - void thread_sleep(unsigned int milli_seconds)
( Q. n7 \9 ^$ d - {//MCU alway implemnet driver code in APP.
; b9 f4 h0 Y x7 o: }/ |; r - uint32_t tt = ((uint32_t)milli_seconds)*1000;% z1 O$ Y, s3 s
- delay_us(tt); q' h& m* f& |
- }
复制代码
g1 I' d7 L/ S3 t \( ^ 【3】在main.c文件,引用各外设驱动头文件
5 o8 u% r3 a: ~2 {2 W5 j- k5 O- /* USER CODE BEGIN Includes */$ U# a+ ^& p: B: q" m1 d M
- #include "../../ICore/key/key.h"/ R: m& y4 \9 k0 |
- #include "../../ICore/led/led.h"9 g* `" E: R3 ]1 Q" H! E# Q8 X
- #include "../../ICore/print/print.h"( W+ A* U% u/ I5 }" A* H. m
- #include "../../ICore/usart/usart.h"
- b; N0 y% t! f ~. F1 [ - #include "../../ICore/oled/oled.h"
+ A' `; H$ A F9 x" w3 b - /* USER CODE END Includes */
复制代码
R1 j! H5 d$ | 在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。
: j/ N- g% ?& M: K1 l8 q- o- /* USER CODE BEGIN 0 */% {# d# _0 T/ N9 P1 s6 T
- //Transfer GuiLite 32 bits color to your LCD color
7 H' w: V1 `& @) N, B& r3 b - #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8))
$ z, V. X$ G$ Y2 C$ [5 _- l/ F - //Encapsulate your LCD driver:) c! Z8 n$ u8 y" B* d
- void gfx_draw_pixel(int x, int y, unsigned int rgb)
, h$ `' O4 p7 n0 x: v - {
# i M% O! I4 \* `# P: h1 R - //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的! `4 a' P6 H! b- `/ B
- OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)2 I4 f: @9 T: E& R# @ U m* j
- }; l' f# G# {) ~; G9 d
- //Implement it, if you have more fast solution than drawing pixels one by one.
0 }6 e" m+ }- X; T% F - //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}
9 y% s, g4 U* P- ^. s! F. U6 u -
4 J- O9 n" E$ I$ @; D. G4 B - //UI entry$ E6 d8 B0 [. J
- struct DISPLAY_DRIVER/ L* ^. o! p$ l# T7 X* n
- {0 T2 v; n4 e2 x! o5 D
- void (*draw_pixel)(int x, int y, unsigned int rgb);
( h$ q& [9 Z; T - void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);6 u8 v. Y% Z+ \
- } my_driver;
, w& V* h: h1 N% n - extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
- C# f1 V \- s5 g8 U0 @0 ] - /* USER CODE END 0 */
复制代码
/ h0 Q. Z0 O" f8 Y5 D$ n/ P, `( l) i( q. Y
在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。
$ j0 y* q6 ?0 h# f+ k0 }& {- delay_init();3 o" w, g, o6 b1 w! `, U
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
( _( C6 W7 y$ {9 @9 Y - LCD_Init();
1 i3 v% D- u+ }, m -
) b5 _9 k/ T8 c5 O) W- s - //Link your LCD driver & start UI:
3 `# i$ X! U# q; u - my_driver.draw_pixel = gfx_draw_pixel;
5 T+ Y0 }; U; n \8 s! f - my_driver.fill_rect = NULL;//gfx_fill_rect;
`5 \1 i/ ?# {) G - startHello3D(NULL, 240, 320, 2, &my_driver);
: n7 w! i5 ?1 M6 g - while(1);
复制代码 4 U0 c# e. q* f+ V B7 ^ d
在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。
8 o! z5 r7 w( l2 h8 q- /* USER CODE BEGIN 2 */# D7 C7 d) i* y4 w+ f8 q- [. ?9 Y
- ResetPrintInit(&hlpuart1);
# v4 ~$ n& l$ b5 s - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
5 {1 w7 g0 s/ y4 h G! ?# q - HLPUSART_RX_STA = 0;
( z, S7 M1 A% |; H+ j( ] - //. V* y9 _7 l8 ?
- OLED_init();
. B1 T- q L8 u$ b: { - //设置OLED蓝色背景显示
+ L# V3 p* I/ c2 c8 h, j7 L - BSP_LCD_Clear_DMA(LCD_DISP_BLUE);( P1 z1 y5 Q0 O# J3 V8 N
- printf("OLED_Clear_DMA\r\n");
$ B# ]: N- L. c* ~- y" n/ W - /* USER CODE END 2 */
复制代码 5 O6 e. Z. k! r
然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。
% Y( Z5 M- P6 V3 Y: ]9 l- /* USER CODE BEGIN WHILE */
' k& o7 Z y) A" h1 p( o - while (1)
# R' [: V% X. H7 d F - {4 S4 \# m1 i) i% b7 r
- if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
. b, _" F! Y6 ` - //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);; i2 |+ O0 s% ?2 `6 J% n1 o7 {
- OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
2 [# L# b9 X) l: H+ }, ?( [ - HLPUSART_RX_STA=0;//接收错误,重新开始# u2 k9 k! q; B. D) r
- HAL_Delay(100);//等待
y8 F# b; k* m - }
) g$ ~# A6 |0 Z |0 c - if(KEY_0())
) K, v8 r6 g0 t" x S0 |/ x - {# U; U, H9 p9 k9 L" |$ x
- BSP_LCD_login(24,108);
7 m$ g- }" n: T8 S9 S6 x }" e. Y4 Q - }( i; r: h1 X: O+ D1 r
- if(KEY_1())6 M7 B( I) e- k+ v: {7 J2 g
- {# O6 V; }7 z) `: E2 c. o/ o7 F
- BSP_LCD_img_DMA();# A- P# f! P, i8 ^% g
- }
: U9 E/ F( h4 V5 |* J - if(KEY_2())
( j/ _" z B4 F1 A - {/ I7 w* R: F* t1 y
- my_driver.draw_pixel = gfx_draw_pixel;3 o+ ?: V# X+ d
- my_driver.fill_rect = NULL;//gfx_fill_rect;
+ h& F$ U4 N7 {3 P* Z" Y - startHello3D(NULL, 240, 240, 2, &my_driver);; ~: M# ?+ |, m5 i9 L- {1 e. f
- }
, j% {0 ]0 b, d' y& @6 ~# x6 ^ - /* USER CODE END WHILE */
复制代码
: e, x+ F8 C9 u& R四、编译及测试' V% t0 }3 u L7 K4 a6 ~$ u
完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。
, Y; g8 [' {, i+ _% C$ j! [! Q, w. x! W9 A' j
9 z3 R1 ?6 ?1 Y9 [% i
: s5 Y! Y: T! y7 X
按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。$ d: @, e; i" I" [3 E
. v T9 ^; n D. Y& k3 t/ S
E' n z4 h9 E' |. @7 Y2 E
3 M; Y; z% H0 E# u
. D) J* {3 Y# ?" W————————————————* X' D# b- U! q, }$ K
版权声明:py_free-物联智能# g1 b. a( O' Y7 w2 H# {! p
如有侵权请联系删除( V7 Q$ s3 _, O- N6 e
|
内容很详细,谢谢分享
这个GUI效果真不错,有空可以尝试移植一下看看。