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

STM32CubeIDE GuiLite图形库移植经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-6 16:38
一、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
77b7c5fa691448e28c500c6f201140a5.png
; ?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
  1. volatile uint8_t one_frame_done;; ^3 b( L  j. D
  2. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi). p! B$ M4 W. O7 }+ l
  3. {
    : I5 S+ h  w0 O9 z
  4.         one_frame_done = 1;
    . @% @/ B7 m+ W3 U1 u- E
  5. }
复制代码
& n& W; z) }5 }
1605f3e1bd77401e93684107453c004c.png 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
  1. #ifndef DELAY_DELAY_H_
    9 l. \0 `0 C; e, i% Y1 \
  2. #define DELAY_DELAY_H_
    ' n/ D5 Y; |: F' P
  3. #ifdef __cplusplus
    & }9 G1 Z: H  f
  4. extern "C" {  |3 \" O: |, q  M/ _& R0 e
  5. #endif
    7 D& ^$ z9 F0 E
  6. ! W- U; L' g, t* {# B. [" V
  7. #include "stm32l4xx_hal.h" //HAL库文件声明  [" y1 ?9 A- ^6 d: F) ~& I
  8. void delay_us(uint32_t us); //C文件中的函数声明
    7 ~# {9 O3 M4 u4 I: W+ O5 J
  9. # N* {! R0 A3 D0 [
  10. #ifdef __cplusplus8 T3 w/ D: D5 q3 q/ q
  11. }7 V6 h; H- A7 w8 l
  12. #endif8 l: Y+ C& {% b; {! M7 Y
  13. #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
  1. #include "delay.h"; o; M6 i' ?$ |! |

  2. 2 s. j' S" @# N) m
  3. void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
    ! H4 o  W5 }* x& s& \+ A
  4. {
    , @+ M# d5 B, d. |, {7 B
  5.     uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数. ?0 H3 g+ O) g3 t
  6.     while (delay--); //循环delay次,达到1微秒延时8 u: Z8 V% t1 i5 x+ L  c
  7. }
复制代码
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
  1. #ifdef __cplusplus: ?" l9 P) f! \/ b
  2. extern "C" {% ^, n: n8 Q1 L% X, {
  3. #endif
    * d' y9 z  y# X. d% Z
  4. 0 l1 R$ X8 Q( w
  5. //you code- V5 _4 V7 W/ A# L, U' Z4 k
  6. # ]0 y: \, }1 f" H
  7. #ifdef __cplusplus
    ; [8 |8 L5 r2 t2 R/ K. a3 ]- U0 b) j
  8. }
复制代码
- G, Q# O$ Q) W$ l7 ]
        【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下
4 d; Z, c7 t5 U
  1. #ifndef GUILITE_H_
    - ~6 V/ `/ Q1 `  E/ i6 V
  2. #define GUILITE_H_
    7 o! L7 R% [' S8 z6 `; X$ {3 B
  3. #include "../delay/delay.h"
复制代码
& j0 O( ~+ q5 w
        在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。' S: I* ]8 J2 U9 u1 M; L
  1. extern "C" void delay_ms(unsigned short nms);
    . X# b, G) I: b& {1 G/ `: C: o% q
  2. void thread_sleep(unsigned int milli_seconds)
    * s9 g& y2 w1 {0 B
  3. {//MCU alway implemnet driver code in APP.
    ' x0 e. f& `! L/ p, m2 Z7 R
  4.                 delay_ms(milli_seconds);7 Q: a1 {, l# f% `! V& p/ w
  5. }
复制代码

& p9 z) x0 t$ m9 R        调整为8 T+ d6 s4 |, }7 W6 ]7 V
  1. //extern "C" void delay_ms(unsigned short nms);
    6 O3 f: b9 b% M& Z
  2. void thread_sleep(unsigned int milli_seconds)9 t$ Q$ P2 t, P' m  r" p9 a  u: U
  3. {//MCU alway implemnet driver code in APP.' K0 @3 u  x! N" W, ^
  4.         uint32_t tt = ((uint32_t)milli_seconds)*1000;7 ~2 X! N) u# ?) x. K
  5.         delay_us(tt);
    6 a& Z2 q1 R: o7 `! X9 q) y
  6. }
复制代码
; B: s( C8 V; ^
        【3】在main.c文件,引用各外设驱动头文件2 i. V0 p; L: S3 \8 |( @6 S: X
  1. /* USER CODE BEGIN Includes */
      d  W! A" s6 w+ H# l/ m3 W
  2. #include "../../ICore/key/key.h"
      i, l) K2 Y8 D* u8 |# b9 I  ]
  3. #include "../../ICore/led/led.h"
    * I$ L! T7 i. @, Y# w
  4. #include "../../ICore/print/print.h"3 o, C- }5 b% j  i
  5. #include "../../ICore/usart/usart.h"; T' T" s( Q2 @' o* Y
  6. #include "../../ICore/oled/oled.h"4 d4 Q- D' ]9 V0 x& r. i3 f
  7. /* 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
  1. /* USER CODE BEGIN 0 */2 e* M. [3 p) l1 K
  2. //Transfer GuiLite 32 bits color to your LCD color, a' Y9 J1 ~$ Q4 Q
  3. #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
  4. //Encapsulate your LCD driver:
    " \. C; C% k( n- a
  5. void gfx_draw_pixel(int x, int y, unsigned int rgb)
    $ c9 z) ?" y4 ], z6 J+ _
  6. {
    8 V# q" E9 h# e4 n
  7.     //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的
    * K) ^  Q! n4 ~- U7 {. x. A& N  V' r
  8.         OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)
    3 R6 U( G6 i4 R
  9. }
    8 @0 C& a0 s, _( `" g' m' d4 C9 F
  10. //Implement it, if you have more fast solution than drawing pixels one by one.
    ) k; ]5 e" Y" f/ R+ p9 A% E" o
  11. //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}" Q7 D! l) c. c# X9 w+ j' b8 ?
  12. ! L7 b* E+ @# v5 Q9 Q- j! I
  13. //UI entry
    ; m$ {2 ^' A) Y' \
  14. struct DISPLAY_DRIVER
    2 x8 L6 j) w$ H9 |3 c. M: w
  15. {! {! |0 y. l* H
  16.         void (*draw_pixel)(int x, int y, unsigned int rgb);" y5 Y6 T6 z" d2 x5 g, _0 x
  17.         void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);" m0 U: o; C2 F6 G1 I
  18. } my_driver;# [: N4 B+ a% x5 I6 P; v+ {
  19. extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
    ! _+ i/ N; I& o' X7 m
  20. /* USER CODE END 0 */
复制代码

; }: X$ M2 E: w  S% r

% W: d; N' \  e/ \. z0 c        在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。

: D# M  A; r0 ~
  1. delay_init();
    6 |5 L# m1 K  ~3 Y! E9 m6 i
  2.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      `! L. t- U) G0 [# l3 U; T% b" n, ]
  3.         LCD_Init();" s( _9 ]+ o4 I0 V
  4. % A) u2 C: [0 k2 x& B. |
  5.         //Link your LCD driver & start UI:
    ! W. G8 N1 y' [1 e
  6.         my_driver.draw_pixel = gfx_draw_pixel;* ~+ R" V+ R/ x, s/ F
  7.         my_driver.fill_rect = NULL;//gfx_fill_rect;
    ; I5 T' L2 E9 N  R: a/ T
  8.         startHello3D(NULL, 240, 320, 2, &my_driver);
    " u' B5 f4 x1 r% D. [/ n) a
  9.         while(1);
复制代码
. L$ f+ k+ `1 E$ N% x
        在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。
4 _3 N- m. m* A: |% h; l
  1.   /* USER CODE BEGIN 2 */
    ) S$ K' H7 X# d/ ?
  2.   ResetPrintInit(&hlpuart1);$ I7 g" x: s# i$ V+ U7 X
  3.   HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
    4 x6 [+ r% t% b/ s. t3 @
  4.   HLPUSART_RX_STA = 0;
    * ?1 A5 T! R6 g
  5.   //! l. H/ k/ v+ [% ^7 H7 U% l9 p# i
  6.   OLED_init();
    % g# C/ I% S3 w7 Q6 M/ L
  7.   //设置OLED蓝色背景显示" X5 {" `, V- z. N5 C# K/ j( k, Z  _
  8.   BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
    3 m3 W4 Y2 e& z5 F2 H0 ]( v. B0 x
  9.   printf("OLED_Clear_DMA\r\n");
    & Q9 R: [5 D; I. M: U
  10.   /* 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
  1.   /* USER CODE BEGIN WHILE */- d5 }" }% V* o, I
  2.   while (1)
    5 s3 K) T- w- g  |+ \1 m4 O; Z
  3.   {! W4 A3 j% T* j" _7 F
  4.           if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始! ^$ S! F- T4 `; M4 O3 K& n
  5.                   //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);8 Y9 s# M2 p6 I* ?2 g
  6.                   OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);* u  r# z- v+ a; z" V6 a
  7.                   HLPUSART_RX_STA=0;//接收错误,重新开始3 K* w/ u3 s9 h4 w$ E& j) i
  8.                   HAL_Delay(100);//等待
    9 L' X  W. Z: K: r
  9.           }
    7 W4 ^% e9 T% d% f/ F7 Q& X# ]
  10.           if(KEY_0())
    4 n1 _3 \3 E& K$ @: Y; [
  11.           {4 A! p  P# ?& o6 V6 X" Z
  12.                   BSP_LCD_login(24,108);
    , ~  r- F, ^0 j
  13.           }$ d- q2 V6 A% J  h6 y* j4 W
  14.           if(KEY_1())1 P  l# W; p0 R2 O3 l2 z
  15.             {  [' w/ b: P# t! R( q9 M
  16.                   BSP_LCD_img_DMA();
    2 T1 T, t& j4 K5 a7 R
  17.             }, k& B+ m  m1 M) t" v
  18.           if(KEY_2())% R- T' U, Y  {. L; c2 E+ m
  19.             {
    4 B7 d( z5 [3 ~
  20.                   my_driver.draw_pixel = gfx_draw_pixel;
    ' T% h& A, y, H" Z
  21.                   my_driver.fill_rect = NULL;//gfx_fill_rect;
    % a7 v8 s* [  m* Y& ^( i8 a* p
  22.                   startHello3D(NULL, 240, 240, 2, &my_driver);1 V  E  i/ u3 l; u: u  @
  23.             }
    2 o" @: M  f' A& ^3 W$ [) |; _: ?
  24.     /* 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
2f01fdf679234e349c31a2a72f836efa.png
) 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: `
722f590bf3e54cb2bc457cca2ee1d67e.png , H/ O; v: Z6 J) }6 w7 R; b+ Y2 b
' ?7 p+ ~# N7 H, @1 _4 A
366b7d4b58f54925bde9496b94657fa6.png
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
赞 收藏 评论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 手机版