你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32CubeIDE GuiLite图形库移植经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-6 16:38
一、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 |% `
77b7c5fa691448e28c500c6f201140a5.png 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
  1. volatile uint8_t one_frame_done;
    8 T8 i% p( D9 D' V! @
  2. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi): X: @3 r' G" k0 c8 [4 H
  3. {; R& E* S* @  |7 u: }9 V. f8 ~! r
  4.         one_frame_done = 1;$ e% S; `, b, c# M8 w4 e) y8 h/ a
  5. }
复制代码
2 H% j- A5 B6 c% j4 p* n/ U# _
1605f3e1bd77401e93684107453c004c.png
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  [
  1. #ifndef DELAY_DELAY_H_
    7 ~5 |3 ?' ^  _, p6 C) d
  2. #define DELAY_DELAY_H_
    2 ~4 d; F9 C6 v* d
  3. #ifdef __cplusplus/ ^0 Q# z' _, {1 P- L& Y
  4. extern "C" {! F  e0 ?/ Q( s9 M) t
  5. #endif
    - {& t, l1 ^9 _5 N! j- ^

  6. 8 |" m% z) u2 W: K' r
  7. #include "stm32l4xx_hal.h" //HAL库文件声明
    1 J9 E) G- H8 E" `3 @) }! a
  8. void delay_us(uint32_t us); //C文件中的函数声明: S* _, G7 K. n" U1 u) L

  9. ( @+ W3 f& c) N! O! I+ F% y' S
  10. #ifdef __cplusplus
    9 S3 K% N- I, F2 E
  11. }! x: r6 C$ P/ a9 k) Z1 q) @
  12. #endif
    # g1 V$ G0 C2 h8 F, r
  13. #endif /* DELAY_DELAY_H_ */
复制代码

+ \* S) E: D+ I* q" `
% }5 X/ T8 _  D  Y3 ]
        delay.c
, U$ D  s4 w6 y7 Q
  1. #include "delay.h"
    7 A. W: K1 y# j# j( i
  2. 8 E$ y, q6 N  ^# }4 F3 F2 j8 D
  3. void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数  ?" f/ l  ]# j; E
  4. {. O+ R  l' O8 Z/ q5 n+ @) b+ A
  5.     uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数; D1 P, Q0 m" u  y) w$ r% X
  6.     while (delay--); //循环delay次,达到1微秒延时
    9 M9 v) x: I3 U' v/ _% k* H
  7. }
复制代码
: 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
  1. #ifdef __cplusplus. R# ^: |# g9 r# G8 e1 P4 Y* r
  2. extern "C" {
    % ~: n0 K2 v9 Q% W" t
  3. #endif
    2 U, d1 `& l7 f' a/ Z+ Y' b$ {
  4. * j( v, f! U" u( _% ]0 s
  5. //you code
    . x1 @6 x/ r+ t. n3 Z; a
  6. 4 w3 p9 {5 X9 T& |
  7. #ifdef __cplusplus' S, ^4 q# f) b
  8. }
复制代码

2 [, z/ g  x8 o- F        【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下9 ?1 C8 U' z' b. \0 x; K
  1. #ifndef GUILITE_H_
    4 @) z- l: R# ^8 w- }
  2. #define GUILITE_H_. u/ |# A* B; q+ d( L; H9 r1 Q& f
  3. #include "../delay/delay.h"
复制代码
- p' \* L- G1 t; h* H3 k2 S
        在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。
( e! F, v7 L1 R; |
  1. extern "C" void delay_ms(unsigned short nms);
    4 c1 I' {( @# J# r0 v
  2. void thread_sleep(unsigned int milli_seconds)
    , t2 c& M( [/ B+ M
  3. {//MCU alway implemnet driver code in APP.
    ' I. S0 K& y3 M
  4.                 delay_ms(milli_seconds);1 r# o0 q( [. R! D
  5. }
复制代码

& B; E" b' N- ~" e9 x, w' Z        调整为
& n8 C* g, x8 d1 K2 R$ F$ g% M
  1. //extern "C" void delay_ms(unsigned short nms);/ c. a( D# m" S0 ~
  2. void thread_sleep(unsigned int milli_seconds)- N2 X8 Y$ S* h6 s% Q$ i& Y4 O
  3. {//MCU alway implemnet driver code in APP.
    1 J  L  ?# y4 H- _8 [, b8 W
  4.         uint32_t tt = ((uint32_t)milli_seconds)*1000;
    + ?; N! `0 X/ t: C6 t: p7 k2 y; s+ p
  5.         delay_us(tt);
    5 I3 b3 I% j, }- A* Z* N9 Z
  6. }
复制代码
( a4 v3 f* j+ j: e
        【3】在main.c文件,引用各外设驱动头文件/ \$ W: |& I! x% t3 I
  1. /* USER CODE BEGIN Includes */7 I0 ~' U2 N& d
  2. #include "../../ICore/key/key.h"
      d3 I; q. c+ [" d8 J5 |' N% r
  3. #include "../../ICore/led/led.h"
    0 m( v$ L2 ~( Q+ b+ V9 R2 [
  4. #include "../../ICore/print/print.h"
    ' c3 X; _4 h. O/ m, n
  5. #include "../../ICore/usart/usart.h"- ?/ _0 P) f4 Y7 E' U5 U! D
  6. #include "../../ICore/oled/oled.h"  y% |$ a. U4 j6 z- V
  7. /* 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
  1. /* USER CODE BEGIN 0 */
    5 L! k- N0 H2 Z
  2. //Transfer GuiLite 32 bits color to your LCD color
    # [# q  J$ s4 V7 B
  3. #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
  4. //Encapsulate your LCD driver:
    5 o7 Y2 N* ]+ H- E! }4 d3 w  W9 z
  5. void gfx_draw_pixel(int x, int y, unsigned int rgb)
    ! ?$ m# u3 p9 s. z& i# A
  6. {9 Y: K, [+ }& ]' L5 Q
  7.     //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的& ^1 Z0 Y6 R7 j* M  }
  8.         OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)
    " r: D1 i. [! T# c: y5 K
  9. }( }& t# c& H  P0 ?2 T
  10. //Implement it, if you have more fast solution than drawing pixels one by one., @& T2 w9 d5 g$ K4 [# x% e
  11. //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}
    & F" f: A- U/ Q, Q4 j/ z: T4 ~

  12. 7 Y6 s/ z" H6 e7 t; }4 r
  13. //UI entry
    4 O. O9 Y; v$ p1 B# c- F5 S
  14. struct DISPLAY_DRIVER' ]/ F8 V1 V% k8 O2 d1 B5 K+ w5 D
  15. {
    # j6 [4 {1 a* k7 S) j! y2 Z
  16.         void (*draw_pixel)(int x, int y, unsigned int rgb);
    ) v  }5 Y! }( k; R" A' l0 g+ G
  17.         void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);! T7 z3 ^) P: C# \, u8 q. L
  18. } my_driver;
    ) i) @7 M6 _9 E) s" Z' g
  19. extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
    , g6 P4 ]$ b; T: J9 t
  20. /* 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. [
  1. delay_init();
    - V; ?8 f( Z7 x
  2.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    . j. h8 C0 n0 U6 Z+ a( n; D: h+ t
  3.         LCD_Init();% b; n$ E3 V7 I# N4 t( o8 U' c
  4. " e" Y- O# E; h: H& D+ i9 _
  5.         //Link your LCD driver & start UI:% `: S7 A; A( X
  6.         my_driver.draw_pixel = gfx_draw_pixel;# L" c& e2 f. W
  7.         my_driver.fill_rect = NULL;//gfx_fill_rect;" }% J( K6 v7 v: d' l3 J$ y$ N  f
  8.         startHello3D(NULL, 240, 320, 2, &my_driver);
    . w* ?" s+ y8 s# q- F/ w
  9.         while(1);
复制代码

, w$ ~$ F2 d% h4 |) _2 R' U0 B        在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。6 {( H% ]6 x% `9 o$ ?- p
  1.   /* USER CODE BEGIN 2 */
    * A: D7 l# T! I
  2.   ResetPrintInit(&hlpuart1);
    2 P- y- H, ]' ^$ y
  3.   HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断6 v2 d9 {; g& j% g9 [$ x# t
  4.   HLPUSART_RX_STA = 0;
    5 g& r6 T1 K" l  b
  5.   //3 p- J/ e0 V2 c4 g, W
  6.   OLED_init();3 j9 `: L# t8 P# Y$ V. B9 q$ g
  7.   //设置OLED蓝色背景显示& p" C6 n: k9 n4 g5 Q
  8.   BSP_LCD_Clear_DMA(LCD_DISP_BLUE);8 j7 V, R. \/ i, n1 a3 _4 N; s
  9.   printf("OLED_Clear_DMA\r\n");% B& L! r- w5 v* q
  10.   /* 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
  1.   /* USER CODE BEGIN WHILE */
    8 A' [) s! w2 q  ?
  2.   while (1), e# u/ C, Z) c& N' l
  3.   {" Z% T3 ^9 r  @7 h
  4.           if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
    ' v6 c2 q# e2 F2 f  D) n9 ^" ]
  5.                   //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);/ o) \: x; }6 P4 P, ~- m5 L
  6.                   OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);+ I: s, p9 T% V, t; z& y' R8 A; D! G
  7.                   HLPUSART_RX_STA=0;//接收错误,重新开始. l# y% P& J: }( N3 z8 {
  8.                   HAL_Delay(100);//等待
    6 O% F* Q3 q1 i% C" f
  9.           }/ w% t9 ?3 U! j$ F. Y( \, i
  10.           if(KEY_0())5 w7 l2 I. f) w; A) G9 D# a4 o
  11.           {) k& b7 Q4 @2 g4 V! Y( R8 J
  12.                   BSP_LCD_login(24,108);
    % U/ @- U. M% j4 F4 ]
  13.           }# R* L* V6 s# u: K5 A9 z
  14.           if(KEY_1())
    , m6 y* D' o( g) C& |& K
  15.             {
    : n# S9 e2 W# F9 I
  16.                   BSP_LCD_img_DMA();
    6 H, m9 I9 `7 f0 M
  17.             }6 z% o6 R! Z) e! D
  18.           if(KEY_2()): J$ q  W' J! L: k
  19.             {4 y) p6 e' F9 l. H) d3 ~$ S
  20.                   my_driver.draw_pixel = gfx_draw_pixel;
    : u: I% g* U3 N1 r4 B
  21.                   my_driver.fill_rect = NULL;//gfx_fill_rect;
    7 E; \+ x* m$ S: w$ Y4 A, T8 d
  22.                   startHello3D(NULL, 240, 240, 2, &my_driver);
    8 R" E- I) r1 z' k1 j
  23.             }9 g& t" [8 F7 J1 m9 A' b( {. W9 t
  24.     /* 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 _
2f01fdf679234e349c31a2a72f836efa.png 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
722f590bf3e54cb2bc457cca2ee1d67e.png # S$ G+ F. f* X; y

) s  S1 I2 q( i3 f% {0 w9 O
366b7d4b58f54925bde9496b94657fa6.png 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
赞 收藏 评论4 发布时间:2023-4-6 16:38

举报

4个回答
shenxiaolin_mai 回答时间:2023-8-10 10:14:10
不知道c++库可以用在keil上么?还是非常开心的。
shenxiaolin_mai 回答时间:2023-8-10 10:14:39
感谢分享,更详细一点就好了
lospring 回答时间:2023-8-10 10:15:03

内容很详细,谢谢分享

STMWoodData 回答时间:2023-8-10 10:21:30

这个GUI效果真不错,有空可以尝试移植一下看看。

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版