一、GuiLite 7 d+ G2 b- d) J/ A6 x1 r+ G6 ]
GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。, t1 A9 d3 k- ]7 q7 o ~/ G) Z
& `0 @7 V1 d% c( z二、创建工程
' R( ^( e+ ^8 R. ~* N& Y+ F. S# T1 K1 P. z9 F4 |% o1 S
实现了LCD屏幕点亮功能。. n' N1 K/ e* B
2 f/ I; f2 p/ \- E
【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。6 t# t. v* S: |% g6 b: Z2 L
) Y$ P/ D. U& d: _3 d 在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。* ^* f) E& S5 a
+ R: k6 z/ T; A; v, D* H
; ?3 \% Z+ P' v3 \& ^" \9 Q' j g0 j* R/ u% M
【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容: v% z" P! s8 P5 G$ ~3 @) t" Y
- volatile uint8_t one_frame_done;; ^3 b( L j. D
- void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi). p! B$ M4 W. O7 }+ l
- {
: I5 S+ h w0 O9 z - one_frame_done = 1;
. @% @/ B7 m+ W3 U1 u- E - }
复制代码 & n& W; z) }5 }
8 `9 N0 D, ?# K/ Y+ ?: S
# ^5 U, ~) L7 b" B$ z
9 }$ |1 i, w( p6 I6 y三、GuiLite库及Hello3D样例移植
7 Z- w# { W5 ]- K& D5 O 在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。
7 o9 d ]8 f: e' R
~+ z9 @& J! d6 b& m 【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。
- q1 V( m: d5 q0 [% ^. R; Y% ]+ E6 d* @ S
delay.h
' |% K8 V( O0 e& c7 g: m& M9 v5 {- C- #ifndef DELAY_DELAY_H_
9 l. \0 `0 C; e, i% Y1 \ - #define DELAY_DELAY_H_
' n/ D5 Y; |: F' P - #ifdef __cplusplus
& }9 G1 Z: H f - extern "C" { |3 \" O: |, q M/ _& R0 e
- #endif
7 D& ^$ z9 F0 E - ! W- U; L' g, t* {# B. [" V
- #include "stm32l4xx_hal.h" //HAL库文件声明 [" y1 ?9 A- ^6 d: F) ~& I
- void delay_us(uint32_t us); //C文件中的函数声明
7 ~# {9 O3 M4 u4 I: W+ O5 J - # N* {! R0 A3 D0 [
- #ifdef __cplusplus8 T3 w/ D: D5 q3 q/ q
- }7 V6 h; H- A7 w8 l
- #endif8 l: Y+ C& {% b; {! M7 Y
- #endif /* DELAY_DELAY_H_ */
复制代码
8 r: G) C: U2 q, z3 ^
. c3 v0 d/ I8 x i5 o. n0 n5 ? delay.c
7 u- _$ J" s' h% ^: `8 N& O7 m- #include "delay.h"; o; M6 i' ?$ |! |
-
2 s. j' S" @# N) m - void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
! H4 o W5 }* x& s& \+ A - {
, @+ M# d5 B, d. |, {7 B - uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数. ?0 H3 g+ O) g3 t
- while (delay--); //循环delay次,达到1微秒延时8 u: Z8 V% t1 i5 x+ L c
- }
复制代码 7 b$ U2 E, w! R; d
由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。; M. f! p% u& {( O" E9 Z
- #ifdef __cplusplus: ?" l9 P) f! \/ b
- extern "C" {% ^, n: n8 Q1 L% X, {
- #endif
* d' y9 z y# X. d% Z - 0 l1 R$ X8 Q( w
- //you code- V5 _4 V7 W/ A# L, U' Z4 k
- # ]0 y: \, }1 f" H
- #ifdef __cplusplus
; [8 |8 L5 r2 t2 R/ K. a3 ]- U0 b) j - }
复制代码 - G, Q# O$ Q) W$ l7 ]
【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下
4 d; Z, c7 t5 U- #ifndef GUILITE_H_
- ~6 V/ `/ Q1 ` E/ i6 V - #define GUILITE_H_
7 o! L7 R% [' S8 z6 `; X$ {3 B - #include "../delay/delay.h"
复制代码 & j0 O( ~+ q5 w
在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。' S: I* ]8 J2 U9 u1 M; L
- extern "C" void delay_ms(unsigned short nms);
. X# b, G) I: b& {1 G/ `: C: o% q - void thread_sleep(unsigned int milli_seconds)
* s9 g& y2 w1 {0 B - {//MCU alway implemnet driver code in APP.
' x0 e. f& `! L/ p, m2 Z7 R - delay_ms(milli_seconds);7 Q: a1 {, l# f% `! V& p/ w
- }
复制代码
& p9 z) x0 t$ m9 R 调整为8 T+ d6 s4 |, }7 W6 ]7 V
- //extern "C" void delay_ms(unsigned short nms);
6 O3 f: b9 b% M& Z - void thread_sleep(unsigned int milli_seconds)9 t$ Q$ P2 t, P' m r" p9 a u: U
- {//MCU alway implemnet driver code in APP.' K0 @3 u x! N" W, ^
- uint32_t tt = ((uint32_t)milli_seconds)*1000;7 ~2 X! N) u# ?) x. K
- delay_us(tt);
6 a& Z2 q1 R: o7 `! X9 q) y - }
复制代码 ; B: s( C8 V; ^
【3】在main.c文件,引用各外设驱动头文件2 i. V0 p; L: S3 \8 |( @6 S: X
- /* USER CODE BEGIN Includes */
d W! A" s6 w+ H# l/ m3 W - #include "../../ICore/key/key.h"
i, l) K2 Y8 D* u8 |# b9 I ] - #include "../../ICore/led/led.h"
* I$ L! T7 i. @, Y# w - #include "../../ICore/print/print.h"3 o, C- }5 b% j i
- #include "../../ICore/usart/usart.h"; T' T" s( Q2 @' o* Y
- #include "../../ICore/oled/oled.h"4 d4 Q- D' ]9 V0 x& r. i3 f
- /* USER CODE END Includes */
复制代码
) d6 K) \& o( o @6 X 在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。
5 F, M& d. _" H7 V- /* USER CODE BEGIN 0 */2 e* M. [3 p) l1 K
- //Transfer GuiLite 32 bits color to your LCD color, a' Y9 J1 ~$ Q4 Q
- #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8)) X" w9 n" F6 o* q( R6 |8 k% r, J& d
- //Encapsulate your LCD driver:
" \. C; C% k( n- a - void gfx_draw_pixel(int x, int y, unsigned int rgb)
$ c9 z) ?" y4 ], z6 J+ _ - {
8 V# q" E9 h# e4 n - //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的
* K) ^ Q! n4 ~- U7 {. x. A& N V' r - OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)
3 R6 U( G6 i4 R - }
8 @0 C& a0 s, _( `" g' m' d4 C9 F - //Implement it, if you have more fast solution than drawing pixels one by one.
) k; ]5 e" Y" f/ R+ p9 A% E" o - //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}" Q7 D! l) c. c# X9 w+ j' b8 ?
- ! L7 b* E+ @# v5 Q9 Q- j! I
- //UI entry
; m$ {2 ^' A) Y' \ - struct DISPLAY_DRIVER
2 x8 L6 j) w$ H9 |3 c. M: w - {! {! |0 y. l* H
- void (*draw_pixel)(int x, int y, unsigned int rgb);" y5 Y6 T6 z" d2 x5 g, _0 x
- void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);" m0 U: o; C2 F6 G1 I
- } my_driver;# [: N4 B+ a% x5 I6 P; v+ {
- extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
! _+ i/ N; I& o' X7 m - /* USER CODE END 0 */
复制代码
; }: X$ M2 E: w S% r
% W: d; N' \ e/ \. z0 c 在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。
: D# M A; r0 ~- delay_init();
6 |5 L# m1 K ~3 Y! E9 m6 i - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
`! L. t- U) G0 [# l3 U; T% b" n, ] - LCD_Init();" s( _9 ]+ o4 I0 V
- % A) u2 C: [0 k2 x& B. |
- //Link your LCD driver & start UI:
! W. G8 N1 y' [1 e - my_driver.draw_pixel = gfx_draw_pixel;* ~+ R" V+ R/ x, s/ F
- my_driver.fill_rect = NULL;//gfx_fill_rect;
; I5 T' L2 E9 N R: a/ T - startHello3D(NULL, 240, 320, 2, &my_driver);
" u' B5 f4 x1 r% D. [/ n) a - while(1);
复制代码 . L$ f+ k+ `1 E$ N% x
在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。
4 _3 N- m. m* A: |% h; l- /* USER CODE BEGIN 2 */
) S$ K' H7 X# d/ ? - ResetPrintInit(&hlpuart1);$ I7 g" x: s# i$ V+ U7 X
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
4 x6 [+ r% t% b/ s. t3 @ - HLPUSART_RX_STA = 0;
* ?1 A5 T! R6 g - //! l. H/ k/ v+ [% ^7 H7 U% l9 p# i
- OLED_init();
% g# C/ I% S3 w7 Q6 M/ L - //设置OLED蓝色背景显示" X5 {" `, V- z. N5 C# K/ j( k, Z _
- BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
3 m3 W4 Y2 e& z5 F2 H0 ]( v. B0 x - printf("OLED_Clear_DMA\r\n");
& Q9 R: [5 D; I. M: U - /* USER CODE END 2 */
复制代码 ) L$ g2 I4 R, D. Y3 F% _5 N
然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。
3 q' s6 m6 Y* K- /* USER CODE BEGIN WHILE */- d5 }" }% V* o, I
- while (1)
5 s3 K) T- w- g |+ \1 m4 O; Z - {! W4 A3 j% T* j" _7 F
- if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始! ^$ S! F- T4 `; M4 O3 K& n
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);8 Y9 s# M2 p6 I* ?2 g
- OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);* u r# z- v+ a; z" V6 a
- HLPUSART_RX_STA=0;//接收错误,重新开始3 K* w/ u3 s9 h4 w$ E& j) i
- HAL_Delay(100);//等待
9 L' X W. Z: K: r - }
7 W4 ^% e9 T% d% f/ F7 Q& X# ] - if(KEY_0())
4 n1 _3 \3 E& K$ @: Y; [ - {4 A! p P# ?& o6 V6 X" Z
- BSP_LCD_login(24,108);
, ~ r- F, ^0 j - }$ d- q2 V6 A% J h6 y* j4 W
- if(KEY_1())1 P l# W; p0 R2 O3 l2 z
- { [' w/ b: P# t! R( q9 M
- BSP_LCD_img_DMA();
2 T1 T, t& j4 K5 a7 R - }, k& B+ m m1 M) t" v
- if(KEY_2())% R- T' U, Y {. L; c2 E+ m
- {
4 B7 d( z5 [3 ~ - my_driver.draw_pixel = gfx_draw_pixel;
' T% h& A, y, H" Z - my_driver.fill_rect = NULL;//gfx_fill_rect;
% a7 v8 s* [ m* Y& ^( i8 a* p - startHello3D(NULL, 240, 240, 2, &my_driver);1 V E i/ u3 l; u: u @
- }
2 o" @: M f' A& ^3 W$ [) |; _: ? - /* USER CODE END WHILE */
复制代码 3 s T [. w9 t* t
四、编译及测试
) I8 \9 o& F( c! E( \# T. j 完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。
6 d# J/ a8 W% o5 {
8 X7 A1 K' e1 a6 m
) E6 y5 X4 l* r' s! k
/ Y# c8 b x# n8 w# x( o 按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。
& g9 i/ I" _3 Q' L4 c. ]' b% {# ?5 @' R9 K' h: `
, H/ O; v: Z6 J) }6 w7 R; b+ Y2 b
' ?7 p+ ~# N7 H, @1 _4 A
0 X }2 Y8 D8 a. E4 h————————————————3 l3 R, w5 [( f3 M9 D
版权声明:py_free-物联智能& k& d6 x6 f' G# G
如有侵权请联系删除- l* X" P' ]5 ?0 k
|
内容很详细,谢谢分享
这个GUI效果真不错,有空可以尝试移植一下看看。