From 6770f35a0737f1e930ea7ac1b014cfe802810549 Mon Sep 17 00:00:00 2001 From: pNexus Date: Wed, 20 May 2026 20:12:53 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(ads1256):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=AF=84=E5=AD=98=E5=99=A8=E5=BF=AB=E7=85=A7=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E4=B8=8E=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=BC=BA=E6=81=A2=E5=A4=8D=E8=BF=87=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E5=8F=AF=E8=BF=BD=E8=B8=AA=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ads1256.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/ads1256.c b/src/ads1256.c index 59c876f..c20c639 100644 --- a/src/ads1256.c +++ b/src/ads1256.c @@ -22,6 +22,13 @@ LOG_MODULE_REGISTER(ads1256, LOG_LEVEL_INF); #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) @@ -32,6 +39,20 @@ static const struct gpio_dt_spec cs_spec = GPIO_DT_SPEC_GET(DT_ALIAS(ads_cs), 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); +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 拉低(转换结果可读),带超时保护。 * @@ -95,6 +116,71 @@ uint8_t ads1256_read_reg(uint8_t reg) { return rx_val; } +/** + * @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。 * @@ -259,6 +345,9 @@ int ads1256_init(void) { * @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); @@ -269,10 +358,17 @@ int ads1256_recover(int max_retries) { 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; } From ec1ba14e4661548b03388d1c8d3d7f0cee45d3c8 Mon Sep 17 00:00:00 2001 From: pNexus Date: Thu, 21 May 2026 15:01:26 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(ads1256):=20=E4=BF=AE=E5=A4=8D=20SPI=20?= =?UTF-8?q?=E6=97=A0=E6=B3=A2=E5=BD=A2=E9=97=AE=E9=A2=98=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20PDWN=20=E5=BC=95=E8=84=9A=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PDWN 浮空导致 ADS1256 进入 power-down 模式,DRDY 无响应且 SPI 总线 无输出。同时将 CS 管理从手动 GPIO 改为 SPIM 驱动自动控制,消除引脚 所有权冲突;读寄存器和读数据改用 spi_transceive 保证单次 CS 内完成。 --- app.overlay | 10 +++---- src/ads1256.c | 78 +++++++++++++++++++++++---------------------------- 2 files changed, 40 insertions(+), 48 deletions(-) diff --git a/app.overlay b/app.overlay index 8b7a876..5fbb63f 100644 --- a/app.overlay +++ b/app.overlay @@ -15,7 +15,7 @@ pinctrl-0 = <&spi1_default>; pinctrl-1 = <&spi1_sleep>; pinctrl-names = "default", "sleep"; - + /* CS 引脚配置 */ cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; @@ -46,21 +46,21 @@ / { aliases { - ads-cs = &ads_cs_pin; ads-drdy = &ads_drdy_pin; ads-reset = &ads_reset_pin; + ads-pdwn = &ads_pdwn_pin; }; ads1256_control { compatible = "gpio-keys"; - ads_cs_pin: ads_cs { - gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; - }; ads_drdy_pin: ads_drdy { gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; }; ads_reset_pin: ads_reset { gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; }; + ads_pdwn_pin: ads_pdwn { + gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + }; }; }; diff --git a/src/ads1256.c b/src/ads1256.c index c20c639..36f86df 100644 --- a/src/ads1256.c +++ b/src/ads1256.c @@ -35,9 +35,10 @@ LOG_MODULE_REGISTER(ads1256, LOG_LEVEL_INF); /* --- 硬件资源 --- */ static struct spi_config spi_cfg; static const struct device *spi_dev; -static const struct gpio_dt_spec cs_spec = GPIO_DT_SPEC_GET(DT_ALIAS(ads_cs), gpios); +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; @@ -82,12 +83,10 @@ int ads1256_wait_drdy(uint16_t timeout_ms) { */ 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); } @@ -95,25 +94,25 @@ void ads1256_write_reg(uint8_t reg, uint8_t val) { /** * @brief 从 ADS1256 读单个寄存器。 * + * ADS1256 RREG 时序要求:命令+数据在同一次 CS 拉低内完成, + * 中间需 t6 延时。这里用单次 transceive 保证 CS 不释放。 + * * @param reg 目标寄存器地址。 * * @return 读取到的寄存器值。 */ 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 }; + /* 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 }; - 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 rx = { .buf = rx_buf, .len = 4 }; 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; + spi_transceive(spi_dev, &spi_cfg, &tx_set, &rx_set); + return rx_buf[3]; } /** @@ -188,31 +187,23 @@ static void ads1256_log_snapshot_delta( */ 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); } /** * @brief 发送 SYNC + WAKEUP 命令,触发一次同步采样。 * - * @return 无返回值。 + * SYNC 和 WAKEUP 必须在同一次 CS 内完成,用单次 transceive 保持 CS。 + * 中间插入 dummy 字节满足 t11 延时 (24 × tCLKIN ≈ 3.1µs)。 */ 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); + /* [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); } /** @@ -235,22 +226,21 @@ int ads1256_hwreset(void) { /** * @brief 读取当前 24 位转换结果,符号扩展为 int32_t。 * + * RDATA 命令后需 t6 延时再读 3 字节数据,全部在同一次 CS 内完成。 + * * @return 当前 ADC 转换原始值。 */ 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 }; + /* 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 }; - 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 rx = { .buf = rx_buf, .len = 5 }; 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]; + 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; } @@ -265,16 +255,18 @@ int32_t ads1256_read_data(void) { */ int ads1256_init(void) { /* GPIO */ - /* CS 加内部上拉:EMI 干扰时维持高电平,阻止 ADS1256 误收命令 */ - gpio_pin_configure_dt(&cs_spec, GPIO_OUTPUT_INACTIVE | GPIO_PULL_UP); + /* 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 */ + /* 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"); From 7bf7339f3f7f9a1c5cc9294a6181485ec421e9d6 Mon Sep 17 00:00:00 2001 From: pNexus Date: Wed, 27 May 2026 09:35:29 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(button):=20=E6=96=B0=E5=A2=9E=20EMI=20?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=20LED=20=E6=8C=87=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADS1256 受电机 EMI 干扰自恢复后,点亮 LED1 并 3 秒后自动熄灭, 给出可见的故障告警。复用按键反馈所用的 LED1,但用独立的 emi_off_work 延时句柄,避免与按键闪烁的 blink_remaining 节拍 状态机混用导致错乱;重复触发用 k_work_reschedule 刷新熄灭 时刻,支持连续 EMI 事件累计指示。 sensor 线程在 EMI 自恢复路径调用 button_led_indicate_emi() 触发指示。 --- inc/button.h | 10 ++++++++++ src/button.c | 24 ++++++++++++++++++++++++ src/sensor.c | 3 +++ 3 files changed, 37 insertions(+) diff --git a/inc/button.h b/inc/button.h index f3389f2..87d3655 100644 --- a/inc/button.h +++ b/inc/button.h @@ -1,3 +1,13 @@ #pragma once int button_init(void); + +/** + * @brief EMI 自恢复事件指示:点亮 LED1 并在 3 秒后自动熄灭。 + * + * 复用按键反馈所用的 LED1,外部子系统(如 sensor)在检测到 ADS1256 + * 寄存器被 EMI 干扰并完成恢复后调用,给出可见的故障告警。 + * + * @return 无返回值。 + */ +void button_led_indicate_emi(void); diff --git a/src/button.c b/src/button.c index 7b9f475..299bd60 100644 --- a/src/button.c +++ b/src/button.c @@ -13,9 +13,11 @@ static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); static struct gpio_callback btn_cb_data; static struct k_work_delayable led_off_work; +static struct k_work_delayable emi_off_work; #define LED_FLASH_MS 200 #define LED_BLINK_COUNT 3 +#define EMI_LED_HOLD_MS 3000 static struct k_work tare_work; static volatile int blink_remaining; @@ -38,6 +40,21 @@ static void led_off_handler(struct k_work *work) { } } +/** + * @brief EMI 指示熄灯 work:3 秒延时到期后熄灭 LED1。 + * + * 与 led_off_work 分开是因为按键闪烁序列依赖 blink_remaining 的奇偶节拍, + * EMI 指示是单次长亮,混用会让 blink_remaining 状态机错乱。 + * + * @param work Zephyr delayable work 入口参数,当前实现未直接使用。 + * + * @return 无返回值。 + */ +static void emi_off_handler(struct k_work *work) { + ARG_UNUSED(work); + gpio_pin_set_dt(&led1, 0); +} + /** * @brief 在工作队列上下文执行去皮,并启动 LED 闪烁反馈。 * @@ -102,7 +119,14 @@ int button_init(void) { k_work_init(&tare_work, tare_work_handler); k_work_init_delayable(&led_off_work, led_off_handler); + k_work_init_delayable(&emi_off_work, emi_off_handler); LOG_INF("Button1 → tare + LED1 flash initialized"); return 0; } + +void button_led_indicate_emi(void) { + /* 直接拉高 LED 并延后 3 秒熄灭;重复触发会刷新熄灭时刻,从而支持连续 EMI 事件累计指示 */ + gpio_pin_set_dt(&led1, 1); + k_work_reschedule(&emi_off_work, K_MSEC(EMI_LED_HOLD_MS)); +} diff --git a/src/sensor.c b/src/sensor.c index 9015aac..2717693 100644 --- a/src/sensor.c +++ b/src/sensor.c @@ -1,6 +1,7 @@ #include "sensor.h" #include "ads1256.h" #include "ble_transport.h" +#include "button.h" #include "calibration.h" #include "comm_protocol.h" @@ -237,6 +238,8 @@ static void sensor_thread_fn(void *p1, void *p2, void *p3) { if (acq < 10) { LOG_ERR("EMI detected (acq=%lld ms)", (long long)acq); ads1256_recover(10); + /* 点亮 LED1 作为 EMI 恢复指示,3 秒后由 button 模块自动熄灭 */ + button_led_indicate_emi(); continue; }