""" ADS124S08 ADC 驱动模块 — 树莓派 5 用户空间 SPI 驱动 本模块实现了 ADS124S08 模数转换器(ADC)的核心功能: - 寄存器读写操作 - 通过 GPIO 控制芯片上电时序的设备初始化 - PGA(可编程增益放大器)配置 - 输入多路复用器选择(单端或差分输入) - 数据速率和转换模式设置 - DRDY(数据就绪)引脚处理 - 单次/连续转换模式 SPI 通信简介: SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行通信协议, 使用四根信号线:SCLK(时钟线)、MOSI(主设备输出/从设备输入)、 MISO(主设备输入/从设备输出)、CS(片选线,低电平有效)。 SPI 是全双工通信,主设备(树莓派)发送时钟信号的同时,主设备和从设备(ADC) 可以同时发送和接收数据。 引脚接线表(树莓派 5): - SPI 总线:/dev/spidev0.0(bus=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 线发送给 ADC,ADC 收到后执行对应操作。 # ============================================================================= # 空操作(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 # 失调校准寄存器 0(OFfset CALibration 0),用于细调 ADC 的零偏(低 8 位) ADS124S08_OFCAL0 = 0x0a # 失调校准寄存器 1(OFfset CALibration 1),用于细调 ADC 的零偏(中间 8 位) ADS124S08_OFCAL1 = 0x0b # 失调校准寄存器 2(OFfset CALibration 2),用于细调 ADC 的零偏(高 8 位) ADS124S08_OFCAL2 = 0x0c # 满量程校准寄存器 0(Full SCale CALibration 0),用于校准 ADC 的增益误差(低 8 位) ADS124S08_FSCAL0 = 0x0d # 满量程校准寄存器 1(Full SCale CALibration 1),用于校准 ADC 的增益误差(中间 8 位) ADS124S08_FSCAL1 = 0x0e # 满量程校准寄存器 2(Full 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/device:SPI 总线和设备号,例如 bus=0, device=0 对应 /dev/spidev0.0 - max_speed_hz:SPI 通信时钟频率,单位赫兹(Hz),影响通信速度 - mode:SPI 工作模式(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 MHz(1,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] 表示要写的寄存器数量减 1(0 表示写 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 标准(也称为 HEC,Header 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 其中 LSB(Least 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.5V,Gain=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:参考电压(伏特) # - Gain:PGA 增益倍数 # - 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 使能 - 2:L0 + L1 使能 - 3:L0 + 电阻 - bit 5 (en_bufp):正参考电压缓冲器(1=禁用缓冲器直通,0=使能缓冲器) - bit 4 (en_bufn):负参考电压缓冲器(1=禁用缓冲器直通,0=使能缓冲器) - bit 3~2 (refsel):参考电压源选择 - 0:P0+N0(外部引脚对) - 1:P1+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 = 要测量的引脚(如 AIN0),neg_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 3:PGA 使能位(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=Sinc3,1=低延迟滤波器) - 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 5,filter_type 放在 bit 4,dr 编码放在 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:上电复位标志(POR,Power-On Reset),1 表示芯片发生过上电复位 - bit 6:ADC 未就绪标志,1 表示 ADC 还没有准备好进行转换 - bit 5~0:其他状态标志位 通过检查这些标志可以判断 ADC 是否工作正常。 """ # 读取 STATUS 寄存器(地址 0x01)的当前值 val = self.read_reg(ADS124S08_STATUS) # 检查 bit 7:上电复位标志 if val & 0b10000000: print("检测到上电复位(POR),需要清除该标志") # 检查 bit 6:ADC 就绪标志 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 5:0=连续模式,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