RP2350-Touch-AMOLED-1.75 使用教程

RP2350-Touch-AMOLED-1.75

RP2350-Touch-AMOLED-1.75 产品图

本产品是一款微雪 (Waveshare) 设计的高性能、高集成的微控制器开发板。在较小的板型下,板载了 1.75 英寸电容高清 AMOLED 屏、高度集成的电源管理芯片、六轴传感器 (三轴加速度计与三轴陀螺仪)、RTC、低功耗音频编解码芯片等外设,便于开发和集成到产品中。

产品特性

  • 采用 Raspberry Pi 设计的 RP2350A 微控制器芯片
  • 独特的双核、双架构,搭载了双核 ARM Cortex-M33 处理器和双核 Hazard3 RISC-V 处理器,时钟运行频率均高达 150MHz,支持用户在两种架构间灵活切换
  • 内置 520KB 的 SRAM 和 16MB 的片上 Flash,并预留一个 PSRAM 焊盘
  • 采用 Type-C 接口,无需纠结正反插
  • 板载 1.75 英寸电容触摸高清 AMOLED 屏,466 × 466 分辨率,65K 彩色,能清晰地显示彩色图片
  • 内置 CO5300 驱动芯片和 CST9217 电容触控芯片,分别使用 QSPI 和 I2C 接口通信,不占用过多接口引脚资源
  • 板载 3.7V MX1.25 锂电池充放电接口,有利于移动场景使用
  • USB1.1 主机和从设备支持
  • 支持低功耗睡眠和休眠模式
  • 可通过 USB 识别为大容量存储器进行拖放式下载程序
  • 引出 5 个多功能 GPIO 引脚
  • 1 个 SPI,2 个 I2C,2 个 UART,4 个 12 位 ADC,以及 5 个可控 PWM 通道
  • 精确的片上时钟和定时器
  • 内置温度传感器,可实时监测芯片温度
  • 12 个可编程 I/O (PIO) 状态机,用于自定义外设支持

板载资源

RP2350-Touch-AMOLED-1.75 硬件资源 1
  1. RP2350A 双核、双架构处理器,最高 150MHz 运行频率
  2. Debug 接口 方便程序下载与在线调试
  3. PCF85063 RTC 时钟芯片
  4. PWR 电源按键 可控制电源通断,支持自定义功能
  5. MX1.25 2P 扬声器接口 MX1.25 2P 连接器,可用于接入扬声器
  6. MX1.25 锂电池接口 可用于接入 3.7V 锂电池,支持充放电并可调节充电电流
  7. 麦克风 用于音频采集
  8. ES8311 低功耗音频编解码芯片
  9. Type-C 接口 用于程序下载,支持 USB 1.1 主机和从设备模式
  10. IPEX 1 代 GPS 天线座 带 GPS 版本内置 LC76G 模组,外接 GNSS 陶瓷天线
  11. AXP2101 高集成度电源管理芯片
  12. 2.54mm 间距 8PIN 排母 方便外接调试或连接传感器
  13. QMI8658 六轴惯性测量单元(IMU),包含 3 轴陀螺仪和 3 轴加速度计
  14. PSRAM 接口 兼容主流 PSRAM 芯片,用于用户扩展升级
  15. BOOT 按键 复位时按下进入下载模式
  16. Micro SD 卡座 支持 Micro SD 卡扩展存储

接口介绍

RP2350-Touch-AMOLED-1.75 引脚定义

产品尺寸

RP2350-Touch-AMOLED-1.75 产品尺寸
RP2350-Touch-AMOLED-1.75 产品尺寸

开发方式

RP2350-Touch-AMOLED-1.75 支持 MicroPython、C/C++ 和 Arduino 三种编程语言,为开发者提供灵活的选择。您可以根据项目需求和个人偏好,选择合适的开发工具和编程方式:

  • Thonny IDE(MicroPython 开发):Thonny 是一款面向初学者的轻量级 Python 集成开发环境,专为教学场景设计,现已广泛应用于 MicroPython / CircuitPython 开发。界面简洁直观,内置 Python 解释器,支持串口 REPL、代码烧录与调试,配置过程简单。MicroPython 简单易学、无需编译即可运行,非常适合初学者快速上手嵌入式开发。您可以参考 Thonny IDE 开发说明 完成初始设置,教程中提供了详细的环境配置步骤和示例程序。

  • VSCode + Pico SDK(C/C++ 开发):VSCode 是一款功能强大的跨平台代码编辑器。通过安装 Pico VS Code 插件,可快速构建完整的 C/C++ 开发环境。该插件集成了 Pico SDK 工具链、CMake 构建系统、烧录与调试工具,支持图形化操作,开发效率高。C/C++ 开发可充分利用硬件性能,适合对性能要求较高的项目和专业开发者,更适用于复杂的嵌入式应用开发。您可以参考 VSCode 开发说明 完成初始设置,教程中提供了详细的环境配置步骤和示例程序。

  • Arduino IDE(Arduino 开发):Arduino IDE 是一款便捷灵活、易于上手的开源电子原型平台。Arduino 拥有庞大的全球用户社区,提供海量开源代码、项目示例和教程,以及丰富的库资源。这些库封装了复杂功能,让开发者无需深入底层细节即可快速实现各种功能,非常适合快速开发和原型验证,能够大幅缩短开发周期。您可以参考 Arduino IDE 开发说明 完成初始设置,教程中提供了详细的环境配置步骤和示例程序。

    MicroPython 开发

    配置开发环境

    请参考 安装和配置 Thonny IDE 教程 下载安装 Thonny IDE。

    示例程序

    MicroPython 示例程序位于 示例程序包 的 MicroPython 目录中。

    示例程序基础例程说明依赖库
    01_SD挂载 SD 卡-
    02_RTC获取 RTC 数据-
    03_IMU获取六轴传感器数据-
    04_GPS_UART使用 UART 与 GPS 模块通讯,获取 GPS 数据-

    01_SD

    【程序说明】

    • 使用 SPI 与 SD 卡通讯,并将 SD 卡挂载到开发板,挂载成功后,可通过 Thonny 查看和修改 SD 卡中的内容。

    【硬件连接】

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

    【代码分析】

    • sdcard.SDCard(spi, cs, baudrate):创建 SD 卡对象,并将初始化的 SPI 接口与 CS 引脚绑定到 SD 卡驱动中。
    • uos.mount(sd, '/sd'):将 SD 卡文件系统挂载到 /sd 目录下。挂载成功后,用户即可通过 /sd 路径对 SD 卡进行文件读写操作,例如创建、读取或删除文件。

    【运行效果】

    • 通过 thonny 将 01_SD 文件下的所有 py 文件上传到开发板中,并复位。开发板复位后会根据 boot.py 的程序,自动将 SD 挂载到 sd 目录下。

    02_RTC

    【程序说明】

    • 使用 I2C 与 板载 RTC 芯片通讯,设定并读取 RTC 时间数据,同时测试 RTC 中断是否正常。

    【硬件连接】

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

    【代码分析】

    • RTC = PCF85063():创建 RTC 对象。
    • RTC.setDate(weekday, day, month, year):设置 RTC 日期。
    • RTC.setTime(hour, minute, second):设置 RTC 时间。
    • RTC.readTime():读取 RTC 时间。
    • RTC.setAlarm(second, minute, hour, day, weekday):设置 RTC 闹钟。
    • RTC.enableAlarm():使能 RTC 闹钟。

    【运行效果】

    • 使用 thonny 运行 02_RTC 文件下的 py 文件。

    03_IMU

    【程序说明】

    • 使用 I2C 与 板载六轴传感器通讯,读取六轴传感器数据。

    【硬件连接】

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

    【代码分析】

    • IMU = QMI8658():创建 IMU 对象。
    • IMU.Read_XYZ():读取六轴传感器数据。

    【运行效果】

    • 使用 thonny 运行 03_IMU 文件下的 py 文件。

    04_GPS_UART

    【程序说明】

    • 使用 UART 与 板载 GPS 通讯,查询 LC76G 的 NMEA 数据。

    【硬件连接】

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

    【代码分析】

    • gnss_l76b=l76x.L76X():创建 GPS 对象。
    • gnss_l76b.l76x_send_command():发送初始化指令。
    • gnss_l76b.uart_any():判断串口是否有数据。
    • gnss_l76b.uart_receive_byte():接收串口数据。

    【运行效果】

    • 使用 thonny 将 04_GPS_UART 下所有目录和文件上传到开发板,运行 example 文件下的 coordinates_converted.py 文件。


    C/C++ 开发

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

    配置开发环境

    请参考 安装和配置 Pico C/C++ 开发环境教程 下载安装 Pico VS Code。

    示例程序

    C/C++ 示例程序位于 示例程序包 的 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: 背景颜色

    【运行效果】

    • 使用 VS Code 导入并编译 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():播放音乐。

    【运行效果】

    • 使用 VS Code 导入并编译 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():发送指令,读取数据。

    【运行效果】

    • 使用 VS Code 导入并编译 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.hexamples\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);

    【运行效果】

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


    Arduino 开发

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

    配置开发环境

    请参考 安装和配置 Arduino IDE 教程 下载安装 Arduino IDE。

    示例程序

    Arduino 示例程序位于 示例程序包 的 Arduino 目录中。

    示例程序基础例程说明依赖库
    01_GUI液晶 GUI 显示程序-
    02_LVGL液晶 LVGL 显示程序LVGL V8.2

    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: 背景颜色

    【运行效果】

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

    02_LVGL

    【环境配置】

    1. 安装 LVGL 库

      1.点击左侧边栏第三个图标或点击菜单栏中的 “工具” > “管理库。..”
      2.在搜索框中输入 “lvgl”,找到 “lvgl” 库
      3.选择 8.2.0 版本
      4.点击 “INSTALL” 按钮进行安装

    2. 添加配置文件

      1.LVGL 有自己的配置文件,名为 lv_conf.h
      2.进入已安装的 Arduino 库的目录,在这里可以看到我们安装的 lvgl 库目录
      3.下载程序包,将程序包中 Arduino\lv_conf.h 复制到本地 Arduino 的库目录下
      4.最终布局 lv_conf.h 看起来应该是这样的:

      arduino
      |-libraries
      |-lvgl
      |-other_lib_1
      |-other_lib_2
      |-lv_conf.h

    【程序说明】

    • 使用 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.hexamples\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);

    【运行效果】

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

    1. 硬件资料

    开发板设计文件

    2. 技术手册

    数据手册

    3. 官方资料

    4. 开发软件

    5. 示例程序

    售后

    周一-周五(9:30-6:30)周六(9:30-5:30)

    手机:13434470212

    邮箱:services04@spotpear.cn

    QQ:202004841