搜索
bottom↓
回复: 16

探讨使用STM32嵌入式汇编PLC指令表实现方法。

[复制链接]

出0入0汤圆

发表于 2009-7-27 14:28:33 | 显示全部楼层 |阅读模式
一直潜水,冒个泡,探讨下STM32使用嵌入式汇编PLC指令表实现方法,基本成功。

我的思路实现办法如下:

1. PLC的资源变量定义为一个结构,绝对定位到内存中一个固定地址。
typedef struct
{
        //----------------------------------------------------------------------//
        //D_0x600-0x6FF                                        (512Byte)                                       
        //----------------------------------------------------------------------//
        vu16 usT_Cnt[T_Size*32];                       
       
        //----------------------------------------------------------------------//
        //D_0x700-0x7FF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu16 usT_Set[T_Size*32];                       
       
        //----------------------------------------------------------------------//
        //D_0x800-0x8FF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulS[S_Size];                                // M0000 => S000-S1023
        vu32 ulX[X_Size];                                // M1024 => X000-X377
        vu32 ulY[Y_Size];                                // M1280 => Y000-Y377
        vu32 ulT[T_Size];                                // M1536 => T000-T255       
        vu32 ulM[M_Size];                                // M2048 => M000-M1535
        vu32 ulC[C_Size];                                // M3584 => C000-C255

        //----------------------------------------------------------------------//
        //D_0x900-0x9FF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulSL[S_Size];                                // SXYTMC 前次值
        vu32 ulXL[X_Size];       
        vu32 ulYL[Y_Size];
        vu32 ulTL[T_Size];       
        vu32 ulML[M_Size];               
        vu32 ulCL[C_Size];               

        //----------------------------------------------------------------------//
        //D_0xA00-0xAFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulSP[S_Size];                                // SXYTMC 上升沿
        vu32 ulXP[X_Size];
        vu32 ulYP[Y_Size];
        vu32 ulTP[T_Size];
        vu32 ulMP[M_Size];               
        vu32 ulCP[C_Size];       

        //----------------------------------------------------------------------//
        //D_0xB00-0xBFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulSF[S_Size];                                // SXYTMC 下降沿
        vu32 ulXF[X_Size];
        vu32 ulYF[Y_Size];
        vu32 ulTF[T_Size];
        vu32 ulMF[M_Size];               
        vu32 ulCF[C_Size];

        //----------------------------------------------------------------------//
        //D_0xC00-0xCFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulTS[T_Size];                                // M1792 => TE00-TE255
        vu32 ulCS[C_Size];                                // M3840 => CE00-CE255
        vu32 ulTR[T_Size];                                // 定时器复位控制
        vu32 ulCR[C_Size];                                // 计数器复位控制

        vu32 ulCE[Y_Size];                                // 计数器边沿选择位       
        vu32 ulX_IDR[X_Size];                                // 输入引脚状态缓存
        vu32 ulX_IMG[X_Size];                                // 输入位映像
        vu32 ulT_IMG[T_Size];                                // 定时器输出位映像
       
        //----------------------------------------------------------------------//
        //D_0xD00-0DFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu8 ucXFilterSet[X_Size*32];                // 输入去抖动计数器
        vu8 ucXFilter[X_Size*32];                        // 输入去抖动计数器
       

        //----------------------------------------------------------------------//
        //D_0xE00-0EFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu16 usC_Cnt[(C_Size*32+56)];        // D2048->0x800

        //----------------------------------------------------------------------//
        //D_0xF00-0FFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu16 usC_Set[(C_Size*32+56)];         // D        2360->0x938

        //----------------------------------------------------------------------//
        //D_0x1000-07FFF                                        (4KByte)
        //----------------------------------------------------------------------//
        vu16 usD[D_Size];                                //D
       
} PLC_TypeDef;

//---------------------------------------------------------------------------//
// 定义PLC  数据区
//---------------------------------------------------------------------------//
PLC_TypeDef         plc __attribute__(( at(0x20000800) ));        

2. 资源结构变量既然已经绝对定位,就和内部工作寄存器(如GPIOA等)一样,有了固定的地址,对应位域也固定了,可以轻松计算出来,定位为位宏。

//-------------------------------------------------------//
// 位宏
//-------------------------------------------------------//
#define          S(n)             *(u32*)(0x22000000+0x0C00*8*4+n*4)
#define          X(n)            *(u32*)(0x22000000+0x0C80*8*4+n*4)
#define          Y(n)            *(u32*)(0x22000000+0x0CA0*8*4+n*4)
#define          T(n)            *(u32*)(0x22000000+0x0CC0*8*4+n*4)
#define          M(n)              *(u32*)(0x22000000+0x0CE0*8*4+n*4)
#define          C(n)             *(u32*)(0x22000000+0x0DA0*8*4+n*4)

#define          SL(n)               *(u32*)(0x22000000+0x0DC0*8*4+n*4)
#define          XL(n)               *(u32*)(0x22000000+0x0E40*8*4+n*4)
#define          YL(n)               *(u32*)(0x22000000+0x0E60*8*4+n*4)
#define          TL(n)               *(u32*)(0x22000000+0x0E80*8*4+n*4)
#define          ML(n)               *(u32*)(0x22000000+0x0EA0*8*4+n*4)
#define          CL(n)               *(u32*)(0x22000000+0x0F60*8*4+n*4)

#define          SP(n)               *(u32*)(0x22000000+0x0F80*8*4+n*4)
#define          XP(n)               *(u32*)(0x22000000+0x1000*8*4+n*4)
#define          YP(n)               *(u32*)(0x22000000+0x1020*8*4+n*4)
#define          TP(n)               *(u32*)(0x22000000+0x1040*8*4+n*4)
#define          MP(n)               *(u32*)(0x22000000+0x1060*8*4+n*4)
#define          CP(n)               *(u32*)(0x22000000+0x1120*8*4+n*4)

#define          SF(n)               *(u32*)(0x22000000+0x1140*8*4+n*4)
#define          XF(n)               *(u32*)(0x22000000+0x11C0*8*4+n*4)
#define          YF(n)               *(u32*)(0x22000000+0x11E0*8*4+n*4)
#define          TF(n)               *(u32*)(0x22000000+0x1200*8*4+n*4)
#define          MF(n)               *(u32*)(0x22000000+0x1220*8*4+n*4)
#define          CF(n)                *(u32*)(0x22000000+0x12E0*8*4+n*4)

#define          TS(n)                *(u32*)(0x22000000+0x1300*8*4+n*4)
#define          CS(n)                *(u32*)(0x22000000+0x1320*8*4+n*4)
#define          TR(n)                *(u32*)(0x22000000+0x1340*8*4+n*4)
#define          CR(n)                *(u32*)(0x22000000+0x1360*8*4+n*4)

#define          CE(n)                *(u32*)(0x22000000+0x1380*8*4+n*4)
#define          X_IDR(n)                   *(u32*)(0x22000000+0x13A0*8*4+n*4)
#define          X_IMG(n)                   *(u32*)(0x22000000+0x13C0*8*4+n*4)
#define          T_IMG(n)                   *(u32*)(0x22000000+0x13E0*8*4+n*4)

3. 这时候,主程序可以使用位宏实现逻辑功能。比如
Y(0) = X(0);

if( X(1)) Y(1) = 1;

if( X(2)) Y(1) = 0;

4.按照堆栈实现PLC指令表原理,定义嵌入式汇编宏。

//===========================================================//
//         嵌入式汇编
//===========================================================//
__asm void START(u32* M)
{
        // R3 = 0
        MOV R3,#0
       
        // R7 =  位区基址
        MOV R7, R0
       
              BX    lr
}

__asm void LD( u32* M )
{
        // R3 压栈
        PUSH {R3}
       
        // R3 =  R0
        LDR R3,[R0,#0]
       
              BX    lr
}


__asm void LDI( u32* M )
{
        // R3 压栈
        PUSH {R3}

        // R3 =  ~R0
        LDR R1,[R0,#0]
        MVN R3,R1
       
              BX    lr
}

__asm void AND( u32* M )
{
        //  R3 = ( R3 & R0 )
        LDR R1,[R0,#0]
        AND R3, R1
       
              BX    lr
}

__asm void ANDI( u32* M )
{
        //  R3 = ( R3 & (!R0) )
        LDR R1,[R0,#0]
        MVN R2,R1
        AND R3,R2
       
              BX    lr
}

__asm void OR( u32* M )
{
        //  R3 = ( R3 | R0 )
        LDR R1,[R0,#0]
        ORR R3,R1
       
              BX    lr
}

__asm void ORI( u32* M )
{
        //  R3 = ( R3 | (!R0) )
        LDR R1,[R0,#0]
        MVN R2,R1
        ORR R3,R2
       
              BX    lr
}

__asm void ANB( void )
{
        //  R1 出栈
        POP {R1}
       
        //  R3 = ( R3 & R1 )
        AND R3,R1
       
              BX    lr
}

__asm void ORB( void )
{
        //  R1 出栈
        POP {R1}
       
        //  R3 = ( R3 | R1 )
        ORR R3,R1
       
              BX    lr
}

__asm void OUT( u32* M )
{
        //  R1 出栈
        POP {R1}
       
        //  *R0 = R3
        STR R3,[R0,#0]

              BX    lr
}

__asm void SBT( u32* M )
{
        //  R1 出栈
        POP {R1}
       
        //  *R0 |= R3
        LDR R1,[R0,#0]
        ORR R3,R1
        STR R3,[R0,#0]
       
              BX    lr
}

__asm void RST( u32* M )
{
        //  R1 出栈
        POP {R1}
       
        //  *R0 &= (!R3)
        LDR R1,[R0,#0]
        MVN R2,R1       
        ORR R3,R2
        STR R3,[R0,#0]
       
              BX    lr
}

5. PLC的指令执行循环里便可以用指令表执行逻辑动作了。

void PLC_Poll(void)
{
        START(&X(0));
       
        LD( &X(0) );
        OUT( &Y(0));

        LDI( &X(1) );
        OUT( &Y(1));
}

6.指令执行效率,从编译的汇编代码看,效率非常高,指令执行速度应该是非常快的。

   231:         LD( &X(0) );
0x08000EE0 4807      LDR      r0,[pc,#28]  ; @0x08000F00
0x08000EE2 F7FFF955  BL.W     LD (0x08000190)
   232:         OUT( &Y(0));  
   233:  
0x08000EE6 4807      LDR      r0,[pc,#28]  ; @0x08000F04
0x08000EE8 F7FFF976  BL.W     OUT (0x080001D8)


    96: __asm void LD( u32* M )
    97: {
    98:         // R3 压栈
0x0800018E 4770      BX       lr
    99:         PUSH {R3}
   100:         
   101:         // R3 =  R0
0x08000190 B408      PUSH     {r3}
   102:         LDR R3,[R0,#0]
   103:         
0x08000192 6803      LDR      r3,[r0,#0x00]
   104:         BX    lr
   105: }

   180: __asm void OUT( u32* M )
   181: {
   182:         //  R1 出栈
0x080001D6 4770      BX       lr
   183:         POP {R1}
   184:         
   185:         //  *R0 = R3
0x080001D8 BC02      POP      {r1}
   186:         STR R3,[R0,#0]
   187:  
0x080001DA 6003      STR      r3,[r0,#0x00]
   188:         BX    lr
   189: }

LD/OUT实测实170ns, 按照72Mhz STM32 指令执行周期算,大致估计单周期基本指令执行速度应该在0.5us内


7. 硬件使用STM32103RBT6, 资源为14点入,10点出, 1RS232, 2RS485 , 1CAN ,1SPI ,2ADC, 1RTC,RS232可直接下载程序。板上设置了ISP(TTL)和JTAG接口。 1RS232, 2RS485 使用高速磁偶隔离,3000V DC/DC, 1CAN 可选另外一组3000V DC/DC ,高速磁偶隔离,也可不不隔离。 SPI不隔离。输入点位双向光耦。端子使用西门子PLC上使用的黑色可拔插端子。


224_CPU (原文件名:CPU.gif)


224_IO (原文件名:IO.gif)

代码仅供参考思路,已经在硬件测试功能可以实现。


另外问下阿莫,上次开源PLC项目时,上面的那个运行开关在那里买到,我怎么也买不到。就是5脚,单刀双掷,三个拨动位置的那种。

8. 思路延伸:优化汇编代码,使汇编代码简单、格式固定且可在任意位置运行,找到汇编指令和机器码中间的变化编译规律,就可以将指令表对应为机器码数组,然后做一个简单变量替换,就可以实现下位机编译,所以做一款下位机编译执行的PLC应该没有什么太大的技术问题。

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。

出0入0汤圆

发表于 2009-7-27 14:47:30 | 显示全部楼层
好东西,该加裤子

出90入4汤圆

发表于 2009-7-27 15:09:26 | 显示全部楼层
壳哪的?

出0入0汤圆

发表于 2009-7-27 15:29:53 | 显示全部楼层
简易PLC

出0入0汤圆

发表于 2009-7-27 19:52:50 | 显示全部楼层
不错!

出0入0汤圆

发表于 2010-8-30 22:12:29 | 显示全部楼层
非常好的内容。领教了!像您学习!
不过汇编代码不容易移植,不知道大牛对这种汇编的代码有什么好的办法用来移植吗?

出0入0汤圆

 楼主| 发表于 2010-9-3 11:58:20 | 显示全部楼层
基本没办法移植,因为这些代码用了stm32的位域的特性,没有位域的mcu,肯定是无法移植的。

汇编实现plc指令,就有可能实现plc指令的下位机编译执行。在程序下载的同时,将plc程序在下位机里编译为汇编指令对应的机器码,我研究了一下机器码和汇编指令的关系,确定通过简单的替换机器码中操作数的方法,完全可以实现下位机编译。

虽然汇编实现plc内核速度应该是最快的,但是用汇编实现,也有许多缺点,比如实现实时的错误检测,和实现复杂的应用指令比较吃力。

所以,我后来实现了全部的下载监控等的通讯功能后,内核实现暂时也是以先用解释方式实现的。这样做工作量小多了。

出0入0汤圆

发表于 2010-9-4 01:55:53 | 显示全部楼层
回复【6楼】21ele  
-----------------------------------------------------------------------

谢谢!
最近再拜读您的几篇文章,受益匪浅。也开始尝试设计一个简单的控制器。需要向您学习的地方还很多。
作为logic Engine这部分,是整个PLC控制的核心部分。也是各大厂商比拼速度的地方。
因为现在没有一块比较好的MCU可以胜任所有我们需要的PLC功能,所以我考虑的也是采用C写解释型的LOGIC ENGINE。
如果最终找到了一款比较适合做的,则对相应的部分进行代码的优化,就如您的方法,更改成汇编代码,除了用CORTEX-M3的位域外,还可以大量的使用内核寄存器进行操作,速度估计提升更多。当然并不是所有的代码都用汇编,只是基本的61131-3的规定的一些指令使用,复杂的仍旧使用C来完成您所说的容错,复杂控制等。

出0入0汤圆

发表于 2010-9-11 16:11:24 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-11-22 22:16:58 | 显示全部楼层
嗯,以前用C函数来做PLC,速度没有优势,看了楼主的汇编型PLC,有了新的思路,把原来的PLC改下,提高运行速度,哈哈。

出0入0汤圆

发表于 2011-12-6 13:15:51 | 显示全部楼层
效率高 但会不会很繁琐?

出0入0汤圆

发表于 2014-4-29 11:21:16 | 显示全部楼层
好资料,值得学习。

出0入0汤圆

发表于 2014-6-15 17:34:01 来自手机 | 显示全部楼层
标记,探讨使用STM32嵌入式汇编PLC指令表

出0入0汤圆

发表于 2015-1-3 20:32:44 | 显示全部楼层
非常精彩非常精彩

出0入0汤圆

发表于 2015-9-17 13:40:58 | 显示全部楼层
我知道,那个拨动开关产自上海的一个日本企业

出0入0汤圆

发表于 2015-12-11 13:38:47 | 显示全部楼层
学习了,谢谢分享!

出0入0汤圆

发表于 2015-12-12 16:12:45 | 显示全部楼层
MARK,学习
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-10-3 00:09

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表