当前位置:首页 > 工业控制 > 工业控制
[导读]梯形图不是给C直接“照着翻”的图形,PLC CPU本身也不认LD,开发系统一般先转成指令表IL,再由runtime解释执行。 ARM工业网关跑软PLC,正确做法是:上位机把梯形图编译成字节码/指令表,网关固件里写一个轻量虚拟机,按“输入采样→逻辑执行→输出刷新”循环扫描。


梯形图不是给C直接“照着翻”的图形,PLC CPU本身也不认LD,开发系统一般先转成指令表IL,再由runtime解释执行。 ARM工业网关跑软PLC,正确做法是:上位机把梯形图编译成字节码/指令表,网关固件里写一个轻量虚拟机,按“输入采样→逻辑执行→输出刷新”循环扫描。

一、IO映像区先隔离

别在逻辑里直接读GPIO、直接写继电器,扫描周期中会读到半更新状态。建输入/输出映像:

#define BIT(x,n) (((x)>>(n))&1)

uint16_t in_img;     // I0.0~I0.15

uint16_t out_img;    // Q0.0~Q0.15

uint16_t mem_img;    // M中间继电器


void sample_input(void){ in_img = gpio_in_port(); }

void refresh_output(void){ gpio_out_port(out_img); }

每个扫描周期开头拷物理输入,结尾刷物理输出,逻辑只碰映像。

二、梯形图转指令表

一行启保停:

LD I0.0

OR Q0.0

AN I0.1

= Q0.0

转成定长指令结构:

typedef struct {

   uint8_t op;     // LD/AND/OR/ANI/OUT

   uint8_t area;   // 0=I 1=Q 2=M

   uint8_t bit;

} ins_t;


const ins_t prog[] = {

   {OP_LD, AREA_I,0},

   {OP_OR, AREA_Q,0},

   {OP_ANI,AREA_I,1},

   {OP_OUT,AREA_Q,0},

   {OP_END,0,0}

};

复杂梯形图靠AOV图/二叉树后序遍历生成IL,分支压栈用AND_STK/OR_STK即可,不用在C里写一堆if嵌套。

三、C虚拟机执行

用值栈模拟母线电流:

uint8_t stack[8];

uint8_t sp;


uint8_t rd(uint8_t a,uint8_t b){

   if(a==0)return BIT(in_img,b);

   if(a==1)return BIT(out_img,b);

   return BIT(mem_img,b);

}


void run_scan(void){

   sp=0;

   for(int pc=0; prog[pc].op!=OP_END; pc++){

       ins_t* p=&prog[pc];

       switch(p->op){

       case OP_LD: stack[sp++]=rd(p->area,p->bit); break;

       case OP_OR: stack[sp-1]|=rd(p->area,p->bit); break;

       case OP_AND:stack[sp-1]&=rd(p->area,p->bit); break;

       case OP_ANI:stack[sp-1]&=!rd(p->area,p->bit); break;

       case OP_OUT:

           if(stack[--sp]) out_img|=(1<>p->bit);

           break;

       }

   }

}

母线结果出栈写映像,不直接动硬件。

四、定时器做成功能块

梯形图TON不能直接用delay(),要在扫描周期里累计ET:

typedef struct {

   uint8_t en;

   uint16_t et;

   uint16_t pt;

   uint8_t q;

} ton_t;


void ton_update(ton_t*t, uint8_t en, uint16_t pt, uint32_t cyc_ms){

   t->en=en; t->pt=pt;

   if(!en){ t->et=0; t->q=0; }

   else {

       t->et += cyc_ms;

       if(t->et>=pt){ t->et=pt; t->q=1; }

   }

}

扫描周期1ms就按1ms累加,别在定时器中断里另起一套状态,否则和逻辑扫描不同步。

五、ARM网关主循环

Linux用户态跑软PLC,周期稳定性靠PREEMPT_RT+CPU隔离兜底;纯离散逻辑10~50ms也够用,运动控制才要1ms以内。

void plc_task(void){

   while(1){

       sample_input();

       run_scan();

       ton_update(&t1, BIT(out_img,0), 1000, cycle_ms);

       refresh_output();

       usleep(cycle_ms*1000);

   }

}

更稳的做法:

实时线程SCHED_FIFO,绑独立核

输入映像用共享内存,南向Modbus/EtherCAT驱动写映像

变量名绑定网关tag(site0_val)

在线修改只换prog[]不重启runtime

六、落坑清单

逻辑里直接读gpio→同周期输入输出混扰

梯形图逐行翻成if-else→多输出/栈逻辑崩

双线圈Q在两处OUT→后者覆盖前者

TON用系统定时中断自加→和扫描周期脱钩

输出立即写硬件→外部抖动

Linux默认调度跑PLC→周期跳到几十ms

没输入映像消抖→按钮毛刺误触发

定时器ET用float秒→累积误差

指令表不校验END→pc越界

把软PLC当安全PLC用→SIL场景不合格

七、落地顺序

上位机LD→IL编译器(或接OpenPLC/CODESYS)

2 定指令集:LD/AND/OR/ANI/OUT/END

C虚拟机值栈执行,只操作映像

输入采样+输出刷新包三层

TON/TOF/计数器做功能块

ARM Linux起实时线程,周期1~10ms

IO走Modbus/EtherCAT映像层

在线下载字节码,不重启runtime

梯形图转C的本质不是“翻译图形”,是把LD编译成IL字节码,再用C写一个确定性扫描虚拟机。逻辑层只碰IO映像、定时器跟扫描周期走、输出统一刷新——这三件事定了,ARM网关就能当软PLC跑。至于实时性,普通Linux够离散控制,PREEMPT_RT+核隔离才碰1ms以下;安全回路别省硬件继电器,软PLC只管逻辑不解安全认证。



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