RP2040-Touch-LCD-1.28
说明
| ||||||||||||||||||||
| ||||||||||||||||||||
产品概述
RP2040-Touch-LCD-1.28 是一款微雪 (Waveshare) 设计的低成本,高性能的微控制器开发板。 在较小的板型下,板载了 1.28 英寸电容触摸 LCD 屏、锂电池充电芯片、六轴传感器(三轴加速度计与三轴陀螺仪)等外设,方便开发并嵌入应用到产品中。
产品特性
- 采用了Raspberry Pi研发的 RP2040 微控制器芯片
- 搭载了双核 ARM Cortex M0 + 处理器,运行频率高达 133MHz 灵活时钟
- 内置了 264KB 的 SRAM 和 4MB 的片上 Flash
- 采用Type-C接口,紧跟时代潮流,无需纠结正反插
- 板载 1.28 英寸电容触摸 LCD 屏,240×240 分辨率,65K 彩色
- 板载锂电池充放电接口,有利于移动场景使用
- USB1.1 主机和设备支持
- 支持低功耗睡眠和休眠模式
- 可通过 USB 识别为大容量存储器进行拖放式下载程序
- 精确的片上时钟和定时器
- 温度传感器
- 片上加速浮点库
- 通过 SH1.0 连接器引出 6 个 GPIO
产品参数
LCD参数 | |||
触摸芯片 | CST816S | 触摸接口 | I2C |
显示芯片 | GC9A01A | 显示接口 | SPI |
分辨率 | 240(H)RGB x 240(V) | 显示尺寸 | Φ32.4mm |
显示面板 | IPS | 像素大小 | 0.135(H)x0.135(V)mm |
IMU参数 | |||
传感器名称 | QMI8658 | ||
加速度计特性 | 分辨率:16 位 量程 (可选):±2、±4、±8、±16g | ||
陀螺仪特性 | 分辨率:16 位 量程 (可选):±16、±32、±64、±128、±256、±512、 ±1024、±2048°/sec |
注意事项
- 圆形触摸屏的边缘,触摸灵敏和准确度会有所下降,这是圆形触摸屏的结构导致的。
引脚分布
尺寸图
Pico快速上手
固件下载
| ||
|
基础介绍
MicroPython系列
安装Thonny IDE
为了方便在电脑上使用MicroPython开发Pico/Pico2板,建议下载Thonny IDE
- 下载Thonny IDE并按照步骤安装,安装包均为Windows版本,其他版本请参考Thonny官网
- 安装完成之后,第一次要配置语言和主板环境,由于我们是为了使用Pico/Pico2,所以注意主板环境选择Raspberry Pi 选项
- 配置Micrpython环境及选择Pico/Pico2端口
- 先将Pico/Pico2接入电脑,左键点击Thonny右下角的配置环境选项--》选择configture interpreter
- 在弹出的窗口栏中选择MicroPython(Raspberry Pi Pico),同时选择对应的端口
烧录固件
- 点击ok后返回到Thonny主界面,下载对应的固件库并烧录到设备中,然后点击停止按钮,在Shell窗口中即可显示当前使用到的环境
- 注意:烧录Micropython官方提供的Pico2固件可能导致无法识别设备,请使用下方或程序包中的固件
- Pico/Pico2在windows下载固件库方法: 按住BOOT键后连接电脑后,松开BOOT键,电脑会出现一个可移动磁盘,将固件库复制进去即可
- RP2040/RP2350在windows下载固件库方法: 连接电脑后,同时按下BOOT键跟RESET键,先松开RESET键再松开BOOT键,电脑会出现一个可移动磁盘,将固件库复制进去即可(用Pico/Pico2的方式也可以)
讲解视频
【MicroPython】machine.Pin类函数详解
【MicroPython】machine.PWM类函数详解
【MicroPython】machine.ADC类函数详解
【MicroPython】machine.UART类函数详解
【MicroPython】machine.I2C类函数详解
【MicroPython】machine.SPI类函数详解
【MicroPython】rp2.StateMachine类函数详解
C/C++系列
对于 C/C++,建议使用 Pico VS Code 进行开发,这是一款 Microsoft Visual Studio Code 扩展,旨在让您在为 Raspberry Pi Pico 系列开发板创建、开发和调试项目时更加轻松。无论您是初学者还是经验丰富的专业人士,此工具都可以帮助您自信而轻松地进行 Pico 开发。下面我们介绍如何安装该扩展并使用。
- 官网教程:https://www.raspberrypi.com/news/pico-vscode-extension/
- 本教程适用于树莓派Pico、Pico2与本公司开发的RP2040、RP2350系列开发板
- 开发环境默认以 Windows11 为例,其他环境请参考官网教程进行安装
安装VSCode
-
首先,点击下载 pico-vscode 程序包,解压并打开程序包,双击安装 VSCode
注意:如果已安装 vscode 注意检查版本是否为 v1.87.0 或更高版本
安装扩展
-
点击扩展,选择从 VSIX 安装
-
选择 vsix 后缀的软件包,点击安装
-
随后 vscode 会自动安装 raspberry-pi-pico 及其依赖扩展,可以点击刷新查看安装进度
-
右下角显示完成安装,关闭 vscode
配置扩展
-
打开目录 C:\Users\用户名,将整个 .pico-sdk 拷贝至该目录
-
拷贝完成
-
打开 vscode,对 Raspberry Pi Pico 扩展中各个路径进行配置
配置如下:Cmake Path: ${HOME}/.pico-sdk/cmake/v3.28.6/bin/cmake.exe Git Path: ${HOME}/.pico-sdk/git/cmd/git.exe Ninja Path: ${HOME}/.pico-sdk/ninja/v1.12.1/ninja.exe Python3 Path: ${HOME}/.pico-sdk/python/3.12.1/python.exe
新建工程
-
配置完成,测试新建工程,输入工程名、选择路径后点击 Creat 创建工程
测试官方示例,可以点击工程名旁的 Example 进行选择
-
创建工程成功
-
选择SDK版本
-
选择 Yes 进行高级配置
-
选择交叉编译链,13.2.Rel1 适用 ARM 核,RISCV.13.3 适用 RISCV 核,这里根据您的需求任意选择其中一个即可
-
CMake 版本选择 Default(前面配置的路径)
-
Ninja 版本选择 Default
-
选择开发板
-
点击 Complie 进行编译
-
成功编译出 uf2 格式文件即可
导入工程
- 导入工程的 Cmake 文件不能有中文(包括注释),否则可能导致导入失败
-
导入自己的工程需要在 Cmake 文件中加一行代码,才能正常切换 pico 和 pico2,否则即使选择 pico2,编译得到的固件仍是适用于 pico 的
set(PICO_BOARD pico CACHE STRING "Board type")
更新扩展
Arduino IDE 系列
安装Arduino IDE
-
首先到Arduino官网下载Arduino IDE的安装包。
-
这里选择仅下载就可以了。
-
下载完成后,点击安装。
注意:安装过程中会提示你安装驱动,我们点击安装即可
Arduino IDE中文界面
在Arduino IDE中安装Arduino-Pico Core
-
打开Arduino IDE,点击左上角的文件,选择首选项
-
在附加开发板管理器网址中添加如下链接,然后点击OK
https://github.com/earlephilhower/arduino-pico/releases/download/4.0.2/package_rp2040_index.json
注意:如果您已经有ESP32板URL,您可以使用逗号分隔 URL,如下所示:https://dl.espressif.com/dl/package_esp32_index.json,https://github.com/earlephilhower/arduino-pico/releases/download/4.0.2/package_rp2040_index.json
-
点击工具>开发板>开发板管理器>搜索pico,由于我的电脑已经安装过了,所以显示已安装
国内用户
- 因为网络原因,国内用户连接github并不稳定,我们另外提供了一份安装包,可以跳过在线的过程
- 若已经成功配置了pico环境,可以直接跳过本章
-
下载rp2040压缩包,将解压的rp2040文件夹复制到如下路径下
C:\Users\[username]\AppData\Local\Arduino15\packages
第一次上传程序
-
按住Pico板上的BOOTSET按键,将pico通过Micro USB线接到电脑的USB接口,待电脑识别出一个可移动硬盘(RPI-RP2)后,松开按键。
- 下载程序,打开arduino\PWM\D1-LED路径下的D1-LED.ino
-
点击工具>端口,记住已有的COM,不需要点击这个COM(不同电脑显示的COM不一样,记住自己电脑上已有的COM)
-
用USB线将驱动板和计算机连接起来,再点击工具>端口,第一次连接选择uf2 Board,上传完成后,再次连接就会多出一个COM口
-
点击工具>开发板>Raspberry Pi Pico>Raspberry Pi Pico或Raspberry Pi Pico 2
- 设置完成后,点击向右箭头上传将程序
- 如果期间遇到了问题,需要重新安装或者更换Arduino IDE版本时,卸载Arduino IDE需要卸载干净,卸载软件后需要手动删除C:\Users\[name]\AppData\Local\Arduino15这个文件夹内的所有内容(需要显示隐藏文件才能看到) 再重新安装
开源例程
MircoPython视频例程(github)
MicroPython固件/Blink例程(C)
树莓派官方C/C++示例程序 (github)
树莓派官方micropython示例程序 (github)
Arduino官方C/C++示例程序 (github)
GUI API详解
如果您以前使用过我们的SPI屏幕应该会对这份例程比较熟悉
C
底层硬件接口
我们进行了底层的封装,由于硬件平台不一样,内部的实现是不一样的,如果需要了解内部实现可以去对应的目录中查看
在DEV_Config.c(.h)可以看到很多定义,在目录:c\lib\Config
- 数据类型:
#define UBYTE uint8_t #define UWORD uint16_t #define UDOUBLE uint32_t
- 模块初始化与退出的处理:
void DEV_Module_Init(void); void DEV_Module_Exit(void); 注意: 1.这里是处理使用液晶屏前与使用完之后一些GPIO的处理。
- GPIO读写:
void DEV_Digital_Write(UWORD Pin, UBYTE Value); UBYTE DEV_Digital_Read(UWORD Pin);
- SPI写数据
void DEV_SPI_WriteByte(UBYTE Value);
上层应用
对于屏幕而言,如果需要进行画图、显示中英文字符、显示图片等怎么办,这些都是上层应用做的。这有很多小伙伴有问到一些图形的处理,我们这里提供了一些基本的功能
在如下的目录中可以找到GUI,在目录:c\lib\GUI\GUI_Paint.c(.h)
在如下目录下是GUI依赖的字符字体,在目录:c\lib\Fonts
- 新建图像属性:新建一个图像属性,这个属性包括图像缓存的名称、宽度、高度、翻转角度、颜色
void Paint_NewImage(UWORD *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color) 参数: image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针; Width: 图像缓存的宽度; Height: 图像缓存的高度; Rotate: 图像的翻转的角度 Color: 图像的初始颜色;
- 选择图像缓存:选择图像缓存,选择的目的是你可以创建多个图像属性,图像缓存可以存在多个,你可以选择你所创建的每一张图像
void Paint_SelectImage(UBYTE *image) 参数: image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
- 图像旋转:设置选择好的图像的旋转角度,最好使用在Paint_SelectImage()后,可以选择旋转0、90、180、270
void Paint_SetRotate(UWORD Rotate) 参数: Rotate: 图像选择角度,可以选择ROTATE_0、ROTATE_90、ROTATE_180、ROTATE_270分别对应0、90、180、270度
- 图像镜像翻转:设置选择好的图像的镜像翻转,可以选择不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像。
void Paint_SetMirroring(UBYTE mirror) 参数: mirror: 图像的镜像方式,可以选择MIRROR_NONE、MIRROR_HORIZONTAL、MIRROR_VERTICAL、MIRROR_ORIGIN分别对应不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像
- 设置点在缓存中显示位置和颜色:这里是GUI最核心的一个函数、处理点在缓存中显示位置和颜色;
void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color) 参数: Xpoint: 点在图像缓存中X位置 Ypoint: 点在图像缓存中Y位置 Color: 点显示的颜色
- 图像缓存填充颜色:把图像缓存填充为某颜色,一般作为屏幕刷白的作用
void Paint_Clear(UWORD Color) 参数: Color: 填充的颜色
- 图像缓存部分窗口填充颜色:把图像缓存的某部分窗口填充为某颜色,一般作为窗口刷白的作用,常用于时间的显示,刷白上一秒
void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color) 参数: Xstart: 窗口的X起点坐标 Ystart: 窗口的Y起点坐标 Xend: 窗口的X终点坐标 Yend: 窗口的Y终点坐标 Color: 填充的颜色
- 画点:在图像缓存中,在(Xpoint, Ypoint)上画点,可以选择颜色,点的大小,点的风格
void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD 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(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD 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(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD 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(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD 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(UWORD Xstart, UWORD Ystart, const char Ascii_Char, sFONT* Font, UWORD Color_Foreground, UWORD 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(UWORD Xstart, UWORD Ystart, const char * pString, sFONT* Font, UWORD Color_Foreground, UWORD 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(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Foreground, UWORD 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(UWORD Xpoint, UWORD Ypoint, double Nummber, sFONT* Font, UWORD Digit,UWORD Color_Foreground, UWORD 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(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font, UWORD Color_Background, UWORD 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: 背景颜色
QMI8658
- 模块初始化
unsigned char QMI8658_init(void);
- 读取数据
void QMI8658_read_xyz(float acc[3], float gyro[3], unsigned int *tim_count); 参数: float acc[3]: 存储加速度值的数组,表示为浮点数,包含三个元素,分别是 X、Y 和 Z 轴上的加速度 float gyro[3]: 存储陀螺仪值的数组,表示为浮点数,包含三个元素,分别是 X、Y 和 Z 轴上的角速度
Python
底层硬件接口
- 模块初始化
def __init__(self)
- 发送命令
def write_cmd(self, cmd)
- 发送数据
def write_data(self, buf)
- 调节背光
def set_bl_pwm(self,duty)
画图GUI
- 导入库
import framebuf
- 创建对象
自定义类 LCD_1inch28 继承 MicroPython 中的 framebuf.FrameBuffer 类,这个类提供了许多绘制图像的方法,我们先创建 LCD_1inch28 类对象
LCD = LCD_1inch28()
- 画线
LCD.line(x1, y1, x2, y2, color) 参数: x1, y1: 起始点的 x 和 y 坐标 x2, y2: 终点的 x 和 y 坐标 color: 线的颜色
- 画框
LCD.fill_rect(x1, y1, w, h, color) 参数: x1, y1: 矩形左上角的 x 和 y 坐标 w, h: 矩形的宽度和高度 color: 矩形的填充颜色
- 绘制文本
LCD.text(str, x, y, color) 参数: str: 显示文本 x, y: 文本左上角的 x 和 y 坐标 color: 文本的颜色
- 更改文本大小
LCD.write_text(str, x, y, size, color) 参数: size: 该函数与 LCD.text 的区别是支持自定义字体大小,该参数用于指定字体大小
- 显示
LCD.show()
QMI8658
- 创建对象
qmi8658=QMI8658()
- 读取并解析传感器的XYZ数据
xyz=qmi8658.Read_XYZ() 返回值: xyz[0]~xyz[2]: 函数返回一个数组,前三个元素表示 X、Y 和 Z 轴上的加速度 xyz[3]~xyz[5]: 这三个元素表示 X、Y 和 Z 轴上的角速度
应用示例
LVGL 示例程序
示例效果
本例显示的效果为三个可以通过触摸屏滑动或六轴传感器抬动切换的界面。
- 第一个界面
- 第二个界面
- 第三个界面
示例简介
本例用于测试 LVGL 控件交互、样式美化等, LVGL 的具体开发请参考LVGL开发文档。
源码结构
- LVGL 库的源码位于工程文件夹的 ./lib/lvgl ,使用的版本号为8.1,二次开发请参考对应版本的开发文档。
- LVGL 库的相关设置在工程文件夹的 ./examples/inc/lv_conf.h 中,可以设置显示刷新频率、系统占用数据等。
- LVGL 库的应用代码位于工程文件夹的 ./examples/src/LVGL_example.c 。
实现功能
- 本例使用 DMA 传输颜色数据到 SPI 总线,降低 CPU 的利用率,在进行简单交互时可以控制 CPU 的占用率在 20% 以下,内存占用在 30% 以下。
- 本例系统时钟为 270MHz ,设置 SPI 的外围时钟频率与系统时钟一致,同时使用 LVGL 库的双缓冲区机制,在进行一个缓冲区的数据传输时另一个缓冲区进行渲染,保证了动画的流畅度。
- 本例使用触摸屏与六轴传感器两路输入设备,其中六轴模拟器利用陀螺仪数据模拟为编码器输入设备,可以应用于多个交互场景。
编译运行
- 进入源码目录,如果 build 目录已经存在,则可以直接进入。如果没有,执行:
mkdir build
- 进入 build 目录,并添加SDK的地址:
cd build export PICO_SDK_PATH=../../pico-sdk
注意:pico-sdk 的路径需要按照实际情况进行调整
- 执行 cmake ,自动生成 Makefile 文件:
cmake ..
- 执行 make 生成可执行文件,然后在终端中输入:
make
等待编译好后将生成的 .uf2 格式的文件复制到pico中即可。
源码解析
LVGL 初始化
- LVGL 库的初始化函数
- 实现功能:主要用于初始化 LVGL 所需的硬件和结构体变量。
void LVGL_Init(void);
- LVGL 库核心结构体变量定义
- 定义功能:*LVGL 库的初始化流程主要就是对几个 LVGL 核心结构体变量的初始化,LVGL库的运行依靠核心结构体变量。
- 定义方式: buf0 和 buf1 大小设置为屏幕显示面积的一半是为了实现 LVGL 双缓冲机制,能够在降低大面积刷屏锯齿的同时有效提高屏幕刷新率;在使用单缓冲区时最好设置为屏幕显示面积的10%,可以有效降低系统占用但在大面积图像刷新时会出现较明显的锯齿。
// LVGL static lv_disp_draw_buf_t disp_buf; //LVGL显示缓冲区 static lv_color_t buf0[DISP_HOR_RES * DISP_VER_RES/2];//LVGL颜色数据缓冲区0 static lv_color_t buf1[DISP_HOR_RES * DISP_VER_RES/2];//LVGL颜色数据缓冲区1 static lv_disp_drv_t disp_drv; //LVGL显示驱动 static lv_indev_drv_t indev_ts; //LVGL触摸屏输入设备驱动 static lv_indev_drv_t indev_en; //LVGL六轴传感器模拟编码器输入设备驱动 static lv_group_t *group; //编码器控件组
- LVGL 库的初始化函数实现
- 实现功能:根据设计需求完善 LVGL 库核心结构体变量。
- 实现方式: LVGL 库的编码器输入设备拥有编辑模式和浏览模式,在浏览模式下触发切换事件就会在控件组内切换选中的控件,触发确认事件进入选中控件的编辑模式。所以让六轴传感器模拟编码器时必须要引入 lv_group_t 结构体变量作为六轴传感器的控件组。
// 初始化LVGL核心 lv_init(); // 初始化LVGL显示缓冲区结构体变量disp_buf lv_disp_draw_buf_init(&disp_buf, buf0, buf1, DISP_HOR_RES * DISP_VER_RES / 2); 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); // 初始化触摸屏输入设备 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); // 初始化编码器输入设备 lv_indev_drv_init(&indev_en); indev_en.type = LV_INDEV_TYPE_ENCODER; indev_en.read_cb = encoder_read_cb; lv_indev_t * encoder_indev = lv_indev_drv_register(&indev_en); group = lv_group_create(); lv_indev_set_group(encoder_indev, group);//将编码器控制设备添加控件组
- LVGL 库运行实现
- 实现功能:LVGL 库定时调用函数 lv_tick_inc 来通知LVGL过去的时间,以便 LVGL 能够更新其内部的时间状态,处理与时间相关的任务,例如动画、定时器等。在主函数的循环中还需要调用 lv_task_handler 函数来处理一些设定好的任务。
- 实现方式:需要确保 lv_task_handler 的优先级低于 lv_tick_inc 的优先级,所以在本例中 lv_tick_inc 在定时器回调函数中调用。
add_repeating_timer_ms(5, repeating_lvgl_timer_callback, NULL, &lvgl_timer);//每5ms调用一次定时器回调函数 static bool repeating_lvgl_timer_callback(struct repeating_timer *t) { lv_tick_inc(5); return true; } int main() { ... while(1) { lv_task_handler(); DEV_Delay_ms(5); } }
LVGL 显示
- LVGL 显示回调函数
- 实现功能:主要完成图像在刷新区的绘制。
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输入读取地址将数据传输到SPI总线,完成图像的绘制
- LVGL 显示颜色设置
- 设置目的:由于 lv_color_t 结构体在默认状态下所构建的像素颜色储存方式与本例需要传输的数据不一致,直接进行传输会导致显示的图像出现色差。
- 设置方式:在 lv_conf.h 文件夹中修改设置改变颜色的储存方式。
#define LV_COLOR_16_SWAP 1
- LVGL 显示刷新率设置
- 设置方式:在 lv_conf.h 中还能设置显示缓冲区刷新频率的时间,可以修改这个定义来改变屏幕的刷新时间。
#define LV_DISP_DEF_REFR_PERIOD 10 /*[ms]*/
- LVGL 显示回调函数实现
- 实现方式:在本例中为了最大化降低处理器的利用率,使用 DMA 来进行颜色数据的传输,设置 color_p 作为读地址,SPI 总线的输出数据寄存器为写地址。
static void disp_flush_cb(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p) { LCD_1IN28_SetWindows(area->x1, area->y1, area->x2 , area->y2);//设置图像的显示区域 dma_channel_configure(dma_tx, &c, &spi_get_hw(LCD_SPI_PORT)->dr, //SPI总线的输出数据寄存器地址 color_p, //待刷新的颜色数据数组地址 ((area->x2 + 1 - area-> x1)*(area->y2 + 1 - area -> y1))*2, true //设置完毕后立刻进行传输 ); }
- LVGL 刷新完成通知实现
- 实现功能:每一次图像刷新完成后都需要通知 LVGL 核心,以便 LVGL 准备下个刷新图像的渲染。
- 实现方式:本例在 DMA 传输完成中断服务函数中通知 LVGL 图像刷新完成,如果使用阻塞通知的机制就无法利用双缓冲机制来提高刷新速度。
static void dma_handler(void) { if (dma_channel_get_irq0_status(dma_tx)) { dma_channel_acknowledge_irq0(dma_tx); lv_disp_flush_ready(&disp_drv); } }
LVGL 输入
- LVGL 的输入设备回调函数
- 实现功能:主要用于更新输入事件。
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 中的输入设备数据结构体指针。在本例中,该结构体用于存储输入设备的状态和数据,包括当前的触摸状态(按下或释放)以及触摸点的坐标 static void encoder_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 中的输入设备数据结构体指针。在本例中,该结构体用于存储输入设备的状态和数据,包括当前的切换事件(上抬或下抬)以及确定事件(右抬)
- LVGL 调用输入设备回调函数的频率设置
- 设置方式: LVGL 默认每 30ms 调用一次输入设备回调函数来更新输入设备所触发的事件,可以在 lv_conf.h 中进行设置。
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
- 触摸屏输入设备的回调函数实现
- 实现方式:主要是通过触摸中断更新触摸屏的触摸状态和触摸点坐标。
static void touch_callback(uint gpio, uint32_t events) { if (gpio == Touch_INT_PIN) { CST816S_Get_Point();//更新Touch_CTS816全局变量 ts_x = Touch_CTS816.x_point; ts_y = Touch_CTS816.y_point; ts_act = LV_INDEV_STATE_PRESSED; } } 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; }
- 六轴传感器输入设备回调函数实现
- 操作方式:模拟编码器作为输入设备,上下抬触发切换事件,右抬触发确定事件。
- 实现方式:由于六轴传感器的数据变换较为频繁,为了减少误操作的情况采用定时多次采样的方式来更新设备的切换事件和确定事件。
static bool repeating_imu_diff_timer_callback(struct repeating_timer *t);//每隔50ms更新一次输入事件 { get_diff_data();//多次采样识别输入事件更新全局变量encoder_diff和encoder_act return true; } static void encoder_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data) { data->enc_diff = encoder_diff; data->state = encoder_act; }
LVGL 控件布局
- LVGL 控件初始化函数
- 实现功能:主要用于风格化控件和布局控件。
void Widgets_Init(void);
- LVGL 控件创建函数
- 实现功能:创建控件,不同控件需要使用不同的函数接口,可以选择父对象进行创建。
lv_obj_t *btn = lv_btn_create(lv_scr_act()); //创建一个控件,其中lv_scr_act层为该按键的父对象,可以替换为list、title等可以有子对象的控件
- LVGL 控件的对齐定位
- 实现功能:使控件能够基于参考点进行偏移定位。控件对齐偏移的参考点控件的中心。
- 对齐标准: LVGL 库具备内部对齐和外部对齐两种方式。默认以左上角作为原点,向左为水平方向的正方向,向下为垂直方向的正方向。
- LVGL 控件的风格化
- 实现功能:使控件能够按照设置的风格进行渲染。利用 lv_obj_add_style 函数可以实现对控件各个部位在不同状态下的渲染。
static lv_style_t style_base; lv_style_init(&style_base); //初始化风格 lv_style_set_bg_color(&style_base, lv_palette_main(LV_PALETTE_LIGHT_GREEN)); //设置背景颜色 lv_style_set_border_color(&style_base, lv_palette_darken(LV_PALETTE_LIGHT_GREEN, 3));//设置边界的颜色 lv_style_set_border_width(&style_base, 2); //设置编辑的宽度 lv_style_set_radius(&style_base, 10); //设置倒角的尺寸 lv_style_set_shadow_width(&style_base, 10); //设置阴影的宽度 lv_obj_add_style(btn,&style_base,0); //设置btn主题的风格,0可以替换为位置和状态
- LVGL 控件组添加函数
- 实现功能:将 LVGL 控件并入控件组,使编码器输入设备可以进入编辑模式对控件的状态进行修改。
lv_group_add_obj(group, btn);//将btn控件添加到控件组group中
资料
配套资料
示例程序
原理图与位号图
3D 图纸
数据手册
官方资料
树莓派官方文档
- Raspberry Pi Pico入门学习MicroPython编程书籍(英文版)
- 树莓派相关书籍下载
- Raspberry Pi Pico原理图
- Pico引脚分布图
- Pico入门使用手册
- Pico C SDK使用手册
- Pico Python SDK使用手册
- Pico数据手册
- RP2040数据手册
- RP2040硬件设计参考手册
树莓派开源例程