一、GuiLite - p/ p0 s( C; Q2 d4 d* b$ J, x
GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。
; G' Q$ i8 ~# J, c8 f( W# _4 D8 m2 }& n$ f' c5 {0 `
二、创建工程4 V, _0 t! y( _! V. Q+ Z
6 ?$ Y/ a; B( p# R 实现了LCD屏幕点亮功能。
% U/ W6 h+ w) @+ R/ b7 p. U! p: M4 R5 |$ E- B# \) U
【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。! g/ S4 [ `' ~9 c1 C
1 b! t* Z" T0 Z; F 在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。6 z) l/ F: [8 }/ W i0 q
, j6 d+ \% v0 i* O8 u) ]' ?
$ p' w. z2 p1 H" k: u
+ e9 g8 {' `5 Z6 n 【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容:
! f( z9 L% W; T+ J2 ~+ q P- volatile uint8_t one_frame_done;$ T7 k) N& R3 O; }. {, J0 e; L
- void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi). F3 E3 y* e1 _* I j/ S
- { K* Z- [3 U" u) Q
- one_frame_done = 1;; N, K0 ]) j6 @& A1 r
- }
复制代码 ( b6 S0 S/ [: K" {% P8 s
3 K% _* z' |% I, H* w! p
* w) U# w+ J6 U2 k- C6 C; g" R
' _( b" Z$ D4 i6 A
三、GuiLite库及Hello3D样例移植
, c0 y1 K. ]* c3 a. |0 ~9 N 在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。+ l l8 A( f' b
- Y W2 f: `8 x9 Q& s
【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。- G& Z- ~) T+ E2 P V' c. x
3 k1 |4 g+ H7 |! c( R
delay.h
* S5 g$ {' e R& N" \: r- #ifndef DELAY_DELAY_H_& W# c0 a- w4 Z* W5 V( U
- #define DELAY_DELAY_H_% d: @; @( W8 u; ]9 m6 X/ g% W
- #ifdef __cplusplus
. c5 A$ c( o' ?2 H$ u; t - extern "C" {. K+ v$ t5 r6 j( {9 v) Z0 V
- #endif. e1 a( ^- A) f
-
% E0 A" J& n( x1 I7 G - #include "stm32l4xx_hal.h" //HAL库文件声明
6 x4 t7 R. K: ^+ \ - void delay_us(uint32_t us); //C文件中的函数声明
4 X: M! i" U4 f -
( Y) |& u. A2 u/ r" d - #ifdef __cplusplus
6 t( @! @* H9 f - }
5 I! b; s* r, t2 P2 a! I7 V7 |: Z - #endif4 s* w) k& c! D
- #endif /* DELAY_DELAY_H_ */
复制代码
' A/ U s1 q7 Z6 j! z k
# q9 v& A# k2 q" M3 {3 i; _. y delay.c2 s; D3 k! C' A. x5 A" \& b
- #include "delay.h"! M1 s4 B6 }) R V
- % J2 t6 R) } t; l
- void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
! N6 Z0 D4 C& @; v - {
% _- r# T1 H+ K- R - uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数
) O I; M: F! l, B - while (delay--); //循环delay次,达到1微秒延时- ^: |' \3 P3 z0 f( N# h5 X
- }
复制代码 5 [ x) E* ?3 T7 k; t: ]
由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。
, M r2 R% W( W+ S- #ifdef __cplusplus
, o/ v" u/ [" y, q* O# s - extern "C" {
8 `8 |' U( Q6 O0 j3 i - #endif3 d" i& c h0 {
- 7 w( W8 N: d! z1 A( u
- //you code, ^) P t, x! x6 }' c9 }
- * w' ?8 ]6 E8 M; z8 e. |
- #ifdef __cplusplus
- C$ X" p* E/ S- a - }
复制代码 0 N$ Z% F- e- P% e5 c) v- J. e* E
【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下
) k# p+ k, J% F5 O* W7 A4 l$ a- #ifndef GUILITE_H_
, i9 h8 |9 ?4 r Q$ z8 J$ Z" T - #define GUILITE_H_7 c' w+ w5 e( [: ^
- #include "../delay/delay.h"
复制代码 ( p% z9 `2 ]1 @( L3 b2 h* h' `
在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。
1 G$ T/ p+ c5 ?- U- extern "C" void delay_ms(unsigned short nms);, d, e3 o) m- m+ Q. |2 W5 J9 u
- void thread_sleep(unsigned int milli_seconds)
4 w! i+ O0 H. q - {//MCU alway implemnet driver code in APP.
2 U+ \$ x' ~# o' q - delay_ms(milli_seconds);
# M( m+ n8 h* @7 M0 z( A - }
复制代码 3 H L, f/ D! i) V6 |; u$ p0 F( H
调整为
/ K* t9 ?6 G, t, ?# ]- //extern "C" void delay_ms(unsigned short nms);
& e R) u& I* f1 \ - void thread_sleep(unsigned int milli_seconds)# g) E3 F, L* I) X1 k4 V& ~
- {//MCU alway implemnet driver code in APP.
. i1 p/ p4 R } N8 J n+ \3 } - uint32_t tt = ((uint32_t)milli_seconds)*1000;" k. a, H+ t& H. d: G; B
- delay_us(tt);% X8 Q! y! [+ d2 k2 U5 n N
- }
复制代码
2 E$ s+ x* m/ H+ C2 Q2 b 【3】在main.c文件,引用各外设驱动头文件
, v9 _* e' _( P- /* USER CODE BEGIN Includes */1 K4 I( {$ m/ U4 M/ \' [
- #include "../../ICore/key/key.h" r4 ^" R. q: L m* [2 H3 F% s
- #include "../../ICore/led/led.h"
( k5 M5 V3 H. E |* [$ t - #include "../../ICore/print/print.h"9 E9 j) h' B0 E
- #include "../../ICore/usart/usart.h"
- @6 s& i6 t4 C- I* l# H - #include "../../ICore/oled/oled.h"
* a* ^! }; l) d: y4 U+ } - /* USER CODE END Includes */
复制代码
5 d0 L" Z% t6 s0 c" C1 Z$ `) c 在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。
* S% ~) b& d1 ^( Z; ^" W, R- /* USER CODE BEGIN 0 */) @4 O8 j% G( ^, D
- //Transfer GuiLite 32 bits color to your LCD color# h- T3 v) F2 b! b3 b' |8 C; o- N
- #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8)) _0 U# O5 E. ]; X
- //Encapsulate your LCD driver:. l2 ^! v j+ \7 t' b2 y7 h
- void gfx_draw_pixel(int x, int y, unsigned int rgb)+ `+ I0 ~( S; H2 ^$ v0 k7 R
- {# y. W5 n! [/ \* }# z8 l, J8 ]0 c; S
- //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的* `! U1 @+ }" a% Y
- OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)
9 ]+ f( f0 K) g - }+ r* o, k% R0 g; c; E7 L2 X
- //Implement it, if you have more fast solution than drawing pixels one by one.
) |% C* X7 G7 x0 E6 T - //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}
, Z/ q, V# U# n) w - 1 N8 W) H7 E1 m# v9 R8 a! `
- //UI entry
. X/ f( x: l5 g6 ` - struct DISPLAY_DRIVER& m9 y9 C. x x5 p0 ?
- {3 G" o" T$ \# m' [/ ^: H
- void (*draw_pixel)(int x, int y, unsigned int rgb);
% Q7 y& ?" R4 e& r% K - void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);
7 e0 d9 K/ M: y) R4 |; g/ M - } my_driver;1 G' F1 X5 W& }- V' S
- extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
) c, u& U2 ~7 [; ~$ c, `' C - /* USER CODE END 0 */
复制代码 }$ m5 _0 A% d& `
* r2 D" S& L; X6 }1 |
在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。
9 T E! \; g, l; D7 Z1 k- delay_init();
1 n* K! G( T1 z/ B3 i - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);$ K8 v& @& p1 P4 d
- LCD_Init();
$ u* y/ Q. J' n - 3 l; A5 y# _. r9 z* {, {* z+ _
- //Link your LCD driver & start UI:) w1 t! z# I2 E+ E. X
- my_driver.draw_pixel = gfx_draw_pixel;3 U a; b) @8 ^8 y2 i) C
- my_driver.fill_rect = NULL;//gfx_fill_rect;
# s& F, Q& P7 p - startHello3D(NULL, 240, 320, 2, &my_driver);' \% G" I5 S' h0 K" m7 S
- while(1);
复制代码
: M3 s, V P ~" l( V5 U v 在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。
7 Z8 R6 ^* l8 A: {8 [5 u- /* USER CODE BEGIN 2 */
7 j' B9 A% O- z8 d; N8 z9 e8 O - ResetPrintInit(&hlpuart1);# b" h/ \/ m! k$ Y0 z
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断5 ?0 P- d+ E8 H* B* O" f
- HLPUSART_RX_STA = 0;
8 E8 B8 D4 ^2 h/ w! I( P1 l - //1 W6 n- S% e- d3 a1 L
- OLED_init();
8 s+ j. o, S$ G' V0 ` Q$ ^+ [; Q - //设置OLED蓝色背景显示3 a2 ~3 ~7 y. t
- BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
) J0 t! x9 Y# z5 A& P - printf("OLED_Clear_DMA\r\n"); b9 R1 h, P9 m
- /* USER CODE END 2 */
复制代码 $ O- G+ @$ Z$ y) I
然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。0 [5 m2 _* C7 z5 C, w
- /* USER CODE BEGIN WHILE */
% P Z/ i w5 @0 T: P" s. p' e/ D w - while (1)9 N7 X+ w% e7 K5 f" x5 \! Y2 Y4 l: ]
- {
- \- q; b8 ?% T2 b+ L* @ - if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始) S1 E: n: w) u$ S8 c: O5 J
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
, }# ~& E* o0 K$ E - OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
& h; R+ \ v* s6 O2 q! c( g/ P- y% b - HLPUSART_RX_STA=0;//接收错误,重新开始" E0 q- h2 ^: l
- HAL_Delay(100);//等待
; b1 g, ^7 U. q9 P, i - }
8 H# M4 J3 j6 H& m+ h6 {+ S - if(KEY_0())
w# @0 D% t; x% P - { k; ~' S8 W$ ^( j% k% j- U E
- BSP_LCD_login(24,108);8 i; h5 V( C" W' k
- }
/ t: R* C, d& {& s. _ - if(KEY_1()): e6 p, S0 s% ]5 l( q$ S
- {6 H \/ @ K. f
- BSP_LCD_img_DMA();# ~8 U3 w5 q2 k" }9 C% P
- }
+ ^) e* b$ A( [5 N - if(KEY_2())0 W$ x7 n9 E7 A2 w' y3 q$ a: J
- {1 v% j2 K& H9 R% I/ J5 I: a
- my_driver.draw_pixel = gfx_draw_pixel;
3 z" y m# |% c" E# m* ]" ] - my_driver.fill_rect = NULL;//gfx_fill_rect;1 p4 J- \0 b6 z$ n$ n
- startHello3D(NULL, 240, 240, 2, &my_driver);' r8 t U! A3 _
- }! ^3 A6 i% W) b ?
- /* USER CODE END WHILE */
复制代码
! p* ^6 k7 @' ~- n4 G四、编译及测试* |( _( I4 H8 F/ H( T. n9 [
完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。6 }/ w$ M" u9 Z0 r4 K* t. H& E
_9 N/ C0 D8 F0 h
. F \5 g) O( C
4 D6 x1 Q4 @1 h. u8 c+ E2 d 按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。
u/ }3 f! ?1 [, b; }. Y
" d% ]2 x9 U( J/ W8 F
- c: e, V7 n2 O% _3 y9 Z# S6 F& K% S8 ~; b8 L% A9 z# s* S
+ ] l: P$ i9 c" r/ c) d t/ ~
————————————————+ Q; @" Y0 G: E# ~1 \" I
版权声明:py_free-物联智能. k, [7 ^* }6 G$ O8 r5 A/ P
如有侵权请联系删除
: e; X" w4 i% ~0 Y |
内容很详细,谢谢分享
这个GUI效果真不错,有空可以尝试移植一下看看。