当前位置:首页 > 工业控制 > 工业控制
[导读]工业IO模块不是“GPIO读高低电平”就完事。现场24V信号进来直接怼MCU,变频器一启动光耦不隔离就误触发;DO直接推电磁阀,关断反电动势能把输出管打穿。 正经做法是DI走光耦隔离+RC滤波+软件去抖,DO走隔离驱动+锁存+故障安全态,MCU只处理逻辑层。


工业IO模块不是“GPIO读高低电平”就完事。现场24V信号进来直接怼MCU,变频器一启动光耦不隔离就误触发;DO直接推电磁阀,关断反电动势能把输出管打穿。 正经做法是DI走光耦隔离+RC滤波+软件去抖,DO走隔离驱动+锁存+故障安全态,MCU只处理逻辑层。

一、硬件分层

DI侧每路:现场24V → 限流电阻 → TVS钳位 → 光耦LED → MCU GPIO。光耦后级建议加施密特缓冲,边沿干净些。

DO侧:MCU GPIO → 光耦/数字隔离 → 高边驱动(BTS/TPIC)或继电器 → 现场负载,感性负载必须续流二极管。

16路可每路独立隔离,也可8路一组共隔离电源,成本折中。

二、DI采集:扫描+去抖

不要中断里边沿触发边解析,定时器1~5ms轮询一次,连续N次一致才认状态。

#define DI_NUM 16

#define DI_PORT GPIOA

#define DEBOUNCE_CNT 3      // 约10ms@3ms周期


static uint8_t di_raw[DI_NUM];

static uint8_t di_stable[DI_NUM];

static uint8_t di_deb[DI_NUM];


uint16_t di_read_hw(void){

   return DI_PORT->IDR & 0xFFFF;   // 低16位映射16路

}


void di_scan_3ms(void){

   uint16_t hw = di_read_hw();

   for(int i=0;i<DI_NUM;i++){

       uint8_t bit = (hw>>i)&1;

       if(bit == di_raw[i]){

           di_deb[i]=0;

       }else{

           di_deb[i]++;

           if(di_deb[i] >= DEBOUNCE_CNT){   // 稳定变化

               di_raw[i]=bit;

               if(bit != di_stable[i]){

                   di_stable[i]=bit;

                   di_event(i,bit);         // 状态变化上报

               }

           }

       }

   }

}

去抖时间别太长,一般10~20ms;急停/安全门另说,硬件滤波为主。

三、DI事件上报

只上报“变化沿”,别每次扫描都推上位,Modbus/总线带宽扛不住。

void di_event(int ch, uint8_t val){

   di_ev_buf[di_ev_in].ch = ch;

   di_ev_buf[di_ev_in].val = val;

   di_ev_buf[di_ev_in].ms = sys_ms();

   di_ev_in = (di_ev_in+1)&(DI_EV_MASK);

}

主循环/通信任务从环形缓冲取事件发Modbus离散输入或上报SCADA。

四、DO输出:锁存+安全态

DO别直接写GPIO,走输出映像寄存器,周期刷新;通信中断只改映像,不碰硬件。

static uint16_t do_img;        // 输出映像

static uint16_t do_hw_last;


void do_set(int ch, uint8_t on){

   if(on) do_img |= (1<<ch);

   else   do_img &= ~(1<<ch);

}


void do_refresh(void){

   if(do_img != do_hw_last){

       GPIOB->ODR = (GPIOB->ODR & 0xFF00) | (do_img & 0xFF);

       do_hw_last = do_img;

   }

}


/* 故障安全:通讯超时/看门狗不喂 */

void do_safe_state(void){

   do_img = 0;            // 全关,或按工艺设安全值

   GPIOB->ODR &= 0xFF00;

   do_hw_last = 0;

}

上电默认安全态必须明确:一般全OFF,风机/急停回路例外按图纸。

五、DO回读校验

晶体管输出可回读状态脚或采样负载电流,发现“写了1没导通”报通道故障:

void do_diag(void){

   for(int i=0;i<DI_NUM;i++){

       uint8_t expect = (do_img>>i)&1;

       uint8_t fb = diag_pin_read(i);

       if(expect && !fb) do_fault[i]=1;

   }

}

短路保护靠驱动芯片自带的过流关断,MCU只收FAULT脚。

六、NPN/PNP与干湿接点

DI初始化前确认现场传感器输出形式:

PNP源型:信号ON=24V,公共端接0V

NPN漏型:信号ON=拉到0V,公共端接24V

干接点:光耦LED电流由模块侧供电

湿接点:确认电压极性,反接加保护二极管

接错表现就是“传感器灯亮但DI不动作”,别先怀疑代码。

七、落坑清单

MCU GPIO直连24V→光耦没接,干扰进来就炸

DI中断里做逻辑→抖动误触发连环进中断

去抖设100ms→光电开关握手时序错

DO直接写GPIO不锁存→通信任务覆盖输出

上电DO默认全ON→电磁阀误动作

感性负载不加续流→驱动管击穿

安全门信号走软件滤波→急停响应滞后

PNP传感器接NPN输入→灯亮不识别

16路同扫不分组→地环路串扰

不回读DO状态→断线发现不了

八、落地顺序

DI每路限流+TVS+光耦,后级施密特缓冲

定时扫描读端口,软件去抖10~20ms

只上报变化沿,进事件队列

DO走映像寄存器,通信层只改映像

上电安全态全OFF,看门狗超时回安全态

晶体管/继电器隔离驱动,感性负载续流

DO状态回读,FAULT进诊断

NPN/PNP按现场传感器配公共端

工业IO模块的核心就一句:DI隔离进来、去抖后再认;DO锁存在映像、安全态兜底。光耦把现场24V和MCU 3.3V切开,软件去抖把机械抖动和EMI毛刺滤掉,输出映像让控制逻辑和硬件引脚解耦,故障安全态保证通讯断了现场不抽风。代码不复杂,复杂的是“上电默认状态对不对、干扰来了稳不稳、断线能不能查出来”——这三件想透,16路DI/DO基本就不会在现场翻车。



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