|
一直潜水,冒个泡,探讨下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碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
|