一、GuiLite
8 g4 Z; w1 t+ ^; ] GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。) a& t1 j( T6 i& z
0 g+ U- D1 x, V1 Q; q二、创建工程* V5 j6 f' N2 o7 Q
' l$ B7 D `+ ] z, y
实现了LCD屏幕点亮功能。: ~1 v. K5 E2 R; K) n- @* G8 q" T H
5 A0 q1 K+ a3 l6 @
【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。
" b+ D! V/ L' |0 G+ l" p
1 m2 C ^7 S' j, ]3 z+ _0 k9 z4 _7 | 在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。, p4 f( S- ]; f6 }) S/ D2 t& o
9 @. w9 T' j% {2 |% `
2 Q6 E4 S$ Z/ I4 b$ U' [5 h, ^* c2 \
Z8 F% L6 I6 ]$ \; s5 }8 k
【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容:9 A( B* G2 D- _; ]: U
- volatile uint8_t one_frame_done;
8 T8 i% p( D9 D' V! @ - void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi): X: @3 r' G" k0 c8 [4 H
- {; R& E* S* @ |7 u: }9 V. f8 ~! r
- one_frame_done = 1;$ e% S; `, b, c# M8 w4 e) y8 h/ a
- }
复制代码 2 H% j- A5 B6 c% j4 p* n/ U# _
5 ]$ B7 |; h; O: c
- }1 n7 W2 f+ l# d. O2 H" L
1 X2 ]9 |5 w+ P: R$ W三、GuiLite库及Hello3D样例移植
9 `; M6 u0 y, B4 K* y 在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。5 s7 L8 c$ Q: x7 X! W% K9 ?9 F
0 p8 m/ w# T/ I6 w( b5 V9 K 【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。/ o1 c2 z2 g( c
" u7 f3 H7 `; p2 D: e delay.h9 e0 m V) Q! i7 s [
- #ifndef DELAY_DELAY_H_
7 ~5 |3 ?' ^ _, p6 C) d - #define DELAY_DELAY_H_
2 ~4 d; F9 C6 v* d - #ifdef __cplusplus/ ^0 Q# z' _, {1 P- L& Y
- extern "C" {! F e0 ?/ Q( s9 M) t
- #endif
- {& t, l1 ^9 _5 N! j- ^ -
8 |" m% z) u2 W: K' r - #include "stm32l4xx_hal.h" //HAL库文件声明
1 J9 E) G- H8 E" `3 @) }! a - void delay_us(uint32_t us); //C文件中的函数声明: S* _, G7 K. n" U1 u) L
-
( @+ W3 f& c) N! O! I+ F% y' S - #ifdef __cplusplus
9 S3 K% N- I, F2 E - }! x: r6 C$ P/ a9 k) Z1 q) @
- #endif
# g1 V$ G0 C2 h8 F, r - #endif /* DELAY_DELAY_H_ */
复制代码
+ \* S) E: D+ I* q" `% }5 X/ T8 _ D Y3 ]
delay.c, U$ D s4 w6 y7 Q
- #include "delay.h"
7 A. W: K1 y# j# j( i - 8 E$ y, q6 N ^# }4 F3 F2 j8 D
- void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数 ?" f/ l ]# j; E
- {. O+ R l' O8 Z/ q5 n+ @) b+ A
- uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数; D1 P, Q0 m" u y) w$ r% X
- while (delay--); //循环delay次,达到1微秒延时
9 M9 v) x: I3 U' v/ _% k* H - }
复制代码 : d y2 N2 {# ?/ V! Q0 d7 n
由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。2 E d3 @, V! t# r9 ]% d. o
- #ifdef __cplusplus. R# ^: |# g9 r# G8 e1 P4 Y* r
- extern "C" {
% ~: n0 K2 v9 Q% W" t - #endif
2 U, d1 `& l7 f' a/ Z+ Y' b$ { - * j( v, f! U" u( _% ]0 s
- //you code
. x1 @6 x/ r+ t. n3 Z; a - 4 w3 p9 {5 X9 T& |
- #ifdef __cplusplus' S, ^4 q# f) b
- }
复制代码
2 [, z/ g x8 o- F 【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下9 ?1 C8 U' z' b. \0 x; K
- #ifndef GUILITE_H_
4 @) z- l: R# ^8 w- } - #define GUILITE_H_. u/ |# A* B; q+ d( L; H9 r1 Q& f
- #include "../delay/delay.h"
复制代码 - p' \* L- G1 t; h* H3 k2 S
在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。
( e! F, v7 L1 R; |- extern "C" void delay_ms(unsigned short nms);
4 c1 I' {( @# J# r0 v - void thread_sleep(unsigned int milli_seconds)
, t2 c& M( [/ B+ M - {//MCU alway implemnet driver code in APP.
' I. S0 K& y3 M - delay_ms(milli_seconds);1 r# o0 q( [. R! D
- }
复制代码
& B; E" b' N- ~" e9 x, w' Z 调整为
& n8 C* g, x8 d1 K2 R$ F$ g% M- //extern "C" void delay_ms(unsigned short nms);/ c. a( D# m" S0 ~
- void thread_sleep(unsigned int milli_seconds)- N2 X8 Y$ S* h6 s% Q$ i& Y4 O
- {//MCU alway implemnet driver code in APP.
1 J L ?# y4 H- _8 [, b8 W - uint32_t tt = ((uint32_t)milli_seconds)*1000;
+ ?; N! `0 X/ t: C6 t: p7 k2 y; s+ p - delay_us(tt);
5 I3 b3 I% j, }- A* Z* N9 Z - }
复制代码 ( a4 v3 f* j+ j: e
【3】在main.c文件,引用各外设驱动头文件/ \$ W: |& I! x% t3 I
- /* USER CODE BEGIN Includes */7 I0 ~' U2 N& d
- #include "../../ICore/key/key.h"
d3 I; q. c+ [" d8 J5 |' N% r - #include "../../ICore/led/led.h"
0 m( v$ L2 ~( Q+ b+ V9 R2 [ - #include "../../ICore/print/print.h"
' c3 X; _4 h. O/ m, n - #include "../../ICore/usart/usart.h"- ?/ _0 P) f4 Y7 E' U5 U! D
- #include "../../ICore/oled/oled.h" y% |$ a. U4 j6 z- V
- /* USER CODE END Includes */
复制代码 6 b/ C. u9 }$ \' o8 F6 V' ~
在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。
# D% n& U3 ]( B; ~4 ^ g3 f! t5 b( [1 j# o- /* USER CODE BEGIN 0 */
5 L! k- N0 H2 Z - //Transfer GuiLite 32 bits color to your LCD color
# [# q J$ s4 V7 B - #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8))( Y/ |; Z% {* @' ?4 \2 w
- //Encapsulate your LCD driver:
5 o7 Y2 N* ]+ H- E! }4 d3 w W9 z - void gfx_draw_pixel(int x, int y, unsigned int rgb)
! ?$ m# u3 p9 s. z& i# A - {9 Y: K, [+ }& ]' L5 Q
- //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的& ^1 Z0 Y6 R7 j* M }
- OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)
" r: D1 i. [! T# c: y5 K - }( }& t# c& H P0 ?2 T
- //Implement it, if you have more fast solution than drawing pixels one by one., @& T2 w9 d5 g$ K4 [# x% e
- //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}
& F" f: A- U/ Q, Q4 j/ z: T4 ~ -
7 Y6 s/ z" H6 e7 t; }4 r - //UI entry
4 O. O9 Y; v$ p1 B# c- F5 S - struct DISPLAY_DRIVER' ]/ F8 V1 V% k8 O2 d1 B5 K+ w5 D
- {
# j6 [4 {1 a* k7 S) j! y2 Z - void (*draw_pixel)(int x, int y, unsigned int rgb);
) v }5 Y! }( k; R" A' l0 g+ G - void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);! T7 z3 ^) P: C# \, u8 q. L
- } my_driver;
) i) @7 M6 _9 E) s" Z' g - extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
, g6 P4 ]$ b; T: J9 t - /* USER CODE END 0 */
复制代码 ' @5 y: b! |; K E
" k. o: Y4 }, s1 U' c! n2 \ 在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。& l" H# G) _ a( |% y. [
- delay_init();
- V; ?8 f( Z7 x - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
. j. h8 C0 n0 U6 Z+ a( n; D: h+ t - LCD_Init();% b; n$ E3 V7 I# N4 t( o8 U' c
- " e" Y- O# E; h: H& D+ i9 _
- //Link your LCD driver & start UI:% `: S7 A; A( X
- my_driver.draw_pixel = gfx_draw_pixel;# L" c& e2 f. W
- my_driver.fill_rect = NULL;//gfx_fill_rect;" }% J( K6 v7 v: d' l3 J$ y$ N f
- startHello3D(NULL, 240, 320, 2, &my_driver);
. w* ?" s+ y8 s# q- F/ w - while(1);
复制代码
, w$ ~$ F2 d% h4 |) _2 R' U0 B 在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。6 {( H% ]6 x% `9 o$ ?- p
- /* USER CODE BEGIN 2 */
* A: D7 l# T! I - ResetPrintInit(&hlpuart1);
2 P- y- H, ]' ^$ y - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断6 v2 d9 {; g& j% g9 [$ x# t
- HLPUSART_RX_STA = 0;
5 g& r6 T1 K" l b - //3 p- J/ e0 V2 c4 g, W
- OLED_init();3 j9 `: L# t8 P# Y$ V. B9 q$ g
- //设置OLED蓝色背景显示& p" C6 n: k9 n4 g5 Q
- BSP_LCD_Clear_DMA(LCD_DISP_BLUE);8 j7 V, R. \/ i, n1 a3 _4 N; s
- printf("OLED_Clear_DMA\r\n");% B& L! r- w5 v* q
- /* USER CODE END 2 */
复制代码 ( \" [% h& a# n: G$ v# \4 j& I
然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。
* s4 P k1 I5 W2 v- /* USER CODE BEGIN WHILE */
8 A' [) s! w2 q ? - while (1), e# u/ C, Z) c& N' l
- {" Z% T3 ^9 r @7 h
- if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
' v6 c2 q# e2 F2 f D) n9 ^" ] - //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);/ o) \: x; }6 P4 P, ~- m5 L
- OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);+ I: s, p9 T% V, t; z& y' R8 A; D! G
- HLPUSART_RX_STA=0;//接收错误,重新开始. l# y% P& J: }( N3 z8 {
- HAL_Delay(100);//等待
6 O% F* Q3 q1 i% C" f - }/ w% t9 ?3 U! j$ F. Y( \, i
- if(KEY_0())5 w7 l2 I. f) w; A) G9 D# a4 o
- {) k& b7 Q4 @2 g4 V! Y( R8 J
- BSP_LCD_login(24,108);
% U/ @- U. M% j4 F4 ] - }# R* L* V6 s# u: K5 A9 z
- if(KEY_1())
, m6 y* D' o( g) C& |& K - {
: n# S9 e2 W# F9 I - BSP_LCD_img_DMA();
6 H, m9 I9 `7 f0 M - }6 z% o6 R! Z) e! D
- if(KEY_2()): J$ q W' J! L: k
- {4 y) p6 e' F9 l. H) d3 ~$ S
- my_driver.draw_pixel = gfx_draw_pixel;
: u: I% g* U3 N1 r4 B - my_driver.fill_rect = NULL;//gfx_fill_rect;
7 E; \+ x* m$ S: w$ Y4 A, T8 d - startHello3D(NULL, 240, 240, 2, &my_driver);
8 R" E- I) r1 z' k1 j - }9 g& t" [8 F7 J1 m9 A' b( {. W9 t
- /* USER CODE END WHILE */
复制代码
3 z! [( _8 v) R; O& w/ K* F8 G四、编译及测试. J" `- o, ^8 J/ }9 i3 }$ d
完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。% d P; B$ Y6 W! ?2 p
6 x8 E# z$ r9 W" O3 _
0 F+ O# w6 U2 O" x
! T' ?% v4 M9 X' k
按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。% G, f/ V/ w7 z# ^+ q N: F
9 C2 S# _: x9 l0 z1 Q
# S$ G+ F. f* X; y
) s S1 I2 q( i3 f% {0 w9 O
4 c0 i9 F$ N) V- U, f
————————————————. B5 K- A A `5 |) O- e+ v0 k6 t$ N
版权声明:py_free-物联智能
% _+ I2 ~! b9 ~如有侵权请联系删除
) X6 X1 D) G/ w- y0 l4 D0 Z( e |
内容很详细,谢谢分享
这个GUI效果真不错,有空可以尝试移植一下看看。