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

STM32CubeIDE GuiLite图形库移植经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-6 16:38
一、GuiLite       
# ]/ ~, J' h6 ]+ ]        GuiLite图形库,仅4千行C++代码,0依赖,单一头文件库(GuiLite.h)的跨平台开源GUI库,支持支持的操作系统有iOS/macOS/WatchOS,Android,Linux(ARM/x86-64),Windows(包含VR),RTOS等,甚至无操作系统的单片机上也能流畅运行。) T# F5 X# v5 E* B1 b( W5 {

, M" q- b& z: c: `& a2 A9 T; e* U二、创建工程
' Z% w7 C; X- C) {* {0 z% `( c! V( P1 k- O4 ^" `  z
        实现了LCD屏幕点亮功能。
) j# u. J% K4 W+ g. F
: b% g" p+ J3 U0 ~: `: N, T        【2】现在CubeIDE工作空间创建一个名为stm32L496VGTx_GUI的文件目录,并将前面工程的stm32L496VGTx_lcm.ioc文件拷贝到该目录下,修改该文件名为stm32L496VGTx_GUI.ioc。, G5 g. }- v! c) O! m, x! o5 l
( h( m. \" E. v* x; o
        在CubeIDE中,在菜单栏的“新建-> Create a New STM32 Project from an Existing STM32CubeMX Configuration files(.ioc)”,进入创建,基于已有ioc创建一个新工程,如下图所示,特别注意,选择C++支持,因为GuiLite库是C++库。
& R5 W/ y  Z4 q. O! e* x/ K! j
2 ^( w) D1 r  H, E5 q4 T
77b7c5fa691448e28c500c6f201140a5.png
/ {; t! m: ~2 `. w4 t, ?: x# c
6 F) N% g$ t! b; I8 Q        【3】完成工程创建后,禁用syscalls.c文件,在工程目录下创建源目录ICore,并移植前面工程的源文件,实现lpuart1串口驱动、按键及LED灯驱动、LCD屏幕驱动,如下图所示;由于前面博文中实现LCD的DMA刷新数据时,使用了SPI回调及全局变量,需要到Core/Src/spi.c文件增加如下内容:& K+ @2 ?4 e7 P' e% S: G% D
  1. volatile uint8_t one_frame_done;
    + w* M1 s9 Q. _7 u' b- z( }1 |
  2. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)9 ?. `' z1 E% ]5 q! ]& w
  3. {
    , e5 d+ ]4 l. ~( t8 ?
  4.         one_frame_done = 1;& P) B/ D  S; z, p& V6 \# L
  5. }
复制代码

* p0 }3 m5 y% ~2 o8 E/ w
1605f3e1bd77401e93684107453c004c.png
6 b5 J" k4 C/ P4 }' p. j5 w  K
" y* f+ R: S) m- X
- v! S- T7 h9 d9 D8 T
三、GuiLite库及Hello3D样例移植3 \5 l5 y- J5 E/ I; \
        在ICore目录下创建guilite文件夹,将GuiLite库即GuiLite.h文件拷贝到该文件目录,将Hello3D样例的UIcode.cpp源文件拷贝到该文件目录。
2 Q* W6 t  V! @* ?

" }0 R  D( x  f5 `& l( a0 o# `; ~        【1】由于GuiLite.h内的thread_sleep函数调用了延时函数,而样例给出的延时函数过于复杂,本文另外设置了新的延时函数,在ICore目录下新建delay目录,并在该目录下创建了delay.h和delay.c源码。# t9 v1 w" B1 A

9 ^, }; x+ j. W+ ]$ z        delay.h
- {% X4 G, f* e* K% @5 T
  1. #ifndef DELAY_DELAY_H_- p; h4 a. t9 P1 j+ C. `( z4 j
  2. #define DELAY_DELAY_H_
    + w8 \8 X3 A6 J; w0 F2 r* t
  3. #ifdef __cplusplus/ f1 X8 {5 e& y) G1 J7 x/ R$ ]
  4. extern "C" {5 W. E% ^6 o/ {5 @8 Q5 A
  5. #endif
    ; i# e$ q3 o- n- j6 ?0 @2 i

  6. # f0 b$ R' D7 }" [
  7. #include "stm32l4xx_hal.h" //HAL库文件声明9 p# _9 K9 s9 V0 t! Z; S4 [. f& q
  8. void delay_us(uint32_t us); //C文件中的函数声明
    2 ]1 ^+ k8 L; x

  9. 6 ]: G; x9 f& O$ g! j1 O! N' Q5 {
  10. #ifdef __cplusplus
    8 Y/ d. M! W% j( C
  11. }
    8 M5 J) t( R, U2 U
  12. #endif
    $ S0 k# L6 c2 a
  13. #endif /* DELAY_DELAY_H_ */
复制代码
' |! y) `. V/ v8 ?. H
/ h" \0 j8 X5 o6 D+ {4 l( |
        delay.c
. m3 x# {6 A% U# d
  1. #include "delay.h"+ _1 }! u! U& ~/ r3 s

  2. ; \5 |: {4 Z4 q
  3. void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
    0 k, v! k8 R9 L1 n6 h
  4. {
    6 r3 i0 l% y6 r* l+ Z
  5.     uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数3 y9 l7 O8 I- Q- i* u- G+ t4 _" ?
  6.     while (delay--); //循环delay次,达到1微秒延时- o- }0 Y% u5 {, ~5 \4 c
  7. }
复制代码
) Y9 ?% }1 f5 r$ @; G
        由于现在采用的是C++编译语言,因此可以看到 delay.h文件中,和以前不一样的是,增加了extern "C"声明,为了严禁,移植过来的print.h、usart.h、key.h、led.h、oled.h本文都增加了extern "C"声明。
% r5 Y- @' {) ]9 ~- L4 G
  1. #ifdef __cplusplus# Z  @9 i' ?( p
  2. extern "C" {; O: v9 l- }- k; f& F1 a
  3. #endif
    0 w! Z; v/ N4 t6 G  H3 j/ U* C

  4. 2 L* l. W. q- s% }! w
  5. //you code
    9 r/ O* ]0 v. i: f

  6. ; C" ]5 }. Q- c; e8 A3 O
  7. #ifdef __cplusplus
    + M5 d/ x; ^6 D, B' ?/ i5 C! W$ y
  8. }
复制代码

; z& @8 [. u% ?3 i        【2】调整文件GuiLite.h,首先,同样的在文件前面增加了delay.h头文件的引用,以及为了统一,将其宏定义#pragma once进行了替换,结果如下2 O( p* X. n3 D4 {9 c
  1. #ifndef GUILITE_H_
    ' y) z  X2 J) K5 g  j1 ~
  2. #define GUILITE_H_+ f% ~& X1 I7 F/ c' d& m. e+ t
  3. #include "../delay/delay.h"
复制代码

; F* K: Q6 S, n        在GuiLite.h文件中找到thread_sleep函数,该函数有几处,需要条件编译的地方暂时不用管找到STM32需要的,如下。
& E: C  @- m: @! _" Y: l5 ^' s
  1. extern "C" void delay_ms(unsigned short nms);4 J7 [# g" Z- y# w; ?6 o
  2. void thread_sleep(unsigned int milli_seconds)
    . p$ E, _4 M3 N! X9 p  J
  3. {//MCU alway implemnet driver code in APP.
    # l9 i& W( p8 ~
  4.                 delay_ms(milli_seconds);5 c: ?" P* }$ \4 |+ \
  5. }
复制代码

" n6 O, z$ l, v: h0 y        调整为
# e7 n- `0 v3 L6 J  h. o: g
  1. //extern "C" void delay_ms(unsigned short nms);5 e9 r1 H" U$ f- |5 U( F
  2. void thread_sleep(unsigned int milli_seconds)) F9 D- y' f6 I, F' x1 O
  3. {//MCU alway implemnet driver code in APP.
    % e* j, B2 Q9 Y: C
  4.         uint32_t tt = ((uint32_t)milli_seconds)*1000;8 N; d  z2 k9 k* m/ z) c) T$ B- ?
  5.         delay_us(tt);
    " R9 C0 G) P* Z# v& L. }
  6. }
复制代码
3 b' P0 ]- d  D# @. y
        【3】在main.c文件,引用各外设驱动头文件
; D0 C2 X9 t9 D, _
  1. /* USER CODE BEGIN Includes */
      W% p1 }: |0 B7 m% b3 Q
  2. #include "../../ICore/key/key.h") }% k( ]9 |% ]; Z( x
  3. #include "../../ICore/led/led.h"
    4 h% C$ F* T  J1 p1 u* U
  4. #include "../../ICore/print/print.h"7 b2 L+ `+ K& k( }
  5. #include "../../ICore/usart/usart.h"- ]7 A0 C' L" C
  6. #include "../../ICore/oled/oled.h"& y# M1 a( p3 \, z, b
  7. /* USER CODE END Includes */
复制代码
2 z2 Y) m+ H/ }0 m( r+ l
        在main.c文件中参考Hello3D样例的main.c源文件(Hello3D/BuildSTM32F103-Keil/USER/main.c · idea4good/GuiLiteSamples - Gitee.com)进行声明GuiLite库调用前置声明如下,主要就是需要改写调用本文LCD屏幕支持依据坐标值及颜色值,进行点绘制功能函数。
  [* c0 ~+ x( D/ l' D
  1. /* USER CODE BEGIN 0 */9 E9 K/ l- I# m: i
  2. //Transfer GuiLite 32 bits color to your LCD color
    3 H- ~5 ?' {) V
  3. #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8))
    2 H; @& I) x7 t/ c
  4. //Encapsulate your LCD driver:
    # p: J3 @+ y) m  G
  5. void gfx_draw_pixel(int x, int y, unsigned int rgb)
    % E) m* Z" e! O. Y6 Y6 g
  6. {6 p5 E( t! g1 E# n
  7.     //LCD_Fast_DrawPoint(x, y, GL_RGB_32_to_16(rgb));//注释参考样例的8 H, F6 f" }5 ]  B, r, {
  8.         OLED_WritePixel(x, y, GL_RGB_32_to_16(rgb));//新增本工程支持绘制点函数调用(oled.h)" M9 y! ^. }6 w
  9. }
    & `* O, j3 a1 ~" _2 ?: a, M
  10. //Implement it, if you have more fast solution than drawing pixels one by one.! K( g7 c2 d- M2 W* |* ^
  11. //void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb){}! I0 b% f+ a) R1 d* Y! `
  12. % ~' F% `: y5 o
  13. //UI entry- r) {0 v/ r7 B/ z1 T5 U1 @* M7 W
  14. struct DISPLAY_DRIVER
    0 y; @  s: A9 g7 J* J
  15. {
    " ~  H" P* |6 O  t6 `
  16.         void (*draw_pixel)(int x, int y, unsigned int rgb);
    1 `6 r. L" r4 X# P$ _
  17.         void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);
    ( Y9 i/ T' [" @# [! x
  18. } my_driver;% _- a4 _) N% d0 L
  19. extern void startHello3D(void* phy_fb, int width, int height, int color_bytes, struct DISPLAY_DRIVER* driver);
    " a/ |$ }( y" {3 K/ G0 s3 p: S
  20. /* USER CODE END 0 */
复制代码

4 J) x! R9 r. L9 L2 ?% e0 ^
" {$ l! N( V' b; y
        在Hello3D样例的main主函数中,先初始化延时函数、在配置中断和LCD屏幕初始化,然后进行3D效果绘制渲染输出。
4 j  @! S  j- G) j9 G
  1. delay_init();
    6 R6 y1 X4 A3 Z6 \
  2.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);' F8 b* K8 w7 o& s
  3.         LCD_Init();
    7 a) I  }) ?& i0 v& w
  4. ) I$ d" J5 w6 G; i
  5.         //Link your LCD driver & start UI:+ a2 [. y. h1 s# u* [  B7 w- n
  6.         my_driver.draw_pixel = gfx_draw_pixel;
    ' t) V, j) q- x, N8 S. m, W* Q
  7.         my_driver.fill_rect = NULL;//gfx_fill_rect;2 \' ?+ t- m0 s! D6 m' i
  8.         startHello3D(NULL, 240, 320, 2, &my_driver);, W% P. i( h3 U+ T
  9.         while(1);
复制代码

% ~9 Z' l% u+ F3 T+ z$ _8 x/ Y        在本博文移植中,在main函数内进行外设初始设置,例如lpuart1中断接收开启、lcd初始化,主要是LCD初始化。' ^7 ?& y& @( {: m2 U, O8 a( U9 T
  1.   /* USER CODE BEGIN 2 */
    & H4 V6 G2 m2 X2 L* s/ q2 ?. t
  2.   ResetPrintInit(&hlpuart1);
    ; O4 \2 U- j' [
  3.   HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断- A7 g8 @) ?% f3 E5 B- \/ U, A
  4.   HLPUSART_RX_STA = 0;, d" o6 _; k/ `  w
  5.   //$ f) x: W9 `" m! [
  6.   OLED_init();
    4 x& @  E* u; O: e" I# P
  7.   //设置OLED蓝色背景显示6 ^! X. T% S- g- E7 ~& [4 J$ T
  8.   BSP_LCD_Clear_DMA(LCD_DISP_BLUE);2 C8 e) y( Y; e8 U
  9.   printf("OLED_Clear_DMA\r\n");5 r! z- s7 \* I& j, P6 I  R6 h
  10.   /* USER CODE END 2 */
复制代码
# A2 ]* f8 F3 _: [1 ?* p' o9 \
        然后在main函数主循环中,通过按键KEY2按键进入3D效果绘制渲染输出,读者可以屏蔽串口接收功能、按键KEY0、KEY1的亮屏功能,直接进入KEY2下的3D效果绘制渲染功能是OK的。注意startHello3D调用需要根据自身屏幕宽、高设值,本文开发板支持240*240像素显示。& ~3 R& p7 X+ f2 @3 A/ O
  1.   /* USER CODE BEGIN WHILE *// b1 M$ }0 B$ O! S* ]
  2.   while (1); i1 r# s4 L! z. W9 q7 p
  3.   {$ `( a+ H8 X& M
  4.           if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
    ! N0 V% D) U& f! A9 h" w( v* J
  5.                   //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
    8 `+ k+ \3 ~2 O- L9 p
  6.                   OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);( I7 `# d' }7 t+ _) L& d% v
  7.                   HLPUSART_RX_STA=0;//接收错误,重新开始- _# V) N, \- F2 q8 [& R* {
  8.                   HAL_Delay(100);//等待
    % I3 S1 N# S3 K
  9.           }
    2 h' N6 u  ~. H( {$ n+ g9 U, F
  10.           if(KEY_0())
    + S: A) X% b5 B/ {0 q
  11.           {5 O; Y( c4 c# ^- ?
  12.                   BSP_LCD_login(24,108);
    7 r2 y& Y" ~& v# J5 ~; W1 m2 t$ f9 b
  13.           }
    3 x  g' h; d% v9 v
  14.           if(KEY_1())+ ?4 T% q9 C) u
  15.             {
    # n3 |# N$ n' l# ^
  16.                   BSP_LCD_img_DMA();$ F; |  f# V' E, x
  17.             }
    , {; X. M8 p- Y) W5 x% A, o
  18.           if(KEY_2())/ u2 l# `, Z# W& F. p3 |6 X& X% {( C
  19.             {
    ! m+ w. c7 `% |/ z. W, X. c
  20.                   my_driver.draw_pixel = gfx_draw_pixel;+ G' Y" h. _6 _' d1 c
  21.                   my_driver.fill_rect = NULL;//gfx_fill_rect;
    1 j4 a% ^3 r4 s% Q
  22.                   startHello3D(NULL, 240, 240, 2, &my_driver);% A0 k- _3 U5 r  p$ _
  23.             }" A! H/ G) X: }1 _3 N! P
  24.     /* USER CODE END WHILE */
复制代码

! K3 F# e" S5 l; e四、编译及测试
0 F5 F% r- p& _        完成代码移植及改写后,进行运行配置设置,点击编辑及运行按钮,将程序下载到开发板上。
9 q. T$ O" f* n# b
! m, K+ B" j! c  y
2f01fdf679234e349c31a2a72f836efa.png * t( ]% ^& b0 l5 V
+ ]* J& z1 B; N4 [8 `7 t5 D
         按键KEY2进入GuiLite库的3D绘制效果,按键KEY2按下时,屏幕会较慢地进行背景色(黑色)逐点绘制,因此较慢。最终处理的是一个3D立方体缓慢旋转刷新显示的动态画面,效果如下图(手指是拍摄时屏幕镜面映射效果,哈哈)。
3 E( x% o1 M& B( _) Q, \( [* y, @' u* D+ b( [
722f590bf3e54cb2bc457cca2ee1d67e.png 5 F0 O7 V" q  f1 f$ w. ?

# L3 w9 [5 p1 ~) e: j6 T. s& w
366b7d4b58f54925bde9496b94657fa6.png
9 M6 r8 V8 Y* b# v: x————————————————. \; G) V; ^: K8 a
版权声明:py_free-物联智能8 G7 I- T# }9 J7 U, _2 L6 j4 N/ Y3 R8 o
如有侵权请联系删除9 m# L, E, ]. O# Z6 Z. K+ }  ~, f
赞 收藏 评论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 手机版