一、GuiLite
# L- H* x, B5 @9 n2 L# w GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。" M6 [8 c- N% L
- U: W) V' v/ w& ~二、创建工程
) p8 V4 \: Z* G$ u. w9 T( R3 @6 G+ e( m( E) W0 N
实现了LCD屏幕点亮功能。# P/ ^( a7 l' X0 z# c8 ^1 C
6 ?4 U6 O# y: y$ F( W) Z 【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。1 v$ t6 k8 N" q2 H
' P& Y0 N N# {$ r6 C7 _ 在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。
! T) M0 k/ d7 \' b# i/ r" o# m+ r. Q/ n+ Q" R, }+ h* W4 K0 { Y
* ?! o% i2 c3 _& W, B7 n; b
) v* n; B7 F- Z) u: @& d 【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容:
; Z. I. S# j+ `. Z2 g3 Q6 _! c- volatile uint8_t one_frame_done;
, q- @4 `2 L# x% H# G k - void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)' O7 {! W/ N2 K* j$ ^# y
- {
$ S e1 \" _) q- [ - one_frame_done = 1;
' C0 @3 w6 j7 m( \2 N - }
复制代码
1 i; C1 b* ?# o& m3 Q) t+ }. Q
[4 {6 C$ o6 `8 _1 Q, Z
7 l. d2 b3 P) y- f$ i- H
! {( J: [* ^2 H' s三、GuiLite库及Hello3D样例移植
9 ^' u9 D' Y" m' @, s2 a 在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。
8 }/ ~' [/ o f" f" t1 R# N: G- R/ p4 z
【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。( m: P) }! a( _
# `+ Q* \7 N o/ K! s
delay.h( d, X9 n8 ~7 P- d6 J# w
- #ifndef DELAY_DELAY_H_3 T* F1 K& v8 F$ @ h1 W- {
- #define DELAY_DELAY_H_
' T! g* x6 s1 B. z' n% k) D" [ - #ifdef __cplusplus& X( [ h \- S. H3 S, b& m
- extern "C" {1 Z- Z) H5 X4 [3 b3 n9 a; F
- #endif
' B4 H. h. p2 X- ]/ T4 g - ; J( d M/ Z; L: i
- #include "stm32l4xx_hal.h" //HAL库文件声明7 G) P$ g+ g$ G- f1 c! s
- void delay_us(uint32_t us); //C文件中的函数声明
* D) A8 I" i% f8 P( F0 U* D* n - - I! f3 _ B* R- h- f% l5 ^6 Q
- #ifdef __cplusplus
! `4 V% W2 W3 P" G' {5 l - }
5 W( t4 {$ ~. y$ n% j - #endif
4 x' s1 K' Q1 x f3 C! `" n8 D S) Y - #endif /* DELAY_DELAY_H_ */
复制代码 & V/ Y8 E r* s8 r0 w: ~
. x5 m' v2 Y! E$ x4 B: Q/ r4 H& p delay.c
' w2 U( ^; ]# G; L* z- #include "delay.h"8 p) r: j6 ?5 x: R/ u) J/ h. N& Q
- 6 ~# B e4 c7 ?1 @
- void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
" k B1 ~9 {5 z, o# V* } - {! r* m4 Z* H8 x' m+ D; ] u# M
- uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数& u: c' U0 r) u( v% X, E$ H
- while (delay--); //循环delay次,达到1微秒延时' a, `/ }( M2 F7 e2 x3 D( R
- }
复制代码
% \% m+ @5 e" n( w9 c 由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。. c( ?3 J" t! D+ c- ]* t! X$ i
- #ifdef __cplusplus2 d% B+ b+ L; A0 W8 z( j4 g! A
- extern "C" {
. N! L. o* s4 X, { - #endif
7 d- H: T- E y4 D' D - % G3 r9 H) j( k
- //you code
7 Y p( ?) s o; b -
* s' W! P9 K. c# D) L" B( o$ z; a - #ifdef __cplusplus- N% I$ m* k& O3 k" @0 T, w# X* u
- }
复制代码
( M: a7 C* g, J6 |6 u8 ^ 【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下' n) g. \. Q+ ?, }; O
- #ifndef GUILITE_H_0 f5 e0 l7 u, n
- #define GUILITE_H_9 I1 v# N/ ]% A. r
- #include "../delay/delay.h"
复制代码 / O. i# R% }2 M7 z
在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。
: y8 S" E$ h# m- extern "C" void delay_ms(unsigned short nms);
; C4 C9 e7 { W% w( s - void thread_sleep(unsigned int milli_seconds)8 p: Y7 q V5 }- V6 l6 F; I
- {//MCU alway implemnet driver code in APP.6 `" x- R: D- O v6 Q
- delay_ms(milli_seconds);7 m' w }( g% R9 O
- }
复制代码 5 Z1 I6 N8 J m6 u
调整为
0 E% P O" H0 {+ H7 Z+ a% P5 \- //extern "C" void delay_ms(unsigned short nms);6 J- \, W7 N7 k7 C. C
- void thread_sleep(unsigned int milli_seconds)
9 `3 f, N) t- Y$ z2 `2 G; |9 K - {//MCU alway implemnet driver code in APP.* B8 p8 k( \) F- ^/ H
- uint32_t tt = ((uint32_t)milli_seconds)*1000;
4 `; ?( T* g: _. j- o - delay_us(tt);6 A3 W; r; S! E; u4 I0 d. T/ V
- }
复制代码 ( L4 \# \& T; D
【3】在main.c文件,引用各外设驱动头文件% Z- x; Y- |3 h. [
- /* USER CODE BEGIN Includes */
9 [; Z+ D+ ?! J& ^$ ~ - #include "../../ICore/key/key.h"6 d0 k) K0 Q6 e6 ^6 G
- #include "../../ICore/led/led.h"6 d) y5 v& l5 I: M# o
- #include "../../ICore/print/print.h"6 g! b C# j. O" y8 X% q
- #include "../../ICore/usart/usart.h"
0 S* ]( m) ^3 Z, X7 w - #include "../../ICore/oled/oled.h"/ A8 Y( z9 _% g. M" ^! Q
- /* USER CODE END Includes */
复制代码 & z. M# D8 i. f( m
在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。2 H9 |, N" r- ^: `' x/ j
- /* USER CODE BEGIN 0 */. C. \+ F3 k" C9 K6 j5 J
- //Transfer GuiLite 32 bits color to your LCD color/ q, V" `' {# ~- `- k, m
- #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8))# _( e3 X: H5 n4 {% U6 B/ p0 n
- //Encapsulate your LCD driver:* W; y% z+ l6 @; _ F6 o
- void gfx_draw_pixel(int x, int y, unsigned int rgb)
/ u. K5 C: ?" c1 Z3 G - {# O, b5 Z" q: g& v+ ^
- //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的
" k- F$ z) ]5 p3 i" O - OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)& v$ t: @, k3 d2 o* R/ f
- }
# P7 [8 M6 Q* m" l - //Implement it, if you have more fast solution than drawing pixels one by one.2 D6 }& g7 B% K9 j) F
- //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}% A* C5 w0 ?/ j/ ?* e
-
. L$ Z9 w9 g0 c" I* U. _ - //UI entry/ j* C* @7 K) i: l
- struct DISPLAY_DRIVER
5 ~" E6 Z3 X# U% g - {
. [; \" _ s' e, P2 n - void (*draw_pixel)(int x, int y, unsigned int rgb); W/ K$ `1 E1 s7 n! Q: u
- void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);* d& ~2 G$ e3 b, ~% J# t& L
- } my_driver;
3 W, u+ ]( c6 q; G* c - extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);; e: l* c# U6 F
- /* USER CODE END 0 */
复制代码
% d5 l( A9 C; p" m/ f4 f( n/ q! D0 M. y4 U3 G: i# g& O7 Q
在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。
4 a) ^* J' A1 n0 e' m: A+ ^2 D' s- delay_init();5 j; C2 G( n2 ]0 y
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);4 E' N; r) f) o: U. P8 ~
- LCD_Init();! X8 f: G* C! j7 X7 J% i
- ; A" j" b+ [! ~9 Z r2 S+ d
- //Link your LCD driver & start UI:( }( u7 n! _ P0 m2 K
- my_driver.draw_pixel = gfx_draw_pixel;2 b( D" l5 R% i
- my_driver.fill_rect = NULL;//gfx_fill_rect;; o; j( k. e; Y0 ?+ @% a& j
- startHello3D(NULL, 240, 320, 2, &my_driver);
9 y' N+ C$ U3 x) I$ ^& f - while(1);
复制代码
# ?3 a" r2 Y5 ~" ~5 }- X7 g 在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。
! D, G5 E( L/ s* j: C- /* USER CODE BEGIN 2 */
4 C7 d$ q' Z- N" t. p! p( T% j - ResetPrintInit(&hlpuart1);1 ]+ C- c9 U) m' g
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
* Z1 E( @% D; t9 ?$ S - HLPUSART_RX_STA = 0;
& L5 M4 H& A, y8 H% a! L1 c - //# W j8 V! @$ d$ G
- OLED_init();9 c( o' T' p: A1 V
- //设置OLED蓝色背景显示
. W: B+ m6 \9 I- ?/ b1 @ - BSP_LCD_Clear_DMA(LCD_DISP_BLUE);' G9 P) d& G; f& R; y- A
- printf("OLED_Clear_DMA\r\n");; } _1 V$ l: o9 Y9 Q% q
- /* USER CODE END 2 */
复制代码
6 W# q" j6 O, b& y 然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。5 t3 ^3 Q* A& X1 N
- /* USER CODE BEGIN WHILE */
3 r( |# P0 p% s, t) s ] - while (1)2 v7 G' Q9 ]! I
- {( L# g! `4 C( H
- if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始. A2 s0 _/ D# D5 s* K/ o- R
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);% t6 C- s* @ ?% H% A
- OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);' t) A# B) d/ g6 ?% Z# d
- HLPUSART_RX_STA=0;//接收错误,重新开始3 I& E" ?9 E/ a+ K, v
- HAL_Delay(100);//等待
, \: v' _. y& ^3 _ - }
5 C- ^$ R' Y4 C( k0 P* H2 i - if(KEY_0())/ E0 u" X2 F3 i# U1 h0 o
- {3 h4 l8 \9 I* L$ I3 t2 s/ C
- BSP_LCD_login(24,108);- N4 X; c C5 g; e5 U
- }
+ G# ]) X7 e4 k- j - if(KEY_1())% l! ]* z- y% |0 j2 B+ h- w' D: f" C
- {) L8 x+ R5 f- a; N6 n8 O5 ^0 D+ n
- BSP_LCD_img_DMA();1 I& L. _. Z a/ ?. o
- }+ ]5 [& e) N6 ~5 ^- _2 q7 c
- if(KEY_2())
6 x4 [; s* e4 I0 o, \1 A# L - {2 v$ A& p' X$ Z6 j
- my_driver.draw_pixel = gfx_draw_pixel;& A. x4 v1 Q# M+ p( m2 [/ u# W" U
- my_driver.fill_rect = NULL;//gfx_fill_rect;+ L, B5 L3 Y% T6 L2 C
- startHello3D(NULL, 240, 240, 2, &my_driver);
( g& O4 {( |/ H* T! K - }
# D9 f+ K; `1 o X! M. [ - /* USER CODE END WHILE */
复制代码 , _9 H6 f+ @2 M# h. G2 B
四、编译及测试& k _( S4 x; e/ W
完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。3 b: x' \ }! N
' S# F4 {' J+ E3 d3 H) f
# b9 g2 X% n+ F! {& x7 B
- f7 K+ C8 w5 e; I) Q5 K+ O& G! I 按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。
- a* o9 W, |( h3 Q+ j# ]% P! w+ T( c5 k E/ n3 C: K. V
. e7 w' O6 L' X0 u a! i5 z
9 Q# x' ~' K: r, I! U$ I
- c# a8 {$ l1 c+ T) z4 R————————————————
* ?0 k/ u: R4 ] A8 a0 h# U版权声明:py_free-物联智能
7 L& S5 F9 K) {, v ^# n如有侵权请联系删除
2 @" W0 s1 o: J2 K7 b& U |
内容很详细,谢谢分享
这个GUI效果真不错,有空可以尝试移植一下看看。