fix(vd960Loop): 灵敏度收敛 4 级制 + 运行中配置立即生效 (协议 V1.08)

根因: 设置不同灵敏度感应高度无区别
1. unpack_pkg_set_mcjq_param 未同步 loop_SensLevel → 改灵敏度需重启才生效
2. 9级制映射 sensitvity & 0x03 折叠 (默认7→3, 3→3, 8→0)
3. (测试陷阱) 金属块过大信号饱和, 建议小金属块测临界高度

修复:
- 提取 sens_level_from_config() 映射函数, 4级制 0~3 一一对应 (9级制后改)
- unpack_pkg_set_mcjq_param 补 loop_SensLevel 同步 (运行中立即生效)
- para_store_init 上电路径改用同一函数
- 协议 V1.08: Sensitivity 0~9级 → 4级制 0~3 (默认2)
- tests/test_sens_mapping.c gcc 隔离单测 PASS (0~3无折叠/兼容旧默认7→3)
This commit is contained in:
wangfq
2026-08-25 11:52:14 +08:00
parent fad99a550b
commit bbf5492fe2
9 changed files with 167 additions and 7 deletions
+4 -3
View File
@@ -147,7 +147,7 @@ A2 00 51 00: 第0级灵敏度,0x00A2为进入 1620x0051为离开 81
* AutoMode: 是否处于自动模式(自动调频),0表示不启用,默认0。
* Sensitivity 低四位表示灵敏度,0~9级,值越大,灵敏度越高,默认7;高四位表示高低频,其中,\[4,5\]表示线圈的高低频。
* Sensitivity 低四位表示灵敏度序号,**4级制 0~3**(0 最低,3 最高),默认 2;9级制灵敏度后续版本实现。高四位表示高低频,其中,\[4,5\]表示线圈的高低频。
* Loop\_Delay: 地感延时时间,0~200, 0.1s为一级,延时时间范围:\[0, 20秒\];
@@ -212,7 +212,7 @@ SensData:传感数据
| 字段 | 字节数 | 内容 | 详情 |
| --- | --- | --- | --- |
| 配置 1 | 1 | freq\_level(2bit), Direction(1bit)freq\_type(1bit), sens(4bit) | 线圈 1 配置freq\_level:高低频,两个比特位,00 表示高频(33nF),01表示中高(43nF), 10表示中低(66nF), 11表示低频(76nF)。Direction: 0 表示触发,1 表示方向判别,freq\_type: 0 表示初始频率,1 表示当前实时频率。sens:低四位表示当前的灵敏度等级 |
| 配置 1 | 1 | freq\_level(2bit), Direction(1bit)freq\_type(1bit), sens(4bit) | 线圈 1 配置freq\_level:高低频,两个比特位,00 表示高频(33nF),01表示中高(43nF), 10表示中低(66nF), 11表示低频(76nF)。Direction: 0 表示触发,1 表示方向判别,freq\_type: 0 表示初始频率,1 表示当前实时频率。sens:低四位表示当前的灵敏度等级(**4级制 0~3,值越大越灵敏**) |
| 线圈评估条件 | 1 | condition(4bit), loop\_state(1bit),<br>car\_state(1bit),<br>report\_msic(2bit) | loop\_condition 正常(0)有效,环境状态条件(环境状态评估值,值越大,干扰越大),高四位有效。低四位中 ,其中第3位表示线圈状态,0 表示正常,1 表示线圈断开;第 2 位表示有无车,0 表示无车,1 表示有车;低两位表示**杂项类型**,0b00表示时间量,0b01表示线圈断开次数;0b10表示车流量数;0b11表示继电器输出次数 |
| 频率 1 | 3 | frequent | 低字节在前,无符号 |
| 变化量 1 | 3 | variation | **有符号** 3 字节,低字节在前(小端),补码表示。定义 `variation = loop_Origin loop_CAPVD`(基线 − 当前值)。**正值**表示当前值低于基线(车辆/金属进入,检测裕量方向);**负值**表示当前值高于基线(反向漂移/异常抬升)。值域 −8388608 ~ +8388607(−2²³ ~ +2²³−1,3B 补码标准值域),发送端已做饱和限幅,不会回绕。<br>**接收端解析**:读入 3 字节后须做符号扩展——`v = b0\|(b1<<8)\|(b2<<16); if (v & 0x800000) v \|= 0xFF000000;` |
@@ -261,4 +261,5 @@ FE 38 校验字节(XOR=0xFE, SUM=0x38
| V1.04 | 2026-07-02 | 主动上报增加继电器输出次数 类型 | wangfq |
| V1.05 | 2026-07-14 | 多线圈上报(0x0C)变化量 variation 由 2 字节无符号扩展为 **3 字节有符号**(补码, 小端),定义 `variation = Origin CAPVD`(正=车/裕量, 负=反向漂移);每单元 11→12 字节,Len 47→51。接收端须做符号扩展。**Loop 固件与 DBN 固件须同版本发布。** | wangfq |
| V1.06 | 2026-07-15 | 修正杂项时间量单位说明:5ms → **50ms**(对齐固件 50ms tick 实现,TMR15 5ms×10 分频) | wangfq |
| V1.07 | 2026-08-25 | variation 值域表述勘误:`±8388607`**8388608 ~ +8388607**(对齐代码负向饱和边界 0x800000,3B 补码标准值域) | wangfq |
| V1.07 | 2026-08-25 | variation 值域表述勘误:`±8388607`**8388608 ~ +8388607**(对齐代码负向饱和边界 0x800000,3B 补码标准值域) | wangfq |
| V1.08 | 2026-08-25 | 灵敏度收敛为 **4级制 0~3**(0 最低,3 最高,默认 2):修复 9级制映射 `&0x03` 折叠(7→3、8→0 错乱)导致"改灵敏度感应高度无区别";运行中配置灵敏度立即生效(不再依赖重启)。9级制后续版本实现 | wangfq |
+37
View File
@@ -4,6 +4,43 @@
---
## 2026-08-25 — 灵敏度 4 级制修复(协议 V1.08)
### 背景
现场用金属块测感应高度,设置不同灵敏度档位**感应高度无区别**。排查发现三个根因:
| # | 根因 | 位置 |
|---|------|------|
| 1 | **运行中配置命令不同步检测单元**`unpack_pkg_set_mcjq_param()` 只更新 `g_loop_cng_info.loop_cng[].sensitvity` + 写 flash**没有同步 `g_loop_states.loop_unit[].loop_SensLevel`** → 改灵敏度不重启不生效(对比上电路径 `para_store_init()` 有同步) | main.c:576 |
| 2 | **9级制映射 `& 0x03` 折叠**:协议 0~9 级(默认 7),`sensitvity & 0x03` 只取低 2 位 → 7→3、**3→3(从默认 7 改到 3 无效)**、8→0(反向折叠) | storage.c:184 |
| 3 | 测试陷阱:金属块过大/过近信号饱和,4 档阈值都在饱和区 → 感应高度差异不可见(建议小金属块/测临界距离) | 测试方法 |
### 修复(方案 B4 级制)
- 提取映射函数 `sens_level_from_config()`storage.c/h):**4级制 0~3 一一对应**,9 级制后续实现时只改这一处
- `unpack_pkg_set_mcjq_param()``loop_SensLevel` 同步 → 运行中配置立即生效
- `para_store_init()` 上电路径改用同一函数(消除双处映射漂移)
- 协议 V1.08Sensitivity 语义 0~9 级 → **4 级制 0~3**(默认 2
- 新增 `tests/test_sens_mapping.c` gcc 隔离单测(mock flash.h/FreeRTOS.h/task.h + FLP GPIO 宏):验证 0~3 无折叠、兼容旧默认 7→3、SensTable 单调
### 改动
| 文件 | 内容 |
|------|------|
| `src/storage.c` | 新增 `sens_level_from_config()``para_store_init` 改用 |
| `src/main.c` | `unpack_pkg_set_mcjq_param` 补 loop_SensLevel 同步(运行中生效) |
| `inc/storage.h` | sensitvity 注释 4 级制;函数声明 |
| `tests/test_sens_mapping.c` | 新增映射单测(gcc PASS |
| `tests/mock/` | flash.h/FreeRTOS.h/task.h 隔离测试 mock |
| `docs/DLD960Loop_串口通信协议.md` | V1.08:灵敏度 4 级制 |
### 验证
gcc 单测 PASS0→0, 1→1, 2→2, 3→3 无折叠;`7&0x0F → 3` 兼容旧默认。实机待金属块复测(建议小金属块测临界感应高度)。
---
## 2026-08-25 — variation ↔ ΔL/ΔL-L 物理对应分析与工具
### 背景
+4
View File
@@ -0,0 +1,4 @@
/* mock FreeRTOS.h — gcc 隔离测试用空壳 */
#ifndef MOCK_FREERTOS_H
#define MOCK_FREERTOS_H
#endif
+15
View File
@@ -0,0 +1,15 @@
/* mock flash.h — gcc 隔离测试用 (storage.c 依赖的 flash API + 宏) */
#ifndef MOCK_FLASH_H
#define MOCK_FLASH_H
#include <stdint.h>
#define DLD_BUFEER_SIZE 100
#define DLD_FLASH_ADDRESS_START (0x08000000 + 1024 * 63 + 512)
#define DLD_FLASH_MAX_SIZE (0x08000000 + 1024 * 64)
#define MAX_Store_Size DLD_BUFEER_SIZE
void flash_write(uint32_t addr, uint16_t *buf, uint16_t len);
void flash_read(uint32_t addr, uint16_t *buf, uint16_t len);
#endif
+4
View File
@@ -0,0 +1,4 @@
/* mock task.h — gcc 隔离测试用空壳 (TaskHandle_t 由 TaskLoop.h 自行定义) */
#ifndef MOCK_TASK_H
#define MOCK_TASK_H
#endif
+87
View File
@@ -0,0 +1,87 @@
/*
* test_sens_mapping.c — 灵敏度映射 gcc 隔离单测 (vd960Loop)
*
* 验证对象: storage.c 的 sens_level_from_config() (4级制 0~3 一一对应)
* 背景: 2026-08-25 修复 "设置不同灵敏度感应高度无区别"
* - 根因1: 运行中配置命令未同步 loop_SensLevel (main.c unpack_pkg_set_mcjq_param)
* - 根因2: 9级制映射 &0x03 折叠 (7→3, 8→0 错乱) → 收敛为 4级制
* 本测试保护: 9级制后续实现时, 0~3 映射不得破坏
*
* 编译运行:
* gcc -I tests/mock -I utilities/at32f421_freertos_demo/inc \
* tests/test_sens_mapping.c -o /tmp/test_sens_mapping && /tmp/test_sens_mapping
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
/* ---- 被测试对象: include storage.c 使 static 逻辑同编译单元可见 ---- */
/* 硬件 GPIO 宏 mock (storage.c 的 set_flp_level 使用, 空实现) */
#define FLPA1_HIGH do { } while (0)
#define FLPA1_LOW do { } while (0)
#define FLPA2_HIGH do { } while (0)
#define FLPA2_LOW do { } while (0)
#define FLPB1_HIGH do { } while (0)
#define FLPB1_LOW do { } while (0)
#define FLPB2_HIGH do { } while (0)
#define FLPB2_LOW do { } while (0)
#define FLPC1_HIGH do { } while (0)
#define FLPC1_LOW do { } while (0)
#define FLPC2_HIGH do { } while (0)
#define FLPC2_LOW do { } while (0)
#define FLPD1_HIGH do { } while (0)
#define FLPD1_LOW do { } while (0)
#define FLPD2_HIGH do { } while (0)
#define FLPD2_LOW do { } while (0)
/* storage.c 引用的系统函数 (消除隐式声明 warning) */
void nvic_system_reset(void);
void delay_ms(uint32_t ms);
#include "../utilities/at32f421_freertos_demo/src/storage.c"
/* ---- 外部符号 stub ---- */
#include "flash.h"
void flash_write(uint32_t addr, uint16_t *buf, uint16_t len) { (void)addr; (void)buf; (void)len; }
void flash_read(uint32_t addr, uint16_t *buf, uint16_t len) { (void)addr; (void)buf; (void)len; }
void nvic_system_reset(void) { abort(); }
void delay_ms(uint32_t ms) { (void)ms; }
const uint16_t SensTable[4] = {216, 108, 36, 10};
const uint16_t SensTable_1[4] = {108, 72, 18, 9};
Loop154_States g_loop_states; /* TaskLoop.c 的全局, 此处提供定义 */
int main(void)
{
/* 4级制: 0~3 一一对应, 无折叠 */
assert(sens_level_from_config(0) == 0);
assert(sens_level_from_config(1) == 1);
assert(sens_level_from_config(2) == 2);
assert(sens_level_from_config(3) == 3);
/* 配置命令链路: pkg 低4位 & 0x0F 后传入 (main.c unpack 语义) */
/* 上位机发 0x00~0x03 → 档位 0~3 */
assert(sens_level_from_config(0x00 & 0x0F) == 0);
assert(sens_level_from_config(0x01 & 0x0F) == 1);
assert(sens_level_from_config(0x02 & 0x0F) == 2);
assert(sens_level_from_config(0x03 & 0x0F) == 3);
/* 兼容旧默认 7 (旧 9 级制默认): 7&0x0F=7 → &0x03=3 最高档, 行为连续 */
assert(sens_level_from_config(0x07 & 0x0F) == 3);
/* 出厂默认 loops_cng_default[0]=2 → SENS=2 (高灵敏度档) */
assert(sens_level_from_config(2) == 2);
/* 灵敏度表逐档单调: sens_in 递减 = 档位越高阈值越低 (越灵敏) */
assert(SensTable[0] > SensTable[1]);
assert(SensTable[1] > SensTable[2]);
assert(SensTable[2] > SensTable[3]);
printf("PASS: sens_level_from_config 4级映射无折叠\n");
printf(" SENS 0→%d, 1→%d, 2→%d, 3→%d\n",
sens_level_from_config(0), sens_level_from_config(1),
sens_level_from_config(2), sens_level_from_config(3));
printf(" SensTable: {216,108,36,10} 单调递减 ✓\n");
return 0;
}
@@ -111,7 +111,7 @@ typedef enum
typedef struct
{
uint8_t sensitvity; // 高四位表示灵敏度的数量,低四位表示当前灵敏度的序号(从0开始)
uint8_t sensitvity; // 灵敏度序号: 4级制 0~3 (值越大越灵敏, 默认2); 9级制后续版本实现
uint8_t delay_time;
uint8_t output_mode;
uint8_t direction_mode; // 方向判别模式,0 触发模式, 非0 表示方向判别模式
@@ -171,6 +171,9 @@ typedef struct _DBN_BLE_STATE_
void set_flp_level(uint8_t loop_num, uint8_t freq_level);
/* 灵敏度配置 → 检测档位映射 (4级制: 0~3 一一对应; 9级制后续实现) */
uint8_t sens_level_from_config(uint8_t sensitvity);
void set_factory_param(void);
void storage_dev(void);
void para_store_init(void);
@@ -584,8 +584,8 @@ static void unpack_pkg_set_mcjq_param(uint8_t *pkg, uint8_t len)
for(j = 0; j < _amount; j++)
{
k = pkg[i++];
unit->sensitvity = k & 0x0F;
unit->loopFreq_Level = k >> 4;
unit->sensitvity = k & 0x0F; // 低4位: 灵敏度序号 (4级制 0~3)
unit->loopFreq_Level = k >> 4; // 高4位: 频率档
unit->delay_time = pkg[i++];
k = pkg[i++];
unit->output_mode = k & 0x03;
@@ -593,6 +593,9 @@ static void unpack_pkg_set_mcjq_param(uint8_t *pkg, uint8_t len)
unit->exist_mode = pkg[i++];
unit->direction_mode = pkg[i++];
// 同步灵敏度到检测单元 (修复: 运行中配置必须立即生效, 不依赖重启)
g_loop_states.loop_unit[j].loop_SensLevel = sens_level_from_config(unit->sensitvity);
unit++;
}
@@ -113,6 +113,12 @@ void storage_dev(void)
}
/* 灵敏度配置 → 检测档位映射 (4级制: 0~3 一一对应; 9级制后续实现) */
uint8_t sens_level_from_config(uint8_t sensitvity)
{
return sensitvity & 0x03;
}
// set frequent level
void set_flp_level(uint8_t loop_num, uint8_t freq_level)
{
@@ -181,7 +187,7 @@ void para_store_init(void)
set_flp_level(i, unitout->loopFreq_Level);
// 同步灵敏度到检测单元
g_loop_states.loop_unit[i].loop_SensLevel = unitout->sensitvity & 0x03;
g_loop_states.loop_unit[i].loop_SensLevel = sens_level_from_config(unitout->sensitvity);
g_loop_states.loop_unit[i].SET_PLUS = unitout->output_mode & 0x01;
g_loop_states.loop_unit[i].SET_DLY = (unitout->output_mode >> 1) & 0x01;
g_loop_states.loop_unit[i].hold_time = unitout->exist_mode * 20 * 5;