玩转树莓派:轻松应用PCF8591模拟数字转换器指南
(1)、数模转换概念
一种将二进制数字量形式的离散信号转换成以标准量(或参考量)为基准的模拟量的转换器,简称DAC。通过控制高电压出现的时间与低电压出现的时间的比例进行数模转换。
(2)、PCF8591数模转换模块
PCF8591是单片、单电源低功耗8位CMOS数据采集器件****,上面有光敏电阻、热敏电阻、和电位器,具有4个模拟输入(其中一个为电压模拟输入)、一个模拟输出和一个串行I2C总线接口。3个地址引脚A0、A1和A2用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件。器件的地址、控制和数据通过两线双向I2C总线传输。器件功能包括多路复用模拟输入、片上跟踪和保持功能、8位模数转换和8位数模拟转换。最大转换速率取决于I2C 总线的最高速率。
I2C总线:
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件,然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下,机负责产生定时时钟和终止数据传送。
SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出,需通过上拉电阻接电源VCC。当总线空闲时.两根线都是高电平,连接总线的外同器件都是CMOS(Complementary Metal Oxide Semiconductor互补金属氧化物半导体)器件,输出级也是开漏电路。在总线上消耗的电流很小,因此,总线上扩展的器件数量主要由电容负载来决定,因为每个器件的总线接口都有一定的等效电容。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件,然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下,主机负责产生定时时钟和终止数据传送。
(3)、PCF8591连接方式
左排接口:
AOUT:芯片DA输出接口,输出模拟信号。
AINO:芯片模拟输入接口0
AIN1:芯片模拟输入接口1
AIN2:芯片模拟输入接口2
AIN3:芯片模拟输入接口3
右边接口:
SCL IIC时钟接口:接树莓派的SCL口(接树莓派 I2C1 SCL口)
SDA IIC数字接口:接树莓派的SDA口(接单树莓派 I2C1 SDA口)
GND 模块地:外接地(接树莓派GND)
VCC 电源接口:外接3.3v-5v (接树莓派电源)这里用的是5V。
(4)\器件地址
PCF8591地址字节:
PCF8591采用典型的I2C总线接口器件寻址方法,即总线地址由器件地址、引脚地址和方向位组成。飞利蒲公司规定A/D器件地址为1001。引脚地址为A2A1A0,其值由用户选择,因此I2C系统中最多可接23=8个具有I2C总线接口的A/D器件。地址的最后一位为方向位R/ ,当主控器对A/D器件进行读操作时为1,进行写操作时为0。总线操作时,由器件地址、引脚地址和方向位组成的从地址为主控器发送的第一字节。
查询PCF8591的地址:sudo i2cdetect -y 1
在树莓派终端上使用命令“sudo i2cdetect -y 1”,查询出PCF8591的地址为0x48
(5)、简单输入实验
准备器材:
PCF8591模数转换器模块1 温度传感器模块1 红外寻迹模块*1
之所以选这个传感器,纯粹是因为这两个传感器的方便测试,比如把温度传感器直接放在嘴里就行了!!
线路连接:
温度传感器AO端连接AIN1 电压5V
红外寻迹模块AO端连接AIN3 电压5V
打开通信:
PCF8591模块采用的是I2C(IIC)总线进行通信的,但是在树莓派的镜像中默认是关闭的,在使用该传感器的时候,我们必须首先允许IIC总线通信。
用Python进行实现:
注意程序的执行顺序,这里比较坑!!
import smbus
import time
address = 0x48 # address 器件的地址(硬件地址 由器件决定)
A0 = 0x40 # A0 器件某个端口的地址(数据存储的寄存器)
A1 = 0x41
A2 = 0x42
A3 = 0x43
bus = smbus.SMBus(1) # 开启总线 创建一个smbus实例
# 循环查询
while True:
bus.write_byte(address, A0) # 获取温度传感器的那个端口的数据
# 获得寻迹数据,150是自己测试给的常数,方便查看结果。
value = 150 - bus.read_byte(address)
bus.write_byte(address, A2) # 获红外寻迹模块的那个端口的数据
# 获得温度数据,208是自己测试给的常数,方便查看结果。
value2 = 208 - bus.read_byte(address)
if value > 0:
print("当前测距数值:%1.0f" % (value))
print("地面可能是白色")
elif value < 0:
print("当前测距数值:%1.0f" % (value))
print("地面可能是黑色")
print("当前温度:%1.0f ℃ " % (value2))
time.sleep(1)
(6)、简单输出实验
准备器材:
LED灯1个、220欧姆电阻1个。
线路连接:
LED的正极连接AOUT端
用Python进行实现:
这里我们写的规范一点,符合python的开发流程。程序执行效果类似呼吸灯,这里亮度数值的取值范围为70到120,只是为了方便观察,还有就是0x40这个地址,就是AOUT的地址。
import smbus #在程序中导入“smbus”模块
import time
# for RPI version 1, use "bus = smbus.SMBus(1)"
# 0 代表 /dev/i2c-0, 1 代表 /dev/i2c-1 ,具体看使用的树莓派那个I2C来决定
bus = smbus.SMBus(1) #创建一个smbus实例
#在树莓派上查询PCF8591的地址:“sudo i2cdetect -y 1”
def setup(Addr):
global address
address = Addr
def read(chn): #channel
if chn == 0:
bus.write_byte(address,0x40) #发送一个控制字节到设备
if chn == 1:
bus.write_byte(address,0x41)
if chn == 2:
bus.write_byte(address,0x42)
if chn == 3:
bus.write_byte(address,0x43)
bus.read_byte(address) # 从设备读取单个字节,而不指定设备寄存器。
return bus.read_byte(address) #返回某通道输入的模拟值A/D转换后的数字值
def write(val):
temp = val # 将字符串值移动到temp
temp = int(temp) # 将字符串改为整数类型
#写入字节数据,将数字值转化成模拟值从AOUT输出
bus.write_byte_data(address, 0x40, temp)
if __name__ == "__main__":
setup(0x48)
tmp = 70
is_add = True
while True:
if tmp <= 70:
is_add = True
if tmp >= 120:
is_add = False
if is_add:
tmp += 1
else:
tmp -= 1
write(tmp)
time.sleep(0.1)