Files
micro_servers/aibox/skills/ads1256.py
T
2026-07-27 21:39:53 +08:00

421 lines
14 KiB
Python
Executable File

#!/usr/bin/env python3
"""
ADS1256: Linux SPI 读取 TI ADS1256 24位 ΔΣ ADC 的命令行工具
"""
import spidev
import os
import sys
import argparse
import time
# ============================================================
# ADS1256 寄存器地址
# ============================================================
ADS1256_REG_STATUS = 0x00
ADS1256_REG_MUX = 0x01
ADS1256_REG_ADCON = 0x02
ADS1256_REG_DRATE = 0x03
ADS1256_REG_IO = 0x04
ADS1256_REG_OFC0 = 0x05
ADS1256_REG_OFC1 = 0x06
ADS1256_REG_OFC2 = 0x07
ADS1256_REG_FSC0 = 0x08
ADS1256_REG_FSC1 = 0x09
ADS1256_REG_FSC2 = 0x0A
# ============================================================
# ADS1256 命令
# ============================================================
ADS1256_CMD_WAKEUP = 0x00
ADS1256_CMD_RDATA = 0x01
ADS1256_CMD_RDATAC = 0x03
ADS1256_CMD_SDATAC = 0x0F
ADS1256_CMD_RREG = 0x10
ADS1256_CMD_WREG = 0x50
ADS1256_CMD_SELFCAL = 0xF0
ADS1256_CMD_SELFOCAL = 0xF1
ADS1256_CMD_SELFGCAL = 0xF2
ADS1256_CMD_SYSOCAL = 0xF3
ADS1256_CMD_SYSGCAL = 0xF4
ADS1256_CMD_SYNC = 0xFC
ADS1256_CMD_STANDBY = 0xFD
ADS1256_CMD_RESET = 0xFE
# ============================================================
# 数据速率 (DRATE 寄存器值)
# ============================================================
DRATE_MAP = {
30000: 0xF0, 15000: 0xE0, 7500: 0xD0, 3750: 0xC0,
2000: 0xB0, 1000: 0xA0, 500: 0x90, 100: 0x80,
60: 0x70, 50: 0x60, 30: 0x50, 25: 0x40,
15: 0x30, 10: 0x20, 5: 0x10,
}
# ============================================================
# PGA 增益
# ============================================================
PGA_MAP = {1: 0x00, 2: 0x01, 4: 0x02, 8: 0x03, 16: 0x04, 32: 0x05, 64: 0x06}
VREF = 2.5 # 参考电压
# ============================================================
# SPI 接口类 (基于 spidev)
# ============================================================
class SpiDev:
def __init__(self, device="/dev/spidev0.0", speed=1920000):
self.device = device
self.speed = speed
self.spi = None
self._bus = None
self._cs = None
def _parse_device(self):
"""从设备路径解析 bus 和 cs, 如 /dev/spidev0.0 -> (0, 0)"""
base = os.path.basename(self.device) # spidev0.0
parts = base.replace("spidev", "").split(".")
return int(parts[0]), int(parts[1])
def open(self):
self._bus, self._cs = self._parse_device()
self.spi = spidev.SpiDev()
self.spi.open(self._bus, self._cs)
self.spi.mode = 1 # SPI 模式 1: CPOL=0, CPHA=1
self.spi.bits_per_word = 8
self.spi.max_speed_hz = self.speed
# 读取验证
print(f"SPI 初始化: {self.device}, 模式={self.spi.mode}, "
f"位数={self.spi.bits_per_word}, 速度={self.spi.max_speed_hz} Hz",
file=sys.stderr)
def transfer(self, tx_data, rx_len=None):
"""SPI 全双工传输"""
if rx_len is None:
rx_len = len(tx_data)
# spidev.xfer2 返回接收到的字节
rx = self.spi.xfer2(list(tx_data), self.speed, 1) # 1 = SPI_MODE_1
# 如果 rx_len 大于发送长度,补充虚拟字节
if rx_len > len(tx_data):
extra = self.spi.xfer2([0xFF] * (rx_len - len(tx_data)), self.speed, 1)
rx.extend(extra)
return list(rx[:rx_len])
def write_byte(self, byte):
self.spi.xfer2([byte], self.speed, 1)
# 不需要接收
def read_byte(self):
rx = self.spi.xfer2([0xFF], self.speed, 1)
return rx[0]
def close(self):
if self.spi:
self.spi.close()
self.spi = None
# ============================================================
# ADS1256 控制类
# ============================================================
class ADS1256:
def __init__(self, spi, pga=2, drate=1000):
self.spi = spi
self.pga = pga
self.drate = drate
self.pga_reg = PGA_MAP.get(pga, 0x01)
def wait_drdy(self):
time.sleep(0.0001) # 100us 模拟等待
def reset(self):
self.spi.write_byte(ADS1256_CMD_RESET)
time.sleep(0.001)
self.spi.write_byte(ADS1256_CMD_WAKEUP)
time.sleep(0.001)
def write_reg(self, reg, value):
self.spi.write_byte(ADS1256_CMD_WREG | reg)
self.spi.write_byte(0x00) # 1 byte
self.spi.write_byte(value)
def read_reg(self, reg):
self.spi.write_byte(ADS1256_CMD_RREG | reg)
self.spi.write_byte(0x00)
return self.spi.read_byte()
def set_channel(self, pos, neg=0x08):
"""设置输入通道, neg=0x08 表示 AINCOM (单端模式)"""
mux = ((pos & 0x07) << 4) | (neg & 0x07)
self.write_reg(ADS1256_REG_MUX, mux)
time.sleep(0.00001)
def init(self):
print("初始化 ADS1256...", file=sys.stderr)
# 复位
self.reset()
time.sleep(0.01)
# STATUS: ACAL=1 (自动校准), ORDER=0 (MSB), BUFEN=0
self.write_reg(ADS1256_REG_STATUS, 0x14)
# MUX: 默认 AIN0 + AINCOM
self.set_channel(0, 0x08)
# ADCON: PGA 增益
self.write_reg(ADS1256_REG_ADCON, self.pga_reg)
# DRATE: 数据速率
drate_val = DRATE_MAP.get(self.drate, 0xA0)
self.write_reg(ADS1256_REG_DRATE, drate_val)
# 自校准
print("自校准中...", file=sys.stderr)
self.spi.write_byte(ADS1256_CMD_SELFCAL)
time.sleep(0.5) # 等待校准完成 (~400ms @ 1000SPS)
# 验证寄存器
status = self.read_reg(ADS1256_REG_STATUS)
mux = self.read_reg(ADS1256_REG_MUX)
adcon = self.read_reg(ADS1256_REG_ADCON)
drate = self.read_reg(ADS1256_REG_DRATE)
print(f"寄存器: STATUS=0x{status:02X}, MUX=0x{mux:02X}, "
f"ADCON=0x{adcon:02X}, DRATE=0x{drate:02X}", file=sys.stderr)
print("ADS1256 初始化完成!", file=sys.stderr)
def read_data(self):
"""单次读取 24 位数据, 返回有符号整数值"""
self.wait_drdy()
self.spi.write_byte(ADS1256_CMD_RDATA)
buf = self.spi.transfer([0xFF, 0xFF, 0xFF], 3)
# 组合 24 位
value = (buf[0] << 16) | (buf[1] << 8) | buf[2]
# 符号扩展 (24 位 -> 32 位)
if value & 0x800000:
value |= 0xFF000000
# Python 中转为有符号
if value >= 0x80000000:
value -= 0x100000000
return value
def raw_to_voltage(self, raw):
"""将原始值转换为电压"""
return raw * VREF / (1 << 23) / self.pga
def read_channel(self, ch):
"""读取指定单端通道"""
self.set_channel(ch, 0x08) # AINch vs AINCOM
time.sleep(0.00001)
self.spi.write_byte(ADS1256_CMD_SYNC)
time.sleep(0.00001)
self.spi.write_byte(ADS1256_CMD_WAKEUP)
# 等待转换完成 (1000SPS = 1ms)
time.sleep(0.002)
return self.read_data()
def read_diff(self, pos, neg):
"""读取差分通道"""
self.set_channel(pos, neg)
time.sleep(0.00001)
self.spi.write_byte(ADS1256_CMD_SYNC)
time.sleep(0.00001)
self.spi.write_byte(ADS1256_CMD_WAKEUP)
time.sleep(0.002)
return self.read_data()
def scan_all(self):
"""扫描所有 8 个单端通道"""
results = []
for ch in range(8):
raw = self.read_channel(ch)
volts = self.raw_to_voltage(raw)
results.append((ch, raw, volts))
return results
# ============================================================
# 主程序
# ============================================================
def main():
parser = argparse.ArgumentParser(
description="ADS1256: Linux SPI 读取 TI ADS1256 24位 ΔΣ ADC 的命令行工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
参数说明:
-d, --device SPI 设备路径 (默认: /dev/spidev0.0)
-s, --speed SPI 时钟频率 (Hz) (默认: 1920000)
-m, --mode 工作模式: single/diff/scan (默认: single)
-c, --channel 单端通道号 0-7 (默认: 0)
--pos 差分正端 0-7 (默认: 0)
--neg 差分负端 0-7 (默认: 1)
-p, --pga PGA 增益 1/2/4/8/16/32/64 (默认: 2)
-r, --drate 数据速率 SPS (默认: 1000)
可选: 5,10,15,25,30,50,60,100,500,1000,
2000,3750,7500,15000,30000
-n, --count 采样次数 (默认: 1)
--avg 输出均值 (多采样时)
-o, --output 输出格式: raw/voltage/both (默认: both)
使用示例:
# 读取 AIN0 单端通道
ads1256 -c 0
# 读取 AIN0 差分 AIN1
ads1256 -m diff --pos 0 --neg 1
# 扫描所有 8 个通道
ads1256 -m scan
# 设置 PGA=1, 速率为 30SPS, 采样 10 次并输出均值
ads1256 -c 0 -p 1 -r 30 -n 10 --avg
# 指定 SPI 设备
ads1256 -d /dev/spidev0.1 -s 1000000 -c 3
"""
)
parser.add_argument("-d", "--device", default="/dev/spidev0.0",
help="SPI 设备路径 (默认: /dev/spidev0.0)")
parser.add_argument("-s", "--speed", type=int, default=1920000,
help="SPI 时钟频率 Hz (默认: 1920000)")
parser.add_argument("-m", "--mode", choices=["single", "diff", "scan"],
default="single", help="工作模式 (默认: single)")
parser.add_argument("-c", "--channel", type=int, default=0, choices=range(8),
help="单端通道号 0-7 (默认: 0)")
parser.add_argument("--pos", type=int, default=0, choices=range(8),
help="差分正端 0-7 (默认: 0)")
parser.add_argument("--neg", type=int, default=1, choices=range(8),
help="差分负端 0-7 (默认: 1)")
parser.add_argument("-p", "--pga", type=int, default=2,
choices=[1, 2, 4, 8, 16, 32, 64],
help="PGA 增益 (默认: 2)")
parser.add_argument("-r", "--drate", type=int, default=1000,
help="数据速率 SPS (默认: 1000)")
parser.add_argument("-n", "--count", type=int, default=1,
help="采样次数 (默认: 1)")
parser.add_argument("--avg", action="store_true",
help="输出均值")
parser.add_argument("-o", "--output", choices=["raw", "voltage", "both"],
default="both", help="输出格式 (默认: both)")
args = parser.parse_args()
# 验证 drate
if args.drate not in DRATE_MAP:
print(f"错误: 不支持的数据速率 {args.drate} SPS", file=sys.stderr)
print(f"支持: {sorted(DRATE_MAP.keys())}", file=sys.stderr)
sys.exit(1)
# 验证 SPI 设备
if not os.path.exists(args.device):
print(f"错误: SPI 设备 {args.device} 不存在", file=sys.stderr)
sys.exit(1)
# 初始化 SPI
spi = SpiDev(device=args.device, speed=args.speed)
try:
spi.open()
except Exception as e:
print(f"错误: SPI 初始化失败 - {e}", file=sys.stderr)
sys.exit(1)
# 初始化 ADS1256
adc = ADS1256(spi, pga=args.pga, drate=args.drate)
try:
adc.init()
except Exception as e:
print(f"ADS1256 初始化失败: {e}", file=sys.stderr)
spi.close()
sys.exit(1)
# 执行读取
try:
if args.mode == "scan":
# 扫描所有通道
results = adc.scan_all()
print("\n===== 多通道扫描 =====")
for ch, raw, volts in results:
_print_result(ch, raw, volts, args.output)
elif args.mode == "diff":
# 差分模式
pos, neg = args.pos, args.neg
channel_label = f"AIN{pos}-AIN{neg}"
raw_values = []
for i in range(args.count):
raw = adc.read_diff(pos, neg)
raw_values.append(raw)
volts = adc.raw_to_voltage(raw)
_print_sample(i, raw, volts, args.output, channel_label)
if args.count > 1 and args.avg:
avg_raw = sum(raw_values) // len(raw_values)
avg_volts = adc.raw_to_voltage(avg_raw)
print(f"\n--- 均值 ---")
_print_result(channel_label, avg_raw, avg_volts, args.output)
else:
# 单端模式
ch = args.channel
channel_label = f"AIN{ch}"
raw_values = []
for i in range(args.count):
raw = adc.read_channel(ch)
raw_values.append(raw)
volts = adc.raw_to_voltage(raw)
_print_sample(i, raw, volts, args.output, channel_label)
if args.count > 1 and args.avg:
avg_raw = sum(raw_values) // len(raw_values)
avg_volts = adc.raw_to_voltage(avg_raw)
print(f"\n--- 均值 ---")
_print_result(channel_label, avg_raw, avg_volts, args.output)
except Exception as e:
print(f"读取错误: {e}", file=sys.stderr)
finally:
spi.close()
def _print_sample(index, raw, volts, output_format, label=""):
if output_format == "raw":
print(f"[{index}] {label}: raw={raw}")
elif output_format == "voltage":
print(f"[{index}] {label}: {_fmt_voltage(volts)}")
else:
print(f"[{index}] {label}: raw={raw:7d}, {_fmt_voltage(volts)}")
def _print_result(label, raw, volts, output_format):
if output_format == "raw":
print(f" {label}: raw={raw}")
elif output_format == "voltage":
print(f" {label}: {_fmt_voltage(volts)}")
else:
print(f" {label}: raw={raw:7d}, {_fmt_voltage(volts)}")
def _fmt_voltage(volts):
if abs(volts) >= 1.0:
return f"电压 = {volts:+.6f} V"
else:
return f"电压 = {volts*1000:+.4f} mV"
if __name__ == "__main__":
main()