RaspberryPi 4B 使用 PCF8591 8位AD和DA转换器
作者:互联网
模数/数模转换在实际应用中非常有用。如何使用树莓派操作挂载到 PCF8591 I²C 线路上的设备?实际上和其他 I²C 设备操作并无差异。首先来学习 PCF8591 的使用方法,然后通过树莓派控制 PCF8591 芯片。
一、 PCF8591 预备知识
PCF8591 是一个单片集成、单独供电、低功耗、8-bit CMOS 数据获取器件。PCF8591 具有 4 个模拟输入、1 个模拟输出和 1 个串行 I²C 总线接口。PCF8591 的 3 个地址引脚 A0, A1 和 A2 可用于硬件地址编程,允许在同个 I²C 总线上接入 8 个器件,而无需额外的硬件。在 PCF8591 器件上输入输出的地址、控制和数据信号都是通过两线双向 I²C 总线以串行的方式进行传输。
特征
-
单独供电
-
工作电压范围 2.5V~6V
-
低待机电流
-
通过 I²C 总线串行输入/输出
-
通过 3 个硬件地址引脚编址
-
采样率由 I²C 总线速率决定
-
4 个模拟输入可编程为单端型或差分输入
-
自动增量通道选择
-
模拟电压范围:VSS~VDD
-
片上跟踪保持电路
-
8-bit 逐次逼近 A/D 转换器
-
带一个模拟输出的乘法 DAC
以下为 PCF8591 的引脚信息
引脚名称 | 含义 |
---|---|
AIN0~AIN3 | 模拟信号输入端。 |
A0~A2 | 引脚地址端。 |
VDD、Vss | 电源端。(2.5~6V) |
SDA、SCL | I²C 总线的数据线、时钟线。 |
OSC | 外部时钟输入端,内部时钟输出端。 |
EXT | 内部、外部时钟选择线,使用内部时钟时 EXT 接地。 |
AGND | 模拟信号地。 |
AOUT | D/A 转换输出端。 |
VREF | 基准电源端。 |
PCF8591 芯片所能接收的地址包含固定部分和可编程部分。可编程部分必须根据地址引脚 A0,A1 和 A2 来设置,在 I²C 总线协议中地址必须是起始条件后作为第一个字节发送,地址字节的最后一位用来设置对目标地址的读或写。地址字节格式如下所示:
发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。控制寄存器的高半字节用于允许模拟输出,和将模拟输入编程为单端或差分输入。低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置 1,每次 A/D 转换后通道号将自动增加。
如果自动增量(auto-increment)模式是使用内部振荡器的应用中所需要的,那么控制字中模拟输出允许标志应置 1。这要求内部振荡器持续运行,因此要防止振荡器启动延时的转换错误结果。模拟输出允许标志可以在其他时候复位以减少静态功耗。
选择一个不存在的输入通道将导致分配最高可用的通道号。所以,如果自动增量(auto-increment)被置 1,下一个被选择的通道将总是通道 0。两个半字节的最高有效位(即 bit 7 和 bit 3)是留给未来的功能,必须设置为逻辑 0。控制寄存器的所有位在上电复位后被复位为逻辑 0。D/A 转换器和振荡器在节能时被禁止。模拟输出被切换到高阻态。
二、PCF8591 模块
原理图如下
从原理图不难看出模块集成了电位计(INPUT0)、光敏电阻(INPUT1)和热敏电阻(INPUT2)作为输入端,同时将 A0、A1 和 A2 都接地,也就是置位 0。另外 INPUT 和 AIN 是通过跳线帽连接的,可以根据需要设置。D3 LED 在上电以后就会发光,作为电源指示灯。D4 LED 是由 AOUT 控制,也就是通过 D4 LED 的明暗程度来反映模拟输出。SCL 和 SDA 用来挂载 I²C。电阻 R7 和 R8 是典型的 I²C 设备上拉电阻。芯片参考电路设计建议在电源和参考电压输入端加上去耦电容(10μF),这反映在了原理图上的 C1 和 C2。这是为了防止额外的地线和电源噪声,以及减小数字信号对模拟信号路径的串扰。
不看最后的读写位,不难得出 PCF8591 模块的 I²C 地址为 0x1001 000,也就是 0x48。
三、smbus2
先给树莓派安装 smbus2 模块,方便接下来通过 smbus2 来读写 I²C 设备(PCF8591 模块)。
pip install smbus2
根据 Putty 的终端输出不难看到 smbus2-0.4.1 已经安装,接下来就可以在 python 中导入该模块使用了。
pi@raspberrypi:~/test $ pip install smbus2
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting smbus2
Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0xb54bc490>: Failed to establish a new connection: [Errno 101] \xe7\xbd\x91\xe7\xbb\x9c\xe4\xb8\x8d\xe5\x8f\xaf\xe8\xbe\xbe',)': /packages/c8/bf/62ef029fb7077fc87c3539f7365859bccc6cedb2bb20796b737b788c8d09/smbus2-0.4.1-py2.py3-none-any.whl
Downloading https://files.pythonhosted.org/packages/c8/bf/62ef029fb7077fc87c3539f7365859bccc6cedb2bb20796b737b788c8d09/smbus2-0.4.1-py2.py3-none-any.whl
Installing collected packages: smbus2
Successfully installed smbus2-0.4.1
使用之前先来学习一番 smbus2 提供的 API,可以直接去网站查看英文资料:https://pypi.org/project/smbus2/
这个库是为了在纯 Python 中替换 smbus-cffi/smbus-python。smbus2 是(另一个)Python -smbus 包的纯 Python 实现。
设计一开始就有两个目标:
- smbus 的替代品,语法必须相同;
- 与 pysmbus 等其他纯 Python 实现相比,更广泛地使用固有的 I²C 结构体和联合。通过这样做,获得更完整的特性和更容易扩展。
目前支持的特性有:
获得 I²C 功能(I2C_FUNCS)
SMBus报文错误检查 (PEC)支持
read_byte
write_byte
read_byte_data
write_byte_data
read_word_data
write_word_data
read_i2c_block_data
write_i2c_block_data
write_quick
process_call
read_block_data
write_block_data
block_process_call
I2c_rdwr 组合写/读事务,重复启动
它是在 Python 2.7 上开发的,但在 Python 3 中无需任何修改即可工作 3.X。
例一 读一个字节
from smbus2 import SMBus
# 打开 i2c 总线 1,从地址 80 读取一个字节,偏移量为 0
bus = SMBus(1)
b = bus.read_byte_data(80, 0)
print(b)
bus.close()
例二 使用 with 读一个字节
from smbus2 import SMBus
with SMBus(1) as bus:
b = bus.read_byte_data(80, 0)
print(b)
例三 写一个字节
from smbus2 import SMBus
with SMBus(1) as bus:
# 写一个字节到地址 80,偏移量为0
data = 45
bus.write_byte_data(80, 0, data)
四、操作 PCF8591 模块
使用下面的 demo 前先要将树莓派 4B 的 I²C 支持打开。
将 PCF8591 模块接入树莓派 4B。
树莓派 | PCF8591 模块 |
---|---|
SCL | SCL |
SDA | SDA |
GND | GND |
3.3V | VCC |
双色 LED 模块 | PCF8591 模块 |
---|---|
S | AOUT |
GND | GND |
程序首先读取通道 0、1 和 2 上的输入,然后将 0 通道的输入值作为输出写出。也就是将电位计的输入值写入到 AOUT,控制绿灯的明暗程度。
#!/usr/bin/env python3
import time
from smbus2 import SMBus
def do(addr):
while True:
vcc_in = read(addr, 0x40)
print('AIN0 = ' + str(vcc_in))
print('AIN1 = ' + str(read(addr, 0x41)))
print('AIN2 = ' + str(read(addr, 0x42)))
with SMBus(1) as bus:
bus.write_byte_data(addr, 0, vcc_in)
time.sleep(0.01)
def read(addr, ctl_byte):
with SMBus(1) as bus:
bus.write_byte(addr, ctl_byte)
return bus.read_byte_data(addr, 0)
if __name__ == "__main__":
try:
do(0x48)
except KeyboardInterrupt:
print("KeyboardInterrupt")
标签:AD,read,bus,smbus2,DA,RaspberryPi,PCF8591,byte,data 来源: https://blog.csdn.net/tyyj90/article/details/118070818