跳到主要内容

VSCode 开发

本章节包含以下部分,请按需阅读:

配置开发环境

请参考 安装和配置 Pico VSCode 教程 下载安装 Pico VSCode。

示例程序

VSCode 示例程序位于 示例程序包C 目录中。

示例程序基础例程说明依赖库
01_GUI液晶 GUI 显示程序-
02_ES8311开发板音频测试程序-
03_FatFsFAT 文件系统,SD卡 支持 SPI/SDIO 通讯-
04_GPS_I2C使用 I2C 与 GPS 模块通讯,获取 GPS 数据-
05_LVGL液晶 LVGL 显示程序LVGL V8.1

01_GUI

【程序说明】

  • 使用 PIO 模拟的 QSPI 与 液晶通讯,并通过 GUI 实现显示文本和图片等功能。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

底层硬件接口

我们对硬件操作进行了底层的封装,由于硬件平台不一样,内部的实现是不一样的,如果需要了解内部实现可以去对应的目录中查看,在 DEV_Config.c(.h) 可以看到很多定义,在目录:c\lib\Config

  • 模块初始化与退出的处理

    void DEV_Module_Init(void);
    void DEV_Module_Exit(void);
    提示

    这里是处理使用液晶屏前与使用完之后一些GPIO的处理。

  • GPIO读写

    void 	DEV_Digital_Write(uint_16 Pin, uint_8 Value);
    uint_8 DEV_Digital_Read(uint_16 Pin);
  • SPI写数据

    void DEV_SPI_WriteByte(uint_8 Value);

上层应用

对于屏幕而言,如果需要进行画图、显示中英文字符、显示图片等怎么办,这些都是上层应用做的。这有很多小伙伴有问到一些图形的处理,我们这里提供了一些基本的功能 在如下的目录中可以找到 GUI,在目录:c\lib\GUI\GUI_Paint.c(.h)

在如下目录下是 GUI 依赖的字符字体,在目录:c\lib\Fonts

  • 新建图像属性:新建一个图像属性,这个属性包括图像缓存的名称、宽度、高度、翻转角度、颜色

    void Paint_NewImage(uint16_t *image, uint16_t Width, uint16_t Height, uint16_t Rotate, uint16_t Color)
    参数:
    image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
    Width: 图像缓存的宽度;
    Height: 图像缓存的高度;
    Rotate: 图像的翻转的角度
    Color: 图像的初始颜色;
  • 选择图像缓存:选择图像缓存,选择的目的是你可以创建多个图像属性,图像缓存可以存在多个,你可以选择你所创建的每一张图像

    void Paint_SelectImage(uint8_t *image)
    参数:
    image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
  • 图像旋转:设置选择好的图像的旋转角度,最好使用在 Paint_SelectImage() 后,可以选择旋转 0、90、180、270 度

    void Paint_SetRotate(uint16_t Rotate)
    参数:
    Rotate: 图像选择角度,可以选择 ROTATE_0、ROTATE_90、ROTATE_180、ROTATE_270 分别对应 090180270
    提示

    不同选择角度下,坐标对应起始像素点不同,这里以 1.14 为例,四张图,按顺序为 0°, 90°, 180°, 270°。仅做为参考。

  • 图像镜像翻转:设置选择好的图像的镜像翻转,可以选择不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像。

    void Paint_SetMirroring(uint8_t mirror)
    参数:
    mirror: 图像的镜像方式,可以选择 MIRROR_NONE、MIRROR_HORIZONTAL、MIRROR_VERTICAL、MIRROR_ORIGIN 分别对应不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像
  • 设置点在缓存中显示位置和颜色:这里是 GUI 最核心的一个函数、处理点在缓存中显示位置和颜色。

    void Paint_SetPixel(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color)
    参数:
    Xpoint: 点在图像缓存中 X 位置
    Ypoint: 点在图像缓存中 Y 位置
    Color: 点显示的颜色
  • 图像缓存填充颜色:把图像缓存填充为某颜色,一般作为屏幕刷白的作用。

    void Paint_Clear(uint16_t Color)
    参数:
    Color: 填充的颜色
  • 图像缓存部分窗口填充颜色:把图像缓存的某部分窗口填充为某颜色,一般作为窗口刷白的作用,常用于时间的显示,刷白上一秒。

    void Paint_ClearWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color)
    参数:
    Xstart: 窗口的 X 起点坐标
    Ystart: 窗口的 Y 起点坐标
    Xend: 窗口的 X 终点坐标
    Yend: 窗口的 Y 终点坐标
    Color: 填充的颜色
  • 画点:在图像缓存中,在(Xpoint, Ypoint)上画点,可以选择颜色,点的大小,点的风格

    void Paint_DrawPoint(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
    参数:
    Xpoint: 点的 X 坐标
    Ypoint: 点的 Y 坐标
    Color: 填充的颜色
    Dot_Pixel: 点的大小,提供默认的 8 种大小点
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Dot_Style: 点的风格,大小扩充方式是以点为中心扩大还是以点为左下角往右上扩大
    typedef enum {
    DOT_FILL_AROUND = 1,
    DOT_FILL_RIGHTUP,
    } DOT_STYLE;
  • 画线:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画线,可以选择颜色,线的宽度,线的风格

    void Paint_DrawLine(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, LINE_STYLE Line_Style , LINE_STYLE Line_Style)
    参数:
    Xstart: 线的 X 起点坐标
    Ystart: 线的 Y 起点坐标
    Xend: 线的 X 终点坐标
    Yend: 线的 Y 终点坐标
    Color: 填充的颜色
    Line_width: 线的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Line_Style: 线的风格,选择线是以直线连接还是以虚线的方式连接
    typedef enum {
    LINE_STYLE_SOLID = 0,
    LINE_STYLE_DOTTED,
    } LINE_STYLE;
  • 画矩形:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画一个矩形,可以选择颜色,线的宽度,是否填充矩形内部

    void Paint_DrawRectangle(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
    Xstart: 矩形的 X 起点坐标
    Ystart: 矩形的 Y 起点坐标
    Xend: 矩形的 X 终点坐标
    Yend: 矩形的 Y 终点坐标
    Color: 填充的颜色
    Line_width: 矩形四边的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Draw_Fill: 填充,是否填充矩形的内部
    typedef enum {
    DRAW_FILL_EMPTY = 0,
    DRAW_FILL_FULL,
    } DRAW_FILL;
  • 画圆:在图像缓存中,以 (X_Center Y_Center) 为圆心,画一个半径为 Radius 的圆,可以选择颜色,线的宽度,是否填充圆内部

    void Paint_DrawCircle(uint16_t X_Center, uint16_t Y_Center, uint16_t Radius, uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
    X_Center: 圆心的 X 坐标
    Y_Center: 圆心的 Y 坐标
    Radius: 圆的半径
    Color: 填充的颜色
    Line_width: 圆弧的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Draw_Fill: 填充,是否填充圆的内部
    typedef enum {
    DRAW_FILL_EMPTY = 0,
    DRAW_FILL_FULL,
    } DRAW_FILL;
  • 写 Ascii 字符:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一个 Ascii 字符,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawChar(uint16_t Xstart, uint16_t Ystart, const uint8_t Ascii_Char, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    Ascii_Char:Ascii 字符
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写英文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串英文字符,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawString_EN(uint16_t Xstart, uint16_t Ystart, const uint8_t * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pString: 字符串,字符串是一个指针
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写中文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串中文字符,可以选择 GB2312 编码字符字库、字体前景色、字体背景色

    void Paint_DrawString_CN(uint16_t Xstart, uint16_t Ystart, const uint8_t * pString, cFONT* font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pString: 字符串,字符串是一个指针
    Font: GB2312 编码字符字库,在 Fonts 文件夹中提供了以下字体:
    font12CN:ascii 字符字体 11*21,中文字体 16*21
    font24CN:ascii 字符字体 24*41,中文字体 32*41
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写数字:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串数字,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawNum(uint16_t Xpoint, uint16_t Ypoint, uint32_t Nummber, sFONT* Font, uint16_t Digit,uint16_t Color_Foreground, uint16_t Color_Background);
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    Nummber: 显示的数字,这里使用的是 32 位长的 int 型保存,可以最大显示到 2147483647
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Digit:显示小数点位数
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 显示时间:在图像缓存中,在 (Xstart Ystart) 为左顶点,显示一段时间,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色;

    void Paint_DrawTime(uint16_t Xstart, uint16_t Ystart, PAINT_TIME *pTime, sFONT* Font, uint16_t Color_Background, uint16_t Color_Foreground)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pTime: 显示的时间,这里定义好了一个时间的结构体,只要把时分秒各位数传给参数;
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色

【运行效果】

  • 使用 VSCode 导入并编译 01_GUI 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 uf2 目录下 01_GUI.uf2 进行快速验证。

02_ES8311

【程序说明】

  • 使用 PIO 模拟的 I2S 与 ES8311 通讯,实现音频的输入与输出。

【硬件连接】

  • 连接喇叭
  • 使用 USB 线把板子接入电脑

【代码分析】

  • es8311_init():初始化 ES8311。
  • es8311_sample_frequency_config():配置采样率。
  • es8311_microphone_config():配置麦克风。
  • es8311_microphone_gain_set():设置麦克风增益。
  • es8311_voice_volume_set():设置音量。
  • Sine_440hz_out():输出 440 Hz 正弦波。
  • Happy_birthday_out():生日快乐电子乐。
  • Loopback_test():录音播放测试。
  • Music_out():播放音乐。

【运行效果】

  • 使用 VSCode 导入并编译 02_ES8311 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 02_ES8311\uf2 目录下 uf2 尾缀文件进行快速验证。

03_FatFs

【程序说明】

  • 使用 SPI 或 PIO 模拟的 SDIO 与 SD 卡通讯,实现类似于 busybox 或 DOS 的命令行界面。

【硬件连接】

  • 插入 SD 卡
  • 使用 USB 线把板子接入电脑

【代码分析】

  • sd_init_driver():初始化 SD 卡驱动。
  • getchar_timeout_us():获取串口输入。
  • process_stdio():处理串口输入。

【运行效果】

  1. 使用 putty 或者 mobaxterm 等终端工具,打开开发板对应的 USB 串行端口

  2. 按 Enter 键启动命令行界面 (CLI)。您应该会看到类似这样的提示:

    >
  3. 输入 help 指令可以得到可用指令,如下

    > help
    setrtc <DD> <MM> <YY> <hh> <mm> <ss>:
    Set Real Time Clock
    Parameters: new date (DD MM YY) new time in 24-hour format (hh mm ss)
    e.g.:setrtc 16 3 21 0 4 0

    date:
    Print current date and time

    lliot <drive#>:
    !DESTRUCTIVE! Low Level I/O Driver Test
    e.g.: lliot 1

    format [<drive#:>]:
    Creates an FAT/exFAT volume on the logical drive.
    e.g.: format 0:

    mount [<drive#:>]:
    Register the work area of the volume
    e.g.: mount 0:

    unmount <drive#:>:
    Unregister the work area of the volume

    chdrive <drive#:>:
    Changes the current directory of the logical drive.
    <path> Specifies the directory to be set as current directory.
    e.g.: chdrive 1:

    getfree [<drive#:>]:
    Print the free space on drive

    cd <path>:
    Changes the current directory of the logical drive.
    <path> Specifies the directory to be set as current directory.
    e.g.: cd 1:/dir1

    mkdir <path>:
    Make a new directory.
    <path> Specifies the name of the directory to be created.
    e.g.: mkdir /dir1

    ls:
    List directory

    cat <filename>:
    Type file contents

    simple:
    Run simple FS tests

    big_file_test <pathname> <size in bytes> <seed>:
    Writes random data to file <pathname>.
    <size in bytes> must be multiple of 512.
    e.g.: big_file_test bf 1048576 1
    or: big_file_test big3G-3 0xC0000000 3

    cdef:
    Create Disk and Example Files
    Expects card to be already formatted and mounted

    start_logger:
    Start Data Log Demo

    stop_logger:
    Stop Data Log Demo

04_GPS_I2C

【程序说明】

  • 本示例演示了使用 I2C 主动查询 LC76G 的 NMEA 数据,通过传输打印到串口上可以直接使用 NMEA 解析工具获取经纬度等信息。

【硬件连接】

  • 连接 GNSS 陶瓷天线
  • 使用 USB 线把板子接入电脑

【代码分析】

  • i2c_master_init():初始化 I2C。
  • writeDataToI2C():发送指令,读取数据。

【运行效果】

  • 使用 VSCode 导入并编译 04_GPS_I2C 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 uf2 目录下 04_GPS_I2C.uf2 文件进行快速验证。

05_LVGL

【程序说明】

  • 使用 PIO 模拟的 QSPI 与 液晶通讯,并通过 LVGL 实现显示文本和图片等功能。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

源码结构

  • LVGL 库的源码位于工程文件夹的 lib\lvgl ,使用的版本号为 8.1,二次开发请参考对应版本的开发文档。

  • LVGL 库的相关设置在工程文件夹的 examples\inc\lv_conf.h 中,可以设置显示刷新频率、系统占用数据等。

  • LVGL 库的应用代码位于工程文件夹的 examples\src\LVGL_example.c。

LVGL 初始化

在使用 LVGL 图像库之前,您需要先初始化 LVGL。

  • LVGL 库的初始化函数

    代码位置:examples\src\LVGL_example.c

    实现功能:主要用于初始化 LVGL 所需的硬件和结构体变量。

    LVGL_Init();
  • LVGL 库核心初始化

    代码位置:examples\src\LVGL_example.c

    /*2.Init LVGL core*/
    lv_init();

LVGL 运行

LVGL 库定时调用心跳接口函数 lv_tick_inc 来通知LVGL过去的时间,以便 LVGL 能够更新其内部的时间状态,处理与时间相关的任务,例如动画、定时器等。在主函数的循环中还需要调用 lv_task_handler 函数,以便 LVGL 及时处理事件和任务,保证用户界面的响应和刷新。

  • LVGL 心跳接口

    代码位置:examples\src\LVGL_example.c

    实现方式:需要确保 lv_task_handler 的优先级低于 lv_tick_inc 的优先级,所以在本例中 lv_tick_inc 在定时器回调函数中调用。

    //每5ms调用一次定时器回调函数
    add_repeating_timer_ms(5, repeating_lvgl_timer_callback, NULL, &lvgl_timer);

    static bool repeating_lvgl_timer_callback(struct repeating_timer *t)
    {
    lv_tick_inc(5);
    return true;
    }
  • LVGL 任务处理器

    代码位置:examples\src\LCD_XinXX_LVGL_test.c

    实现方式:要处理 LVGL 的任务,需要定期调用 lv_timer_handler(),本例中在主函数的循环中进行调用。

    int main()
    {
    ...
    while(1)
    {
    lv_task_handler();
    DEV_Delay_ms(5);
    ...
    }
    }

LVGL 显示

要实现 LVGL 显示,必须初始化一个显示驱动,并设置显示驱动的各个属性,例如,颜色格式、绘制缓冲区、渲染模式以及显示回调函数。在每个 LV_DISP_DEF_REFR_PERIOD(在 lv_conf.h 中设置),LVGL 会检测 UI 上是否发生了一些需要重绘的事情。例如,按下按钮、更改图表、发生动画等。需要重新绘制时,LVGL 调用显示回调函数完成图像在刷新区的绘制。

  • LVGL 显示刷新率设置

    代码位置:examples\inc\lv_conf.h

    设置方式:在 lv_conf.h 中还能设置显示缓冲区刷新频率的时间,可以修改这个定义来改变屏幕的刷新时间。

    #define LV_DISP_DEF_REFR_PERIOD  10 // 单位:ms,这里为10ms
  • LVGL 显示颜色设置

    代码位置:examples\inc\lv_conf.h

    设置目的:由于 lv_color_t 结构体在默认状态下所构建的像素颜色储存方式与本例需要传输的数据不一致,直接进行传输会导致显示的图像出现色差。

    #define LV_COLOR_16_SWAP 1
  • LVGL 显示相关变量定义

    代码位置:examples\src\LVGL_example.c

    实现功能:定义显示驱动 disp_drv,绘制缓冲区 disp_buf。本例绘制缓冲区 buf0 设置为整个屏幕显示面积,能够在降低大面积刷屏锯齿的同时有效提高屏幕刷新率。

    static lv_disp_drv_t disp_drv;
    static lv_disp_draw_buf_t disp_buf;
    static lv_color_t *buf0;
  • LVGL 显示设备注册

    代码位置:examples\src\LVGL_example.c

    实现功能:根据设计需求完善 LVGL 库核心结构体变量,初始化显示驱动 disp_drv,并设置绘制缓冲区,该缓冲区是 LVGL 用来渲染屏幕内容的简单数组。一旦渲染准备就绪,绘制缓冲区的内容将使用显示驱动程序中设置的 disp_drv_flush_cb 函数发送到显示器。

    /*3.Init LVGL display*/
    buf0 = (lv_color_t *)malloc(DISP_HOR_RES * DISP_VER_RES);
    lv_disp_draw_buf_init(&disp_buf, buf0, NULL, DISP_HOR_RES * DISP_VER_RES);
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = disp_flush_cb;
    disp_drv.draw_buf = &disp_buf;
    disp_drv.hor_res = DISP_HOR_RES;
    disp_drv.ver_res = DISP_VER_RES;
    lv_disp_t *disp= lv_disp_drv_register(&disp_drv);
  • LVGL 显示回调函数

    代码位置:examples\src\LVGL_example.c

    实现功能:主要完成图像在刷新区的绘制。

    void disp_flush( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p )
    参数:
    lv_disp_drv_t *disp_drv: 显示驱动结构体指针,包含了与显示相关的信息和函数指针。该参数常用于通知刷新完成
    const lv_area_t *area : 区域结构体指针,包含待刷新区域的位置信息。在本例中,用于创建 TFT 显示的窗口
    lv_color_t *color_p : 颜色结构体指针,表示要在刷新区域内显示的颜色数据。在本例中,作为DMA输入读取地址将数据传输到QSPI总线,完成图像的绘制
  • LVGL 显示回调函数实现

    代码位置:examples\src\LVGL_example.c

    实现方式:在本例中为了最大化降低处理器的利用率,使用 DMA 来进行颜色数据的传输,设置 color_p 作为读地址,QSPI 接口的 TX FIFO 寄存器为写地址。

    static void disp_flush_cb(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
    {
    AMOLED_1IN75_SetWindows(area->x1, area->y1, area->x2+1 , area->y2+1); // Set the LVGL interface display position

    QSPI_Select(qspi);
    QSPI_Pixel_Write(qspi, 0x2c);
    dma_channel_configure(dma_tx,
    &c,
    &qspi.pio->txf[qspi.sm],
    color_p, // read address
    ((area->x2 + 1 - area->x1) * (area->y2 + 1 - area->y1))*2,
    true);// Start DMA transfer
    }
  • LVGL 刷新完成通知实现

    代码位置:examples\src\LVGL_example.c

    实现功能:每一次图像刷新完成后都需要通知 LVGL 核心,以便 LVGL 准备下个刷新图像的渲染。

    实现方式:本例在 DMA 传输完成中断服务函数中通知 LVGL 图像刷新完成。

    static void dma_handler(void)
    {
    if (dma_channel_get_irq0_status(dma_tx))
    {
    dma_channel_acknowledge_irq0(dma_tx);
    QSPI_Deselect(qspi);
    lv_disp_flush_ready(&disp_drv); // Indicate you are ready with the flushing
    }
    }

LVGL 输入

在 LVGL 中,允许用户注册输入设备,如触摸板、鼠标、键盘或编码器等设备。用户可以通过这些输入设备控制用户界面,实现更好的交互。

  • LVGL 调用输入设备回调函数的频率设置

    代码位置:examples\inc\lv_conf.h

    设置方式: LVGL 默认每 30ms 调用一次输入设备回调函数来更新输入设备所触发的事件,可以在 lv_conf.h 中进行设置。

    #define LV_INDEV_DEF_READ_PERIOD 30 // 单位:ms,这里为30ms
  • LVGL 输入设备注册

    代码位置:examples\src\LVGL_example.c

    设置方式:注册触摸屏设备 indev_ts 并初始化。

    /*4.Init touch screen as input device*/ 
    lv_indev_drv_init(&indev_ts); // 设备初始化
    indev_ts.type = LV_INDEV_TYPE_POINTER; // 注册为触摸屏设备
    indev_ts.read_cb = ts_read_cb; // 设置回调函数
    lv_indev_t * ts_indev = lv_indev_drv_register(&indev_ts); // 注册设备
    //Enable IRQ
    DEV_KEY_Config(Touch_INT_PIN);
    DEV_IRQ_SET(Touch_INT_PIN, GPIO_IRQ_EDGE_RISE, &touch_callback);
    DEV_KEY_Config(SYS_OUT);
    DEV_IRQ_SET(SYS_OUT, GPIO_IRQ_EDGE_FALL, &touch_callback);
  • LVGL 的输入设备回调函数

    代码位置:examples\src\LVGL_example.c

    实现功能:主要用于更新输入事件。

    static void ts_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);
    参数:
    lv_indev_drv_t *indev_drv: LVGL 中的输入设备驱动结构体指针。在本例中,该结构体为触摸屏输入设备驱动
    lv_indev_data_t *data : LVGL 中的输入设备数据结构体指针。在本例中,该结构体用于存储输入设备的状态和数据,包括当前的触摸状态(按下或释放)以及触摸点的坐标
  • 触摸屏输入设备的回调函数实现

    代码位置:examples\src\LVGL_example.c

    实现方式:主要是通过触摸中断更新触摸屏的触摸状态和触摸点坐标。

    static void touch_callback(uint gpio, uint32_t events)
    {
    if (gpio == TOUCH_INT_PIN)
    {
    CST9217_Read_Data(); // Get coordinate data
    ts_x = CST9217.data[0].x;
    ts_y = CST9217.data[0].y;
    ts_act = LV_INDEV_STATE_PRESSED;
    }
    else if(gpio == SYS_OUT)
    {
    watchdog_reboot(0,0,0);
    }
    }

    static void ts_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data)
    {
    data->point.x = ts_x;
    data->point.y = ts_y;
    data->state = ts_act;
    ts_act = LV_INDEV_STATE_RELEASED;
    }

LVGL 控件布局

在 LVGL 中,我们能够建立各种不同的用户界面。界面的基本组成部分是对象,也称为控件(Widgets),比如按钮(Button)、标签(Label)、图像(Image)、列表(List)、图表或文本区域。在一个界面中,可以同时创建多个控件,并且我们可以设置它们的位置、尺寸、父对象、样式以及事件处理程序等基本属性。

  • LVGL 控件初始化

    代码位置:examples\src\LVGL_example.c

    实现功能:主要用于风格化控件和布局控件。

    void Widgets_Init(void);
  • LVGL 创建图块

    代码位置:examples\src\LVGL_example.c

    实现功能:Tile 视图是一个容器对象,其元素(称为图块)可以按网格形式排列。用户可以通过滑动来在各个图块之间导航。调用 lv_tileview_add_tile(tileview, row_id, col_id, dir) 在第 row_id 行和第 col_id 列上创建一个新图块。dir可以是 LV_DIR_LEFT/RIGHT/TOP/BOTTOM/HOR/VER/ALL 或值,以便通过滑动移动到给定方向的相邻图块。

    //在 (0,0) 创建一个图块,支持向下滑动到 (0,1)
    tile1 = lv_tileview_add_tile(tv, 0, 0, LV_DIR_BOTTOM);
  • LVGL 创建控件

    代码位置:examples\src\LVGL_example.c

    实现功能:创建控件,不同控件需要使用不同的函数接口,可以选择父对象进行创建。

    //创建一个控件,其中 tab1 为该按键的父对象,可以替换为list、title等可以有子对象的控件
    sw = lv_switch_create(tab1);
  • LVGL 控件的对齐定位

    代码位置:examples\src\LVGL_example.c

    实现功能:使控件能够基于参考点进行偏移定位。控件对齐偏移的参考点控件的中心。

    对齐标准: LVGL 库具备内部对齐和外部对齐两种方式。默认以左上角作为原点,向左为水平方向的正方向,向下为垂直方向的正方向。

    //将btn控件定位在中心点向左偏移45个像素
    lv_obj_align(sw, LV_ALIGN_CENTER, -45, 0);
  • LVGL 控件切换字体大小

    代码位置:examples\inc\lv_conf.h、examples\src\LVGL_example.c

    实现功能:在实际运用时一个界面可能需要运用多种字体大小,可以在 lv_conf.h 中使能多种字体大小,且可以设置默认字体大小。设置字体大小时需要对控件风格化,使控件能够按照设置的风格进行渲染。利用 lv_obj_add_style 函数可以实现对控件各个部位在不同状态下的渲染。

    #define LV_FONT_MONTSERRAT_16 1                                // 使能16号字体
    #define LV_FONT_MONTSERRAT_18 1 // 使能18号字体
    #define LV_FONT_DEFAULT &lv_font_montserrat_18 // 设置默认字体大小为18号

    static lv_style_t style_label;
    lv_style_init(&style_label); // 初始化风格
    lv_style_set_text_font(&style_label, &lv_font_montserrat_16); // 设置字体大小为16号
    lv_obj_add_style(label,&style_label,0); // 设置label主题的风格
  • LVGL 控件事件处理

    代码位置:examples\src\LVGL_example.c

    实现功能:在 LVGL 中,可以给控件添加事件处理回调函数,使控件发生被点击、滚动、重绘等事件时,触发事件从而进入事件处理回调函数。在程序中调用 lv_obj_add_event_cb(obj, event_cb, filter, user_data)函数,为控件 obj 添加事件 filter 的处理函数 event_cb,当控件 obj 触发 filter 事件时,系统会自动调用 event_cb 函数。最后一个参数是指向事件中可用的任何自定义数据的指针。

    //为控件 sw 添加事件 LV_EVENT_VALUE_CHANGED 的处理函数 sw_event_cb
    lv_obj_add_event_cb(sw, sw_event_cb,LV_EVENT_VALUE_CHANGED,NULL);

【运行效果】

  • 使用 VSCode 导入并编译 05_LVGL 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 uf2 目录下 05_LVGL.uf2 文件进行快速验证。