当前位置:首页 > 工业控制 > 工业控制
[导读]工业嵌入式最忌“从main开始写业务”:GPIO直接操作、Modbus解析塞进中断、PID写进HMI任务,最后换颗MCU全仓重写,出bug还查不到层。正路是先定分层边界,再自底向上填代码:驱动只碰寄存器,HAL屏蔽芯片差异,BSP绑定板子,RTOS管调度,控制逻辑走状态机,HMI/通信只消费数据。


工业嵌入式最忌“从main开始写业务”:GPIO直接操作、Modbus解析塞进中断、PID写进HMI任务,最后换颗MCU全仓重写,出bug还查不到层。正路是先定分层边界,再自底向上填代码:驱动只碰寄存器,HAL屏蔽芯片差异,BSP绑定板子,RTOS管调度,控制逻辑走状态机,HMI/通信只消费数据。

一、需求先落地成接口

动代码前先出三张表:

外设表:UART/SPI/I2C/CAN/ADC/定时器用途、引脚、中断号

任务表:周期、优先级、栈大小、是否硬实时

控制变量表:采样量、设定值、输出限幅、联锁条件

架构原则只有一条:上层调下层,下层不反向调上层。应用不认识寄存器,驱动不认识工艺。

二、驱动层:只做硬件读写

Driver层只封装MCU外设,不含任何业务判断:

/* hal_uart.h */

typedef struct {

   int (*init)(uint32_t baud);

   int (*send)(const uint8_t*buf, int len);

   int (*recv)(uint8_t*buf, int len, int timeout_ms);

} uart_drv_t;

STM32/NXP/RISC-V各提供一个uart_drv_t实现,上层拿接口指针调用,换芯片只换底层文件。

BSP层再把“哪根引脚、哪个从机地址、CS脚是谁”绑上去;传感器驱动只写temp_read(),不写HAL_GPIO_WritePin(CS_PORT...)。

规矩:

驱动返回标准错误码(timeout/bus_err/param),不自己printf、不复位系统

不硬编码板级宏进驱动源文件

DMA/中断细节留在驱动内,上层看不到

一个外设只由一个模块持有初始化权

三、RTOS任务分级

工业里最常用错的一件事:把HMI刷新、Modbus轮询、PID控制放同一个while里。正确是按实时性拆任务:

任务 周期 优先级

采样+闭环控制 1~10ms 高

状态机/联锁 20~50ms 中

Modbus/CAN通信 50~100ms 中低

HMI刷新/日志 100ms+ 低

void ctrl_task(void*p){

   for(;;){

       adc_sample(&g_adc);

       filter_run(&g_proc, &g_adc);

       pid_calc(&g_pid, g_proc.temp);

       ao_output(g_pid.out);

       vTaskDelayUntil(&tick, pdMS_TO_TICKS(5));

   }

}

控制任务里禁止printf、文件写、动态分配、长临界区;通信和界面走队列拿数据。

四、控制逻辑走状态机

工艺逻辑别用一层if-else堆:启停条件、故障保护、模式切换、工序流转全收进状态机。

typedef enum {

   ST_IDLE, ST_PREHEAT, ST_RUN, ST_PAUSE, ST_FAULT

} mach_st_t;


void fsm_step(mach_t*m){

   switch(m->st){

   case ST_PREHEAT:

       if(m->temp >= m->setpoint-2) m->st = ST_RUN;

       break;

   case ST_RUN:

       if(over_temp(m)) { fault_set(OT); m->st=ST_FAULT; }

       else if(stop_cmd()) m->st = ST_IDLE;

       break;

   case ST_FAULT:

       ao_disable();                 // 故障时先断执行器

       if(reset_cmd() && fault_clear()) m->st = ST_IDLE;

       break;

   }

}

要点:

故障态只出不进,恢复需显式条件

模式切换(手动/自动)走状态跳转,不靠全局flag乱改

控制输出在状态机外统一下发,避免多处写AO

联锁条件(门禁、气压、急停)在进RUN前判

五、通信与HMI只做搬运

Modbus/CANopen/SOME-IP解析放中间件层,输出“标准化业务数据”给应用层;HMI读全局状态结构,不直读ADC:

batt_state_t g_mach;   // 控制层写,HMI/通信读

规则:

写寄存器、发CAN帧只在控制/通信任务里做

HMI改设定值→发消息到控制队列,不直接改pid.set

通信超时不影响控制回路,只置“通讯丢失”标志

关键写操作走权限校验(前面工业安全编程那套)

六、落地顺序(V模型)

需求表+任务表+变量表

写Driver/HAL,逻辑分析仪验时序

BSP绑定引脚与外设

RTOS建任务,优先级梯度定好

控制状态机+PID+联锁

通信中间件解析报文

HMI/日志消费状态结构

单测(mock HAL)+硬件在环+长时间跑

测试别只烧进去点灯:HAL可mock单测,控制任务可注入假采样,通信层可灌异常帧。

七、落坑清单

main里直接操作寄存器→换MCU重写

驱动里printf+复位→上层没法策略化

控制逻辑放HMI任务→画面卡顿拖控制

状态机用if-else平铺→加状态就乱

通信任务里算PID→总线抖动带偏执行器

HMI直写AO→急停和界面打架

全局变量满天飞→故障追溯不了

中断里做滤波+上报→ISR超时

不画任务周期表→优先级拍脑袋

驱动硬编码CS引脚→换板子改源码

八、一句话收口

工业全栈不是“驱动会写、RTOS会用”就行,是边界清楚:驱动不懂业务,HAL不懂板子,RTOS不管工艺,状态机不管通信,HMI不碰寄存器。自底向上每一层只暴露该暴露的接口,换芯片改HAL、换传感器改BSP、换工艺改状态机、换界面不动控制——这才是工业固件能跑五年还敢改的底子。真耦合起来,第一次交付快三天,第二次维护多三周。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭