增加运维机器人单元
This commit is contained in:
1 parent
479d263b88
commit
1b60ab0a2e
42 files changed
+20469
No files matched your search
Executable
+420
@@ -0,0 +1,420 @@
|
||||
#!/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()
|
||||
Reference in new issue
Block a user