用基于树莓派RP2040的嵌入式系统学习平台实现一个模拟电压表
2022寒假在家练 树莓派pico 模拟电压表使用microPython制作
标签
ADC
MicroPython
RP2040
电压表
2022寒假在家练
pvfcd
更新2022-03-11
北方工业大学
2072

树莓派rp2040 主控实现的模拟电压表

 

按惯例,先放代码:


这个仓库中所有文件克隆下来,上载到游戏机中,然后运行adcproject.py

也可以直接把这里的程序全部上载到板子中

 简介

感谢大家观看我制作的项目。目前我是一名大一的学生,平时对电子电路较为感兴趣。但是因为学校暂时还没有学习此类知识,所以对于对应的知识难免有些地方了解不够全面,系统。有什么讲解错误的,还望老师们指正。

FtNkci3lozGADWSYTEYbxn-tptn8

这是我使用树莓派rp2040完成的电压表,显示的是测量2v电压。经过校准,精度还不错,只是最后两位会有几个字的跳动。12位ADC能达到这种精度,我还是很满意的。

下面简单介绍下程序设计思路:

开机后先检测按键是否按下,按下后进校准程序,未按下进主程序。校准程序完成后也会进入主程序。

测量程序会一直读取ADC值,分散到0-3.3区间内,然后传到屏幕上。指针是将ADC值分散到140个区间内,正好是设定表针长度的两倍。然后利用勾股定理算出坐标,传到屏幕上。

画出流程图是这样的

FrnKNQmNnEmiTtOnrvEXADMCP6l2

电路分析

Fu41AYF1B4L8uFFQYKvW4wqIzNmm

电路由xt3406电压转换管为rp2040片内ADC提供电源,ADC部分需要高稳定性的电源供应,xt3406芯片指标很不错,纹波很低,虽然不能和专用的高精度电压芯片相比,但是对于这种精度还是很够用的。

 

Fub3Dwzq0yoQd9_I7LfGc3G-kNjk

ADC输入排针部分,电路板上没有标注丝印,听工作人员说是为了电路板设计美观。第一次测试的时候,因为没发现交互式物料清单这种好东西,我就直接把板子翻了面对照电路图看,结果插错线,不知道连到了什么地方,导致电脑弹出“USB端口上的电泳”,还好没有造成什么损失。不过后期分析电路,发现5v电源线明显粗,于是找对了正确的方向,应该是翻到背面,USB口朝下,才和电路图相匹配

FmCvE5WtRKEB6WUZjYEdPf9vh1fX

模拟摇杆,原理就是两个滑动变阻器组成的分压电路,这为我测试提供了方便。

FkPWvg7PHMvcJSPoxLzGKTdtItVj

显示屏,采用st7789主控,spi总线,片选信号接地,无需特殊操作。还有一根复位线连接在单片机上。

Fi71KcSZ4NKucsAqYqWuTHOxzFE3

quadspi线驱动的flash,由于器件替换的问题,用的Flash实际上是2MB的。使用过程中并未感觉到空间不足的情况。

到这里,我使用的电路部分分析完毕,我还录制了一个更详细的视频,就是这个视频,有兴趣的可以看下。

程序分析

import machine
from machine import ADC, Pin,Timer
import utime
import st7789 as st7789
from fonts import vga2_8x8 as font1
from fonts import vga1_16x32 as font2
import math

插入的库文件,可见使用了ADC,io,定时器这三种硬件,还使用了utime库来控制延时,math库来反解指针位置,st7789库即为板子中提供的示例库(这个库使用起来很慢,但是GitHub上的原作者也说这个库很慢)这个库中,我增加了一个划半圆的函数,即为

    def drawcircle(self, x,y,r,color=WHITE):
        '''画半圆
        传入参数:
        x,y:圆心坐标
        r:半径
        color:颜色
        '''
        a = 0
        b = r
        while (2 * b * b) >= (r * r):
            self.pixel(x + a, y - b,color)
            self.pixel(x - a, y - b,color) 
#           self.pixel(x - a, y + b,color) 
#           self.pixel(x + a, y + b,color)     
#           self.pixel(x + b, y + a,color) 
            self.pixel(x + b, y - a,color) 
            self.pixel(x - b, y - a,color) 
#           self.pixel(x - b, y + a,color)         
            a = a + 1;
            num = ((a * a) + (b * b) )- (r*r);#计算画的点离圆心的距离
            if num > 0:
                b = b - 1
                a = a - 1

这是我从stm32单片机上移植过来的代码,原作者应该是淘宝上的中景园电子。注释掉的两段是画整个圆的代码,释放后即可画出一个圆。

头文件中的font1和font2是两套字库,分别是8*8和16*32大小的字体。

"""pin设置部分"""
pinout = Pin(22, Pin.OUT)
calib = Pin(6, Pin.IN, Pin.PULL_UP)#即为板子上的按键A

io设置部分,使用了一个输入端口,即为板子上的按键a,来作为校准按键,按住此按键开机即可进入校准。还有一个输出端口,是用于输出pwm波的。

'''ADC选择部分'''
# 排针
CH1 = ADC(Pin(26))     
CH2 = ADC(Pin(27))
adc	= 0	

# 摇杆 
# CH1 = ADC(Pin(28))     
# CH2 = ADC(Pin(29))
# adc = 1	

ADC选择部分,通过此段程序的屏蔽和释放,可以切换输入源,以满足可以切换到12Pin扩展排针上的两个模拟通道输入端的要求。

'''st7789设置部分'''
st7789_res = 0
st7789_dc  = 1
disp_width = 240
disp_height = 240
spi_sck=machine.Pin(2)
spi_tx=machine.Pin(3)
spi0=machine.SPI(0,baudrate=40000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
display = st7789.ST7789(spi0, disp_width, disp_height,reset=machine.Pin(st7789_res, machine.Pin.OUT),dc=machine.Pin(st7789_dc, machine.Pin.OUT),xstart=0, ystart=0, rotation=0)

st7789设置部分,设置spi线对应的引脚

'''测试用io口中断驱动程序'''
timer = Timer()
def timer_callback(timer):
	pinout.toggle()
timer.init(period=2000, mode=Timer.PERIODIC , callback=timer_callback)#创建一个定时器,用于反转电平

io口驱动程序,就是一直翻转一个io口的电平,以输出低频率的数字波形。rp2040没有dac,所以没法输出更多样的电压。

def adcread():
	global dmm1,dmm2
	dmm1 = CH1.read_u16()    
	dmm2 = CH2.read_u16()

ADC读取部分,就是调用两个read函数。dmm是digital multimeter的简写,意为数字多用表,未来可能会增加一些外围电路将其改造为数字多用表

def adcshow():
	global dmm1voltshow,dmm2voltshow
	adcread()
	# print("dmm")
	# print(dmm1)
	# print(dmm2)
	if (adc == 0):
		dmm1volt = dmm1*(3.3/ADC0_full_range)
		dmm1voltshow = round(dmm1volt,3)
		dmm2volt = dmm2*(3.3/ADC1_full_range)
		dmm2voltshow = round(dmm2volt,3)
	else:
		dmm1volt = dmm1*(3.3/ADC2_full_range)
		dmm1voltshow = round(dmm1volt,3)
		dmm2volt = dmm2*(3.3/ADC3_full_range)
		dmm2voltshow = round(dmm2volt,3)
	print("volt")
	print(dmm1volt)
	print(dmm2volt)
	display.text(font2,"CH1:",0,10,st7789.WHITE)
	display.text(font2,"CH2:",0,40,st7789.WHITE)
	display.text(font2,str(dmm1voltshow),63,10,st7789.WHITE) 
	display.text(font2,str(dmm2voltshow),63,40,st7789.WHITE)
	# print("show")
	# print(dmm1voltshow)
	# print(dmm2voltshow)
	display.text(font2,"Volts",149,10)
	display.text(font2,"Volts",149,40)

ADC读数展示部分,可以用来展示ADC的读数,程序大概就是把0-65536之间的数值分散到0-3.3之间,然后保留三位小数,再转换成字符串形式,送至屏幕输出。

def draw_point(value):
	'''
   value:ADC读取的电压值,例如32768之类的数字
	46811:adc满量程为3.3v分散到140的区域上,正好是表针的长度
	然后利用勾股定理,计算出y的位置
	'''
	global lastxposi,lastyposi,lastvalue
	tvalue = value*100
	if (value > 0):
		# print(value)
		if (adc == 0):
			xposi = (tvalue//(int((ADC0_full_range/140)*100)))+50
		else:
			xposi = (tvalue//(int((ADC2_full_range/140)*100)))+50
		if xposi != 0:
			yposi = 200-int((math.sqrt(4900-((120-xposi)**2))))
		else:
			yposi = 200
		# print(xposi,yposi)
		display.line(120,200,xposi,yposi,st7789.RED)
		if xposi != lastxposi:
			display.line(120,200,lastxposi,lastyposi,st7789.BLACK)
		lastxposi = xposi
		lastyposi = yposi

表针显示部分,思路就是把ADC读出的数值分散在140个区间内,这个作为直角三角形的底边,斜边长度即为表针长度,这里我设为70像素,使用斜边长度的平方减去指针长度的平方就是高,再加上对应的偏移量就是表针尽头的坐标,把这一组坐标送入屏幕显示函数,就可以帮我画出指针。(没找到公式编辑器,不过这里应该不是很难)

有几个细节,就是为什么传入数据要乘100,因为可以试着使用python计算下

>>> 32768//327.68
99.0
>>> 3276800//32768
100

虽然两组算式结果应该一模一样,但是因为浮点数运算的精度问题,导致了结果偏差较大。于是手动乘100修复它。

if判断是防止除数等于零的报错。本来有对于除数等于零的处理,但是经过测试,除数接近于零的时候,勾股定理反解出的指针位置和除数等于零的指针位置完全相同,所以删去除数等于零的处理。

还有一个保存上一位置的函数,这是用于清屏。因为前文提到过,这个驱动库很慢,所以当ADC数据改变时,我直接在原位置上画一条黑线,以达到变相加速的作用。

			display.text(font2,"CALIB ADC2",10,10,st7789.WHITE)
			while(ADC2.read_u16() <55000):
				if(ADC2.read_u16() > 55000):
					display.text(font2,"WAIT......",10,10,st7789.WHITE)
					utime.sleep_ms(5000)
					ADC2_full_range = ADC2.read_u16()
					print("ADC2",ADC2_full_range)
					break

校准程序,有四段这样的程序,但除了ADC通道不同以外都一样,所以挑一段解释,就是把最大值记录至单片机内。因为测试中发现,ADC最大值经常不同,甚至有一组ADC输入3.3v,ADC只读出了60000多的数据,这是ADC精准度的一大影响。所以使用这个程序修正它。开机时会判断一下a键的电平,是低电平就会进入校准程序。

	while(True):#主循环
		adcshow()
		draw_point(dmm1)

主循环,就是调用两个展示函数。

到此,程序解析完毕。

心得体会

记得第一次参加硬核学堂的活动,是我高二升高三的时候。当时是nxp的一款芯片,好像叫lpc824。当时我高估了我的水平,以为可以很快的做完,但是最终只做了ADC,就被轰去复习了。今年我大学,又看到硬核学堂的活动,当场决定参加。拿到板子后,微信群里的氛围非常好,所有人都在咨询项目相关的内容,也有些人在交流着我听不懂的话题,让我看到了我和大佬的差距。

接下来说下我做的这个项目。首先,为什么选ADC,因为我之前大一上参加了电赛,做的是那个用电器检测的题,对ADC有了一些相关的开发经验。但是后来发现,microPython的开发真的很简单,可以简单的使用。而且microPython调试程序也很方便,不用编译直接跑程序节省了大量的时间。

项目过程中,其实我是拿到板子后现学习的python,python有很多方便的函数库,我之前不知道,本来自己写了类似的,不完美的功能,然后上网一查,有个内置标准函数库就是干这个事情的。还有数学问题(毕竟数学一直很差),那个画圆的函数想了我好久,不过最后的结果还是很满意的。还有调库的问题,一开始我感觉自带的st7789的库太慢,而且没有gui库。而且可用的函数比较少,所以尝试使用GitHub上的microgui开发。总卡在spi总线的部分,虽然有逻辑分析仪,但是也很难定位问题所在,屏幕就是不显示。后来自己完成了一些gui的设计,虽然不是很完美,但最后呈现的效果还是不错的。

最后,感谢硬禾学堂带给我们这么好的活动,有了一个玩转就退钱的正向激励,督促着我在不想干的时候爬起来继续研究这个东西。这次也翻看了电子森林上的许多项目,感觉这个网站真的很神仙,中文的资料,还有完整的解释,让我受益匪浅。

 

附件下载
开源地址.txt
百度盘链接:https://pan.baidu.com/s/1tNoPVDpSRnQeoIsb-Uqejg?pwd=fhh2 提取码:fhh2 GitHub链接:https://github.com/pvfcd/rp2040_adc_mpy
团队介绍
北方工业大学 交通设备与控制工程 21级 于承浩
团队成员
pvfcd
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号