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

【经验分享】移植LVGL到STM32F103

[复制链接]
STMCU-管管 发布时间:2021-6-7 17:39
一、前言
$ v; m' @+ w' d+ e( o" Clittlevgl是一个小型开源嵌入式 GUI 库(简称LVGL),界面精美,消耗资源小,可移植度高,支持响应式布局,全库采用纯 c 语言开发,移植上手简单。% t$ L+ W% Y5 I4 G, D+ ?' w
littleVGL 的主要特性如下:* k: A) L( E5 b  g6 n! G
• 具有非常丰富的内置控件,像 buttons, charts, lists, sliders, images 等
" O  b3 @; g* J  b; I% P1 L• 高级图形效果:动画,反锯齿,透明度,平滑滚动
# ]  N1 R$ q1 E: F6 H• 支持多种输入设备,像 touchpad, mouse, keyboard, encoder 等
# C: n/ H$ V. w' e! }7 n+ y• 支持多语言的 UTF-8 编码
1 L8 U% t" F4 l6 ?1 M• 支持多个和多种显示设备,例如同步显示在多个彩色屏或单色屏上" v. L4 ~+ l: @7 ~+ P' X5 H; ?' j; T
• 完全自定制的图形元素
$ X4 {% [( q: b) N& n9 T7 M• 硬件独立于任何微控制器或显示器
; n- ?" m2 |- ^2 ?) K( z• 可以缩小到最小内存 (64 kB Flash, 16 kB RAM)$ K- [1 R* s; j& j2 R  c
• 支持操作系统、外部储存和 GPU(非必须)
. o, x# w: a3 [7 R0 e• 仅仅单个帧缓冲设备就可以呈现高级视觉特效
& b, w  U$ R( m) U, a9 ^* _• 使用 C 编写以获得最大兼容性(兼容 C++)  {. u5 N' L: N* e( ]) _3 c
• 支持 PC 模拟器
% v3 a0 w4 b% K& Z" t• 为加速 GUI 设计,提供教程,案例和主题,支持响应式布局4 u, L/ J% B2 `4 a
• 提供了在线和离线文档
5 K$ X9 y0 r, R; u8 l2 K• 基于自由和开源的 MIT 协议
6 b5 l* w: Q9 e* u$ ^效果图以及更多详细的说明请见官网+ S$ R5 G$ I* P1 x4 X6 n# S
littlevgl中文官网:https://littlevgl.cn/
" i9 P" }$ s' w% u! Y5 a% klittlevgl英文官网:https://lvgl.io/
! i/ s' Q6 p$ q% n7 X
0 m6 F$ I5 Y" O" f5 B

' l: D$ h; b  }( T7 x二、学习资料" E3 G6 a# h( Y5 y' ?% V
官方文档:https://docs.lvgl.io/latest/en/html/index.html(注:官网打不开的话可能会需要梯子)
# e( g0 o& ~1 {Github: https://github.com/lvgl/lvgl. G0 Q* o( G/ O) h& @
三、移植前的准备工作
+ `5 |5 N  p0 n' O6 j- [开发环境:
  |# {5 j& M: Z. ?' tKeil MDK5 v5.28/ h3 Y& g7 i* H" m4 h  P7 F
ARM Compiler 6.0版本或以上" e4 H' ?' }+ C0 V
关于编译器,老版本的5.x编译器也能编译,但是新的6版本的编译器在编译速度与效率上有很大的提升,并且解决了一些莫名其妙的bug,强烈建议升级到6版本的编译器。笔者使用的版本是v6.14.1。
2 N5 E! e; l6 I+ P0 K# }1 O. \3 S3 b( z- @; D( {2 N

# ]5 h9 A7 H( O/ h% s: C硬件:8 K0 ~  i# [& _, u' V& {3 b% r
STM32F103开发板* K. j* P4 e! \/ n
屏幕一块(单色点阵屏或者是LCD彩屏都可以)
# p; o% v/ Y  e* Z- I% v笔者使用的开发板是正点原子的战舰V3开发板,芯片是STM32F103ZET6,72M主频+512k flash+64k ram,这样的性能足够跑LVGL了,当然,你的芯片性能越高肯定越好。哦,板上还外挂了一颗1M字节的SRAM,这对提升性能很有帮助。如果你用的不是这款芯片或者这块开发板,那也没关系,笔者会详细指出移植过程中不同的芯片移植需要注意的地方。
( N6 i& P) b6 Z8 C- k, B" A
6 ^$ F4 x# @) a% y) q; b0 [  f
& O/ l; E! Z, u' E* D
获取源码:9 |2 i5 B: Z4 C
源码地址:https://github.com/lvgl/lvgl" D" S1 z! Q3 \4 i
源码有很多的发行版,不同的发行版对应着不同的LVGL版本,注意不同的版本之间是不同的,比如版本V6与版本V7之间就变化很大,具体的版本变化信息可以看它的release note,这里我以v7.1.0版本为例来移植,并且今后的学习都会使用这个版本。% {$ U" F! o) P7 g! u5 }! c- N% z
1.png
7 t  h( o& @* ?8 j  `4 N  B1 U打开下载好的文件夹
7 L0 `& ]; X' m, G3 q" i5 k+ Z 2.png - h3 [; m9 K% u8 D* V
我们重点关注src/目录和lv_conf_template.h, lvgl.h 这几个文件,其它的暂时不用管。
, p0 u. \# T% L* W; g3 z& ?7 Z* i# D) {
+ @5 v7 `2 L: H
准备模板工程:
& ~- ^# R" [5 U为你的开发板准备好一个模板工程! f; t( r. l/ U" s- @$ i
要求:这个模板工程至少能够驱动一块屏幕(做GUI必须得有屏幕呀= =), }; j& G* F* Y! J5 q
如果你的屏幕带有触摸功能,那你的工程还需要有触摸屏的驱动,其实触摸屏驱动需要但非必须,没有触摸功能的话你也可以做一些展示的界面。事实上LVGL也支持键盘,鼠标,触控板等外设,因为在LVGL核心那里,它会将这些全部抽象为输入设备,它只需要从输入设备那里取得数据,并不关心你所用的是何种设备。8 L1 \: F& ]. F7 D
当然,触摸屏已经用得相当广泛,笔者使用的屏幕同样是正点原子家开发板配套的屏幕,型号:4.3英寸MCU屏,800*480分辨率,带电容触摸功能,其接口为16位并口,驱动IC:NT35510,触摸控制IC:GT9147,触摸控制接口为IIC。
. q8 k+ a, y" ?" W/ f" _/ j因此我需要提前准备好并且测试好这些驱动,对于屏幕控制器,除了初始化API(就是函数),还需要提供填充LCD的API,像这样:
2 O7 F0 x0 H- ~9 f3 k9 h
: j4 ^% J9 p) U
# m0 N; a8 A7 w8 _  q0 _, W$ L
LCD_Color_Fill(x1,y1,x2,y2,(u16*)color_p);* T) y4 O9 w0 A* C- E% `
1. T+ ]# [, n% {4 O( e$ ^9 `6 b
(x1,y1), (x2,y2)为俩坐标点,color_p为显存指针,该API要实现的功能为:将显存区域内的像素数据点,依次填充到以(x1,y1)为起点, (x2,y2)为重点的对角矩形区域内。
- g9 _4 c: q7 q9 ^! D, S注意,这个API是LVGL渲染屏幕的唯一API,哈哈哈简单吧。同样LVGL内部也不关心使用的何种屏幕,他们之间仅通过一个填充色块儿的API耦合,这种高内聚、低耦合的模块儿化设计思想是非常值得我们学习借鉴的。
% J" H1 ?' J3 W
5 e2 l4 {7 q: \0 H5 S% Z3 F& E, ^

- X" G# O; g: q/ |四、开始移植
7 i8 \' @7 P" A$ E8 Z8 N* Y①修改MDK工程(默认你已经会使用keil了)
! c6 D  }" A  n( [$ \7 w用keil mdk打开我们的模板工程,添加4个Groups:GUI_core, GUI_drv, GUI_demo, GUI_app,这些组用于添加我们的c代码到工程中,不同的组名意味着会存放不同功能的c代码。(注意:工程中的组与实际文件目录并不相同)$ C2 F1 m$ n6 H- I8 Y  e7 W7 s( D

( N& b( y) P8 ?( @/ g- g

$ t$ }5 w+ O0 _' I# b+ F2 nGUI_core:GUI核心文件层+ H4 {$ v, t3 p
GUI_drv:GUI驱动设备层,包括显示设备与输入设备/ C/ N, c  E) k4 w. [
GUI_demo:存放官方demo
  i" n' M8 g( [& ?. V5 d+ C0 B, y1 f+ MGUI_app:存放我们自己写的GUI应用; ~1 V" A" y: H$ N
修改后的工程结构:+ d$ e0 V- u$ `% r
3.png - K; \6 I- V7 f
②添加源文件9 ]. d& e4 m, r+ e( q
在项目的根路径下新建GUI目录,以及GUI_app目录,然后将准备工作第三步下载到的源码直接解压到GUI/目录下,然后把解压出来的文件夹改名为lvgl;在GUI/目录下新建lvgl_driver目录,然后:
: M& Y1 S( |5 y* @# C+ H
  1. //从GUI\lvgl\examples\porting\目录拷贝:$ ]3 C9 K8 h( h+ r6 U" U3 _
  2. lv_port_disp_template.c% m8 y+ o9 V. a; e6 i
  3. lv_port_disp_template.h2 L" |+ b  }' v: G/ Y
  4. lv_port_indev_template.c# _+ \! c8 t: t" Y
  5. lv_port_indev_template.h
    8 [/ Q% @# p1 D1 I" A+ [9 f
  6. // 到GUI/lvgl_driver/目录下并分别改名为:
    # b. [8 u7 ]% Y, e6 D- D
  7. lv_port_disp.c
    0 q4 x2 ^) Y- h$ F; V
  8. lv_port_disp.h& T) X1 I8 {7 p6 w. c
  9. lv_port_indev.c
    % f9 t( u$ Z2 e9 a1 I/ |
  10. lv_port_indev.h
    5 w1 {" {! j  \6 b

  11. 8 [4 [+ v. s7 t
  12. //添加:* G. m% e  F" w5 q) g0 Y9 w
  13. dma.c
    ! g- p# C; q6 i1 A' [! ?
  14. dma.h. O4 a0 h4 }" w" ~
  15. //这两个文件时我编写的使用DMA进行加速的代码,如果你不使用DMA请忽略
    ) Q& m7 t5 T6 P4 S: `
复制代码
然后把GUI/lvgl/lv_conf_template.h拷贝到GUI/目录下并改名为lv_conf.h。
8 L6 s+ z- J2 u, ~( C修改后的目录结构:
4 r0 ?0 _3 R! l! u 4.png - w4 Q5 G5 L( E! K  |7 S# e3 d
5.png
$ n/ A% ~. x; O6 l6 r) Q 6.png . h/ j1 D% X# `5 Y' Y

6 s6 [/ D+ ?; c' }/ Z5 t% k
9 E+ K$ I2 |8 v! ?. M
③添加源文件到工程中
1 b" {' O; Y) M" k" K% I在GUI_core组中添加以下文件夹中所有的.c文件:
& H! T6 m1 l1 m& Q5 Y
1 t$ d" [& h' }. L9 P: T0 P
  1. GUI/lvgl/src/lv_core
    $ t$ B" v# `/ ]. P7 Q
  2. GUI/lvgl/src/lv_draw
    2 @6 N" P! y" p( q" D/ ^! {' Z
  3. GUI/lvgl/src/lv_font7 S: ^1 g3 _& I1 W
  4. GUI/lvgl/src/lv_hal$ z  v5 E& I+ X5 c
  5. GUI/lvgl/src/lv_misc
    0 m( f2 z9 u2 R: U" R+ S3 f
  6. GUI/lvgl/src/lv_themes
    - ^* B/ x/ a& g# |- A# M
  7. GUI/lvgl/src/lv_widgets. O' |; h" Z9 B3 W; a& ]$ |- A
  8. //注意不要添加 GUI/lvgl/src/lv_gpu中的文件,除非你用到了相关功能' U/ Z! S6 Q/ w$ w
复制代码
在GUI_drv组中添加以下.c文件:
) ]: V$ r  C* }% y& ~# d
  1. GUI/lvgl_driver/dma.c# c* F$ _7 j4 }7 [
  2. GUI/lvgl_driver/lv_port_disp.c
    1 G6 u/ g- G4 K) W3 ]* X
  3. GUI/lvgl_driver/lv_port_indev.c
复制代码
④添加头文件路径- E! |% n. {# f9 T  y8 k
7.png 0 S% C7 d& v6 K( h/ E; \
⑤修改配置文件/ H) G0 x6 l, ]4 y
打开GUI/lv_conf.h
9 n# r; J; E# b* X首先第十行设置为#if 1 使能整个配置文件
6 _/ |) T4 v! _3 \, C/ o/ A找到以下几个宏定义并修改5 L, b, k, B, T5 u
- n* n6 `, k/ J+ _( T! }. z% f
  1. #define LV_HOR_RES_MAX          (480)//定义屏幕的最大水平分辨率. q# v9 l1 s9 ~; E6 G' k5 Z5 S
  2. #define LV_VER_RES_MAX          (800)//定义屏幕的最大垂直分辨率
    0 Y- t# m0 H, k
  3. //修改为你的屏幕分辨率即可
    ! s* h" w& U% _/ Q% M
  4. - e5 a2 L7 s7 H* g2 h+ P7 r
  5. /* Color depth:4 @& f& X% o( L! _
  6. * - 1:  1 byte per pixel& d7 ~! r( I" L" X
  7. * - 8:  RGB332, ^  }) `- t) a/ b
  8. * - 16: RGB5651 M- k# M1 {6 o& X3 G9 j$ i
  9. * - 32: ARGB88889 g9 z) \; m$ R# R
  10. */
    : j: v) q8 ?- f3 f3 J
  11. #define LV_COLOR_DEPTH     16
    ; a6 M8 b; C1 o0 v+ j+ f) j+ Q9 f- V2 f
  12. //定义颜色深度,如果是单色屏的话就改为13 d5 W- V: F# V7 E6 C, n

  13. ! h% L: W5 W  Q) I# V* @
  14. #  define LV_MEM_SIZE    (30U * 1024U)2 X: p" B- x1 V: v9 M2 j: J
  15. //给LVGL分配内存的大小,至少需要2k- p! B$ a& `( d2 C3 d8 v" l; N; ]4 b
复制代码
这个文件很重要,控制着整个LVGL的功能,但我们暂时就先修改这几个参数,其它参数暂时保持默认即可。等到我们深入了解了LVGL或者需要用到其它特性时再去研究修改也无妨。
" \# ~$ t% E, r& j6 F然后我们需要修改GUI/lvgl_driver/目录下的lv_port_disp.c, lv_port_disp.h,lv_port_indev.c, lv_port_indev.h这4个文件,这里直接给处修改好之后的文件。! D$ F7 B% X! q8 n& M

  ?/ {4 }- N9 e
4 D3 d$ l% j1 A
lv_port_disp.c
7 t* m$ {' H4 q/ R" n
  1. #if 1
    " h5 T* G) O# L; |- `6 d3 ]/ Q' P5 Z, x
  2. #include "lv_port_disp.h"4 K6 q' T0 h$ P! }" i- Y' u. ]
  3. #include "LCD.h"
      I" R3 w1 O9 X8 n( o9 R
  4. #include "dma.h"2 b% G1 I/ s5 Z) U8 W
  5. & m6 n5 G: f6 E+ M+ \
  6. #define COLOR_BUF_SIZE (LV_HOR_RES_MAX*LV_VER_RES_MAX)4 y7 |; z8 A4 Q9 m- ~8 l
  7. //分配到外部 1MB sram 的最起始处
    ! N4 w1 a$ a" y1 N$ X" m
  8. static lv_color_t *color_buf;6 j5 H! f8 T- v( S

  9. : J0 J  i5 o) Z+ i2 W# Z9 h% o2 W7 U
  10. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);# x. ^: O  ^# F* k
  11. ' m& C( w3 M' g7 y( ^/ m
  12. void lv_port_disp_init(void)
    ( C) u2 P2 g$ w. `) i" I4 _8 j
  13. {
    1 N% c/ c! x* @" a: X- b- _
  14.     static lv_disp_buf_t disp_buf;+ r' M8 G4 o# T3 ]( N9 T) ?8 k
  15.     color_buf = (lv_color_t *)0X68000000;//显示缓冲区初始化* c/ [* X6 s& W; u0 \0 y
  16.     lv_disp_buf_init(&disp_buf, color_buf, NULL,COLOR_BUF_SIZE);
    . t, }: J$ j" L5 l0 H8 I
  17.     lv_disp_drv_t disp_drv;
    7 \/ Z% H& Y# l- S# \6 [7 F& Q8 i6 E: S
  18.     lv_disp_drv_init(&disp_drv);1 n5 |: N1 L) D2 T% W
  19. / M' ]5 P* a; T2 F1 A  R
  20.     //注册显示驱动回调3 W1 B/ ?1 g* B- v( a8 k/ r) s
  21.     disp_drv.flush_cb = disp_flush;( q3 k+ L9 M! P% z/ R( f
  22.    
    5 @; A4 i7 p5 H( K4 V% W, N# q
  23.     //注册显示缓冲区% y+ n0 N5 Y+ Q
  24.     disp_drv.buffer = &disp_buf;& t8 g+ A) Y2 Q
  25.     . m1 `0 C8 c# H. }, H) o/ N
  26.     //注册显示驱动到 lvgl 中
    ; B3 n( b( z7 n
  27.     lv_disp_drv_register(&disp_drv);
    6 N, A$ W1 \( v+ c  Z+ ?, U' J; Q
  28. }
    , C- {7 R/ `$ Z; c- ?- R) `

  29. * v; s! E4 k8 o
  30. //把指定区域的显示缓冲区内容写入到屏幕上,你可以使用 DMA 或者其他的硬件加速器1 ^/ p4 F& u/ i! ~  ]; H+ H
  31. //在后台去完成这个操作但是在完成之后,你必须得调用 lv_disp_flush_ready()
    ( q* J- l/ K1 D+ v) G+ L
  32. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
    2 U9 K7 j* Q9 o' `" z8 q
  33. {9 R8 E9 x0 ]( S/ F. i$ o" l
  34.     //把指定区域的显示缓冲区内容写入到屏幕6 V1 g  d  d4 h5 C" \- s
  35.     //LCD_Color_Fill(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);//TODO
    , R4 M. X( N% B9 N  c. p: y* w
  36.     DMA_Fill_Color(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);
    - M, b- Z7 c, U% S
  37.     lv_disp_flush_ready(disp_drv);//最后必须得调用,通知 lvgl 库你已经 flushing 拷贝完成了
    0 F5 k# M$ g8 F7 H7 u" q
  38. }3 n# A4 D! c2 P8 B- Q2 Y
  39. 4 }  P& k3 v) [$ B
  40. #else /* Enable this file at the top */
    6 ~/ M! l* Z/ k# K0 u2 k5 @8 t* x
  41. /* This dummy typedef exists purely to silence -Wpedantic. */
    8 o% H2 D0 O* s4 c* ?+ g
  42. typedef int keep_pedantic_happy;, o7 P9 V+ D* m* r& @
  43. #endif
    # u" q6 w5 E2 A5 ?  U" B4 |$ H+ j
复制代码
lv_port_disp.h% D2 H  P) v* \- K4 `

, H: e6 T8 `* X5 `+ S
  1. #if 1
    ) K( f6 _* _8 [) ]8 M
  2. #ifndef LV_PORT_DISP_H
    , Z- x: A/ ?5 M' B) L
  3. #define LV_PORT_DISP_H% R; v! G6 e( O+ \. t% i$ c: @2 o
  4. $ m  b; |$ B9 y: R) [' n
  5. #ifdef __cplusplus) Y7 E" p: T4 `( g. \, W
  6. extern "C" {
    & k$ Q% }0 h3 r2 q, k
  7. #endif
      `  n% ]4 [3 Z7 j. j
  8. " i; E6 X/ i( P
  9. #include "lvgl/lvgl.h"6 k& S! r! `% [+ A( y" j& I

  10. 0 p" {6 ^% T5 e% K! m' I
  11. void lv_port_disp_init(void);
    : T$ D7 D# B. ?6 V3 [$ X# v
  12. . D' l, b1 \9 C0 m9 f5 n7 _
  13. #ifdef __cplusplus4 K8 }% _1 \$ o- X+ \  H
  14. } /* extern "C" */
      ^0 l9 b0 Q% t8 R$ S) p# F
  15. #endif
    * S4 s5 T7 o# p* y+ a
  16.   g$ U  D0 e5 j; ^/ q
  17. #endif /*LV_PORT_DISP_TEMPL_H*/
    & h$ J4 [' h. l
  18. #endif /*Disable/Enable content*/
    & x0 ~0 {) Y4 G! @1 K8 N* K  h2 i
复制代码
lv_port_indev.c" y3 g4 Q( D+ [$ l) w; v
  1. #if 1
    4 G' N% R0 c; d8 A1 ^
  2. #include "lv_port_indev.h"
    / p0 T9 F  E( q
  3. #include "Touch.h"
    ! S3 m( j, A& k. Q5 m

  4. 6 I! E; L) }. x2 j3 l" Z: b. X
  5. static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);0 A" H$ d; o3 G1 r
  6. , ^' T/ e4 e0 u. c5 m- c
  7. void lv_port_indev_init(void)
    1 h" I1 P8 \& P; y- F4 G
  8. {
    ; _1 ]0 C. D" K$ d$ S
  9.     lv_indev_drv_t indev_drv;
    1 V7 y8 }/ M- W8 g0 v  R- r

  10. 0 l! F  @4 H3 s4 E* ~
  11.     /*Register a touchpad input device*/
    : f; u" U$ f! u
  12.     lv_indev_drv_init(&indev_drv);
    " U1 S" w& {% B/ N' N
  13.     indev_drv.type = LV_INDEV_TYPE_POINTER;
    7 L) o; Y! p3 I  I9 w
  14.     indev_drv.read_cb = touchpad_read;) T! I- H2 f( G9 m
  15.     lv_indev_drv_register(&indev_drv);
    6 V# i6 `- C# J- p; U" v1 `) k8 K
  16. }2 z% O( m! ?. j& m2 o
  17. # T3 n: c) J' v1 M* e7 D
  18. static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
    % w9 e  d: _0 `3 K6 X
  19. {! Z7 R5 a# ?8 t# r- H
  20.                 static uint16_t last_x = 0;
    6 O! Y/ S1 c. S) a
  21.                 static uint16_t last_y = 0;; @0 z9 N: |# w
  22.                 Tp_dev.scan();/ U1 A4 D# r3 a
  23.                 if(Tp_dev.sta&0x80)//触摸按下了
    4 G# A7 T( p9 V; C
  24.                 {& u2 Y4 v0 g2 [% Y+ n
  25.                         last_x = Tp_dev.X[0];8 n6 I; x$ B& u& K( O
  26.                         last_y = Tp_dev.Y[0];//竖屏/ R* U6 h; P- E* Y% ?. D
  27.                         $ A: K5 L) W* n+ s0 U; o
  28.                         data->point.x = last_x;
    + @0 G: U0 F6 R; K; ^9 ^. V
  29.                         data->point.y = last_y;
    + J; I5 J# d! y% o/ u/ f& y) l
  30.                         data->state = LV_INDEV_STATE_PR;
    + N5 @/ I+ Z$ t* ]
  31.                 }else{
    # f: T4 G3 P  H9 h$ K) d7 ^+ m
  32.                         data->point.x = last_x;
    5 S3 A9 m' j- \5 n
  33.                         data->point.y = last_y;
    ' J0 |9 W  D, d5 u7 P
  34.                         data->state = LV_INDEV_STATE_REL;  ]4 }5 o0 u3 B6 H9 }
  35.                 }: z3 b0 C1 [/ O. h
  36.     return false;
    " ~5 O/ U  @% {5 K! s! u- V) ^
  37. }
    5 }# p: Q- C0 i3 D' M( G

  38. $ K5 q" D" N& O
  39. #else /* Enable this file at the top */
    3 p  F6 Z: p' O( X. B

  40. . d8 Z6 @# ^5 m
  41. /* This dummy typedef exists purely to silence -Wpedantic. */* W% V/ s! x1 v0 k8 B( q$ `
  42. typedef int keep_pedantic_happy;
    1 T2 Y4 f/ C; o2 m; W2 V' U
  43. #endif
    " E/ X8 e/ v& w  Y9 x
复制代码
lv_port_indev.h6 U+ ~( l- z9 d+ `3 N: D
  1. #if 1
    1 m/ M- r% [. N8 r/ I
  2. #ifndef LV_PORT_INDEV_H1 i7 z1 j8 \! ?1 Z8 _4 U$ V$ C
  3. #define LV_PORT_INDEV_H: \" Y% V9 W$ D1 M
  4. 6 A; {7 T% S. y& r7 }, p( w
  5. #ifdef __cplusplus
    1 e0 x& i" \1 Z7 v" _6 z$ x. N
  6. extern "C" {
    ! w) {% p% X" `# D: h2 d( `
  7. #endif3 |+ R+ M0 f" i+ }9 D6 N

  8. : y1 `) x6 i+ X
  9. #include "lvgl/lvgl.h"+ @; U% k2 @" E( ]
  10. ! t9 T' p! `- H/ R  z  T
  11. void lv_port_indev_init(void);6 W* }3 Z- F5 u7 U  B
  12. ' x' ?4 t7 G0 g, Y; j0 i  S! y( Z
  13. #ifdef __cplusplus
    # C0 d. R- j. H
  14. } /* extern "C" */# U. F" t" l1 ~/ o0 W5 U
  15. #endif: W8 ~1 Q/ g4 y; n! p! ~1 H, W7 e

  16. $ Q5 F$ ^' T! V4 b. K+ w0 h( Q
  17. #endif /*LV_PORT_INDEV_TEMPL_H*/8 c7 S! a. H( I/ S6 Z1 n0 N
  18. #endif /*Disable/Enable content*/
    - h) i- k2 d' S! q- H' Y
复制代码
⑥完善main函数* A) I" c' G* Y4 w) V* L1 w
至此,移植工作基本完成,接下来只需要完善main函数,初始化LVGL,便可放心玩耍。
) l6 K2 Y" O- q修改main.c如下:* `, t2 j8 d* O

5 ^6 J9 Y+ ?2 a" n2 w
  1. #include "include_config.h"! K- A/ B( I+ E) B( }5 H9 `
  2. #include "sys.h"
    ! q1 r' i0 b1 Q$ b5 z" n
  3. #include "delay.h"
      K2 l9 R( A6 s  ~0 D/ ^( j
  4. #include "dma.h"
    5 x' I8 D+ A2 m& X/ Q( Q
  5. #include "lvgl.h"$ i1 h0 ]$ o- [$ ]* j
  6. #include "lv_port_disp.h"
    + f" B) H4 D9 v8 I
  7. #include "lv_port_indev.h"
    8 e; G1 _5 v$ f" p( c$ V
  8. - I$ c7 f/ J& ]& o
  9. void tim_lv_tick(void);
    + h$ F9 W% _  U0 D  P
  10. void lvgl_first_demo_start(void);
    3 F; a1 }' o5 _, V- I( I
  11. void setup()2 \& A9 k, k- F; ?3 t: ~0 E
  12. {
    $ i( L2 u! l; w* ?& ?3 f* m
  13.         JTAG_Set(SWD_ENABLE);   //关闭JTAG,只用SWD
    ) d( F7 k. L3 P# l$ {
  14.         delay_init(72);& W  D5 ]- z5 q$ k: v9 O
  15.         led_init();
    8 g5 R$ n, c, n3 P" l5 b4 t
  16.         myuart_init(USART1,1152000,PA(10),PA(9));
    . P) r8 ^5 w, d; A1 F+ v
  17.         Timer_InitDef TIM_2_conf=
    % w- H: A  d; _
  18.         {/ a* x( ~6 d" [6 {& B
  19.                 .TIMx = TIM2,
    9 S0 g' Y2 y- o) S
  20.                 .ms   = 1,1 @- F' N* n  o; _( u
  21.                 .NVIC_Priority = 0,
    ! Z5 K5 o$ a% }
  22.                 .event_handler = tim_lv_tick,1 ?9 v. l% i8 ~( m# @
  23.         };% u( l6 Y/ k# |) F1 e: l
  24.         timer_init(&TIM_2_conf);
    8 v' G; ]4 _) N9 @. |/ g2 \7 Y
  25.        
    6 ^( g  o/ ^- G1 I9 t+ v2 u
  26.         LCD_init(BKOR); //默认竖屏
    5 |- V0 D( w! B* D3 a6 ~
  27.         if(Tp_dev.init())printf("Tp_dev failed!\r\n");
    / c8 `$ o9 z, B1 u! h, B! v
  28.         else printf("Tp_dev success!\r\n");6 V5 y* W+ R6 s2 q( {- F9 X' Q
  29.        
    6 i/ ^8 N5 O6 }0 l5 n# r, Z
  30.         FSMC_SRAM_Init();
    6 y5 G2 ]# a7 U
  31.         DMA_fsmc_Init();3 @8 p& x7 h- v# J  ~  [( t" ~
  32. }, W: Z8 f: K# D) D, g: d
  33. ! \' b  B, B  K6 K, D- t7 J
  34. int main(void)
    0 P5 B& I2 o6 f) ?, P# N
  35. {                       
    / ~; b; o$ S' [/ x
  36.         setup();- s( O# z2 c! i% ?8 Q
  37.         printf("set up success!\r\n");
    % j8 h4 J; x6 E4 R: f0 I- K( I

  38. ( n# E) K" \9 k2 ^! T; w$ T4 d  V
  39.         lv_init(); //lvgl 系统初始化
    , k+ F* C, V# f$ Q1 t( m; a5 T/ T
  40.         lv_port_disp_init(); //lvgl 显示接口初始化,放在 lv_init()的后面
    5 w5 \# M, b! g
  41.         lv_port_indev_init(); //lvgl 输入接口初始化,放在 lv_init()的后面( r, x( X# m: B+ {9 X: q
  42. % j- v% y  `$ p4 l
  43. # F2 {8 q1 f1 K, N
  44.         lvgl_first_demo_start();: B! Z4 b: ?; R6 q+ d
  45.         while(1)
    $ F) }( D+ ]* }
  46.         {, }0 }& N5 d' t( H3 W* W
  47.                 lv_task_handler();! a! W7 c0 J# k2 ~: t
  48.         }. ~9 |  t) S3 o3 V7 x
  49. }
    % Y5 R3 R5 A7 p$ n+ b
  50. & z% i- |1 \+ c! l
  51. void tim_lv_tick()6 a; h. d' x3 o' K
  52. {- a4 a1 |1 o8 D! B
  53.         lv_tick_inc(1);//lvgl 的 1ms 心跳
    ' ^- J1 a# [6 o9 `; G
  54. }
    ! Q$ o/ c" }( {% c3 f

  55. 9 f  @0 m3 h) |3 L* v) r

  56. ' S- B4 l  [1 W: P, `5 Z% m
  57. static void btn_event_cb(lv_obj_t * btn, lv_event_t event)
    & B  A! i9 `1 D2 w
  58. {5 I: m! z8 D2 V  k
  59.     if(event == LV_EVENT_CLICKED) {. k8 @, j! a& L( G9 t
  60.         static uint8_t cnt = 0;
    0 k9 a3 ~  ?. P
  61.         cnt++;* t( ~% U, ~+ E

  62. 5 f7 e$ E6 I1 O8 p# t6 s. j) J1 ]
  63.         /*Get the first child of the button which is the label and change its text*/* p' J) r2 w! {, A# O
  64.         lv_obj_t * label = lv_obj_get_child(btn, NULL);
    ( j- S/ a, |( F5 D/ d5 S+ G0 U. P
  65.         lv_label_set_text_fmt(label, "Button: %d", cnt);
    5 a6 L$ L8 C4 p  k% c0 Z0 Q
  66.     }
    ( ^! a( M2 b  g% |4 O
  67. }
    0 z7 w" P" [* k6 k% `5 ]. \
  68. void lvgl_first_demo_start(void)1 x. I9 o  l; B7 M
  69. {
    # _( }  q! l" |& [& o9 C0 g, R
  70.     lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL);     /*Add a button the current screen*/. w  B- F, H' f( c; k' P! i
  71.     lv_obj_set_pos(btn, 10, 10);                            /*Set its position*/1 L, P. ?# C" d5 V* m7 ^
  72.     lv_obj_set_size(btn, 120, 50);                          /*Set its size*/
    # o! f+ y- D7 @& E- w; t9 U
  73.     lv_obj_set_event_cb(btn, btn_event_cb);                 /*Assign a callback to the button*/$ c  D  A% g+ T4 k

  74. . @: L) N( F- {$ G
  75.     lv_obj_t * label = lv_label_create(btn, NULL);          /*Add a label to the button*/
    0 {2 |9 [- ]2 G: v* |
  76.     lv_label_set_text(label, "Button");                     /*Set the labels text*/$ K1 l3 V2 P  p, S+ l3 a; ^& v9 T6 P
  77. 1 v- h0 d  E; P1 E: J
  78. 8 U( O- o1 ~5 O7 f- D1 ]: E
  79.         lv_obj_t * label1 = lv_label_create(lv_scr_act(), NULL);4 u* H: \& V4 z2 ]
  80.         lv_label_set_text(label1, "Hello world!"); ! g* F1 u. h; ^, _5 w
  81.         lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0);
    # R( X" O, s) d) e. O/ n' `
  82.         lv_obj_align(btn, label1, LV_ALIGN_OUT_TOP_MID, 0, -10);0 k: V& R+ o+ Y
  83. }- h% d* W9 P4 ^& x" L2 b
复制代码
⑦编译并运行
5 v  {6 f6 N8 H4 M设置编译器版本
" T; [. w. r+ ~; v* U1 W' F 8.png
; N9 g1 t+ u2 u; G  n注意:这一步应该在移植之前就应该执行,模板工程也使用V6编译器编译通过,免得移植后再编译各种报错。这一步非强制的。
1 `0 c. ]; P7 S6 k0 o3 \ 9.png
2 f: g4 R$ T( l+ K( o. [设置编译参数
  U% ~+ w7 N: ^C99:LVGL要求 C99 或更新的编译器,否则编译是会报错的7 ~+ T4 @& H# g' ?3 m) J
勾选Execute-only Code, 会自动去除未使用到的代码函数,大大减小了编译后的固件大小,建议勾选,其它默认即可。' {. F+ @) G$ i: Y7 Z9 G- \

- m2 [, f0 m- S& o3 h3 Z; S4 R9 Q
6 p2 M6 @8 S4 Y+ c2 p+ T. S: q
编译  y1 \9 Z" H) A2 b4 k- k
烧写运行3 l4 |, Q! z6 u3 L
效果图:2 o3 O7 f) J! D( f" x6 Y
10.png
0 b5 \! m8 @7 L. X五、总结* b4 t) d9 d; K( }* }
到这里移植工作已经结束,接下来我会介绍一下移植需要修改的那几个重点文件,假如你要移植到其它平台,请看这里。
( Z& a- I- g6 Y8 Z+ q8 }! K1 }
2 _% t- p  h7 b- j; \6 Q
. T# G; v3 o+ F) a+ t* Z& b1 d+ f6 w- @
lv_port_disp.c8 ~+ e( b: @3 H! F* u
  1. //定义显存的大小
    9 i3 l& U0 [* ^1 y' U, ^' @
  2. //我使用了刚好一整块屏幕的显存
    3 X. L! q0 F% ?8 Z6 V  T# B4 D  i! N
  3. //如果你的内存没有那么大,那么定义10行的显存也是可以的   LV_HOR_RES_MAX*10+ }; l" \. h; _5 S2 u
  4. //LV_HOR_RES_MAX,LV_VER_RES_MAX定义于lv_conf.h
      u& h- j4 q+ s8 ?! \
  5. #define COLOR_BUF_SIZE (LV_HOR_RES_MAX*LV_VER_RES_MAX); l3 `6 n6 v  M. h8 S
  6. * x7 R2 M4 H& l+ R5 Z
  7. static lv_color_t *color_buf;   //定义显存指针" w! Z6 _' z5 |4 H: z5 R
  8. - ^# k, h1 w( s3 \" a
  9. 3 c/ l5 o' M* A3 h6 t0 V8 r* H
  10. //lv_port_disp_init函数
    . s6 @% |9 ?  f" D
  11. //显存初始化,将显存指针指向我指定的内存啊区域
    , P+ @5 E4 y) Q4 ?; Y
  12. //0X68000000是我板子上外挂的SRAM启始地址
    , I. b. Y( A6 e
  13. //显存需要根据你自己的板子合理分配; F* V' z3 n7 l2 }7 @2 e& L
  14. color_buf = (lv_color_t *)0X68000000;
    9 c$ m7 s3 t8 V' X4 t% E

  15. . e- @' ~! a  m0 E9 b8 q5 y, _( |
  16. * X# }; l9 C$ r  }% r; p( F2 v
  17. //把指定区域的显示缓冲区内容写入到屏幕上,你可以使用 DMA 或者其他的硬件加速器
    ! w; c2 U- z& m9 y4 ]1 d
  18. //在后台去完成这个操作但是在完成之后,你必须得调用 lv_disp_flush_ready()7 c5 o. f. s/ u3 _& [! _1 [
  19. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
    0 b, x6 S' [7 Z* S
  20. {! U$ d! a# V0 u' y) V
  21.     //把指定区域的显示缓冲区内容写入到屏幕4 v) n1 j0 F, X4 T: l
  22.     //LCD_Color_Fill(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);//TODO
    - y: _. k8 c% n  L4 j; W7 o
  23.     DMA_Fill_Color(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);
    / t* }% k* f# [
  24.     lv_disp_flush_ready(disp_drv);//最后必须得调用,通知 lvgl 库你已经 flushing 拷贝完成了
    ' K' ^7 ~" z5 j' f' B5 c  _
  25. }
    4 |. e, v5 G- v2 ]
  26. //DMA_Fill_Color或者LCD_Color_Fill函数需要根据你自己的平台实现6 U- {  J8 D6 Y- r7 V; T( H$ @; o/ M
复制代码
lv_port_indev.c
6 g- C4 P1 }  B7 s: Y& d8 _
% n3 k% c  g/ N
  1. static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)" J# P: `  P3 k( i) G; P
  2. {
    & R$ k7 x% m2 O! `7 w% L% \
  3.                 static uint16_t last_x = 0;/ d# `* {+ G) V8 Z+ k- a0 C
  4.                 static uint16_t last_y = 0;) z* `' H) h& W8 I! M
  5.                 Tp_dev.scan();
    ! U9 B( e  G% z$ E4 X- x
  6.                 if(Tp_dev.sta&0x80)//触摸按下了
    # s5 A: f7 b" d% Z
  7.                 {0 @3 Q' ~2 {4 S" T! a8 i! s) d
  8.                         last_x = Tp_dev.X[0];
    6 A- _) @, V+ s: G" f
  9.                         last_y = Tp_dev.Y[0];//竖屏
    2 t+ L8 H3 A+ k; T  @
  10.                        
    . J! [/ V! A( H( m+ d  Z+ k: T  O
  11.                         data->point.x = last_x;
    7 H* n4 c7 l1 F# X& d% s
  12.                         data->point.y = last_y;
    3 H' p1 T& M  y5 f2 N* j# p% j
  13.                         data->state = LV_INDEV_STATE_PR;
    ) n0 \( L- a5 s, f% {, x4 R; O# }
  14.                 }else{
    4 [: u( g) \- m$ {  ~* T
  15.                         data->point.x = last_x;* z/ T9 c0 Y8 P5 `) H% b5 k
  16.                         data->point.y = last_y;
    ' |: H" H: D, P. O; j- }0 ]6 J
  17.                         data->state = LV_INDEV_STATE_REL;
    4 R  t) w& x2 D% Z4 o
  18.                 }: V3 N0 q% {+ G# R6 s
  19.     return false;
    & n9 \8 ], y$ T1 d* W
  20. }
    7 k+ K  W  N8 @- W+ ^+ H2 \
  21. //Tp_dev.scan() 是触摸屏驱动层的函数
    3 Y5 ^, `7 D( c% a4 D. R2 b
  22. //其作用是扫描整块儿屏幕,得到触摸状态写入Tp_dev.sta中,如果有触摸点按下,得到其坐标并存入Tp_dev.X和Tp_dev.Y数组中* g7 Y1 D$ a% v: t+ h2 [5 t5 ]8 Q
  23. //因此这个函数需要根据你自己的板子和屏幕提供
    : u8 ^0 d) U3 b% X# Q
复制代码
六、源码
' N! C3 c7 s" k, L2 M9 Z本篇文章中移植的源码
3 {, R: G' w! V' U' E6 {移植前的工程模板:https://github.com/jesons007/LVGL_7.1.0_STM32/tree/Blank_Temp2 o9 z% o# _% p
移植好的代码:https://github.com/jesons007/LVGL_7.1.0_STM32
! L1 w8 o$ c! @
; x& o# b: ^8 z6 z# m6 g8 ]2 N' Y4 M9 q) G& Z6 |* s* G
+ R& I; o2 A' [
收藏 1 评论1 发布时间:2021-6-7 17:39

举报

1个回答
moticsoft 回答时间:2021-6-9 15:36:02
流畅度怎么样啊,有没有效果视频看?
4 n, k; C2 S' d

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版