2024寒假在家练——基于带屏12指神探的离线串口助手以及带温湿度检测的桌面时钟
该项目使用了带屏12指神探,实现了离线串口助手以及带温湿度检测的桌面时钟的设计,它的主要功能为:作为串口接收器以及放在桌面的带时钟和温湿度的小装饰。
标签
显示
开发板
RyanYuang
更新2024-04-09
广东东软学院
196

根据要求完成如下任务:

任务四:基于RP2040的LVGL图形化控制终端其中的电子表

但是看了这个项目任务中还有其他的选项,比如:温度计、万用表这样的选题,结合我自己的需求——经常需要在作品上(一辆小车)上进行调试,我一般不会连接电脑用串口调参数再回到赛道尝试,调试的时间经常是浪费在电脑和赛道之间。作为一个会自己开发项目的学生,我希望能有一个这样硬件平台:

  1. 带屏幕
  2. 带按键
  3. 带串口
  4. 主频比较快的芯片(可以用来做示波器,逻辑分析仪等便携的设备)

来给我开发出我想要的完美的调试助手,通过恰好在寒假,关注到卓大的公众号宣传的寒假在家练的项目中有这样有意思的平台:

它带屏幕、带按键、带拓展IO(串口)和一个也有不少开源项目做逻辑分析仪和示波器的Rp2040芯片,这就是我想要的~

设计思路

在遇到这个项目之前,移植并且使用LVGL对我来说比较困难,因为我过去使用的是ESP32,使用arduino进行LVGL移植的时候也遇到很多问题(屏幕适配,编译失败….),在那时候因为我的知识不是很够也没有很多的时间,这导致我之前移植和学习LVGL的个人小目标流产了。但是这个项目的任务告诉我,LVGL是一定可以在这个平台上运行的因此我开始专注于移植LVGL….

对于这个项目,最开始大概的设计思路是这样的,这些东西一步一步一个一个实现,然后再组合起来…

开发过程

开发工具

C SDK

一开始比较眼高手低,希望能用pico官方的C SDK开发,但是在搭建编译环境的时候发现会出现很多问题,首先是Cmake的问题,他会经常报错,编译会花上很多时间,后续我环境配置完成再开新的工程时还是会出现一样的问题。相当于绕进了一个循环…..,最后我放弃了。

Micropython

使用Micrioython开发,是一件非常简单的事情,也是小白入手的编码方式,因此我放弃了C SDK后毅然选用它来开发。在这之后就开始了LVGL的移植工作。

GUI

LVGL移植

这里我是直接翻LVGl官方的Github来进行rp2040的Mpy移植。

GitHub - lvgl/lv_micropython: Micropython bindings to LVGL for Embedded devices, Unix and JavaScript

在这里他们的操作都是基于linux环境的,因此我搭建了一个WSL来帮助我编译(如果是Windows环境,在make的过程中会出现很多报错,比如python的路径……)

  • WSL的搭建:

如果是windows11你可以直接在

系统自带的Store下载,

可以直接运行ubuntu的虚拟机系统,相比vmware它的速度会更快,当然需要你习惯命令行的操作方式。

完成最后一步的 make编译之后一个elf文件就生成了:

他在你编译的lvgl目录的:下的:firmware.elf中。


Untitled (10).png

这里你只需要按住带屏12指神探的左侧的boot按钮,同时接入PC的usb,就会发现我的电脑里面出现了一个U盘。

没错,那个就是我们的带屏12指神探的真身——rp2040,直接把.elf文件丢进去就可以了。

这时候我们可以新建我们工程的.py文件并且测试lvgl是否移植成功了。

#导入lvgl
import lvgl as lv
from machine import Pin, SPI

如果没有报错,那么恭喜你!!!移植成功,跨出了第一步。

这时候,移植工作已经完成了,但是怎么驱动我们的屏幕呢?

屏幕驱动

首先要搞清楚我们带屏12指神探所带的屏幕是什么驱动芯片?

通过原理图,我发现这里使用的是ST7789


Untitled (11).png一开始我是想去看电子森林的历程并且自己适配这个LVGL的屏幕驱动的,但是突然发现LVGL是可以直接适配ST7789这一款驱动芯片,那就省了很多事情。


Untitled (12).pngST7789的python文件在上图的路径下:

st77xx.py

就是st77系列芯片的驱动文件

st77xx-test.py


Untitled (13).png

箭头所指的地方就是需要更改的,上面上个是更改它的Pin而下面,就是需要不用启动DMA


Untitled (14).png另外下面的st7785的部分(就是框起来的部分)需要注释掉,才可以进行测试。如果屏幕成功显示一些测试的图像,表明我们的屏幕是适配的,可以用的,没问题的。在这之后我们回到自己的.py文件中

就是屏幕的测试文件,在更改了SPI的IO后就可以显示出测试内容。

  • 导入驱动文件
    #导入屏幕驱动
    import st77xx as dis

把驱动文件st77xx.py上传到rp2040中并且导入st77xx

  • 初始化SPI
# SPI初始化
spi_sck = Pin(2)
spi_tx = Pin(3)
spi0 = SPI(0, baudrate=24_000_000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
  • 初始化ST7789
# 显示屏初始化
rp2_dma = None
display = dis.St7789(rot=0, res=(240, 240), spi=spi0, cs=4, dc=1, bl=15, rst=0, rp2_dma=rp2_dma)
print("OK")

这时候我们就已经可以显示内容了:

import lvgl as lv
import st77xx as dis
from machine import Pin, SPI, RTC, Timer, UART
# SPI初始化
spi_sck = Pin(2)
spi_tx = Pin(3)
spi0 = SPI(0, baudrate=24_000_000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)

# 显示屏初始化
rp2_dma = None
display = dis.St7789(rot=0, res=(240, 240), spi=spi0, cs=4, dc=1, bl=15, rst=0, rp2_dma=rp2_dma)
print("OK")

label = lv.label(lv.scr_act())#创建label对象
label.set_text("Welcome...")#设置显示的内容
label.center()#将label居中

欢迎界面

希望做到简洁和具有体验感,第一个scr就是一个简单的welcome界面并且会慢慢消失,最后直接加载第二个时钟界面:

def label_cb():#消失特效的代码,主要是使用time.sleep进行程序阻塞
global Starting_label_opa
while Starting_label_opa > 0:
print(Starting_label_opa)
label.set_style_text_opa(Starting_label_opa, lv.PART.MAIN | lv.STATE.DEFAULT)
Starting_label_opa-=5
time.sleep(0.05)

# Label-Starting
label = lv.label(lv.scr_act())
label.set_text("Welcome...")
label.center()
label_cb()#

lv.disp_load_scr(scr_1)#加载第二个时钟界面

主界面

在上一个画面之后是一个比较复杂的界面,我是使用GuiGuider来协助我编写的。


Untitled (15).png

一开始希望使用不同的屏幕来实现使用拨钮左右切换的效果,但是发现效果非常差,会出现画面撕裂的情况(猜测是没有开启DMA),对于那时候的我来说是不能接受的,因此我是用了tableview的部件,然后把时钟和另外的部件放进去达到了丝滑切换界面的效果.


Untitled (16).png

通过将上面的选项卡隐藏在屏幕外,实现了这样的效果。然后再将每个tab作为其他部件父类,加进去。

screen_1_tabview_1 = lv.tabview(scr_1, lv.DIR.TOP, 50)#创建tableveiw对象
#下面都是对tableview的样式进行设置
screen_1_tabview_1.set_pos(0, -53)
screen_1_tabview_1.set_size(240, 293)
screen_1_tabview_1.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
# Set style for screen_1_tabview_1, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
screen_1_tabview_1.set_style_bg_opa(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_tabview_1.set_style_text_color(lv.color_hex(0x4d4d4d), lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_tabview_1.set_style_text_font(lv.font_montserrat_14, 0)
screen_1_tabview_1.set_style_text_letter_space(2, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_tabview_1.set_style_text_line_space(16, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_tabview_1.set_style_border_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_tabview_1.set_style_radius(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_tabview_1.set_style_shadow_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
# Set style for screen_1_tabview_1, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
style_screen_1_tabview_1_extra_btnm_main_default = lv.style_t()
style_screen_1_tabview_1_extra_btnm_main_default.init()
style_screen_1_tabview_1_extra_btnm_main_default.set_bg_opa(0)
style_screen_1_tabview_1_extra_btnm_main_default.set_border_width(0)
style_screen_1_tabview_1_extra_btnm_main_default.set_radius(0)
screen_1_tabview_1.get_tab_btns().add_style(style_screen_1_tabview_1_extra_btnm_main_default,
lv.PART.MAIN | lv.STATE.DEFAULT)
# Set style for screen_1_tabview_1, Part: lv.PART.ITEMS, State: lv.STATE.DEFAULT.
style_screen_1_tabview_1_extra_btnm_items_default = lv.style_t()
style_screen_1_tabview_1_extra_btnm_items_default.init()
style_screen_1_tabview_1_extra_btnm_items_default.set_text_color(lv.color_hex(0x4d4d4d))
screen_1_tabview_1.set_style_text_font(lv.font_montserrat_14, 0)
screen_1_tabview_1.get_tab_btns().add_style(style_screen_1_tabview_1_extra_btnm_items_default,
lv.PART.ITEMS | lv.STATE.DEFAULT)
# Set style for screen_1_tabview_1, Part: lv.PART.ITEMS, State: lv.STATE.CHECKED.
style_screen_1_tabview_1_extra_btnm_items_checked = lv.style_t()
style_screen_1_tabview_1_extra_btnm_items_checked.init()
style_screen_1_tabview_1_extra_btnm_items_checked.set_text_color(lv.color_hex(0x2195f6))
screen_1_tabview_1.set_style_text_font(lv.font_montserrat_14, 0)
style_screen_1_tabview_1_extra_btnm_items_checked.set_border_width(4)
style_screen_1_tabview_1_extra_btnm_items_checked.set_border_opa(255)
style_screen_1_tabview_1_extra_btnm_items_checked.set_border_color(lv.color_hex(0x2195f6))
style_screen_1_tabview_1_extra_btnm_items_checked.set_border_side(lv.BORDER_SIDE.BOTTOM)
style_screen_1_tabview_1_extra_btnm_items_checked.set_radius(0)
style_screen_1_tabview_1_extra_btnm_items_checked.set_bg_opa(60)
style_screen_1_tabview_1_extra_btnm_items_checked.set_bg_color(lv.color_hex(0x2195f6))
screen_1_tabview_1.get_tab_btns().add_style(style_screen_1_tabview_1_extra_btnm_items_checked,
lv.PART.ITEMS | lv.STATE.CHECKED)
#创建标签
# Create tab_1
screen_1_tabview_1_tab_1 = screen_1_tabview_1.add_tab("Clock")
# Create tab_2
screen_1_tabview_1_tab_2 = screen_1_tabview_1.add_tab("USART")

时钟界面

Untitled (18).png

界面中我们有4种元素,我会分解出来一个个解释

时钟Label

这里我起初因为发现不能显示那么大的字体,就上传了字体的图片上去显示,但是用了一下在开始开发下一个内容的时候发现它占用的内存以及显示的速度也太慢了。经过同个项目的朋友指点,终于把这个字体的问题解决了。

💡 screen_1_label_1.set_style_text_font(lv.font_montserrat_48, 0)

这一句代码是字体放大的关键。

# Create screen_1_label_2
screen_1_label_2 = lv.label(screen_1_tabview_1_tab_1)
screen_1_label_2.set_long_mode(lv.label.LONG.WRAP)
screen_1_label_2.center()
# screen_1_label_2.set_pos(15, 101)
screen_1_label_2.set_size(210, 75)
# Set style for screen_1_label_2, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
screen_1_label_2.set_style_border_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_radius(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_text_color(lv.color_hex(0x000000), lv.PART.MAIN | lv.STATE.DEFAULT)
# screen_1_label_2.set_style_text_font(test_font("montserratMedium", 48), lv.PART.MAIN|lv.STATE.DEFAULT)
screen_1_label_2.set_style_text_letter_space(2, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_text_line_space(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_text_align(lv.TEXT_ALIGN.CENTER, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_bg_opa(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_pad_top(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_pad_right(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_pad_bottom(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_pad_left(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_shadow_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
screen_1_label_2.set_style_text_font(lv.font_montserrat_48, 0)#设置字体
#
rtc_time = rtc.datetime()#获取时间
screen_1_label_2.set_text(str(rtc_time[4]) + ":" + str(rtc_time[5])) # 设置显示内容

温湿度Label

这里我是直接使用GuiGuider进行生成只是设置了显示的位置

#温湿度计
# Create screen_label_4
screen_label_4 = lv.label(screen_1_tabview_1_tab_1)
screen_label_4.set_text("Tem")
screen_label_4.set_long_mode(lv.label.LONG.WRAP)
screen_label_4.set_pos(118, 166)
screen_label_4.set_size(107, 14)
# Set style for screen_label_4, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
screen_label_4.set_style_border_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_radius(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_text_color(lv.color_hex(0x000000), lv.PART.MAIN|lv.STATE.DEFAULT)
#screen_label_4.set_style_text_font(test_font("montserratMedium", 14), lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_text_letter_space(2, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_text_line_space(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_text_align(lv.TEXT_ALIGN.CENTER, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_bg_opa(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_pad_top(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_pad_right(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_pad_bottom(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_pad_left(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_4.set_style_shadow_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)

# Create screen_label_5
screen_label_5 = lv.label(screen_1_tabview_1_tab_1)
screen_label_5.set_text("Hum")
screen_label_5.set_long_mode(lv.label.LONG.WRAP)
screen_label_5.set_pos(0, 166)
screen_label_5.set_size(107, 20)
# Set style for screen_label_5, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
screen_label_5.set_style_border_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_radius(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_text_color(lv.color_hex(0x000000), lv.PART.MAIN|lv.STATE.DEFAULT)
#screen_label_5.set_style_text_font(test_font("montserratMedium", 14), lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_text_letter_space(2, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_text_line_space(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_text_align(lv.TEXT_ALIGN.CENTER, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_bg_opa(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_pad_top(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_pad_right(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_pad_bottom(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_pad_left(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_5.set_style_shadow_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)

# Create screen_label_3
screen_label_3 = lv.label(screen_1_tabview_1_tab_1)
screen_label_3.set_text("50%")
screen_label_3.set_long_mode(lv.label.LONG.WRAP)
screen_label_3.set_pos(10, 186)
screen_label_3.set_size(97, 26)
# Set style for screen_label_3, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
screen_label_3.set_style_border_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_radius(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_text_color(lv.color_hex(0x000000), lv.PART.MAIN|lv.STATE.DEFAULT)
#screen_label_3.set_style_text_font(test_font("montserratMedium", 14), lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_text_letter_space(2, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_text_line_space(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_text_align(lv.TEXT_ALIGN.CENTER, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_bg_opa(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_pad_top(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_pad_right(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_pad_bottom(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_pad_left(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_3.set_style_shadow_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)

# Create screen_label_2
screen_label_2 = lv.label(screen_1_tabview_1_tab_1)
screen_label_2.set_text("36")
screen_label_2.set_long_mode(lv.label.LONG.WRAP)
screen_label_2.set_pos(161, 186)
screen_label_2.set_size(24, 25)
# Set style for screen_label_2, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
screen_label_2.set_style_border_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_radius(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_text_color(lv.color_hex(0x000000), lv.PART.MAIN|lv.STATE.DEFAULT)
#screen_label_2.set_style_text_font(test_font("montserratMedium", 14), lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_text_letter_space(2, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_text_line_space(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_text_align(lv.TEXT_ALIGN.CENTER, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_bg_opa(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_pad_top(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_pad_right(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_pad_bottom(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_pad_left(0, lv.PART.MAIN|lv.STATE.DEFAULT)
screen_label_2.set_style_shadow_width(0, lv.PART.MAIN|lv.STATE.DEFAULT)

Bar

这里我使用了一个Bar对象来实现一个温度计条的效果

Untitled (19).png


#
# A temperature meter example
#
class bar():#创建一个Bar的对象
def __init__(self,pos,size,bar_range,farther):
print(size)
self.style_indic = lv.style_t()

self.style_indic.init()
self.style_indic.set_bg_opa(lv.OPA.COVER)
self.style_indic.set_bg_color(lv.palette_main(lv.PALETTE.RED))
self.style_indic.set_bg_grad_color(lv.palette_main(lv.PALETTE.BLUE))
self.style_indic.set_bg_grad_dir(lv.GRAD_DIR.VER)

self.bar = lv.bar(farther)
self.bar.add_style(self.style_indic, lv.PART.INDICATOR)
self.bar.set_size(size[0],size[1])
self.bar.set_pos(pos[0],pos[1])
self.bar.set_range(bar_range[0],bar_range[1])
def set_value(self, temp):
self.bar.set_value(temp, lv.ANIM.ON)
d.measure()#DHT11获取温度湿度读数
hum = bar((85,150),(10,50),(0,100),screen_1_tabview_1_tab_1)#创建湿度的bar
hum.set_value(d.humidity())#设置湿度Bar的数值
Tempurture = bar((125,150),(10,50),(-20,40),screen_1_tabview_1_tab_1)#创建温度的bar
Tempurture.set_value(d.temperature())#设置温度Bar的数值

本项目的一个亮点设计之一:

静态布局

Untitled (20).png

这个平平无奇的小部件,其实是一个我对于拨钮这样的操作方式的一个小设计,如果我们波动拨钮,他就会按照我们波动的方向旋转,给用户以视觉和触觉的体验。

  • 以下是它的LVGL静态布局代码:
#创建对象以及布局
# ARC
# Create scrPieChart_arc_1
scrPieChart_arc_1 = lv.arc(scr_1)
scrPieChart_arc_1.set_mode(lv.arc.MODE.SYMMETRICAL)
scrPieChart_arc_1.set_range(0, 300)
scrPieChart_arc_1.set_bg_angles(180, 360)
scrPieChart_arc_1.set_angles(180, 360)
scrPieChart_arc_1.set_rotation(0)
scrPieChart_arc_1.set_pos(173, -12)
scrPieChart_arc_1.set_size(80, 80)
# Set style for scrPieChart_arc_1, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
scrPieChart_arc_1.set_style_bg_opa(0, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_border_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_arc_width(5, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_arc_opa(255, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_arc_color(lv.color_hex(0x919395), lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_radius(6, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_pad_top(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_pad_bottom(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_pad_left(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_pad_right(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_shadow_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)

# Set style for scrPieChart_arc_1, Part: lv.PART.INDICATOR, State: lv.STATE.DEFAULT.
scrPieChart_arc_1.set_style_arc_width(5, lv.PART.INDICATOR | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_arc_opa(255, lv.PART.INDICATOR | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_arc_color(lv.color_hex(0x2195f6), lv.PART.INDICATOR | lv.STATE.DEFAULT)

# Set style for scrPieChart_arc_1, Part: lv.PART.KNOB, State: lv.STATE.DEFAULT.
scrPieChart_arc_1.set_style_bg_opa(255, lv.PART.KNOB | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_bg_color(lv.color_hex(0x2195f6), lv.PART.KNOB | lv.STATE.DEFAULT)
scrPieChart_arc_1.set_style_pad_all(0, lv.PART.KNOB | lv.STATE.DEFAULT)

# Create scrPieChart_arc_2
scrPieChart_arc_2 = lv.arc(scr_1)
scrPieChart_arc_2.set_mode(lv.arc.MODE.SYMMETRICAL)
scrPieChart_arc_2.set_range(0, 300)
scrPieChart_arc_2.set_bg_angles(180, 360)
scrPieChart_arc_2.set_angles(225, 315)
scrPieChart_arc_2.set_rotation(0)
scrPieChart_arc_2.set_pos(168, -16)
scrPieChart_arc_2.set_size(92, 91)
# Set style for scrPieChart_arc_2, Part: lv.PART.MAIN, State: lv.STATE.DEFAULT.
scrPieChart_arc_2.set_style_bg_opa(0, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_border_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_arc_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_radius(6, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_pad_top(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_pad_bottom(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_pad_left(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_pad_right(20, lv.PART.MAIN | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_shadow_width(0, lv.PART.MAIN | lv.STATE.DEFAULT)

# Set style for scrPieChart_arc_2, Part: lv.PART.INDICATOR, State: lv.STATE.DEFAULT.
scrPieChart_arc_2.set_style_arc_width(0, lv.PART.INDICATOR | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_arc_opa(255, lv.PART.INDICATOR | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_arc_color(lv.color_hex(0x2195f6), lv.PART.INDICATOR | lv.STATE.DEFAULT)

# Set style for scrPieChart_arc_2, Part: lv.PART.KNOB, State: lv.STATE.DEFAULT.
scrPieChart_arc_2.set_style_bg_opa(255, lv.PART.KNOB | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_bg_color(lv.color_hex(0x2195f6), lv.PART.KNOB | lv.STATE.DEFAULT)
scrPieChart_arc_2.set_style_pad_all(2, lv.PART.KNOB | lv.STATE.DEFAULT)

在ARC这个对象中,里面有几个子元素:

  • [LV_PART_MAIN](红色的部分)
  • image.png
  • [LV_PART_INDICATOR](红色的部分)
  • image.png
  • [LV_PART_KNOB](红色部分)
  • image.png

这里是三个部分,我通过将其中一个Arc的Main和Indicator的OPA(透明度)设为255隐藏了这两个部分然后留下Knob这个元素,和另外一个单独消除knob的Arc组合做到了这样的效果。示意图如下:

image.png

  • 如图,橙色的是消除Indicate和Main属性但是留下Knob元素的Arc对象。橙色是为了让它会滑动的轨迹可视化,方面我们看。绿色的是Knob按钮属性。
  • 蓝色的那个就是消除Knob的Arc对象。

动画

动画部分我使用了了定时器来进行,这样子既不会占用太多的CPU时间也能让动画做到丝滑。

image.png

ARC会根据这个值转动,这个值的加减在定时中断里面进行

#tim = Timer(period=5, mode=Timer.PERIODIC, callback=time0_irq)初始化定时器
# 定时器0中断函数5ms进入一次
def time0_irq(time0):
global key
global target
key.proceTime += 1
if key.proceTime == 10:
key.proceTime = 0
#上面是在进行按键消抖
if (KeyL.value() == 0):#如果往左拨
target = LEFT_TARTGET#让目标值设为最左边的Arc数值
elif (KeyR.value() == 0):#如果往右拨
target = RIGHT_TARGET#让目标值设为最右边的Arc数值
else:
target = MID_TARGET#放在中间
arc_animation()#动画
key.key_proce()
# Arc 动画
def arc_animation():
cur = scrPieChart_arc_1.get_value()
if cur < target:#对Arc的值进行方向判断
cur = cur + 3#+3的速度比较合适,其实可以加快点也可以加少点
scrPieChart_arc_1.set_value(cur)
scrPieChart_arc_2.set_value(cur)
else:
cur = cur - 3
scrPieChart_arc_1.set_value(cur)
scrPieChart_arc_2.set_value(cur)

串口界面

以下是串口界面的简单示意图,使用了LVGL的textarea对象来实现这个功能,通过接收串口数据,加入到textarea中,至于换行,是串口接收到的数据来决定的。

image.png

如图:红色的部分是一个Scr屏幕对象,而蓝色的部分就是Textarea部分他们都是TableVeiw的子对象,通过这样的组合来实现这部分的内容。

  • textarea静态布局创建
# Create Uart Text area
ta = lv.textarea(screen_1_tabview_1_tab_2)
ta.set_pos(-15, 22)
ta.set_size(245, 220)
ta.set_style_bg_color(lv.color_hex(0xb8b8b8), lv.PART.MAIN | lv.STATE.DEFAULT) #设置背景为黑色
ta.set_style_bg_opa(100, lv.PART.MAIN | lv.STATE.DEFAULT)
# ta.add_state(lv.STATE.FOCUSED) # To be sure the cursor is visible
ta.add_text(">>")
  • 串口初始化
#初始化UART
uart1 = UART(1, baudrate=9600, tx=Pin(24), rx=Pin(25))
  • 串口数据处理以及显示
while (1):
if uart1.any() > 0:
buf = uart1.read(1)
ta.add_text(buf)
if buf == b'\\n':
print(buf)
ta.add_text(">>")

DHT11温湿度传感器

该项目的温湿度读取使用的传感器是DHT11数字温湿度传感器。

是一款已经校准数字信号输出的温湿度符合传感器,使用的是OneWire单总线。其温度测量范围为 0~50℃,误差在±2℃;湿度的测量范围为 20%~90%RH(Relative Humidity 相对湿度—指空气中水汽压与饱和水汽压的百分比),误差在±5%RH。

image.png

引脚说明

Pin

名称

注释

1

VDD

供电 3-5.5V

2

GND

接地,电源负极

3

DATA

串行数据,单总线

对于这个传感器的具体协议我们不需要去了解,因为Micorpython已经封装了这个

#初始化
>>> import dht
>>> import machine
>>> d = dht.DHT11(machine.Pin(24))
#然后使用以下命令测量和读取它们的值:
>>> d.measure()
>>> d.temperature()
>>> d.humidity()

RTC实时时钟

RTC 是一个独立的时钟,用于跟踪日期和时间。

这部分在Micropython官方文档中写的非常详细

rtc = machine.RTC()#RTC初始化
rtc.datetime((2020, 1, 21, 2, 10, 32, 36, 0))#设置data
print(rtc.datetime())#获取RTC时间

未来的开发计划

  • 该作品还不能在用户层修改RTC的时间,这个功能还需要进一步开发。
  • UART的界面设计还需要加入波特率的选择项。
  • 硬件层面需要连接USB才能使用,因此可以在硬件上更新电池板加装电池。
  • 使用ESP32替换PIco作为主控还能开发无线功能,做到无线调参。
  • 增加示波器以及逻辑分析仪的功能,使其成为电子人的瑞士军刀。

谢谢大家观看我的介绍

附件下载
一款基于Rp2040的离线串口助手以及带有温湿度检测的桌面时钟.docx
所有代码烧录进去rp2040.zip
所有的程序代码
firmware.uf2
固件文件(务必烧录)
团队介绍
来自广东的一个人的小团队
团队成员
RyanYuang
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号