diff --git a/inc/ads1256.h b/inc/ads1256.h new file mode 100644 index 0000000..4b37b53 --- /dev/null +++ b/inc/ads1256.h @@ -0,0 +1,53 @@ +#pragma once +#include + +/* sensor.c 切换通道时需要此寄存器地址 */ +#define ADS1256_REG_MUX 0x01 + +/** + * @brief 初始化 ADS1256:配置 GPIO/SPI,复位芯片,写入寄存器,自校准。 + * @return 0 成功,负 errno 失败。 + */ +int ads1256_init(void); + +/** + * @brief 等待 DRDY 拉低(转换结果可读),带超时保护。 + * @param timeout_ms 超时时间(毫秒)。 + */ +void ads1256_wait_drdy(uint16_t timeout_ms); + +/** + * @brief 向 ADS1256 写单个寄存器。 + * @param reg 目标寄存器地址。 + * @param val 要写入的值。 + */ +void ads1256_write_reg(uint8_t reg, uint8_t val); + +/** + * @brief 从 ADS1256 读单个寄存器。 + * @param reg 目标寄存器地址。 + * @return 寄存器值。 + */ +uint8_t ads1256_read_reg(uint8_t reg); + +/** + * @brief 发送 SYNC + WAKEUP 命令,触发一次同步采样。 + */ +void ads1256_sync_wakeup(void); + +/** + * @brief 硬件复位 ADS1256(通过 RESET 引脚),等待复位后自校准完成。 + */ +void ads1256_hwreset(void); + +/** + * @brief 读取当前 24 位转换结果,符号扩展为 int32_t。 + * @return ADC 原始值。 + */ +int32_t ads1256_read_data(void); + +/** + * @brief 发送单字节命令到 ADS1256。 + * @param cmd 命令字。 + */ +void ads1256_write_cmd(uint8_t cmd); diff --git a/inc/protocol.h b/inc/protocol.h new file mode 100644 index 0000000..67ce18a --- /dev/null +++ b/inc/protocol.h @@ -0,0 +1,132 @@ +#pragma once +#include +#include + +/* ─── 帧常量 ─── */ +/* 双帧头比单字节魔数更容易在连续字节流里完成包起始判定。 */ +#define PROTO_SYNC0 0xAAU +#define PROTO_SYNC1 0x55U +#define PROTO_TYPE_COP 0x01U /* 上行: 压力中心 (CoP) 帧 */ +#define PROTO_TYPE_ENCODER 0x02U /* 上行: 电机编码器帧(预留) */ +#define PROTO_TYPE_RESISTANCE 0x10U /* 下行: 阻力参数 (K, B, Tau) */ +#define PROTO_TYPE_SPOTTER 0x11U /* 下行: Spotter Mode 保护阈值 */ +#define PROTO_TYPE_HEARTBEAT 0x20U /* 下行: 心跳包 */ + +/* CoP flags 位定义 */ +#define COP_FLAG_FORCE_VALID 0x01U /* 总力 > 阈值,CoP 坐标有意义 */ + +/* ─── 板面几何 & 传感器标定 ─── */ +/* 传感器到板面中心的半宽、半长 (mm)。 */ +#define BOARD_HALF_WIDTH_MM 100.0f +#define BOARD_HALF_LENGTH_MM 100.0f + +/* + * ADC → kg 换算推导: + * GML670 50kg: 灵敏度 1.75 mV/V, 激励 5V → 满量程输出 8.75 mV + * ADS1256: PGA=64, VREF=2.5V → 满量程 ±78.125 mV + * 50kg 对应 ADC counts = 8.75 / 78.125 × 8388607 ≈ 939524 + * 1 LSB = 50.0 / 939524 ≈ 5.322e-5 kg + * 如实际激励电压或 VREF 不同,按比例修正此系数。 + */ +#define SENSOR_EXCITATION_V 5.0f +#define SENSOR_SENSITIVITY_MVV 1.75f +#define SENSOR_RATED_LOAD_KG 50.0f +#define ADS1256_VREF_V 2.5f +#define ADS1256_PGA 64 + +#define ADC_COUNTS_AT_RATED \ + ((SENSOR_SENSITIVITY_MVV * SENSOR_EXCITATION_V) / (2.0f * ADS1256_VREF_V * 1000.0f / ADS1256_PGA) * 8388607.0f) +#define ADC_TO_FORCE_SCALE (SENSOR_RATED_LOAD_KG / ADC_COUNTS_AT_RATED) + +/* 总重低于此值时 CoP 无意义(除零保护),单位 kg */ +#define COP_MIN_FORCE_THRESHOLD 0.5f + +/* ─── 上行: CoP 帧 (17 bytes, 50 Hz) ─── + * + * [AA 55 01 seq flags cop_x(4) cop_y(4) force(4)] + */ +struct cop_frame_t { + uint8_t sync0; /* = PROTO_SYNC0 */ + uint8_t sync1; /* = PROTO_SYNC1 */ + uint8_t type; /* = PROTO_TYPE_COP */ + uint8_t seq; /* 包序号 0-255 滚动,用于丢包检测 */ + uint8_t flags; /* bit0: force_valid */ + float cop_x; /* 压力中心 X (mm),板面坐标系 */ + float cop_y; /* 压力中心 Y (mm),板面坐标系 */ + float force; /* 总重量 (kg) */ +} __attribute__((packed)); + +union cop_pkt_t { + struct cop_frame_t frame; + uint8_t bytes[sizeof(struct cop_frame_t)]; +}; + +/* ─── 上行: 电机编码器帧 (13 bytes, 预留) ─── + * + * [AA 55 02 seq flags cable_length(4) velocity(4)] + */ +struct encoder_frame_t { + uint8_t sync0; /* = PROTO_SYNC0 */ + uint8_t sync1; /* = PROTO_SYNC1 */ + uint8_t type; /* = PROTO_TYPE_ENCODER */ + uint8_t seq; /* 包序号 */ + uint8_t flags; /* 预留,填 0 */ + float cable_length; /* 拉索长度 (mm) */ + float velocity; /* 拉索速度 (mm/s) */ +} __attribute__((packed)); + +union encoder_pkt_t { + struct encoder_frame_t frame; + uint8_t bytes[sizeof(struct encoder_frame_t)]; +}; + +/* ─── 下行: 阻力参数帧 (15 bytes) ─── + * + * [AA 55 10 K(4) B(4) Tau(4)] + */ +struct resistance_frame_t { + uint8_t sync0; /* = PROTO_SYNC0 */ + uint8_t sync1; /* = PROTO_SYNC1 */ + uint8_t type; /* = PROTO_TYPE_RESISTANCE */ + float K; /* 刚度 (N/m) */ + float B; /* 阻尼 (Ns/m) */ + float Tau; /* 时间常数 (s) */ +} __attribute__((packed)); + +union resistance_pkt_t { + struct resistance_frame_t frame; + uint8_t bytes[sizeof(struct resistance_frame_t)]; +}; + +/* ─── 下行: Spotter Mode 帧 (8 bytes) ─── + * + * [AA 55 11 threshold(4) enable] + */ +struct spotter_frame_t { + uint8_t sync0; /* = PROTO_SYNC0 */ + uint8_t sync1; /* = PROTO_SYNC1 */ + uint8_t type; /* = PROTO_TYPE_SPOTTER */ + float threshold; /* 保护力阈值 (N) */ + uint8_t enable; /* 0 = 关闭, 1 = 启用 */ +} __attribute__((packed)); + +union spotter_pkt_t { + struct spotter_frame_t frame; + uint8_t bytes[sizeof(struct spotter_frame_t)]; +}; + +/* ─── 下行: 心跳帧 (4 bytes, ~1 Hz) ─── + * + * [AA 55 20 counter] + */ +struct heartbeat_frame_t { + uint8_t sync0; /* = PROTO_SYNC0 */ + uint8_t sync1; /* = PROTO_SYNC1 */ + uint8_t type; /* = PROTO_TYPE_HEARTBEAT */ + uint8_t counter; /* 滚动计数,用于活性检测 */ +} __attribute__((packed)); + +union heartbeat_pkt_t { + struct heartbeat_frame_t frame; + uint8_t bytes[sizeof(struct heartbeat_frame_t)]; +}; diff --git a/src/ads1256.c b/src/ads1256.c new file mode 100644 index 0000000..ed4d430 --- /dev/null +++ b/src/ads1256.c @@ -0,0 +1,190 @@ +#include "ads1256.h" + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ads1256, LOG_LEVEL_INF); + +/* --- ADS1256 命令 --- */ +#define CMD_WAKEUP 0x00 +#define CMD_RDATA 0x01 +#define CMD_RREG 0x10 +#define CMD_WREG 0x50 +#define CMD_SELFCAL 0xF0 +#define CMD_SYNC 0xFC +#define CMD_SDATAC 0x0F + +/* --- ADS1256 寄存器地址 --- */ +#define REG_STATUS 0x00 +#define REG_MUX 0x01 +#define REG_ADCON 0x02 +#define REG_DRATE 0x03 + +#define SPI_OP (SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_LINES_SINGLE) + +/* --- 硬件资源 --- */ +static const struct device *spi_dev; +static struct spi_config spi_cfg; +static const struct gpio_dt_spec cs_spec = GPIO_DT_SPEC_GET(DT_ALIAS(ads_cs), gpios); +static const struct gpio_dt_spec drdy_spec = GPIO_DT_SPEC_GET(DT_ALIAS(ads_drdy), gpios); +static const struct gpio_dt_spec reset_spec = GPIO_DT_SPEC_GET(DT_ALIAS(ads_reset), gpios); + +void ads1256_wait_drdy(uint16_t timeout_ms) { + /* drdy_spec 配了 GPIO_ACTIVE_LOW,逻辑 1 表示 DRDY 有效(物理拉低) */ + /* 用 k_usleep 代替 k_busy_wait,让出 CPU 给其他线程 */ + int64_t deadline = k_uptime_get() + timeout_ms; + while (gpio_pin_get_dt(&drdy_spec) == 0) { + k_usleep(10); + if (k_uptime_get() >= deadline) { + LOG_WRN("DRDY timeout (%u ms)", timeout_ms); + break; + } + } +} + +void ads1256_write_reg(uint8_t reg, uint8_t val) { + ads1256_wait_drdy(50); + gpio_pin_set_dt(&cs_spec, 1); + uint8_t tx_buf[3] = { CMD_WREG | reg, 0x00, val }; + struct spi_buf tx = { .buf = tx_buf, .len = 3 }; + struct spi_buf_set tx_set = { .buffers = &tx, .count = 1 }; + spi_write(spi_dev, &spi_cfg, &tx_set); + gpio_pin_set_dt(&cs_spec, 0); + /* t11: WREG 后至少 4 × tCLKIN ≈ 0.5µs */ + k_busy_wait(2); +} + +uint8_t ads1256_read_reg(uint8_t reg) { + ads1256_wait_drdy(50); + gpio_pin_set_dt(&cs_spec, 1); + uint8_t tx_buf[2] = { CMD_RREG | reg, 0x00 }; + struct spi_buf tx = { .buf = tx_buf, .len = 2 }; + struct spi_buf_set tx_set = { .buffers = &tx, .count = 1 }; + spi_write(spi_dev, &spi_cfg, &tx_set); + /* t6: RREG 后至少 50 × tCLKIN ≈ 6.5µs */ + k_busy_wait(10); + uint8_t rx_val = 0; + struct spi_buf rx = { .buf = &rx_val, .len = 1 }; + struct spi_buf_set rx_set = { .buffers = &rx, .count = 1 }; + spi_read(spi_dev, &spi_cfg, &rx_set); + gpio_pin_set_dt(&cs_spec, 0); + return rx_val; +} + +void ads1256_write_cmd(uint8_t cmd) { + ads1256_wait_drdy(50); + gpio_pin_set_dt(&cs_spec, 1); + struct spi_buf tx = { .buf = &cmd, .len = 1 }; + struct spi_buf_set tx_set = { .buffers = &tx, .count = 1 }; + spi_write(spi_dev, &spi_cfg, &tx_set); + gpio_pin_set_dt(&cs_spec, 0); +} + +void ads1256_sync_wakeup(void) { + uint8_t cmd_sync = CMD_SYNC; + uint8_t cmd_wakeup = CMD_WAKEUP; + struct spi_buf tx_s = { .buf = &cmd_sync, .len = 1 }; + struct spi_buf tx_w = { .buf = &cmd_wakeup, .len = 1 }; + struct spi_buf_set set_s = { .buffers = &tx_s, .count = 1 }; + struct spi_buf_set set_w = { .buffers = &tx_w, .count = 1 }; + gpio_pin_set_dt(&cs_spec, 1); + spi_write(spi_dev, &spi_cfg, &set_s); + /* t11: SYNC 后至少 24 × tCLKIN ≈ 3.1µs */ + k_busy_wait(4); + spi_write(spi_dev, &spi_cfg, &set_w); + gpio_pin_set_dt(&cs_spec, 0); +} + +void ads1256_hwreset(void) { + /* reset_spec 配了 GPIO_ACTIVE_LOW:逻辑 1 = 物理 LOW = 断言复位 */ + gpio_pin_set_dt(&reset_spec, 1); + /* t16: 最小复位脉宽 4 × tCLKIN ≈ 0.5µs,取 1ms 余量 */ + k_msleep(10); + gpio_pin_set_dt(&reset_spec, 0); + /* 复位释放后芯片执行自校准,等待完成 */ + k_msleep(10); + ads1256_wait_drdy(500); +} + +int32_t ads1256_read_data(void) { + uint8_t cmd = CMD_RDATA; + uint8_t rx_buf[3] = { 0 }; + gpio_pin_set_dt(&cs_spec, 1); + struct spi_buf tx = { .buf = &cmd, .len = 1 }; + struct spi_buf_set tx_set = { .buffers = &tx, .count = 1 }; + spi_write(spi_dev, &spi_cfg, &tx_set); + /* t6: RDATA 后至少 50 × tCLKIN ≈ 6.5µs */ + k_busy_wait(10); + struct spi_buf rx = { .buf = rx_buf, .len = 3 }; + struct spi_buf_set rx_set = { .buffers = &rx, .count = 1 }; + spi_read(spi_dev, &spi_cfg, &rx_set); + gpio_pin_set_dt(&cs_spec, 0); + int32_t val = ((int32_t)rx_buf[0] << 16) | ((int32_t)rx_buf[1] << 8) | rx_buf[2]; + if (val & 0x800000) val |= 0xFF000000; + return val; +} + +int ads1256_init(void) { + /* GPIO */ + gpio_pin_configure_dt(&cs_spec, GPIO_OUTPUT_INACTIVE); + gpio_pin_configure_dt(&reset_spec, GPIO_OUTPUT_INACTIVE); + gpio_pin_configure_dt(&drdy_spec, GPIO_INPUT); + + /* SPI */ + spi_dev = DEVICE_DT_GET(DT_NODELABEL(spi1)); + spi_cfg.operation = SPI_OP; + spi_cfg.frequency = 500000; + spi_cfg.slave = 0; + + if (!device_is_ready(spi_dev)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + ads1256_hwreset(); + + /* 关闭连续输出模式,进入命令模式 */ + ads1256_write_cmd(CMD_SDATAC); + k_busy_wait(100); + + /* + * STATUS = 0x06: ORDER=MSB, ACAL=1(自动校准), BUFEN=1(开启输入缓冲) + * GML670 50kg 传感器输出阻抗 1kΩ,Buffer OFF + PGA=64 时输入阻抗仅 ~8kΩ + * (datasheet Table 10),导致 ~11% 负载误差。开 Buffer 后输入阻抗 >10MΩ。 + */ + ads1256_write_reg(REG_STATUS, 0x06); + ads1256_write_reg(REG_MUX, 0x01); + /* ADCON: CLK_OUT=OFF, SDCS=OFF, PGA=64 */ + ads1256_write_reg(REG_ADCON, 0x07); + /* + * DRATE: 3750 SPS (0xC0) + * 50Hz 循环 = 20ms 预算。4 通道各采 10 次均值: + * 单通道 = t18(0.44ms) + 9 × 1/3750(0.27ms) = 2.87ms + * 4 通道 = 11.5ms,余量 8.5ms + * 相比 30kSPS: ENOB 从 16.5 → 17.4 bit (Buffer On, PGA=64) + */ + ads1256_write_reg(REG_DRATE, 0xC0); + + /* 显式自校准:Buffer/PGA 变更后必须重新校准 (datasheet p25) */ + ads1256_write_cmd(CMD_SELFCAL); + /* SELFCAL 完成后 DRDY 拉低 (datasheet: "Do not send additional commands + after issuing this command until DRDY goes low") */ + ads1256_wait_drdy(500); + + /* 回读关键寄存器验证 SPI 通信正确性 */ + uint8_t status = ads1256_read_reg(REG_STATUS); + uint8_t adcon = ads1256_read_reg(REG_ADCON); + uint8_t drate = ads1256_read_reg(REG_DRATE); + LOG_INF("ADS1256 regs: STATUS=0x%02X ADCON=0x%02X DRATE=0x%02X", status, adcon, drate); + + if (drate != 0xC0) { + LOG_ERR("ADS1256 register verify failed (DRATE=0x%02X, expected 0xC0)", drate); + return -EIO; + } + + LOG_INF("ADS1256 initialized (BUFEN=1, PGA=64, 3750SPS)"); + return 0; +}