logo头像

学如逆水行舟

一起玩转树莓派(8)——树莓派模数/数模转换实践(2)

一起玩转树莓派(8)——树莓派模数/数模转换实践(2)

在上一篇博客中,我们介绍了在树莓派中使用模数转换芯片的基本方法,如果你对上一篇博文中介绍的内容已经有了深入的理解,那后面的应用试验对你来说将非常容易。如果不是,那么我建议你先将之前介绍的内容在研究一下:

https://my.oschina.net/u/2340880/blog/5142788

现在,如果你决定继续本篇博客的学习,那么我认为你已经了解了PCF8591芯片的基本用法,明白了PCF8591的接线方式,I2C总线的基本工作原理以及PCF8591的设置命令的意义和读数据的方法。

一、读取外部传感器的模拟信号

在本系列博客的上一篇文章中,我们通过读取PCF8591实验模块自带的可调节电压输出、光敏传感器和热敏传感器实现了对当前环境信息的的读取,除了可以读取当前设置的输出电压外,我们还可以获得当前的环境的温度和亮度。PCF8591实验模块自带的传感器可以让我们很方便的进行实验,其实在实际应用中,更多时候我们需要从AIN0到AIN3引脚来获取外接传感器的模拟信号。现在,我们尝试下通过AIN0和AIN1两个输入引脚来获取外接光敏和热敏传感器的数据。

1.关于实验所使用的传感器

我们这里的小实验将使用光敏和热敏传感器。

光敏模块

光敏传感器能够感应光纤的明暗变化,其实现此功能的核心在于光敏电阻,只做光敏电阻的常用材料有硫化镉、硫化铝等。这些材料在特定波长的光波照射下,其阻值会产生明显的变化。本次实验,我们使用的光敏传感器模块如下图所示:

可以看到,此模块的核心是一个光敏电阻,提供了可调节灵敏度的功能单元和两个LED指示灯,其中一个LED是电源指示灯,接通电源后会亮,另一个LED灯是电平指示灯,当光亮达到阈值时,输出引脚输出低电平,此LED灯亮,当光亮度较暗时,输出引脚输出高电平,此LED灯不亮。我们再看此模块的4个引脚:

VCC:电源引脚

GND:接地引脚

DO:数字信号输出引脚(高低电平)

AO:模拟信号输出引脚

热敏模块

本次实验我们使用的热敏模块的功能与上面将的光敏模块类似,如下图所示:

此热敏模块同样包含两个LED指示灯、灵敏度调节单元和4个引脚,引脚如下:

VCC:电源引脚

GND:接地引脚

DO:数字信号输出引脚(高低电平)

AO:模拟信号输出引脚

2.实验连线

本次实验,我们使用PCF8591读取光敏和热敏传感器的模拟信号,将其转换成数字信号被树莓派程序处理,同时,我们使用树莓派的GPIO端口来读取传感器本身输出的数字信号,首先,我们先确定要使用的PCF8591的输入引脚和要使用的树莓派GPIO引脚。

PCF8591输入引脚使用:AINO和AIN1,其中AINO读取光敏模拟信号,AIN1读取热敏模拟信号。

GPIO输入引脚使用:GPIO17和GPIO18(BCM编码方式),其中17引脚读取光敏数字信号,18引脚读取热敏数字信号。

PCF8591连线:

PCF8591 树莓派功能引脚
SCL SCL
SDA SDA
GND GND
VCC 5V

光敏传感器:

光敏传感器 树莓派/PCF8591
VCC 树莓派3.3V
GND 树莓派GND
DO 树莓派GPIO11(物理引脚)
AO PCF8591 AIN0

热敏传感器:

热敏传感器 树莓派/PCF8591
VCC 树莓派3.3V
--- ---
GND 树莓派GND
--- ---
DO 树莓派GPIO12(物理引脚)
--- ---
AO PCF8591 AIN1
--- ---

连线最终如下图所示:

和之前相比,我们这次直接在树莓派上连接了3个元件,连线也复杂了很多,只要按照上面的表格,注意引脚的正确即可。

3.编写程序

步入正题,先上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#coding:utf-8

#SMBus (System Management Bus,系统管理总线)
import smbus #在程序中导入“smbus”模块
import RPi.GPIO as GPIO
import time

bus = smbus.SMBus(1) #创建一个smbus实例


# 通过PCF8591读取模拟信号

# 数据亮度的模拟数据
def readLight():
#发送一个控制字节到设备 表示要读取AIN0通道的数据
bus.write_byte(0x48,0x40)
bus.read_byte(0x48) # 空读一次,消费掉无效数据
return bus.read_byte(0x48) # 返回某通道输入的模拟值A/D转换后的数字值

def readTemperature():
#发送一个控制字节到设备 表示要读取AIN1通道的数据
bus.write_byte(0x48,0x41)
bus.read_byte(0x48) # 空读一次,消费掉无效数据
return bus.read_byte(0x48) # 返回某通道输入的模拟值A/D转换后的数字值

# 通过GPIO读取数字信号

# 设置使用的引脚编码模式
GPIO.setmode(GPIO.BOARD)
# 光敏模块的数字输出引脚 BCM 17
LP = 11
# 热敏模块的数字输出引脚 BCM 18
TP = 12
# 引脚初始化
GPIO.setup(LP, GPIO.IN)
GPIO.setup(TP, GPIO.IN)

while True:
print('--------分割线----------')
print('亮度数字信号:', GPIO.input(LP))
print('亮度模拟信号:', readLight())
print('温度数字信号:', GPIO.input(TP))
print('温度模拟信号:', readTemperature())
time.sleep(2)

上面的代码有着比较详尽的注释,这里我们无需多说,在树莓派上运行此代码,即可观察到控制台的数据输出。

二、使用操纵杆外设控制圆球移动

如果你顺利完成了上面的实验,先别急着庆祝,你会发现,和本系列前面几篇博客的内容较比,到目前为止我们并没有介绍新的知识,同时也没有做什么新颖的事情。的确如此,但是通过上面实验的练习,可以帮助你更深入的理解数模/模数转换的应用场景,并且让你能够更加灵活的对I2C总线与通用GPIO串口结合进行使用。下面我们要来做一些好玩的事情了,不知道你小时候是否有玩过“大把机”,这是一种摇杆游戏机,摇杆可以朝上下左右4个方向转动,也可以从中间按下。通常,上下左右用来控制游戏人物的行动方向,按下用来进行人物跳跃。现在,我们要来做一个简单的游戏,为树莓派连接操纵杆,控制游戏程序页面上圆球的行为,其中方向控制圆球的移动,按下操纵杆则使圆球变色。

此实验所使用的操纵杆如下图所示:

可以看到,此元件有5个引脚:

GND:接地引脚

+5V:5V电源引脚

VRX:横向坐标模拟信号输出引脚

VRY:纵向坐标模拟信号输出引脚

SW:按钮数字信号输出引脚

操作杆内部实际上封装了双向的电阻器,其阻值会根据摇杆的方向变动产生变化,从而影响引脚信号的产生变化。

1.进行连线

我相信,现在连线对你来说应该是最简单的工作了。操纵杆有模拟信号输出同时也有数字信号输出,我们依然需要结合PCF8591与树莓派GPIO一起使用。关于PCF8591的接线上面有介绍,这里不再重复。操作杆的接线方式如下:

操纵杆 树莓派/PCF8591
GND 树莓派GND
+5V 树莓派5.5V
VRX PCF8591 AIN0
VRY PCF8591 AIN1
SW 树莓派GPIO 11(物理引脚)

2.编写代码

对于本实验来说,有涉及到UI开发,我们依然采用Python自带的Tkinter库,其有很好的移植性,并且其提供了Canvas画布,我们可以灵活的渲染所需要的图形。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#coding:utf-8

# 导入UI模块
import tkinter as Tkinter

#SMBus (System Management Bus,系统管理总线)
import smbus #在程序中导入“smbus”模块
import RPi.GPIO as GPIO # 导入树莓派GPIO模块
import time # 导入定时器模块
import threading


# 主页面设置
top = Tkinter.Tk()
top.geometry('500x300')
top.title("操纵杆控制圆球")

# 当前圆球的坐标
currentX = 0
currentY = 0
# 当前圆球的颜色是否红色
currentColor = True

# 进行窗口的初始化
canvas = Tkinter.Canvas(top, width=500, height=300, borderwidth=0, highlightthickness=0)
canvas.grid()

# 进化画布的初始化
circle = canvas.create_oval(currentX, currentY, 100, 100, fill="red", outline="")

# 定义移动圆球的方法
def moveCircle(c, x, y):
global currentX, currentY
moveX = x
moveY = y
if x >= 0:
if x + currentX > 400:
moveX = 400 - currentX
currentX = 400
else:
currentX += x
else:
if x + currentX < 0:
moveX = -currentX
currentX = 0
else:
currentX += x
if y >= 0:
if y + currentY > 200:
moveY = 200 - currentY
currentY = 200
else:
currentY += y
else:
if y + currentY < 0:
moveY = -currentY
currentY = 0
else:
currentY += y
canvas.move(c, moveX, moveY)

# 定义改变圆球颜色的方法
def changeColor(c):
global currentColor
canvas.itemconfig(c, fill= 'red' if currentColor else 'blue')
currentColor = not currentColor


bus = smbus.SMBus(1) #创建一个smbus实例

# 通过PCF8591读取模拟信号

# 摇杆X引脚的模拟数据
def readX():
#发送一个控制字节到设备 表示要读取AIN0通道的数据
bus.write_byte(0x48,0x40)
bus.read_byte(0x48) # 空读一次,消费掉无效数据
return bus.read_byte(0x48) # 返回某通道输入的模拟值A/D转换后的数字值

# 摇杆Y引脚的模拟数据
def readY():
#发送一个控制字节到设备 表示要读取AIN1通道的数据
bus.write_byte(0x48,0x41)
bus.read_byte(0x48) # 空读一次,消费掉无效数据
return bus.read_byte(0x48) # 返回某通道输入的模拟值A/D转换后的数字值

# 通过GPIO读取数字信号

# 设置使用的引脚编码模式
GPIO.setmode(GPIO.BOARD)
# 按键使用引脚 BCM 17
BTN = 11
# 引脚初始化 设置下拉高电平
GPIO.setup(BTN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# 创建定时器函数,用来检查摇杆动作
def fun_timer():
global timer
x = readX()
y = readY()
press = GPIO.input(BTN)
print('X:', x)
print('Y:', y)
print('按钮:', press)

if x <= 10:
moveCircle(circle, -10, 0)
if x >= 245:
moveCircle(circle, 10, 0)
if y <= 10:
moveCircle(circle, 0, -10)
if y >= 245:
moveCircle(circle, 0, 10)

timer = threading.Timer(0.2, fun_timer)
timer.start()

timer = threading.Timer(0.2, fun_timer)
timer.start()

# 定义GPIO输入端口的回调
def btnCallback(channel):
if not GPIO.input(channel):
changeColor(circle)

# 添加输入引脚电平变化的回调函数
GPIO.add_event_detect(BTN, GPIO.FALLING, callback=btnCallback, bouncetime=200)

top.mainloop()

上面的代码有些长,但是有详尽的注释,关于UI开发方面的内容不是我们本系列博客的重点,这里我们不做过多介绍。moveCircle函数是核心的圆球移动函数,内部通过边界判定逻辑可以确保圆球不会移动到视图界面外。changeColor方法用来修改圆球的颜色,这里我们让每次按键后在红绿颜色间进行切换。readX和readY函数我们无需做过多介绍了,其通过PCF8591的AIN0和AIN1来传输摇杆的横纵坐标信号。GPIO的相关操作我们也非常熟悉了,我们通过注册回调函数来监听操作杆按钮按下的行为。

在树莓派上运行上面的代码,尝试操作下,感受下使用操纵杆控制页面元素的喜悦吧。

3.一点扩展

观察上面的示例代码,你会发现,我们使用了一些临界值来作为触发方向动作的阈值,例如10,245这种,这是因为PCF8591是8位的数模转换模块,即其转换出的数字量在0-255之间(包括0和255),对于本实验来说,我们并没有让摇杆元件传输的模拟量发挥正真的作用,想一下,你是否能够根据操作杆的旋转程度来调整圆球移动的速度呢?动手试试吧!

专注技术,懂的热爱,愿意分享,做个朋友

QQ:316045346