一、GuiLite ' E$ M( |: w( Z* q" ]9 Q3 T- k
GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。9 X" r; R) ]! n7 H# M
9 y$ }4 p% j H9 B! Y# h
二、创建工程
& k, \4 X6 y+ l* X6 K" V1 g. E5 I$ e4 q& m4 p
实现了LCD屏幕点亮功能。
' D) L3 O/ Z) l# B& N( f- t: k" c! j6 y9 l
【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。3 h5 T, X4 q4 V9 M
( O" |0 M& p! V3 X$ ]1 b 在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。- r4 k' C$ H7 _
* a) e2 M3 z. i+ F* e2 {8 V
1 O6 [0 E: ^% S: Z3 v8 _) g0 B
4 ^6 Y, r# I1 h7 | 【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容:( b+ H1 j- C u
- volatile uint8_t one_frame_done;, W& f4 |$ }- @% L) N6 I3 u7 m
- void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)) U9 G7 ]" t% ?; ]1 ~; Z
- {
) ^ D/ m4 l, N8 H3 K% G* w/ k - one_frame_done = 1;2 n+ ?: S! W \- P
- }
复制代码
3 A0 k- k u% h
; b4 S/ V! D: y5 W5 m& u" i) ?
( `# j. ^& d8 M7 ~4 f- V3 I
. K. ~( k6 D- Y! z9 @* t三、GuiLite库及Hello3D样例移植
% e5 I E, M! D5 r( ] 在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。# Q( X9 r5 d7 u: z' L
3 @7 {& k7 B8 n! x* A$ w1 O
【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。
0 F! f! _- n6 X4 O/ e1 ?! z- M# K" v8 p5 z5 n* i, Z& d0 p( }
delay.h
: N, M1 d* d. H' H0 t+ x5 U/ I- #ifndef DELAY_DELAY_H_
& G! o3 n) u5 B6 B. v! b- Z - #define DELAY_DELAY_H_
4 j* E- T3 H7 Z. k0 C - #ifdef __cplusplus% L, o2 F: D; f+ @8 m
- extern "C" {+ m9 H0 p# l2 \. i) h( z4 }
- #endif
" S0 E$ d* r! t! @( I - ; J* {- D. R. ^' C {# n; S
- #include "stm32l4xx_hal.h" //HAL库文件声明
9 Y( A# i& s! |& h - void delay_us(uint32_t us); //C文件中的函数声明/ ]! ? K. K) {6 X4 E
-
) ?6 ]" D! ]& i5 f! \* j3 b - #ifdef __cplusplus8 Q! Y2 x4 e0 i
- }
2 Y. Y0 C! |( [* w/ y - #endif
8 r- u8 u J1 _0 _ t- D% P$ ~& h - #endif /* DELAY_DELAY_H_ */
复制代码 ( f! a) s3 y7 Q0 P) i/ K
& Y( L7 `0 a R' |2 O% ~8 _ delay.c! A. y' {7 N2 y3 [
- #include "delay.h"
; Y$ K( h' Q/ U! b -
$ h3 ~# u* |( M8 f - void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数1 d% }9 t& T1 Z+ z$ s3 C
- {6 [9 ^! R) p: J
- uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数# t; L( i) S0 ~# ]
- while (delay--); //循环delay次,达到1微秒延时, ` U" k* k7 F- b1 g
- }
复制代码 6 q- s& r+ B a" X, \ J% r5 N% s( ~0 b
由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。, q2 k7 t7 o# w* T6 t5 s
- #ifdef __cplusplus, B: V' i3 p$ C8 O" ?0 b1 C
- extern "C" { T9 b9 P/ w1 }+ @9 U8 _1 k
- #endif# B/ `- `9 d* E) f7 F
-
# J8 K3 X4 n/ E K - //you code2 N0 | r& I/ M" Y7 B
-
# e$ y8 M/ _8 V2 |2 H2 \5 h - #ifdef __cplusplus
9 {8 p7 r# U" J- Z - }
复制代码
; h3 `) y& e5 v k 【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下
* x9 U4 N; I3 R9 h8 W2 f/ |; Q6 J- #ifndef GUILITE_H_
6 v0 X6 l) M' B5 U4 z - #define GUILITE_H_
" ~0 K+ q, `: Z0 O( k( Y% e4 L - #include "../delay/delay.h"
复制代码 7 Q5 @) b: Y: o
在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。
. N+ k- L b3 ^ c2 ~1 V. w& h- extern "C" void delay_ms(unsigned short nms);$ G4 |1 Q/ T6 n# i0 l' V! h! Q
- void thread_sleep(unsigned int milli_seconds)
/ ?, H# `* ]* A9 G- B8 r& u0 {+ [ - {//MCU alway implemnet driver code in APP.( B! y: u, h: r7 Z& e0 z; S
- delay_ms(milli_seconds);
/ z) S- `! y; Z0 o* c+ k - }
复制代码
1 C+ F$ N& [4 f6 U0 A! F 调整为5 C* J3 Y. g* n. Z& i
- //extern "C" void delay_ms(unsigned short nms);
1 M/ U3 w( c2 R9 L& z7 A1 ` - void thread_sleep(unsigned int milli_seconds)
p1 q, l: {7 {) ]8 o0 |4 b- s - {//MCU alway implemnet driver code in APP.1 H8 J% W4 `* ]4 l$ ?: L) q, r
- uint32_t tt = ((uint32_t)milli_seconds)*1000;
- @* a2 m9 W( x' g4 w1 o/ `/ s - delay_us(tt);8 f; A- M+ e( T$ g2 g4 e. x
- }
复制代码 + u9 l. n3 q+ V' I& ~( C, m/ S
【3】在main.c文件,引用各外设驱动头文件5 ^' u; @) \5 T
- /* USER CODE BEGIN Includes */
1 i. i) t9 N0 \% ~7 ] - #include "../../ICore/key/key.h"2 }2 H- o, R$ K: W/ z( z7 r- X. _
- #include "../../ICore/led/led.h"% c1 Y1 o4 `* Z
- #include "../../ICore/print/print.h"
6 x6 B: |+ j6 N0 ]& ^* D - #include "../../ICore/usart/usart.h"
n% o, {* L: E. t4 @7 g - #include "../../ICore/oled/oled.h"/ D/ ]( f# Z5 F$ `
- /* USER CODE END Includes */
复制代码
& M- U( J9 [! |+ `, f& i 在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。
' L8 O8 Y" f( B/ f' Q. U- /* USER CODE BEGIN 0 */; m! B4 r& P! a1 u) [
- //Transfer GuiLite 32 bits color to your LCD color
# f$ h2 Z8 U. U e - #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8))
0 B5 h9 @6 q3 I- f2 t - //Encapsulate your LCD driver:
& X2 U( @/ \' j5 Q9 A2 Z: {( \* K J - void gfx_draw_pixel(int x, int y, unsigned int rgb)6 N) K) Y' p9 a; s9 P
- {
/ W9 d# d7 z3 C, { - //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的+ g3 r+ c& f7 S3 ?+ [, J9 W
- OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)
6 v$ x& T/ j9 s% \% A' m& r# |* E - }7 i* V- ]( d. ]( U" R7 F6 r3 h5 T
- //Implement it, if you have more fast solution than drawing pixels one by one.
1 w* ?5 H- ~( E - //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}* D" {& I8 o3 c9 Z5 @* K
- : O8 V2 {* x% X+ \# Z
- //UI entry5 E. c$ [' k+ ~- j/ u
- struct DISPLAY_DRIVER, H- k( C: B/ ~6 `3 [9 C' A3 R
- {
n% O. r g; ^. I: \; n+ I: c8 U - void (*draw_pixel)(int x, int y, unsigned int rgb);7 C" Z }& p. Y* |' r% b b5 z! i
- void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);% X6 J/ q" l; B; N0 p$ \5 x6 w9 }9 n
- } my_driver;' y) T3 e! A) I C
- extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);- { d3 ]; D. _7 c. U) h
- /* USER CODE END 0 */
复制代码 , o0 }( l" Q* s1 L! G: W
6 y6 `1 o. D$ o5 @- {( A
在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。
! B- r# i1 Q# P- delay_init();! S ?9 S# d5 d' l. T
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/ r' f" O" _) ?* q - LCD_Init();* k, p7 E, R$ R/ R$ `
- 6 u7 E0 z3 w% ]9 W7 i9 ]
- //Link your LCD driver & start UI:
8 j% A, v" M# m+ n - my_driver.draw_pixel = gfx_draw_pixel;
3 j# R; F0 h* d( p) o; ~ - my_driver.fill_rect = NULL;//gfx_fill_rect;9 q0 r5 i* A' c; g" J
- startHello3D(NULL, 240, 320, 2, &my_driver);7 Q* v7 J) _+ W9 S7 i
- while(1);
复制代码 * w& K: x" X$ P4 l: M# t
在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。, W, Q0 L* c; \' u2 b: M6 s
- /* USER CODE BEGIN 2 */
* ^; R/ @1 E6 D# N - ResetPrintInit(&hlpuart1);
5 P/ u( } y# p Q: J5 T1 X - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断# n& D0 F( Z. {0 q" \
- HLPUSART_RX_STA = 0;
# M& F+ t2 y! ?& y" A - //
0 l; o4 H( F2 m: i - OLED_init();
3 [1 T& \2 i& o9 w - //设置OLED蓝色背景显示
, p6 \& K4 Y# |; {& [8 }. H - BSP_LCD_Clear_DMA(LCD_DISP_BLUE);. W: g, g4 v/ Z0 l) z e; b/ P
- printf("OLED_Clear_DMA\r\n");
% s6 n2 n6 ]2 ], I y& ^ - /* USER CODE END 2 */
复制代码 * i) s3 y7 ?- T& K5 l* }
然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。# E) Z+ R9 N) P' @) ?2 c
- /* USER CODE BEGIN WHILE */
0 |9 u$ K/ @4 U9 S& f - while (1) m8 k5 |8 ~: `4 l" P5 }
- {7 o+ w- V) M0 D% O0 T# P9 {3 J( ^. B0 L
- if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始+ R& [- F; W+ B8 R# L/ K0 Q9 w
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
% d, t/ P! U5 r' R5 Q% l - OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);) q. @! [) W2 t4 i* a
- HLPUSART_RX_STA=0;//接收错误,重新开始
$ w, Y6 e2 V2 ] - HAL_Delay(100);//等待
$ @# v8 E! q8 N$ \$ E" F - }& _! W5 J2 u9 m# W( ]) E# A
- if(KEY_0()); Q z( P8 g7 v2 z5 ^/ l% ]- M
- {
1 o- r1 ` }4 T* U8 f - BSP_LCD_login(24,108);" t5 P E3 w) }( o! v
- }
' `9 \; H- I) j6 F3 E8 b - if(KEY_1())
7 @. [( R+ w. C, |6 ]. m0 j& [ - {3 U6 Y( O; ]( z: p' p
- BSP_LCD_img_DMA();: R- G4 R7 x4 k6 t
- }' F' n' V3 {; ^+ u; \' L
- if(KEY_2())0 a+ h" r2 e& F
- {7 x: d; b% |2 V/ K
- my_driver.draw_pixel = gfx_draw_pixel;% j1 d; W! `5 V0 _ N6 ~
- my_driver.fill_rect = NULL;//gfx_fill_rect;4 N' W( {. a2 W, h1 [2 @/ X
- startHello3D(NULL, 240, 240, 2, &my_driver);5 y: d9 m6 `& k
- }, d' p+ |1 L& h
- /* USER CODE END WHILE */
复制代码 & X: O6 I8 f( }/ `& ~
四、编译及测试 P* D" {4 v: J% t& J% z
完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。# I+ Y) b' {2 g1 F( Z- k
8 g" u* o5 ^& V6 F# S
5 X: {' ~, Y& `4 W0 f* `; ^2 G3 m. r- y9 s
按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。6 I* D$ I; Q' q; ]
' q! q( b! @+ |2 U$ [1 H$ l8 u
$ |; \+ W. ^5 p
, O8 _9 Q9 R$ Z: L' T; M9 m
: U- g) q3 S& j m' v+ ` y+ K
————————————————
2 H9 i& {: l- F0 r, E8 K版权声明:py_free-物联智能/ ~% {0 I/ F" j
如有侵权请联系删除) S# R$ F' e* J- e% m( N/ T
|
内容很详细,谢谢分享
这个GUI效果真不错,有空可以尝试移植一下看看。