简介:无线电流传感器软件流程图以PDF形式呈现,是一份面向嵌入式开发与物联网工程师的软件运行机制说明文档。该图完整梳理了传感器软件从初始化到电流数据采集、过滤转换、存储,再到无线通信收发的全流程,重点展示主程序、采样中断、无线接收中断及无线发送中断之间的协作关系。包体为单文件PDF,大小约40KB,轻量易查。目前已吸引66人学习浏览,适合在智能电网、工业自动化等场景下进行无线传感器软件架构设计或代码优化时参考。通过对照流程图,可清晰定位各功能模块的交互边界,理解中断处理与数据缓冲区的使用逻辑,有助于提升系统实时性与通信可靠性,为实际工程排错和性能调校提供直观依据。
1. 无线电流传感器的软件流程图,到底要画到什么粒度
拿到《无线电流传感器软件流程图.pdf》这个标题,第一反应往往是找一份现成的图。但真正做过物联网感知设备的人都知道,这种图的难点不在“画框”本身,而在于你想表达哪一层逻辑:是 MCU 的主循环调度,还是无线上报状态机,还是断线重传的数据补偿策略。同一台设备,三张图画出来完全不同。
无线电流传感器的软件核心,其实是把“交流/直流电流采样”和“无线传输”这两件事,在低功耗约束下拼在一起。采样要求周期性、确定性,无线要求非阻塞、可重连,两者天然有冲突。软件流程图要回答的,就是这个冲突怎么被调度解决。这篇博文面向的是要自己写固件、调协议、或者被要求“先补一张软件流程图”的工程师,内容会从图的结构一路落到代码和参数,最后讲讲怎么用图反过来校验协议。
先说一个反直觉的结论:这类设备的软件瓶颈通常不在采样精度,而在“数据的出生时间到真正发出去的时间”这段延迟管理。所以流程图里最先要画的不是采样函数,而是消息从哪里产生、在哪里排队、什么时候被无线模块取走。
2. 先理清软件流程图里的三条主路径
2.1 一条完整的无线电流传感器软件流程,必须有哪几段
设备上电之后,软件不会只跑一条直线。常见的设计是分成三条路径:内部自检路径、周期性测量路径、外部事件触发路径。画图时如果只画一条主循环,后面做低功耗和断线重传时一定会返工。
内部自检路径负责 ADC 基准校准、无线模块初始化、Flash 里配置参数的 CRC 校验。周期性测量路径是主路径,负责按设定间隔完成电流采样、数值计算、打包、上报。外部事件路径则处理按键唤醒、磁铁触发、通信指令下发这类异步事件。
三条路径不是并列执行的,而是在一个超级循环或者 RTOS 调度器里分时复用。流程图里要体现的是“谁让谁暂停”“谁可以打断谁”。通常我会把无线模块的接收回调放在最高优先级,因为网关下发的校时和阈值修改指令如果不能及时响应,整个采集系统的时间戳就会漂移。
2.2 两种典型的软件架构,图的结构完全不同
2.2.1 裸机超级循环
while(1) { process_power_manage(); // 低功耗调度,决定是否进入休眠 if(time_to_sample()) // 判断是否到达采样周期 { adc_sample_and_filter(); calculate_rms_or_dc(); } if(tx_queue_not_empty()) // 发送队列里有数据 { wireless_send_packet(); } process_serial_command(); // 处理配置指令 }这段代码对应流程图的主干:一个死循环,四个判断分支。注意process_power_manage()必须放在最前面,如果电量或休眠标志不允许本次采样,后面的采样分支就直接跳过。这样画出来的流程图会有一个“休眠判定”的菱形节点位于整个循环的入口处。
2.2.2 RTOS 多任务
如果使用 FreeRTOS 这类系统,流程图就要画成任务级的:一个采样任务、一个无线发送任务、一个指令处理任务,外加一个消息队列做任务间通信。流程图里会出现“阻塞等待队列”和“超时返回”这样的节点,和裸机版本有本质区别。
架构选型不影响流程图的基本骨架,但会影响你在图里画不画“中断服务函数”。裸机方案里 ADC 采样完成中断是重要节点,RTOS 方案里通常用信号量代替。建议先确定架构再画图,否则画到一半会被打断嵌套关系绕晕。
设备主循环当中最容易被漏画的,是“无线发送失败后的重试”这条回边。很多初版流程图把发送画成一步到位,实际代码里必须考虑发送队列在极端情况下被占满的情况,此时是丢弃最老数据还是丢弃当前数据,要有一个明确的策略节点。
3. 用流程图绘制软件从空图开始,画出可评审的第一版
3.1 先画事件流,再补功能块
直接打开流程图绘制软件就开画容易陷入细节。我建议的顺序是:先用一张表列出系统涉及的全部事件,再画图。事件表包含事件名称、触发源、是否可丢失、最大延迟,这四列信息直接决定流程图里要画哪些判定和超时分支。
| 事件名称 | 触发源 | 可丢失性 | 最大延迟 |
|---|---|---|---|
| 周期性采样触发 | 定时器 | 可丢一次 | 采样周期 |
| 校时指令 | 无线下行 | 不可丢 | 1 秒 |
| 阈值修改 | 无线下行 | 不可丢 | 1 秒 |
| 断线重连 | 链路检测 | 可丢 | 30 秒 |
| 低电压报警 | ADC 检测 | 不可丢 | 1 分钟 |
有了这张表,图里的每个判定节点就都有据可依。比如“采样触发”这个事件,在流程图上对应的是一个时间判定节点,旁边的注释要写明“允许跳过本次采样”,否则评审的人会问:为什么采样还有失败分支?
3.2 最小可用的流程图主干
用绘图工具新建文件时,推荐从竖向主干开始画,不要一开始就左右分叉。主干从上到下依次是:系统初始化、外设自检、加入无线网络、主循环、休眠判断、周期采样、数据滤波、队列写入、无线发送、发送结果判定。
主循环和休眠判断之间,标注“清醒时间比例”这个参数,让看图的人一眼知道设备功耗预算。采样和数据滤波之间标注“采样耗时上限”,比如 200 毫秒,这是为了保证无线发送不被打断。
具体的图形符号约定也值得统一:矩形表示处理步骤,菱形表示判断,圆角矩形表示状态,圆柱表示数据存储。团队里如果没有统一约定,同一张图在不同人机器上打开就是另一种含义,评审效率很低。
绘制过程中注意不要画成程序代码的结构图,流程图里的节点应该是一个相对独立的“阶段”,而不是一条条语句。比如“计算真有效值”是一个节点,它内部是两个周期的过采样加一次开方运算,这些内部动作不需要展开画。
3.3 关键节点必须带参数注释
图里每个判定节点旁边至少要有一个注释。比如发送失败重试判定,注释要写:重试次数上限 3 次,每次间隔 2 秒,超过后丢弃该帧并记录错误码。再比如电量判定节点,注释写:电压低于 3.3V 时停止无线发送,仅保留本地采样。
参数直接写在图上,比写在文档里有效得多。这样做的好处是流程图本身就成了设计文档,不需要再额外维护一份参数表。绘制软件建议选择一个支持图层功能的,把参数注释放在独立图层里,正式评审时打开,出图给客户时隐藏。
4. 从流程图到可下载运行的固件:状态机与调度实现
4.1 把流程图翻译成一个精简的调度器
画好的流程图最终要变成 C 代码。常见做法是写一个事件驱动的调度器,把流程图里的每个矩形节点变成一个函数指针,把判定节点变成条件分支。下面是一个适合无线电流传感器的精简示例:
typedef enum { ST_INIT, ST_SELF_CHECK, ST_JOIN_NETWORK, ST_IDLE, ST_SAMPLE, ST_PROCESS, ST_SEND, ST_SEND_RETRY, ST_SLEEP } sys_state_t; sys_state_t state = ST_INIT; while(1) { switch(state) { case ST_INIT: hardware_init(); state = ST_SELF_CHECK; break; case ST_SELF_CHECK: if(adc_selfcheck() == OK && rf_module_check() == OK) state = ST_JOIN_NETWORK; else state = ST_SLEEP; // 自检失败进入低功耗,等待外部唤醒 break; case ST_JOIN_NETWORK: if(join_network(10) == SUCCESS) // 10 秒超时 state = ST_IDLE; else state = ST_SLEEP; break; case ST_IDLE: if(time_to_sample()) state = ST_SAMPLE; else if(rx_cmd_pending()) handle_remote_cmd(); else state = ST_SLEEP; break; case ST_SAMPLE: adc_samples = adc_capture(CURRENT_CH, 128); // 连续采样 128 点 state = ST_PROCESS; break; case ST_PROCESS: rms_value = calculate_rms(adc_samples, 128); pack_payload(&tx_frame, rms_value); state = ST_SEND; break; case ST_SEND: if(wireless_send(&tx_frame) == SUCCESS) state = ST_IDLE; else state = ST_SEND_RETRY; break; case ST_SEND_RETRY: retry_count++; if(retry_count >= 3) { save_frame_to_flash(); // 三次失败落盘,等待下次上报 retry_count = 0; state = ST_IDLE; } else { delay_ms(2000); state = ST_SEND; } break; case ST_SLEEP: enter_sleep_mode(WAKEUP_TIMER); state = ST_IDLE; break; } }这段代码里最重要的参数有两个:adc_capture的采样点数和发送重试次数。采样点数决定测量精度和耗时,128点对应 50Hz 工频信号下的约两个完整周波,足以计算 RMS 又不会占用太长的 CPU 时间。重试次数3和间隔2000毫秒是功耗与可靠性的折中,改小影响可靠性,改大影响响应速度。
发送队列接收来自采样任务的待发送数据,发送失败后数据可以放进 Flash 留待下次,这种方式在网络不稳定时比较实用。流程图在 ST_SEND_RETRY 这个分支处,应画一个菱形判定“重试次数是否达到上限”,这样评审的人才能看出“落盘”这一动作的触发条件。
4.2 无线上报周期与采样周期的联动设置
无线电流传感器实际调试时最常被问到的问题是:“采样周期设多少合适,上报周期设多少合适?” 我的经验是两个周期解耦设计,不要在代码里写死同一个时间参数。
设置上报周期涉及一个关键参数:采样周期。采样周期决定测量分辨率,上报周期决定数据新鲜度,两者含义不同。代码里用两个独立定时器控制:
#define SAMPLE_INTERVAL_MS 1000 // 采样周期 1 秒 #define REPORT_INTERVAL_S 30 // 上报周期 30 秒 static uint32_t sample_tick = 0; static uint32_t report_tick = 0; void timer_isr_1s(void) { if(++sample_tick >= SAMPLE_INTERVAL_MS / 1000) { set_sample_flag(); sample_tick = 0; } if(++report_tick >= REPORT_INTERVAL_S) { set_report_flag(); report_tick = 0; } }流程图里要体现这两个标志位的检查位置:set_sample_flag()对应采样分支入口,set_report_flag()对应无线发送分支入口。如果设备每隔 30 秒才上报一次,那么中间 30 次采样结果只有最后一次会被发送,其余数据可以根据需要做均值或者丢弃,这个取舍同样要在流程图里注释清楚。
如果你用的是 NB-IoT 模组,上报周期的设置还受运营商 PSM 休眠策略影响。常见做法是在入网时向核心网申请一个较长的活跃定时器,让设备上报完立即进入 PSM 状态。这个行为对应流程图里的“发送成功”分支后面直接接“休眠”,而不是回到主循环空转。
5. 通过本地回环验证流程图对应的软件链路
代码写完以后,先用本地回环方式验证一遍整个数据链路,再接入真实电流负载。硬件的电流采样部分如果不方便用大电流测试,可以用信号发生器输出一个已知有效值的正弦波到 ADC 输入引脚。
先看一组可靠的验证步骤:
- 用信号发生器输出 50Hz、1V 有效值的正弦波到 ADC 输入。
- 打开串口调试助手,波特率设置为 115200。
- 在代码初始化阶段加入一条串口日志:
fprintf(uart, "RMS=%d mV\r\n", (int)(rms_value*1000)); - 观察打印数据是否稳定在 1000 上下,波动范围不超过正负 1%。
- 拔掉无线模组的供电,模拟断网,检查重试 3 次后数据是否写入 Flash。
- 恢复供电,确认上电后 Flash 里的数据是否补发成功。
上述步骤里,第 5 步验证的正是流程图里“发送失败重试”这条回边。很多现场问题是图上有这条路径,代码里却没实现,或者实现了但超时时间设得不对。
实际调试中比较隐蔽的坑是:ADC 采样的触发方式。有些工程师习惯直接在定时器中断里调用adc_capture(),这个做法会拉长中断服务程序的执行时间,导致无线协议栈的底层处理被耽误。更稳妥的做法是定时器中断里只置一个标志位,在主循环里检查到标志后再开始采样,这样中断服务程序既短又不会阻塞协议栈。
本地回环验证通过后,再接入真实电流测试。此时用钳形表或者标准电流源做对比,记录一组数据的误差统计。通常误差不超过正负 1% 就说明软件链路的精度符合出厂要求。如果误差偏大,优先检查 ADC 参考电压是否稳定,以及采样窗口是否覆盖了完整的工频周期。
6. 把流程图当协议设计工具用,校验漏掉的边界路径
到了设备联调阶段,流程图还能发挥一个经常被忽略的作用:反推协议字段的设计。打开你画好的流程图,从“接收无线数据”这个节点出发,列出所有会走到这个节点的路径,再对照协议里的每个字段,就能发现哪些字段是协议里有了但软件没有置位,哪些是软件置位了但协议里没有定义。
举一个实际的例子:无线电流传感器上报的数据帧里通常会包含一个“测量类型”字段,用来区分当前上报的是电流有效值、电量累计值还是阈值报警事件。如果流程图里只有“周期采样上报”这一个路径能走到发送节点,那么“测量类型”字段就可以省去。但如果流程图上存在“报警触发上报”这个分支,这个字段就必须保留,否则网关收到数据后无法区分类型。
联调时用无线抓包工具抓一发数据,对照协议文档逐字节核对,过程比较费时但很有必要。核对表格可以这样设计:
| 字节偏移 | 字段名 | 预期值 | 实际值 | 来源节点 |
|---|---|---|---|---|
| 0 | 帧头 | 0xAA55 | 0xAA55 | 组包函数 |
| 2 | 测量类型 | 0x01 | 0x01 | 周期采样分支 |
| 3 | 数据长度 | 0x04 | 0x04 | 载荷长度 |
| 4-7 | RMS 电流值 | 0x03E8 | 0x03E8 | 数据处理模块 |
| 8 | 电池电压 | 0x0D | 0x0D | 自检模块 |
这张表把协议字段和流程图里产生该字段的节点对应起来,联调时一旦发现某字节不对,立刻能定位是组包逻辑错还是上游数据错。如果要画正式的流程图交付文档,这张表可以作为附页放进去。
最后一招建议:在流程图的标题栏里写上固件版本号和修改日期。现场维护的人拿到设备,看到固件版本跟图纸对不上,第一反应是翻图纸看新旧,而不会盲目开拆设备。这属于工程交付习惯,但能省掉很多不必要的扯皮。
本文还有配套的精品资源,点击获取