pinball_exp_rp5/drv_adc.py
2026-06-11 19:37:42 +08:00

1006 lines
49 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
ADS124S08 ADC 驱动模块 — 树莓派 5 用户空间 SPI 驱动
本模块实现了 ADS124S08 模数转换器ADC的核心功能
- 寄存器读写操作
- 通过 GPIO 控制芯片上电时序的设备初始化
- PGA可编程增益放大器配置
- 输入多路复用器选择(单端或差分输入)
- 数据速率和转换模式设置
- DRDY数据就绪引脚处理
- 单次/连续转换模式
SPI 通信简介:
SPISerial Peripheral Interface串行外设接口是一种同步串行通信协议
使用四根信号线SCLK时钟线、MOSI主设备输出/从设备输入)、
MISO主设备输入/从设备输出、CS片选线低电平有效
SPI 是全双工通信主设备树莓派发送时钟信号的同时主设备和从设备ADC
可以同时发送和接收数据。
引脚接线表(树莓派 5
- SPI 总线:/dev/spidev0.0bus=0, device=0
- DRDY 引脚数据就绪指示低电平表示有数据可读GPIO25
- 上电阶段 1 控制引脚(先拉高,给 ADC 供电的第一部分GPIO22、GPIO17
- 上电阶段 2 控制引脚100ms 后拉高,给 ADC 供电的第二部分GPIO23、GPIO27
- 保持低电平引脚初始化期间强制保持低电平GPIO24、GPIO18
为什么要分两步上电:
ADS124S08 芯片的模拟电源和数字电源需要按顺序启动,如果同时上电可能
导致芯片内部状态不确定。先启动第一部分电源Stage 1等待 100ms 让电源稳定后,
再启动第二部分电源Stage 2可以确保芯片可靠启动。
上电时序流程:
1. 将阶段 1 的 GPIO 引脚拉高(输出高电平),开启第一阶段供电,等待 100ms
2. 将阶段 2 的 GPIO 引脚拉高,开启第二阶段供电
3. 发送复位命令,让 ADC 芯片重新启动
4. 配置寄存器PGA 增益、输入多路复用器、数据速率等)
ADC 芯片数据手册参考链接:
https://www.ti.com.cn/product/cn/ADS124S08?keyMatch=ADS124S08&tisearch=universal_search
"""
# 从 __future__ 模块导入 annotations 特性。
# 这允许在方法的类型注解中使用字符串形式(如 -> "SomeClass"),而不是直接引用类名,
# 从而避免在类定义内部引用自身或其他尚未完全定义的类时出现 NameError 错误。
from __future__ import annotations
# 从 Python 标准库 signal 模块导入 pause 函数。
# pause() 会让程序进入等待状态,直到接收到信号(如 SIGINT即按 Ctrl+C才会继续执行。
# 在这里用于在主程序中保持程序持续运行,不退出。
from signal import pause
# 导入 spidev 库。spidev 是树莓派上用户空间操作 SPI 总线的 Python 库,
# 它封装了 Linux SPI 设备文件(如 /dev/spidev0.0)的 ioctl 系统调用,
# 让我们可以方便地通过 SPI 协议与外部设备(如 ADS124S08 ADC进行数据收发。
import spidev
# 从 gpiozero 库导入 DigitalOutputDevice数字输出设备和 Button按钮/输入设备)。
# DigitalOutputDevice 用于控制 GPIO 引脚输出高电平或低电平,比如控制 ADC 芯片的电源开关。
# Button 用于检测 GPIO 引脚的电平变化(从高到低或从低到高),
# 在这里用于检测 ADC 的 DRDY数据就绪引脚是否变低。
from gpiozero import DigitalOutputDevice, Button
# 导入整个 gpiozero 库(上面导入的是其中的特定类,这里导入整个库以备需要其他功能)。
import gpiozero
# 导入 Python 标准库中的 time 模块用于执行延时sleep、获取当前时间等操作。
# 在本驱动中time.sleep() 用于在 GPIO 上电步骤之间等待电源稳定,
# 以及在等待 ADC 完成转换时等待足够的时间。
import time
# =============================================================================
# ADS124S08 命令码定义
# 下面这些是发给 ADC 芯片的命令字节,每个命令都是一个 1 字节8 位)的值。
# 使用时通过 SPI 的 MOSI 线发送给 ADCADC 收到后执行对应操作。
# =============================================================================
# 空操作No OPeration发给 ADC 后它什么也不做,通常用于占位或填充 SPI 时钟周期
ADS124S08_CMD_NOP = 0x00
# 唤醒命令,让处于休眠(断电)模式的 ADC 芯片恢复到正常工作状态
ADS124S08_CMD_WAKEUP = 0x02
# 断电命令PoWeR DoWN让 ADC 芯片进入低功耗休眠模式,以节省电能
ADS124S08_CMD_PWRDWN = 0x04
# 复位命令RESET让 ADC 芯片重新启动,所有寄存器恢复到默认值
ADS124S08_CMD_RESET = 0x06
# 启动转换命令START告诉 ADC 开始进行模数转换
ADS124S08_CMD_START = 0x08
# 停止转换命令STOP告诉 ADC 停止当前的模数转换
ADS124S08_CMD_STOP = 0x0a
# 系统失调校准命令SYstem OCal Calibration对整个系统进行偏移量校准
ADS124S08_CMD_SYOCAL = 0x16
# 系统增益校准命令SYstem Gain Calibration对整个系统进行增益校准
ADS124S08_CMD_SYGCAL = 0x17
# 自失调校准命令SelF OCal Calibration只对 ADC 自身进行偏移量校准
ADS124S08_CMD_SFOCAL = 0x19
# 读取数据命令Read DATA告诉 ADC 把最新的转换结果发送回来
ADS124S08_CMD_RDATA = 0x12
# 读寄存器命令Read REGister后面需要跟寄存器地址和要读取的寄存器数量
ADS124S08_CMD_RREG = 0x20
# 写寄存器命令Write REGister后面需要跟寄存器地址和要写入的数据
# 注意RREG 和 WREG 的最高位不同0x20 vs 0x40这是 ADC 用来区分读写操作的标志
ADS124S08_CMD_WREG = 0x40
# =============================================================================
# ADS124S08 寄存器地址定义
# 寄存器是 ADC 芯片内部的一些存储单元,每个寄存器有 1 字节8 位)宽度。
# 我们可以通过 SPI 发送 RREG读寄存器或 WREG写寄存器命令来读取或修改它们。
# 寄存器控制着 ADC 的各种工作参数,比如选择哪个输入通道、设置放大倍数、选择数据速率等。
# =============================================================================
# 芯片 ID 寄存器(出厂固化),只读,存放芯片型号和版本号,可以用来验证 SPI 通信是否正常
ADS124S08_ID_REG = 0x00
# 状态寄存器STATUS存放 ADC 的工作状态标志,如是否发生过上电复位、是否有数据就绪等
ADS124S08_STATUS = 0x01
# 输入多路复用器寄存器INPUT MUX控制选择哪个模拟输入引脚接到 ADC 内部
ADS124S08_INPUT_MUX = 0x02
# PGA可编程增益放大器寄存器控制是否启用前置放大以及放大多少倍1~128 倍)
ADS124S08_PGA = 0x03
# 数据速率寄存器DATA RATE控制 ADC 每秒采样多少次2.5 SPS ~ 4000 SPS
ADS124S08_DATA_RATE = 0x04
# 参考电压寄存器REF配置参考电压的来源和缓冲器
ADS124S08_REF = 0x05
# 激励电流源幅度寄存器IDAC MAGnitude设置恒流源电流大小
ADS124S08_IDACMAG = 0x06
# 激励电流源输出引脚选择寄存器IDAC MUX选择恒流源接到哪个 AIN 引脚
ADS124S08_IDACMUX = 0x07
# 偏置电压寄存器VBIAS配置是否在输入引脚上叠加一个偏置电压
ADS124S08_VBIAS = 0x08
# 系统控制寄存器SYS控制 CRC 校验等系统级功能
ADS124S08_SYS = 0x09
# 失调校准寄存器 0OFfset CALibration 0用于细调 ADC 的零偏(低 8 位)
ADS124S08_OFCAL0 = 0x0a
# 失调校准寄存器 1OFfset CALibration 1用于细调 ADC 的零偏(中间 8 位)
ADS124S08_OFCAL1 = 0x0b
# 失调校准寄存器 2OFfset CALibration 2用于细调 ADC 的零偏(高 8 位)
ADS124S08_OFCAL2 = 0x0c
# 满量程校准寄存器 0Full SCale CALibration 0用于校准 ADC 的增益误差(低 8 位)
ADS124S08_FSCAL0 = 0x0d
# 满量程校准寄存器 1Full SCale CALibration 1用于校准 ADC 的增益误差(中间 8 位)
ADS124S08_FSCAL1 = 0x0e
# 满量程校准寄存器 2Full SCale CALibration 2用于校准 ADC 的增益误差(高 8 位)
ADS124S08_FSCAL2 = 0x0f
# GPIO 数据寄存器GPIO DATa通过 SPI 读写 ADC 芯片上的通用输入输出引脚电平
ADS124S08_GPIODAT = 0x10
# GPIO 配置寄存器GPIO CONfiguration配置 ADC 芯片上 GPIO 引脚的方向(输入/输出)
ADS124S08_GPIOCON = 0x11
# =============================================================================
# ADS124S08 模拟输入通道号定义
# ADS124S08 有 12 个模拟输入引脚AIN0 ~ AIN11可以通过 INPUT_MUX 寄存器
# 选择连接到 ADC 内部的哪个通道。此外还有一个 AINCOM公共端引脚
# 用于单端测量时作为参考地(负端)。
# =============================================================================
# 模拟输入引脚 0地址编码为 0x00二进制 0000
ADS124S08_AIN0 = 0x00
# 模拟输入引脚 1地址编码为 0x01二进制 0001
ADS124S08_AIN1 = 0x01
# 模拟输入引脚 2地址编码为 0x02二进制 0010
ADS124S08_AIN2 = 0x02
# 模拟输入引脚 3地址编码为 0x03二进制 0011
ADS124S08_AIN3 = 0x03
# 模拟输入引脚 4地址编码为 0x04二进制 0100
ADS124S08_AIN4 = 0x04
# 模拟输入引脚 5地址编码为 0x05二进制 0101
ADS124S08_AIN5 = 0x05
# 模拟输入引脚 6地址编码为 0x06二进制 0110
ADS124S08_AIN6 = 0x06
# 模拟输入引脚 7地址编码为 0x07二进制 0111
ADS124S08_AIN7 = 0x07
# 模拟输入引脚 8地址编码为 0x08二进制 1000
ADS124S08_AIN8 = 0x08
# 模拟输入引脚 9地址编码为 0x09二进制 1001
ADS124S08_AIN9 = 0x09
# 模拟输入引脚 10地址编码为 0x0a二进制 1010
ADS124S08_AIN10 = 0x0a
# 模拟输入引脚 11地址编码为 0x0b二进制 1011
ADS124S08_AIN11 = 0x0b
# 公共端引脚AINCOM地址编码 0x0c在单端测量时作为负输入端参考地
# 即测量 AINx 相对于 AINCOM 的电压。在差分配置下AINCOM 也可以作为一个普通的输入端。
ADS124S08_AINCOM = 0x0c
# =============================================================================
# 定义 ADS124S08 类,封装了 ADC 芯片的所有操作
# =============================================================================
# 定义一个名为 ADS124S08 的类class类相当于一个模板通过它可以创建 ADC 对象实例。
# 每个实例有自己的 SPI 连接、GPIO 引脚设置等,可以独立控制一块 ADC 芯片。
class ADS124S08:
"""ADS124S08 ADC 驱动类(用户空间驱动)
这个类封装了对 ADS124S08 模数转换器的所有操作,包括:
- SPI 总线的打开和关闭
- GPIO 电源控制(分步上电)
- 寄存器读写
- 配置增益、输入通道、数据速率等
- 读取转换结果
基本用法示例:
adc = ADS124S08(bus=0, device=0) # 创建 ADC 对象,连接到 SPI0.0
adc.reset() # 复位 ADC 芯片
val = adc.read_channel(0) # 读取通道 0 的电压值
参数说明:
- bus/deviceSPI 总线和设备号,例如 bus=0, device=0 对应 /dev/spidev0.0
- max_speed_hzSPI 通信时钟频率单位赫兹Hz影响通信速度
- modeSPI 工作模式0~3由时钟极性CPOL和时钟相位CPHA组合决定
- reset_pin可选的复位引脚BCM GPIO 编号),如果提供则通过 GPIO 硬件复位(需要 RPi.GPIO 库)
"""
def __init__(self):
"""初始化 ADC 对象,完成以下步骤:
1. 设置 SPI 总线和设备号(默认 bus=0, device=0对应 /dev/spidev0.0
2. 打开 SPI 设备文件,建立与 ADC 芯片的通信连接
3. 配置 SPI 通信参数(时钟频率 1MHz模式 1
4. 设置 GPIO 引脚编号DRDY、上电控制、保持低电平
5. 初始化 GPIO 引脚的电平状态
6. 分两步给 ADC 上电Stage 1 -> 100ms 等待 -> Stage 2
7. 发送复位命令,让 ADC 恢复到默认状态
8. 读取所有寄存器值,验证 SPI 通信是否正常
9. 清除上电复位标志
10. 配置参考电压源
注意:构造函数不需要额外参数,所有配置使用默认值。
如果要修改配置,可以在创建对象后调用对应的 set_xxx() 方法。
"""
# -----------------------------------------------------------------------
# SPI 总线配置
# -----------------------------------------------------------------------
# 设置 SPI 总线编号为 0树莓派有 2 个 SPI 总线SPI0 和 SPI1
self.bus = 0
# 设置 SPI 设备编号为 0每个 SPI 总线上可以有多个设备,通过片选引脚 CS 区分)
self.device = 0
# 创建一个 SpiDev 对象,这是 spidev 库提供的 SPI 通信接口
self.spi = spidev.SpiDev()
# 尝试打开 SPI 设备文件(在 Linux 中对应 /dev/spidev0.0 这个设备文件)
try:
self.spi.open(self.bus, self.device)
# 如果 SPI 设备文件不存在(例如内核没加载 SPI 驱动,或设备号不对),会抛出 FileNotFoundError
except FileNotFoundError as e:
# 抛出更清晰的异常信息,告诉用户是哪个 SPI 设备打开失败了
raise Exception(f"SPI device /dev/spidev{self.bus}.{self.device} not found: {e}")
# 设置 SPI 通信时钟频率为 1 MHz1,000,000 Hz
# 这是主设备树莓派和从设备ADC之间数据传输的速率。
# 频率越高通信越快但信号质量可能下降。1 MHz 是 ADC 芯片支持的常用频率。
self.spi.max_speed_hz = 1_000_000 # 1 MHz 默认值
# 设置 SPI 模式为 0b01即模式 1
# SPI 有 4 种模式0~3由时钟极性CPOL和时钟相位CPHA决定
# 模式 1 = CPOL=0, CPHA=1空闲时时钟为低电平在下降沿采样数据
self.spi.mode = 0b01 # 默认使用 SPI 模式 1
# 设置 CRC 校验默认关闭(由 set_crc() 方法控制)
self.en_crc = False # 默认禁用 CRC 校验
# -----------------------------------------------------------------------
# GPIO 引脚配置
# -----------------------------------------------------------------------
# DRDY 引脚Data Ready数据就绪连接 ADC 的 DRDY 引脚到树莓派的 GPIO25。
# 当 ADC 完成一次转换且有新数据可读时DRDY 引脚会从高电平变为低电平。
self.drdy_pin = 25
# 上电阶段 1 的 GPIO 引脚列表GPIO22 和 GPIO17
# 这两个引脚在初始化时先被拉高(输出高电平),开启 ADC 的第一部分电源
self.power_stage1 = [22, 17]
# 上电阶段 2 的 GPIO 引脚列表GPIO24 和 GPIO27
# 这两个引脚在阶段 1 完成 100ms 后被拉高,开启 ADC 的第二部分电源
self.power_stage2 = [24, 27]
# 保持低电平的 GPIO 引脚列表GPIO23 和 GPIO18
# 这些引脚在整个初始化期间被强制保持为低电平(输出低电平)
self.gpio_hold_low = [23, 18]
# 创建一个 Button 对象来监控 DRDY 引脚。
# gpiozero 的 Button 类默认检测引脚从高电平变为低电平(下降沿触发),
# 这正好对应 DRDY 引脚"数据就绪时变低"的行为。
# 如果 drdy_pin 为 None则 self._drdy_pin 也设为 None不使用 DRDY 引脚检测)。
self._drdy_pin = Button(self.drdy_pin) if self.drdy_pin is not None else None
# -----------------------------------------------------------------------
# GPIO 初始化 — 将所有控制引脚设为初始状态
# -----------------------------------------------------------------------
# 创建一个空字典,用于存储所有 GPIO 引脚对应的 DigitalOutputDevice 对象,
# 方便后续通过引脚编号快速找到对应的设备对象来控制其电平。
self._gpio_devices = {}
# 遍历 power_stage1、power_stage2 和 gpio_hold_low 中的所有引脚编号,
# 用 set() 去重,确保每个引脚只创建一次设备对象。
for p in set(self.power_stage1 + self.power_stage2 + self.gpio_hold_low):
# 尝试为每个引脚创建一个 DigitalOutputDevice 对象,初始电平设为 False低电平
try:
self._gpio_devices[p] = DigitalOutputDevice(p, initial_value=False)
# 如果创建失败(例如引脚被占用或 GPIO 权限不足),则跳过该引脚
except Exception:
pass
# 确保"保持低电平"的引脚确实处于低电平状态(再次确认)
for p in self.gpio_hold_low:
dev = self._gpio_devices.get(p)
if dev:
dev.off()
# -----------------------------------------------------------------------
# 上电阶段 1先给 ADC 提供第一部分电源
# 为什么要分两步ADS124S08 的模拟和数字部分需要按顺序供电,
# 如果同时上电可能导致内部电路状态不确定。先让第一部分稳定,再启动第二部分。
# -----------------------------------------------------------------------
for p in self.power_stage1:
dev = self._gpio_devices.get(p)
if dev:
# 调用 on() 方法将引脚设为高电平(输出 3.3V),开启第一路供电
dev.on()
# 等待 100 毫秒0.1 秒),让第一部分电源稳定下来,消除电源波动
time.sleep(0.100)
# -----------------------------------------------------------------------
# 上电阶段 2给 ADC 提供第二部分电源
# -----------------------------------------------------------------------
for p in self.power_stage2:
dev = self._gpio_devices.get(p)
if dev:
# 将引脚设为高电平,开启第二路供电
dev.on()
# 再次等待 100 毫秒,等待 ADC 芯片内部电源管理电路完成初始化
time.sleep(0.100)
# 发送复位命令,让 ADC 芯片重新启动,所有寄存器恢复到数据手册规定的默认值
self.reset()
# 等待 100 毫秒,让复位完成(复位后芯片内部需要时间重新初始化)
time.sleep(0.100)
# 通过 SPI 发送 19 个字节来读取所有寄存器(地址 0x00 ~ 0x11共 18 个):
# - 第 1 个字节 = RREG 命令0x20 | 0x01表示从地址 0x01 开始读(实际第一个字节是地址 0x00
# - 第 2 个字节 = 0x10十进制 16表示要连续读取 17 个寄存器(寄存器数量 = 该值 + 1
# - 第 3~19 个字节 = 填充 0x00用于给 SPI 提供时钟信号以读取返回数据
rx = self.spi.xfer2([ADS124S08_CMD_RREG | (0x01 & 0xFF), 0x10] + [0x00]*17)
# 复位后各寄存器的默认值列表(从数据手册获得),用于验证 SPI 通信是否正确
default_regs = [0x80, 0x01, 0x00, 0x14, 0x10, 0x00, 0xff, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00]
# 如果收到的数据长度是 19 字节,说明通信正常(发送 3 字节收到 3 字节SPI 是全双工的)
if len(rx) == 19:
# 将读取到的寄存器值(从 rx[2] 开始,前两个字节是命令的回显)与默认值逐一对比
for i, (actual, expected) in enumerate(zip(rx[2:], default_regs)):
# 如果某个寄存器的值与期望值不符,说明 SPI 通信有问题或芯片工作异常
if actual != expected:
# 抛出异常,报告是哪个寄存器不匹配
raise Exception(f"Register {i} mismatch: expected 0x{expected:02x}, got 0x{actual:02x}")
# 清除 STATUS 寄存器中的上电复位Power-On Reset标志位
# 告诉芯片"我们已经知道发生过上电复位了",这样下次就可以通过检查这个标志来判断是否掉电重启过
self.clear_por()
# 配置参考电压源:
# - en_monitor=0禁用参考电压监控
# - en_bufp=1使能正参考电压缓冲器提高带负载能力
# - en_bufn=1使能负参考电压缓冲器
# - refsel=2选择内部参考电压2.5V
# - refcon=1内部参考电压开启但在待机模式下可以关闭以省电
self.set_ref(en_monitor=0, en_bufp=1, en_bufn=1, refsel=2, refcon=1)
def close(self) -> None:
"""关闭并清理所有资源,释放 SPI 总线和 GPIO 引脚。
在程序结束或不再需要使用 ADC 时调用此方法,确保:
1. 关闭 SPI 设备文件,释放 SPI 总线资源
2. 将所有 GPIO 控制引脚设置为低电平
3. 关闭并释放所有 GPIO 设备对象
4. 关闭 DRDY 引脚检测对象
"""
# 使用 try...finally 结构,无论 SPI 关闭是否成功,都会执行后续的清理操作
try:
# 检查 SPI 对象是否存在(防止重复关闭)
if self.spi:
# 关闭 SPI 设备文件 /dev/spidev0.0,释放总线资源
self.spi.close()
finally:
# 将 SPI 对象设为 None标记为已关闭防止后续误用
self.spi = None # type: ignore
# 清理所有 gpiozero 创建的 GPIO 设备对象
if hasattr(self, '_gpio_devices'):
# 遍历字典中所有 GPIO 设备
for dev in self._gpio_devices.values():
try:
# 先将引脚设为低电平(关断电源)
dev.off()
# 关闭并释放 GPIO 设备对象
dev.close()
except Exception:
# 如果关闭失败(例如设备已被其他程序占用),静默跳过
pass
# 检查 DRDY 引脚对象是否存在
if self._drdy_pin is not None:
try:
# 关闭 DRDY 引脚检测对象
self._drdy_pin.close()
except Exception:
pass
def reset(self) -> None:
"""复位 ADC 芯片。
通过 SPI 向 ADC 发送复位命令RESET = 0x06让芯片重新启动。
复位后所有寄存器恢复为数据手册中的默认值,等效于给芯片断电再重新上电的效果。
"""
# 通过 SPI 发送复位命令字节 0x06让 ADC 芯片执行硬件复位
self.write_cmd(ADS124S08_CMD_RESET)
# 根据数据手册要求,复位后需要等待一段时间让芯片内部完成初始化。
# 这里等待 10 毫秒0.01 秒),确保复位操作完成。
time.sleep(0.01)
def write_cmd(self, cmd: int) -> None:
"""通过 SPI 向 ADC 发送单字节命令。
这是最底层的命令发送函数,只发送一个字节。
常用于发送 NOP空操作、RESET复位、START启动转换、STOP停止转换等命令。
参数:
cmd: 要发送的命令字节0~255 的整数),例如 ADS124S08_CMD_RESET = 0x06
"""
# 通过 SPI 的 xfer2 方法发送 1 个字节。
# xfer2 是 spidev 库的全双工传输函数,会同时从 MISO 线接收相同数量的字节。
# cmd & 0xFF 确保只取低 8 位,防止数值超过一个字节。
self.spi.xfer2([cmd & 0xFF])
def write_reg(self, reg: int, data: int) -> None:
"""向 ADC 的指定寄存器写入数据1 个寄存器)。
通过 SPI 发送 3 个字节:
- 第 1 字节WREG 命令码0x40与寄存器地址按位或|)组合
- 第 2 字节0x00表示只写 1 个寄存器(寄存器数量 = 该值)
- 第 3 字节:要写入寄存器的数据
参数:
reg: 寄存器地址0x00 ~ 0x11例如 ADS124S08_PGA = 0x03
data: 要写入的数据0~255按寄存器的位定义填充
"""
# 通过 SPI 发送 3 个字节:
# [0x40 | reg] 把 WREG 命令码和寄存器地址组合成一个字节
# [0x00] 表示要写的寄存器数量减 10 表示写 1 个)
# [data & 0xFF] 是要写入寄存器的数据,& 0xFF 确保不超过一个字节
self.spi.xfer2([ADS124S08_CMD_WREG | (reg & 0xFF), 0x00, data & 0xFF])
def read_reg(self, reg: int) -> int:
"""读取 ADC 指定寄存器的值1 个寄存器)。
通过 SPI 发送 3 个字节,同时收到 3 个字节:
- 发送的第 1 字节RREG 命令码0x20与寄存器地址按位或组合
- 发送的第 2 字节0x00表示只读 1 个寄存器
- 发送的第 3 字节0x00填充字节用于产生 SPI 时钟信号让 ADC 把数据发送回来)
- 接收的第 3 字节:寄存器的当前值
参数:
reg: 寄存器地址0x00 ~ 0x11
返回:
寄存器的当前值0~255 的整数)
"""
# RREG 命令格式:[命令码 | 寄存器地址, 寄存器数量-1, 填充字节]
# 其中 0x20 是 RREG 命令的基地址0x00 表示读 1 个寄存器
rx = self.spi.xfer2([ADS124S08_CMD_RREG | (reg & 0xFF), 0x00, 0x00])
# 检查接收到的数据长度是否足够(至少 3 字节),防止 SPI 通信异常
if len(rx) < 3:
raise Exception("Short SPI read from device")
# 返回第 3 个字节(索引 2即寄存器的值& 0xFF 确保只取低 8 位
return rx[2] & 0xFF
def set_crc(self, en: bool) -> None:
"""启用或禁用 ADC 通信的 CRC 校验功能。
CRC循环冗余校验是一种数据校验方法用于检测 SPI 通信过程中数据是否被干扰。
启用后ADC 在发送数据时会附加一个 CRC 校验字节,接收方根据数据重新计算 CRC
如果与收到的 CRC 不一致,说明通信发生了错误。
参数:
en: True 表示启用 CRC 校验False 表示禁用
"""
# 在 Python 对象中记录 CRC 是否启用的状态
self.en_crc = en
# 先读取 SYS系统控制寄存器的当前值
val = self.read_reg(ADS124S08_SYS)
if en:
# 如果要启用 CRC将 SYS 寄存器的 bit 1二进制 00000010置 1
# 按位或运算(|=):要置 1 的位写 1其他位保持不变
val |= 0b00000010 # 设置 CRC 使能位
else:
# 如果要禁用 CRC将 SYS 寄存器的 bit 1二进制 00000010清 0
# 先取反(~),然后按位与(&=):要清 0 的位写 0其他位保持不变
val &= ~0b00000010 # 清除 CRC 使能位
# 将修改后的值写回 SYS 寄存器
self.write_reg(ADS124S08_SYS, val)
def _calculate_crc(self, data_bytes: list[int]) -> int:
"""计算数据字节的 CRC-8-ATM (HEC) 校验值。
这是 CRC-8-ATM 标准(也称为 HECHeader Error Control
使用多项式 x^8 + x^2 + x + 1对应二进制 0x07
ADS124S08 芯片也使用同样的 CRC 算法,所以我们需要用同样的算法
计算数据的 CRC然后与芯片发送过来的 CRC 字节比较,以验证数据的完整性。
参数:
data_bytes: 要计算 CRC 的数据字节列表(每个元素 0~255
返回:
计算出的 CRC 值0~255 的整数)
"""
# CRC 初始值为 0
crc = 0
# CRC-8-ATM 的多项式(生成多项式),二进制为 00000111即 x^8 + x^2 + x + 1
poly = 0x07 # CRC-8-ATM 多项式
# 遍历每个数据字节
for byte in data_bytes:
# 将当前字节与 CRC 值按位异或XOR把当前字节"加入"CRC 计算
crc ^= byte
# 对每个字节的 8 个位逐位处理
for _ in range(8):
# 检查 CRC 的最高位bit 7是否为 1
if crc & 0x80:
# 如果最高位为 1左移一位然后与多项式异或做多项式除法
crc = (crc << 1) ^ poly
else:
# 如果最高位为 0直接左移一位相当于多项式除法的商为 0
crc <<= 1
# 返回低 8 位作为最终的 CRC 值(& 0xFF 确保结果在 0~255 范围内)
return crc & 0xFF
def cmd_read_data(self) -> int:
"""主动读取 ADC 的转换结果(通过发送 RDATA 命令)。
这个方法向 ADC 发送 RDATA读取数据命令然后读取 3 个字节的转换数据。
ADC 输出的数据是 24 位有符号整数以大端big-endian格式排列
即第一个字节是最高位MSB第三个字节是最低位LSB
如果启用了 CRC 校验,还会多读一个 CRC 字节并进行校验。
返回:
24 位有符号整数表示的 ADC 转换原始值(范围:-8388608 ~ 8388607
"""
# 如果启用了 CRC 校验,需要多发送和接收 1 个字节CRC 字节)
if self.en_crc:
# 通过 SPI 发送 5 个字节:
# - 第 1 字节RDATA 命令0x12告诉 ADC 把转换结果发回来
# - 第 2~4 字节:填充 0x00用于产生 SPI 时钟,让 ADC 把 24 位数据发回来
# - 第 5 字节:填充 0x00用于产生时钟让 ADC 把 CRC 字节发回来
rx = self.spi.xfer2([ADS124S08_CMD_RDATA, 0x00, 0x00, 0x00, 0x00])
# 检查收到的数据是否够 5 个字节
if len(rx) < 5:
raise Exception("Short SPI read from device")
# 合并 3 个数据字节为 24 位整数(大端格式):
# 第 1 个字节rx[1])左移 16 位作为最高 8 位
# 第 2 个字节rx[2])左移 8 位作为中间 8 位
# 第 3 个字节rx[3])直接作为最低 8 位
data = (rx[1] << 16) | (rx[2] << 8) | rx[3]
# 第 5 个字节rx[4])是芯片计算的 CRC 校验值
crc_received = rx[4] & 0xFF
# 对收到的 3 个数据字节重新计算 CRC与芯片发送的 CRC 比较
crc_calculated = self._calculate_crc([rx[1], rx[2], rx[3]])
# 如果计算出的 CRC 与收到的 CRC 不一致,说明数据传输过程中发生了错误
if crc_calculated != crc_received:
raise Exception(f"CRC mismatch: calculated 0x{crc_calculated:02x}, received 0x{crc_received:02x}, data: 0x{data:06x}")
# CRC 校验通过,将数据赋给 val
val = data
else:
# 未启用 CRC 校验,发送 4 个字节(少一个 CRC 字节):
# - 第 1 字节RDATA 命令0x12
# - 第 2~4 字节:填充 0x00用于产生 SPI 时钟读取 24 位数据
rx = self.spi.xfer2([ADS124S08_CMD_RDATA, 0x00, 0x00, 0x00])
# 检查收到的数据是否够 4 个字节
if len(rx) < 4:
raise Exception("Short SPI read from device")
# 合并 3 个数据字节为 24 位整数(大端格式)
val = (rx[1] << 16) | (rx[2] << 8) | rx[3]
# 被注释掉的行:原来可能只取低 16 位(只使用 16 位分辨率),但实际使用了完整的 24 位
# val = (rx[1] << 16) | (rx[2] << 8) | 0x00
# 处理负数符号扩展如果最高位bit 23为 1说明是负数
if val & 0x800000:
# 将 24 位有符号数转换为 Python 的有符号整数:
# 减去 2^24即 16777216得到负数
return val - (1 << 24)
# 如果是正数,直接返回
return val
def drdy_read_data(self) -> int:
"""等待 DRDY 引脚变低后,直接读取 ADC 转换结果(不发送 RDATA 命令)。
当 ADC 完成一次转换后DRDY 引脚会自动变为低电平,表示数据已就绪。
此时不需要发送 RDATA 命令,直接发送 3 个(或 4 个,含 CRC填充字节
来产生 SPI 时钟ADC 就会把最新的转换结果发送回来。
这种方法比 cmd_read_data() 更高效,适合连续采样场景。
注意:此方法不主动等待 DRDY 变低,调用者需要确保在调用此方法前
DRDY 引脚已经变为低电平(即数据已就绪)。
返回:
24 位有符号整数表示的 ADC 转换原始值(范围:-8388608 ~ 8388607
"""
# 如果启用了 CRC 校验
if self.en_crc:
# 发送 4 个填充字节(不需要 RDATA 命令DRDY 变低表示数据已就绪):
# 第 1~3 字节用于产生 SPI 时钟读取 24 位数据
# 第 4 字节用于读取 CRC
rx = self.spi.xfer2([0x00, 0x00, 0x00, 0x00])
# 检查长度
if len(rx) < 4:
raise Exception("Short SPI read from device")
# 合并 3 个数据字节为 24 位整数(大端格式)
data = (rx[0] << 16) | (rx[1] << 8) | rx[2]
# 获取 CRC 字节
crc_received = rx[3] & 0xFF
# 计算并验证 CRC
crc_calculated = self._calculate_crc([rx[0], rx[1], rx[2]])
if crc_calculated != crc_received:
raise Exception(f"CRC mismatch: calculated 0x{crc_calculated:02x}, received 0x{crc_received:02x}, data: 0x{data:06x}")
val = data
else:
# 未启用 CRC发送 3 个填充字节读取 24 位数据
rx = self.spi.xfer2([0x00, 0x00, 0x00])
if len(rx) < 3:
raise Exception("Short SPI read from device")
# 合并为 24 位整数(大端格式)
val = (rx[0] << 16) | (rx[1] << 8) | rx[2]
# 被注释掉的行:原来可能只取低 16 位
# val = (rx[0] << 16) | (rx[1] << 8) | 0x00
# 处理负数(符号扩展)
if val & 0x800000:
return val - (1 << 24)
return val
def convert_to_voltage(self, raw_value: int) -> float:
"""将 ADC 的原始数值转换为实际的电压值(单位:伏特)。
ADS124S08 是一个 24 位 ADC可以测量双极性信号。
转换公式为:电压 = 原始值 × LSB
其中 LSBLeast Significant Bit最低有效位是 ADC 能分辨的最小电压变化。
LSB 的计算公式:
LSB = Vref / (Gain × 2^23)
推导过程:
- 24 位 ADC 的总量程是 2^24 = 16777216 个码值
- 因为是双极性(可测正负电压),有效量程是 -Vref/Gain 到 +Vref/Gain
- 双极性下,码值范围是 -2^23 到 2^23-1共 2^24 个码值
- 所以 LSB = (Vref/Gain - (-Vref/Gain)) / 2^24 = 2×Vref/Gain / 2^24 = Vref / (Gain × 2^23)
举例Vref=2.5VGain=1 时LSB = 2.5 / 8388608 ≈ 0.298 微伏
参数:
raw_value: ADC 的原始 24 位有符号数值(由 cmd_read_data 或 drdy_read_data 返回)
返回:
转换后的电压值单位伏特V
"""
# 读取 REF参考电压配置寄存器获取当前参考电压的设置
ref_reg = self.read_reg(ADS124S08_REF)
# 从 REF 寄存器中提取 refsel参考电压选择字段bit 3~2
refsel = (ref_reg >> 2) & 0x03
# 检查参考电压选择:如果 refsel == 2表示使用芯片内部参考电压
if refsel == 2:
# ADS124S08 的内部参考电压为 2.5V
vref = 2.5 # 使用内部参考电压 2.5V
else:
# 如果是外部参考电压,这里也假设为 2.5V(实际应根据外部电路确定)
vref = 2.5 # 假设外部参考电压也是 2.5V,实际项目中需要根据电路调整
# 读取 PGA可编程增益放大器寄存器获取当前的增益设置
gain_factors = [1, 2, 4, 8, 16, 32, 64, 128]
pga_reg = self.read_reg(ADS124S08_PGA)
# 从 PGA 寄存器提取 PGA 使能位bit 3
pga_en = (pga_reg >> 3) & 0x01
if pga_en:
# 如果 PGA 已启用,从低 3 位bit 2~0读取增益编码并查表得到实际增益值
gain = gain_factors[pga_reg & 0x07]
else:
# 如果 PGA 未启用,增益为 1 倍(信号直通,不放大)
gain = 1
# 计算 LSB最低有效位对应的电压值
# LSB = Vref / (Gain × 2^23)
# - Vref参考电压伏特
# - GainPGA 增益倍数
# - 2^23 = 8388608对应 24 位有符号 ADC 的一半量程
lsb = vref / (gain * (1 << 23)) # 24 位双极性 ADC 的 LSB 计算
# 最终电压值 = ADC 原始数值 × LSB
return raw_value * lsb
def set_ref(self, en_monitor: int = 0, en_bufp: int = 0, en_bufn: int = 1, refsel: int = 0, refcon: int = 0) -> None:
"""配置 ADC 的参考电压REF寄存器。
参考电压决定了 ADC 的测量范围。例如参考电压为 2.5V 时,
双极性模式下可测量的范围是 -2.5V 到 +2.5V。
这个函数允许用户配置参考电压的监控、缓冲、来源和控制模式。
REF 寄存器的位定义8 位):
- bit 7~6 (en_monitor):参考电压监控模式
- 0禁用监控
- 1仅 L0 使能
- 2L0 + L1 使能
- 3L0 + 电阻
- bit 5 (en_bufp)正参考电压缓冲器1=禁用缓冲器直通0=使能缓冲器)
- bit 4 (en_bufn)负参考电压缓冲器1=禁用缓冲器直通0=使能缓冲器)
- bit 3~2 (refsel):参考电压源选择
- 0P0+N0外部引脚对
- 1P1+N1外部引脚对
- 2内部参考电压2.5V
- bit 1~0 (refcon):内部参考电压控制
- 0内部参考关闭
- 1内部参考开启待机时可关闭以省电
- 2内部参考始终开启
参数:
en_monitor: 参考电压监控模式0~3
en_bufp: 正参考电压缓冲器控制0=使能1=禁用)
en_bufn: 负参考电压缓冲器控制0=使能1=禁用)
refsel: 参考电压源选择0~2
refcon: 内部参考电压控制0~2
"""
# 校验参数是否在有效范围内
if not (0 <= en_monitor <= 3):
raise ("en_monitor must be 0-3")
if not (0 <= en_bufp <= 1):
raise ("en_bufp must be 0 or 1")
if not (0 <= en_bufn <= 1):
raise ("en_bufn must be 0 or 1")
if not (0 <= refsel <= 2):
raise ("refsel must be 0-2")
if not (0 <= refcon <= 2):
raise ("refcon must be 0-2")
# 将各个参数拼接到 REF 寄存器的对应位上:
# REF 寄存器的位布局8 位二进制数):
# bit 7~6 = en_monitor左移 6 位
# bit 5 = en_bufp 左移 5 位
# bit 4 = en_bufn 左移 4 位
# bit 3~2 = refsel 左移 2 位
# bit 1~0 = refcon 不移位
# 使用按位或(|)运算将各个字段组合成一个字节
ref_val = ((en_monitor & 0x03) << 6) | ((en_bufp & 0x01) << 5) | ((en_bufn & 0x01) << 4) | \
((refsel & 0x03) << 2) | (refcon & 0x03)
# 将组合好的值写入 REF 寄存器(地址 0x05
self.write_reg(ADS124S08_REF, ref_val)
def check_ref(self) -> None:
"""读取并打印 REF参考电压寄存器的当前配置。
从 ADC 的 REF 寄存器读取当前值,解析出每个字段(监控模式、缓冲器设置、
参考电压选择、参考控制等),并以可读的文字形式打印到控制台。
方便调试时确认参考电压配置是否正确。
"""
# 读取 REF 寄存器(地址 0x05的当前值
val = self.read_reg(ADS124S08_REF)
# 从寄存器值中提取 en_monitor 字段bit 7~6
en_monitor = (val >> 6) & 0x03
# 提取 en_bufp 字段bit 5
en_bufp = (val >> 5) & 0x01
# 提取 en_bufn 字段bit 4
en_bufn = (val >> 4) & 0x01
# 提取 refsel 字段bit 3~2
refsel = (val >> 2) & 0x03
# 提取 refcon 字段bit 1~0
refcon = val & 0x03
# 定义枚举值的文字描述,用于打印
monitor_names = ['已禁用', 'L0 使能', 'L0+L1 使能', 'L0+电阻']
refsel_names = ['P0+N0外部引脚', 'P1+N1外部引脚', '内部参考2.5V']
refcon_names = ['内部参考关闭', '内部参考开启', '始终开启']
# 打印参考电压监控模式(如果索引超出范围则显示 None
print(f"参考电压监控: {monitor_names[en_monitor] if en_monitor < len(monitor_names) else None}")
# 打印正参考电压缓冲器状态en_bufp=0 表示使能,=1 表示禁用
print(f"正参考电压缓冲器: {'已禁用' if en_bufp else '已使能'}")
# 打印负参考电压缓冲器状态
print(f"负参考电压缓冲器: {'已禁用' if en_bufn else '已使能'}")
# 打印参考电压源选择
print(f"参考电压源: {refsel_names[refsel] if refsel < len(refsel_names) else '未知'}")
# 打印内部参考电压控制模式
print(f"参考电压控制: {refcon_names[refcon] if refcon < len(refcon_names) else '未知'}")
def set_input_mux(self, pos_channel: int, neg_channel: int = ADS124S08_AINCOM) -> None:
"""配置输入多路复用器INPUT MUX选择要测量的模拟输入引脚。
ADS124S08 有 12 个模拟输入引脚AIN0 ~ AIN11通过输入多路复用器
我们可以选择哪两个引脚连接到 ADC 内部进行测量。
两种模式:
- 单端测量pos_channel = 要测量的引脚(如 AIN0neg_channel = AINCOM公共端
此时测量的是该引脚相对于公共端的电压。
- 差分测量pos_channel = 正输入端neg_channel = 负输入端
此时测量的是两个引脚之间的电压差。
INPUT_MUX 寄存器布局8 位):
- bit 7~4正输入通道号pos_channel
- bit 3~0负输入通道号neg_channel
参数:
pos_channel: 正输入端通道号AIN0~AIN11或 AINCOM=0x0C
neg_channel: 负输入端通道号,默认 AINCOM适用于单端测量
"""
# 将正输入通道号放在高 4 位(左移 4 位),负输入通道号放在低 4 位,
# 然后按位或(|)组合成一个字节
mux_val = ((pos_channel & 0x0F) << 4) | (neg_channel & 0x0F)
# 将组合后的值写入 INPUT_MUX 寄存器(地址 0x02
self.write_reg(ADS124S08_INPUT_MUX, mux_val)
def check_input_mux(self) -> None:
"""读取并打印 INPUT_MUX输入多路复用器寄存器的当前配置。
用于调试时确认当前选择的是哪个输入通道。
"""
# 读取 INPUT_MUX 寄存器(地址 0x02的当前值
val = self.read_reg(ADS124S08_INPUT_MUX)
# 高 4 位是正输入端通道号,低 4 位是负输入端通道号,以十六进制打印
print(f"正输入端: AIN{(val >> 4) & 0x0F}, 负输入端: AIN{val & 0x0F}")
def set_pga(self, pga_en: int = 0, gain: int = 0) -> None:
"""配置 PGA可编程增益放大器
PGA 可以在 ADC 转换之前对模拟输入信号进行放大,这样即使是很微弱的信号
(如应变片输出的毫伏级电压)也能被 ADC 准确测量。
增益越大,可测量的信号幅度越小,但分辨率越高。
PGA 寄存器的位布局8 位):
- bit 3PGA 使能位1=使能0=禁用)
- bit 2~0增益选择码
增益编码对应表:
0: ×1 倍 1: ×2 倍 2: ×4 倍 3: ×8 倍
4: ×16 倍 5: ×32 倍 6: ×64 倍 7: ×128 倍
参数:
pga_en: PGA 使能1=启用前置放大0=禁用,信号直通)
gain: 增益编码0~7对应 ×1 到 ×128 倍放大
"""
# 校验参数是否在有效范围内
if not (0 <= pga_en <= 1):
raise ("pga_en must be 0 or 1")
if not (0 <= gain <= 7):
raise ("gain must be 0-7")
# 将 PGA 使能位放在 bit 3增益编码放在 bit 2~0组合成一个字节
pga_val = ((pga_en & 0x01) << 3) | (gain & 0x07)
# 将组合后的值写入 PGA 寄存器(地址 0x03
self.write_reg(ADS124S08_PGA, pga_val)
def check_pga(self) -> None:
"""读取并打印 PGA可编程增益放大器寄存器的当前配置。
用于调试时确认当前的增益设置是否正确。
"""
# 读取 PGA 寄存器(地址 0x03的当前值
val = self.read_reg(ADS124S08_PGA)
# 提取低 3 位作为增益编码
gain_val = val & 0x07
# 增益编码对应的实际放大倍数
gain_factors = [1, 2, 4, 8, 16, 32, 64, 128]
# 打印 PGA 使能状态和实际增益倍数
print(f"PGA 使能: {(val >> 3) & 0x01}, 增益: ×{gain_factors[gain_val]}")
def set_datarate(self, dr: int = 4, filter_type: int = 1,
mode: int = 0) -> None:
"""配置 DATA_RATE数据速率寄存器设置 ADC 的采样速度和转换模式。
数据速率决定了 ADC 每秒进行多少次采样。速率越高,采样越快,
但噪声也越大(信噪比降低)。速率越低,采样越慢,但精度越高。
DATA_RATE 寄存器的位布局8 位):
- bit 7~6保留位未使用
- bit 5转换模式0=连续转换1=单次转换)
- bit 4滤波器类型0=Sinc3 滤波器1=低延迟滤波器)
- bit 3~0数据速率编码
数据速率编码对应表(编码 0~13
0: 2.5 SPS 1: 5 SPS 2: 10 SPS 3: 16.6 SPS
4: 20 SPS 5: 50 SPS 6: 60 SPS 7: 100 SPS
8: 200 SPS 9: 400 SPS 10: 800 SPS 11: 1000 SPS
12: 2000 SPS 13: 4000 SPS
参数:
dr: 数据速率编码0~13
filter_type: 滤波器类型0=Sinc31=低延迟滤波器)
- Sinc3 滤波器:噪声低但转换速度慢,适合高精度测量
- 低延迟滤波器:响应快但噪声略高,适合需要快速读取的场景
mode: 转换模式0=连续转换1=单次转换)
- 连续模式ADC 持续不断地进行转换,每次完成后自动开始下一次
- 单次模式:每次转换完成后停止,需要再次发送 START 命令才继续转换
"""
# 校验参数在有效范围内
if not (0 <= dr <= 13):
raise Exception("dr must be 0-13")
if not (0 <= filter_type <= 1):
raise Exception("filter_type must be 0-1")
if not (0 <= mode <= 1):
raise Exception("mode must be 0-1")
# 将 mode 放在 bit 5filter_type 放在 bit 4dr 编码放在 bit 3~0
dr_val = ((mode & 0x01) << 5) | ((filter_type & 0x01) << 4) | (dr & 0x0F)
# 将组合后的值写入 DATA_RATE 寄存器(地址 0x04
self.write_reg(ADS124S08_DATA_RATE, dr_val)
def check_datarate(self) -> None:
"""读取并打印 DATA_RATE数据速率寄存器的当前配置。
用于调试时确认当前的数据速率、滤波器类型和转换模式。
"""
# 读取 DATA_RATE 寄存器(地址 0x04的当前值
val = self.read_reg(ADS124S08_DATA_RATE)
# 提取低 4 位作为数据速率编码
dr = val & 0x0F
# 提取 bit 4 作为滤波器类型
filter_type = (val >> 4) & 0x01
# 提取 bit 5 作为转换模式
mode = (val >> 5) & 0x01
# 数据速率编码对应的实际每秒采样次数SPS = Samples Per Second
dr_values = [2.5, 5, 10, 16.6, 20, 50, 60, 100, 200, 400, 800, 1000, 2000, 4000]
# 滤波器类型名称
filter_names = ['Sinc3 滤波器', '低延迟滤波器']
# 转换模式名称
mode_names = ['连续转换', '单次转换']
# 打印数据速率(如果编码超出范围则显示 None
print(f"数据速率: {dr_values[dr] if dr < len(dr_values) else None} SPS")
# 打印滤波器类型
print(f"滤波器类型: {filter_names[filter_type] if filter_type < len(filter_names) else None}")
# 打印转换模式
print(f"转换模式: {mode_names[mode] if mode < len(mode_names) else None}")
def check_status(self) -> None:
"""读取并打印 STATUS状态寄存器的当前值检查 ADC 工作状态。
STATUS 寄存器的位含义:
- bit 7上电复位标志PORPower-On Reset1 表示芯片发生过上电复位
- bit 6ADC 未就绪标志1 表示 ADC 还没有准备好进行转换
- bit 5~0其他状态标志位
通过检查这些标志可以判断 ADC 是否工作正常。
"""
# 读取 STATUS 寄存器(地址 0x01的当前值
val = self.read_reg(ADS124S08_STATUS)
# 检查 bit 7上电复位标志
if val & 0b10000000:
print("检测到上电复位POR需要清除该标志")
# 检查 bit 6ADC 就绪标志
if val & 0b01000000:
print("ADC 尚未就绪,可能还在初始化中")
# 检查低 6 位的其他标志位
if val & 0b00111111:
print("ADC 状态标志:", bin(val & 0b00111111))
def clear_por(self) -> None:
"""清除 STATUS 寄存器的上电复位POR标志位。
在芯片上电复位后STATUS 寄存器的 bit 7 会被设为 1
表示发生过上电复位。向 STATUS 寄存器写入 0x00 可以将这个标志清零。
之后程序就可以通过检查这个标志来判断芯片是否意外掉电重启过。
"""
# 向 STATUS 寄存器(地址 0x01写入 0x00将所有标志清零
self.write_reg(ADS124S08_STATUS, 0x00)
def request_channels(self, channels: list[int]) -> list[int]:
"""按顺序逐个测量指定的多个单端通道,并返回测量结果。
此方法适用于单次转换模式Single-shot mode它会
1. 检查 ADC 是否配置为单次转换模式
2. 对于列表中的每个通道,切换到该通道,启动一次转换,等待转换完成,读取数据
3. 收集所有通道的原始 ADC 值并返回
参数:
channels: 要测量的通道号列表,例如 [0, 1, 2] 表示测量 AIN0、AIN1、AIN2
每个通道对应单端测量(以 AINCOM 为公共端)
返回:
每个通道的原始 ADC 值列表24 位有符号整数),顺序与输入 channels 相同
"""
# 读取 DATA_RATE 寄存器,检查当前是否配置为单次转换模式
val = self.read_reg(ADS124S08_DATA_RATE)
# 检查 DATA_RATE 寄存器的 bit 50=连续模式1=单次模式
if val & 0b00100000:
# 如果是单次模式直接通过pass 语句什么都不做)
pass # 设备已处于单次转换模式
else:
# 如果是连续模式,抛出异常,因为此方法只适用于单次模式
raise Exception("设备未处于单次转换模式")
# 数据速率编码到实际 SPS 的映射表
dr_values = [2.5, 5, 10, 16.6, 20, 50, 60, 100, 200, 400, 800, 1000, 2000, 4000]
# 计算等待时间1 秒 ÷ 每秒采样次数 + 额外 1 毫秒的余量
# 例如 20 SPS 时,每次转换需要 1/20 = 50ms再加上 1ms 余量
wait_time = 1.0 / dr_values[val & 0x0F] + 0.001 # 增加一点余量,确保转换完成
# 创建一个空列表,用于存储每个通道的测量结果
values = []
# 遍历每个要测量的通道
for ch in channels:
# 将输入多路复用器切换到当前通道(单端模式,负端接 AINCOM
self.set_input_mux(pos_channel=ch, neg_channel=ADS124S08_AINCOM)
# 如果配置了 DRDY 引脚并且成功创建了 Button 对象
if self.drdy_pin and self._drdy_pin is not None:
# 发送 START 命令,启动一次单次转换
self.write_cmd(ADS124S08_CMD_START)
# 被注释掉的代码:原来的实现可能用 wait_for_press() 等待 DRDY 引脚变低
# 但被替换为固定延时,因为 DRDY 引脚检测在某些情况下可能不稳定
# time.sleep(0.001)
# self._drdy_pin.wait_for_press()
# 等待足够长的时间让 ADC 完成转换
time.sleep(wait_time)
# 读取转换结果
val = self.cmd_read_data()
else:
# 如果没有配置 DRDY 引脚,直接发送 START 命令
self.write_cmd(ADS124S08_CMD_START)
# 等待转换完成
time.sleep(wait_time)
# 读取转换结果
val = self.cmd_read_data()
# 将当前通道的测量值添加到结果列表中
values.append(val)
# 返回所有通道的测量结果列表
return values