#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 REG_IO 0x04 #define REG_OFC0 0x05 #define REG_OFC1 0x06 #define REG_OFC2 0x07 #define REG_FSC0 0x08 #define REG_FSC1 0x09 #define REG_FSC2 0x0A #define SPI_OP (SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_LINES_SINGLE) /* --- 硬件资源 --- */ static struct spi_config spi_cfg; static const struct device *spi_dev; static const struct gpio_dt_spec cs_spec = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(ads1256)); 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); static const struct gpio_dt_spec pdwn_spec = GPIO_DT_SPEC_GET(DT_ALIAS(ads_pdwn), gpios); struct ads1256_reg_snapshot { uint8_t status; uint8_t mux; uint8_t adcon; uint8_t drate; uint8_t io; uint8_t ofc0; uint8_t ofc1; uint8_t ofc2; uint8_t fsc0; uint8_t fsc1; uint8_t fsc2; }; /** * @brief 等待 DRDY 拉低(转换结果可读),带超时保护。 * * @param timeout_ms 超时时间(毫秒)。 * * @retval 0 DRDY 在超时前变为可读状态。 * @retval -ETIMEDOUT 超时后仍未等到本次转换结果。 */ int ads1256_wait_drdy(uint16_t timeout_ms) { /* drdy_spec 配了 GPIO_ACTIVE_LOW,逻辑 1 表示 DRDY 有效(物理拉低) */ 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); return -ETIMEDOUT; } } return 0; } /** * @brief 向 ADS1256 写单个寄存器。 * * @param reg 目标寄存器地址。 * @param val 要写入的寄存器值。 */ void ads1256_write_reg(uint8_t reg, uint8_t val) { ads1256_wait_drdy(50); 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); /* t11: WREG 后至少 4 × tCLKIN ≈ 0.5µs */ k_busy_wait(2); } /** * @brief 从 ADS1256 读单个寄存器。 * * ADS1256 RREG 时序要求:命令+数据在同一次 CS 拉低内完成, * 中间需 t6 延时。这里用单次 transceive 保证 CS 不释放。 * * @param reg 目标寄存器地址。 * * @return 读取到的寄存器值。 */ uint8_t ads1256_read_reg(uint8_t reg) { ads1256_wait_drdy(50); /* tx: [RREG|reg, 0x00, dummy_for_t6, dummy_read] * rx: [x, x, x, data] — 前 3 字节是命令+延时期间的垃圾 */ uint8_t tx_buf[4] = { CMD_RREG | reg, 0x00, 0xFF, 0xFF }; uint8_t rx_buf[4] = { 0 }; struct spi_buf tx = { .buf = tx_buf, .len = 4 }; struct spi_buf_set tx_set = { .buffers = &tx, .count = 1 }; struct spi_buf rx = { .buf = rx_buf, .len = 4 }; struct spi_buf_set rx_set = { .buffers = &rx, .count = 1 }; spi_transceive(spi_dev, &spi_cfg, &tx_set, &rx_set); return rx_buf[3]; } /** * @brief 连续读取 ADS1256 关键寄存器,形成一次诊断快照。 * * @param snapshot 输出快照对象,不能为空。 */ static void ads1256_read_snapshot(struct ads1256_reg_snapshot *snapshot) { snapshot->status = ads1256_read_reg(REG_STATUS); snapshot->mux = ads1256_read_reg(REG_MUX); snapshot->adcon = ads1256_read_reg(REG_ADCON); snapshot->drate = ads1256_read_reg(REG_DRATE); snapshot->io = ads1256_read_reg(REG_IO); snapshot->ofc0 = ads1256_read_reg(REG_OFC0); snapshot->ofc1 = ads1256_read_reg(REG_OFC1); snapshot->ofc2 = ads1256_read_reg(REG_OFC2); snapshot->fsc0 = ads1256_read_reg(REG_FSC0); snapshot->fsc1 = ads1256_read_reg(REG_FSC1); snapshot->fsc2 = ads1256_read_reg(REG_FSC2); } /** * @brief 仅打印恢复前后发生变化的寄存器。 * * @param tag 日志标签,用于区分恢复成功与失败后的变化。 * @param before 恢复前寄存器快照。 * @param after 恢复后寄存器快照。 */ static void ads1256_log_snapshot_delta( const char *tag, const struct ads1256_reg_snapshot *before, const struct ads1256_reg_snapshot *after) { if (before->status != after->status) { LOG_WRN("%s STATUS: 0x%02X -> 0x%02X", tag, before->status, after->status); } if (before->mux != after->mux) { LOG_WRN("%s MUX: 0x%02X -> 0x%02X", tag, before->mux, after->mux); } if (before->adcon != after->adcon) { LOG_WRN("%s ADCON: 0x%02X -> 0x%02X", tag, before->adcon, after->adcon); } if (before->drate != after->drate) { LOG_WRN("%s DRATE: 0x%02X -> 0x%02X", tag, before->drate, after->drate); } if (before->io != after->io) { LOG_WRN("%s IO: 0x%02X -> 0x%02X", tag, before->io, after->io); } if (before->ofc0 != after->ofc0) { LOG_WRN("%s OFC0: 0x%02X -> 0x%02X", tag, before->ofc0, after->ofc0); } if (before->ofc1 != after->ofc1) { LOG_WRN("%s OFC1: 0x%02X -> 0x%02X", tag, before->ofc1, after->ofc1); } if (before->ofc2 != after->ofc2) { LOG_WRN("%s OFC2: 0x%02X -> 0x%02X", tag, before->ofc2, after->ofc2); } if (before->fsc0 != after->fsc0) { LOG_WRN("%s FSC0: 0x%02X -> 0x%02X", tag, before->fsc0, after->fsc0); } if (before->fsc1 != after->fsc1) { LOG_WRN("%s FSC1: 0x%02X -> 0x%02X", tag, before->fsc1, after->fsc1); } if (before->fsc2 != after->fsc2) { LOG_WRN("%s FSC2: 0x%02X -> 0x%02X", tag, before->fsc2, after->fsc2); } } /** * @brief 发送单字节命令到 ADS1256。 * * @param cmd 命令字。 */ void ads1256_write_cmd(uint8_t cmd) { ads1256_wait_drdy(50); 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); } /** * @brief 发送 SYNC + WAKEUP 命令,触发一次同步采样。 * * SYNC 和 WAKEUP 必须在同一次 CS 内完成,用单次 transceive 保持 CS。 * 中间插入 dummy 字节满足 t11 延时 (24 × tCLKIN ≈ 3.1µs)。 */ void ads1256_sync_wakeup(void) { /* [SYNC, dummy(延时), WAKEUP] — 500kHz 下每字节 16µs,1 字节 dummy 远超 3.1µs */ uint8_t tx_buf[3] = { CMD_SYNC, 0xFF, CMD_WAKEUP }; 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); } /** * @brief 硬件复位 ADS1256(通过 RESET 引脚),等待复位后自校准完成。 * * @retval 0 芯片完成复位并重新进入可通信状态。 * @retval -ETIMEDOUT 复位后等待 DRDY 超时,芯片未按预期响应。 */ int 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); return ads1256_wait_drdy(500); } /** * @brief 读取当前 24 位转换结果,符号扩展为 int32_t。 * * RDATA 命令后需 t6 延时再读 3 字节数据,全部在同一次 CS 内完成。 * * @return 当前 ADC 转换原始值。 */ int32_t ads1256_read_data(void) { /* tx: [RDATA, dummy(t6延时), MSB, MID, LSB] * rx: [x, x, MSB, MID, LSB] */ uint8_t tx_buf[5] = { CMD_RDATA, 0xFF, 0xFF, 0xFF, 0xFF }; uint8_t rx_buf[5] = { 0 }; struct spi_buf tx = { .buf = tx_buf, .len = 5 }; struct spi_buf_set tx_set = { .buffers = &tx, .count = 1 }; struct spi_buf rx = { .buf = rx_buf, .len = 5 }; struct spi_buf_set rx_set = { .buffers = &rx, .count = 1 }; spi_transceive(spi_dev, &spi_cfg, &tx_set, &rx_set); int32_t val = ((int32_t)rx_buf[2] << 16) | ((int32_t)rx_buf[3] << 8) | rx_buf[4]; if (val & 0x800000) val |= 0xFF000000; return -val; } /** * @brief 初始化 ADS1256:配置 GPIO/SPI,复位芯片,写入寄存器,自校准。 * * @retval 0 初始化成功,芯片参数已经写入并校验通过。 * @retval -ENODEV SPI 控制器未就绪,无法访问 ADS1256。 * @retval -ETIMEDOUT 复位或校准等待阶段未收到芯片响应。 * @retval -EIO 寄存器回读校验失败,SPI 通信结果不可信。 */ int ads1256_init(void) { /* GPIO */ /* PDWN 低有效:配为 INACTIVE(物理高电平) 保持芯片正常运行 */ gpio_pin_configure_dt(&pdwn_spec, GPIO_OUTPUT_INACTIVE); gpio_pin_configure_dt(&reset_spec, GPIO_OUTPUT_INACTIVE); gpio_pin_configure_dt(&drdy_spec, GPIO_INPUT); /* SPI — CS 由 SPIM 驱动自动管理 */ spi_dev = DEVICE_DT_GET(DT_NODELABEL(spi1)); spi_cfg.operation = SPI_OP; spi_cfg.frequency = 500000; spi_cfg.slave = 0; spi_cfg.cs.gpio = cs_spec; spi_cfg.cs.delay = 0; if (!device_is_ready(spi_dev)) { LOG_ERR("SPI device not ready"); return -ENODEV; } int ret = ads1256_hwreset(); if (ret) { LOG_ERR("ADS1256 not detected (DRDY no response after reset)"); return ret; } /* 关闭连续输出模式,进入命令模式 */ 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) { if (status == 0x00 && adcon == 0x00 && drate == 0x00) { LOG_ERR("ADS1256 SPI read failure (all regs 0x00) — check MISO wiring"); } else if (status == 0xFF && adcon == 0xFF && drate == 0xFF) { LOG_ERR("ADS1256 SPI read failure (all regs 0xFF) — MISO floating or not connected"); } else { 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; } /** * @brief EMI 恢复:重写关键寄存器并验证,用于马达干扰后自恢复。 * * @param max_retries 最大重试次数,每次间隔 20ms。 * * @retval 0 恢复成功。 * @retval -EIO 达到最大重试次数仍未恢复。 */ int ads1256_recover(int max_retries) { struct ads1256_reg_snapshot before_snapshot; ads1256_read_snapshot(&before_snapshot); for (int i = 0; i < max_retries; i++) { ads1256_write_reg(REG_STATUS, 0x06); ads1256_write_reg(REG_ADCON, 0x07); ads1256_write_reg(REG_DRATE, 0xC0); ads1256_sync_wakeup(); k_msleep(20); uint8_t adcon = ads1256_read_reg(REG_ADCON); uint8_t drate = ads1256_read_reg(REG_DRATE); if (adcon == 0x07 && drate == 0xC0) { struct ads1256_reg_snapshot after_snapshot; ads1256_read_snapshot(&after_snapshot); ads1256_log_snapshot_delta("ADS1256 recover reg change", &before_snapshot, &after_snapshot); LOG_WRN("ADS1256 recovered after %d retries", i + 1); return 0; } } struct ads1256_reg_snapshot failed_snapshot; ads1256_read_snapshot(&failed_snapshot); ads1256_log_snapshot_delta( "ADS1256 recover reg change after retries exhausted", &before_snapshot, &failed_snapshot); LOG_ERR("ADS1256 recovery failed after %d retries", max_retries); return -EIO; }