21ele 发表于 2009-7-27 14:28:33

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

一直潜水,冒个泡,探讨下STM32使用嵌入式汇编PLC指令表实现方法,基本成功。

我的思路实现办法如下:

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

        //----------------------------------------------------------------------//
        //D_0x900-0x9FF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulSL;                                // SXYTMC 前次值
        vu32 ulXL;       
        vu32 ulYL;
        vu32 ulTL;       
        vu32 ulML;               
        vu32 ulCL;               

        //----------------------------------------------------------------------//
        //D_0xA00-0xAFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulSP;                                // SXYTMC 上升沿
        vu32 ulXP;
        vu32 ulYP;
        vu32 ulTP;
        vu32 ulMP;               
        vu32 ulCP;       

        //----------------------------------------------------------------------//
        //D_0xB00-0xBFF                                        (512Byte)
        //----------------------------------------------------------------------//
        vu32 ulSF;                                // SXYTMC 下降沿
        vu32 ulXF;
        vu32 ulYF;
        vu32 ulTF;
        vu32 ulMF;               
        vu32 ulCF;

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

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

        //----------------------------------------------------------------------//
        //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
       
} 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,
       
              BX    lr
}


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

        // R3 =~R0
        LDR R1,
        MVN R3,R1
       
              BX    lr
}

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

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

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

__asm void ORI( u32* M )
{
        //R3 = ( R3 | (!R0) )
        LDR R1,
        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,

              BX    lr
}

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

__asm void RST( u32* M )
{
        //R1 出栈
        POP {R1}
       
        //*R0 &= (!R3)
        LDR R1,
        MVN R2,R1       
        ORR R3,R2
        STR R3,
       
              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,; @0x08000F00
0x08000EE2 F7FFF955BL.W   LD (0x08000190)
   232:         OUT( &Y(0));
   233:
0x08000EE6 4807      LDR      r0,; @0x08000F04
0x08000EE8 F7FFF976BL.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,
   103:         
0x08000192 6803      LDR      r3,
   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,
   187:
0x080001DA 6003      STR      r3,
   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上使用的黑色可拔插端子。

http://cache.amobbs.com/bbs_upload782111/files_17/ourdev_464952.gif
224_CPU (原文件名:CPU.gif)

http://cache.amobbs.com/bbs_upload782111/files_17/ourdev_464953.gif
224_IO (原文件名:IO.gif)

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


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

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

sunmj 发表于 2009-7-27 14:47:30

好东西,该加裤子

john78 发表于 2009-7-27 15:09:26

壳哪的?

zhike200 发表于 2009-7-27 15:29:53

简易PLC

eddia2000 发表于 2009-7-27 19:52:50

不错!

gpfrank 发表于 2010-8-30 22:12:29

非常好的内容。领教了!像您学习!
不过汇编代码不容易移植,不知道大牛对这种汇编的代码有什么好的办法用来移植吗?

21ele 发表于 2010-9-3 11:58:20

基本没办法移植,因为这些代码用了stm32的位域的特性,没有位域的mcu,肯定是无法移植的。

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

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

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

gpfrank 发表于 2010-9-4 01:55:53

回复【6楼】21ele
-----------------------------------------------------------------------

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

richey07 发表于 2010-9-11 16:11:24

mark

hwdpaley 发表于 2010-11-22 22:16:58

嗯,以前用C函数来做PLC,速度没有优势,看了楼主的汇编型PLC,有了新的思路,把原来的PLC改下,提高运行速度,哈哈。

serger 发表于 2011-12-6 13:15:51

效率高 但会不会很繁琐?

lgtomlgam 发表于 2014-4-29 11:21:16

好资料,值得学习。

BrightWang 发表于 2014-6-15 17:34:01

标记,探讨使用STM32嵌入式汇编PLC指令表

emobbs 发表于 2015-1-3 20:32:44

非常精彩非常精彩

wenchm2015 发表于 2015-9-17 13:40:58

我知道,那个拨动开关产自上海的一个日本企业

kuailechengyang 发表于 2015-12-11 13:38:47

学习了,谢谢分享!

siemenswjl 发表于 2015-12-12 16:12:45

MARK,学习
页: [1]
查看完整版本: 探讨使用STM32嵌入式汇编PLC指令表实现方法。