基于STEP Pico制作电压表
一、项目介绍
具体要求:利用板上的电位计调节电压从0-3.3V之间变化,在OLED显示屏上显示电压值,可以以数字的方式,也可以以图形的方式来显示。
实现方式:调节电位计产生0-3.3V之间变化的电压,树莓派Pico内部的ADC对该电压进行采集,得到0-4095之间的数值,经过计算以后对应到相应的电压值,再通过OLED显示屏显示出来。
二、环境配置
1、软件开发工具thonny
官方推荐的IDE工具,页面简洁,操作简便,基础功能齐全,非常适合初学者上手敲代码。唯一缺点是thonny 4.0.1版本似乎将部分源代码挂载到了GitHub上,在官网下载70%时莫名要求GitHub账号和密码,且输入账号后仍无法继续下载,且下载速度奇慢无比。这里我推荐使用pip下载方式,仅需几秒钟即可下载完毕,且配置方式在B站上就很容易搜到,十分方便。
2、硬件开发平台
我是用的是硬禾提供的step pico嵌入式开发平台,这个平台较为基础,适合初学者使用。其核心板管脚图如下所示:
二、实现的功能
利用板上的电位计调节电压,使其在0-3.3V之间变化,并在oled屏幕上显示电压的波形或瞬时值,同时可以通过按键将两者相互转化。
显示当前电压值:
显示一段时间内的电压波形,且波形从右向左运动:
三、程序实现
全部程序使用micropython编写,micropython是python的精简版,目的是运行在单片机这样的性能有限的微控制器上,最小体积仅256K,运行时仅需16K内存。其语法基本上同Python的语法相同,如果对Python掌握得较为熟练的话学习起来将会事半功倍。
1、使用模块介绍
硬禾的老师为我们提供的开源的模块代码,借助这些可以很方便地使我们将更多地精力投入到项目的设计当中去。为了更加方便的编写程序,首先需要在pico中导入一些文件,使我们无需反复查阅管脚标号,其本质上为microPython编写了一些新的module。初始化方式如下所示:
from board import pin_cfg
from button import k1, k2
from oled import oled
from writer import Writer
import ws2812b
import freesans20 # 字体
from time import sleep
from machine import ADC, Pin
其中“board”,“button”,“oled”,“ws2812b”皆为为方便读取管脚值、编写程序而导入的文件,“writer”是为了在oled屏幕上实现字体的设置,“freesans20”为编译为.py文件的字体文件,有利于减小pico内存的占用。
2、记录电压值并绘制波形
def wave_flow(v_64, flag):
if flag != -1:
for i in range(127):
hist[i] = hist[i + 1] # 实现波形连续波动
hist[127] = v_64 # 电压在128*64的oled屏幕上的显示位置
if flag != -1:
oled.fill(0) # clear oled
for i in range(128):
#oled.pixel(i, hist[i], 1)
if i >= 1:
oled.line(i - 1, hist[i - 1], i, hist[i], 1)
oled.show()
'''
定义:line(self, x0, y0, x1, y1, color):
参数:
x0:起点横坐标
y0:起点纵坐标
x1:终点横坐标
y1:终点纵坐标
color:线的颜色
作用是由起点向终点画线,这样的图像绘制得更平滑,但是速度较慢,适用于对实时性要求不高的项目
'''
4、整体程序实现
from board import pin_cfg
from button import k1, k2
from oled import oled
from writer import Writer
import ws2812b
import freesans20
# import font6
from time import sleep
from machine import ADC, Pin
def light_value(l):
return "#{0:02x}{1:02x}{2:02x}".format(l, l, l)
pot = ADC(Pin(pin_cfg.pot))
hist = [0 for i in range(128)]
# 令波形连续波动
def wave_flow(v_64, flag):
if flag != -1:
for i in range(127):
hist[i] = hist[i + 1]
hist[127] = v_64
# 在oled屏显示字符
wri = Writer(oled, freesans20) # Writer(device, font)
def oled_print(flag):
if flag == -1:
oled.fill(0)
voltage = round(pot.read_u16() * 3.3 / 65535.0, 3) # 读取16位数并将范围限定在0~3.3V之间,最多不超过3位小数
wri.set_textpos(oled, 24, 32) # set_textpos(device, row=None, col=None)
wri.printstring(str(voltage) + "v")
oled.show()
flag = 0 # 避免主循环不断对flag赋值,妨碍程序实现
while True:
# v_256 = 255 - int(pot.read_u16() * 255.0 / 65535.0) # ws2812b
v_64 = 63 - int(pot.read_u16() * 63.0 / 65535.0) # oled 颠倒v_64取值,原因是自上而下像素点编号由小到大
v_256 = int(pot.read_u16() * 255.0 / 65535.0) # ws2812b
# v_64 = int(pot.read_u16() * 63.0 / 65535.0) # oled
# 转换显示方式
if flag == -1:
if k2.value() == True: # flag为-1 时,按下k2,将flag置为0,实现复原
flag = 0
if k1.value() == True:
flag = -1
oled_print(flag)
else:
wave_flow(v_64, flag)
# for i in range(127):
# hist[i] = hist[i + 1]
# hist[127] = v_64
print(v_256)
ws2812b.on(12, light_value(v_256))
if flag != -1: # 按下k1,oled屏幕不再显示波形
oled.fill(0) # clear oled
for i in range(128):
#oled.pixel(i, hist[i], 1)
if i >= 1:
oled.line(i - 1, hist[i - 1], i, hist[i], 1)
oled.show()
sleep(0.1)
四、遇到的难题
遇到的难题首先在于相关知识的积累与学习,因为对micropython函数的不熟悉,让我在许多时候无法便捷地使用micropython提供一系列的函数去实现我想要的功能,需要去micropython的HTML文件中一个个检索,这花费了我大量的时间。因为对pico外设及其使用方法的不熟悉,我需要查询raspberry的sdk文件,了解各个外设的使用时所用的函数和需要的参数,这些查询过程是一个初学者(我)必不可少的,但也拖慢了我完成项目的进度。
五、未来的规划
本项目的要求虽然已基本被实现,但我觉得还是有一些地方可以被优化,首先是在测试过程中电压有时会出现细微的波动,在图像上表现为会产生幅度为1个像素的毛刺,我认为这可能同电源本身存在一定的波动有关,因此在测量过程开始前提前做好消抖应该可以有效消除毛刺,提高测量的精度。
其次虽然我在完成本项目时提供了两种读数的方式,但是两种读数方式都各有优劣。如果能够在显示电压波形图像的同时在oled屏幕上显示出坐标轴,或许能够更好地给使用者直观且误差度在允许范围内的读数方式,从而增强电压表的实用性。
另外,或许可以更进一步利用面包板和公母线实现对其他电路中的电压的测量,从而使该电压表具有一定的实用价值,但这至少需要电源、电阻等基本器件以及电路的基本知识,可能实现的价值并不是很大。
六、总结
硬禾举办的的本次活动让我对嵌入式系统有了更加深入的了解,同时极大丰富了我对micropython这一面向微控制器的编程语言的认识,提高了我的实践能力,激发了我学习硬件知识的热情,或许我会进一步学习总线以及Verilog方面的知识,理清硬件模块之间的逻辑关系,在硬件方面的探索与学习更进一步。希望我与硬禾有缘再见。