记一次EtherCAT IO扩充

因业务需要,现需要将EtherCAT的IO点位扩充至128入128出。设计过程中碰到以下问题,一一解决掉了:

SSC生成的XML文件,EEPROM烧录后,提示:AdsError:1818, query interface failed.

具体报错截图如下:

问题原因:使用SSC时,import的LAN9252-SDK,此后生成的XML文件经测试会导致这一问题,无论SSC5.11还是5.12,无论IO怎么规划,生成的XML烧录进eeprom都会报这个错误。

解决办法:使用SSC生成XML时,不要import LAN9252的SDK,直接使用最原始的模板建立SSC功能,如下图所示:

用SSC生成协议栈代码时,再用LAN9252的SDK。

DC模式下进不了OP

问题原因:这个比较低级,定时中断,和三个外部中断配置有误,CubeMX配置时要小心。

解决办法:中断向量这么配置:

static void MX_NVIC_Init(void) {
    /* EXTI2_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 2);
    //HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    /* EXTI1_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 1);
    //HAL_NVIC_EnableIRQ(EXTI1_IRQn);
    /* EXTI0_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
    //HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    /* TIM8_UP_TIM13_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
}

这样一个定时器中断和三个外部中断上电后不会一下子启动,协议栈会启动这些中断,注意不要把定时器中断的使能关掉,定时器中断的还有另一个函数用来控制启动和停止:HAL_TIM_Base_Start_IT(&htim8);,这个在协议栈的宏中配置好。

STM32与LAN9252构建EtherCAT从站(六):TwinCAT2的使用和从站测试

  1. 一、 STM32与LAN9252构建EtherCAT从站(一):项目简介
  2. 二、 STM32与LAN9252构建EtherCAT从站(二):使用SSC生成EtherCAT协议栈和XML文件
  3. 三、 STM32与LAN9252构建EtherCAT从站(三):LAN9252的XML文件
  4. 四、 STM32与LAN9252构建EtherCAT从站(四):STM32配置SPI
  5. 五、 STM32与LAN9252构建EtherCAT从站(五):STM32与LAN9252适配
  6. 六、 STM32与LAN9252构建EtherCAT从站(六):TwinCAT2的使用和从站测试

STM32与LAN9252构建EtherCAT从站(六):TwinCAT2的使用和从站测试

这一章讲解在XP虚拟机里使用TwinCAT2的一些知识。内容主要包括:

  1. TwinCAT连接PLC
  2. 从站设备发现
  3. 点位观察和强制
  4. PLC测试

1. TwinCAT连接PLC

1.1 TwinCAT简介

TwinCAT是德国倍福开发的工业控制程序,有PLC控制,NC控制,HMI等丰富的功能,最厉害的地方是它可以将一台普通PC变成一台工业控制器,在上面可以跑诸如法那科,西门子,三菱等NC控制系统。TwinCAT2跟TwinCAT3本质上没什么特别大的区别,这二者的取舍大多数时候并不由用户决定,而是由设备决定。TwinCAT的授权机制比较复杂,TwinCAT2有30天免费试用,想授权的话需要电话、传真或者email跟倍福联系,比较原始,感觉跟Win98年代的激活码一样;TwinCAT3官网介绍分为很多类型,有部分用途是无需授权的,因为硬件产品的价格已经集成了,这里就不去详细了解了,感兴趣的朋友可以去倍福官网详细了解一下。
我这边测试环境有一台倍福CX9020-0111,这是一台倍福的PLC控制器,集成有TwinCAT2主站,官网介绍如下:



cx9020
TwinCAT2的主站,我试过很多次,用TwinCAT3是连不上去的,后来装了个XP虚拟机,虚拟机里面装了TwinCAT2,一下子就连上了,因此我认为TwinCAT2和TwinCAT3连通性上并不兼容。
此外TwinCAT3是以Virtual Studio插件的形式发布的,可以配合VS的强大能力开发出很多基于C#和Windows GUI的EtherCAT工控程序,但鉴于Windows本身并非实时操作系统,开发一些采集型的看板还可以,如果需要开发运动控制型的工控程序,是不太合适的,这方面还是寻求嵌入式解决方案比较靠谱。

1.2 TwinCAT2连接CX9020

我这边的环境并不是想让个人PC当做PLC控制器,因此安装TwinCAT仅仅是用来连接控制第三方主站设备CX9020。安装完TwinCAT2后,打开TwinCAT System Manager,选择Choose Target,弹出的列表中,如果没有目标设备,则需要点击Search(Ethernet),然后Brodadcast Search,找到设备节点,点击Add Route,这时候会弹出用户名密码的对话框,如果目标设备是WinCE的系统,用户名密码都为空,如果是XP系统,则用户名为Administrator,密码为1。添加完成后,即可在Choose Target中找到设备节点。相关截图如下:
tc01
tc02
tc03
再次提醒一下,TwinCAT2和TwinCAT3不通用,目标设备如果是TwinCAT2作为主站,则一定要用TwinCAT2去连接控制它。

2 从站设备发现

2.1 Run Mode 和 Config Mode

TwinCAT设备分为Run Mode和Config Mode,两种运行状态。最显著的区别就是设备本身会有一个LED灯,绿色代表RunMode,蓝色代表ConfigMode。
一般我们对设备进行【从站发现】、【IO关联】、【IO强制】等操作时,只可以在ConfigMode下进行;让设备开始跑PLC程序时,只可以在RunMode下进行。

2.2 发现从站

首先,确保您已经将前面章节配置的XML设备描述文件放入【TwinCAT安装目录/IO/EtherCAT】路径下。
在TwinCAT System Manager中,Choose Target选择远程CX9020设备后,点击菜单栏这个按钮,进入Config Mode:

ConfigMode

然后在左边菜单栏选中【I/O Devices】,右击,选择【Scan Devices】进行设备扫描。

ConfigMode

稍等一会儿,弹出对话框,告诉你当前主站发现有列表中的设备,点击【OK】进入下一步。

系统弹出一个对话框,询问是否自动搜索boxes,点击【是】

此时就可以找到我们定义的LAN9252设备了,这个设备是以EtherCAT总线的一个【Box】节点来体现的。

box

2.3 烧录EEPROM

在LAN8252设备的父节点【Device2】上选中,然后右边主界面选择【Online】,下方列表中选择节点,然后右击,选择【EEPROM Update】,即可烧录我们制定的XML文件。

eeprom

烧录过程比较缓慢,完成后,需要删除设备,重新扫描设备。

3. 点位观察和强制

烧录好的EtherCAT从站设备,会有图标和名称信息显示。
点击左侧+号键,可以看到这个设备有【INPUTS process data mapping】和【OUTPUTS process data mapping】两个主节点,展开后,可发现各有64bit输入输出数据。

led0

不出问题的情况下,此时LAN9252控制的RUN LED灯已经可以常亮了,代表设备主从设备正在正常通信。我们仍然保持设备在ConfigMode下运行,下面我们来强制一下点位,看看硬件设备是否能正常工作。

在强制数据之前,我们编写一套测试逻辑,在上一篇 STM32与LAN9252构建EtherCAT从站(五):STM32与LAN9252适配 介绍了需要适配的几个函数,其中有一个最核心的函数APPL_Application(void),这个是编写主业务逻辑的地方。我们编写如下代码:

void APPL_Application(void) {
    OUTPUTS0x7000.LED0 ? ( HAL_GPIO_WritePin( USER_OUT_GPIO_Port, USER_OUT_Pin, GPIO_PIN_RESET) ) : ( HAL_GPIO_WritePin( USER_OUT_GPIO_Port, USER_OUT_Pin, GPIO_PIN_SET) );
}

这段代码很简单,如果OUTPUTS0x7000.LED0这个变量是真,那么USER_OUT这个口置低电平,否则置高电平,PCB上这个LED设计的低电平有效。

强制数据的方法很简单,在TwinCAT中,找到需要强制的点位,右击,【Online Write】,输入1,点击OK即可。
此时可以观察到,随着TwinCAT强制【LED0】点位1和0,PCB板上的USER_OUT这个LED也同时点亮和熄灭。

4. PLC测试

4.1 APPL_Application()中的输入逻辑

继续完善APPL_Application()函数,LED是输出,至少需要完成1个点位的输入。
这里顺带提一下,如果场景跟我一样,输入信号全是按键,那么要注意了,一般PCB设计的时候,按键都是扫描的,比如64个bit的输入,那么至少需要8+8=16个IO。同时,扫描是需要时间的,还需要考虑防抖,因此当把8条线全都扫描出来,可能已经耗时5ms还多了,这里要注意一下,尽可能保证APPL_Application()函数最短耗时,实测当APPL_Application()函数所需时间大于3ms时,连接极为困难。所以如果有耗时操作需要在APPL_Application()中完成,可以考虑分时完成,比如扫描这种操作,可以一个周期只扫描两根或者四根线。如果有更复杂的操作,也不能分时完成,那就要考虑使用RTOS来加持了,多线程通信,消息队列这些机制,或者选用性能更强的芯片。
假设我们的APPL_Application()有如下逻辑:

void APPL_Application(void){
    INPUTS0x6000.B0 =  ((KeylineValue[0] & ((u8)1 << 0)) == ((u8)1 << 0)) ? 1 : 0;
    OUTPUTS0x7000.LED0 ? ( HAL_GPIO_WritePin( USER_OUT_GPIO_Port, USER_OUT_Pin, GPIO_PIN_RESET) ) : ( HAL_GPIO_WritePin( USER_OUT_GPIO_Port, USER_OUT_Pin, GPIO_PIN_SET) );
}

其中,INPUTS0x6000.B0是输入赋值,OUTPUTS0x7000.LED0是输出赋值。接下来我们可以用PLC来测试系统了。

4.2 PLC

打开TwinCAT PLC Control,File->New,弹出系统选择对话框。我们的设备是CX9020,这是一台基于ARM WinCE的控制系统,因此选择ARM平台。
ConfigMode
弹出PLC语言类型选择对话框,我们试着使用结构化文本来写PLC,因此选择ST模式。

新建MAIN文件后,主体界面切换成上下两个编辑区,上面编辑区用来定义变量,下面编辑区用来编写逻辑。测试代码如下:

PROGRAM MAIN
VAR
    B0 AT%IX0.0: BOOL;
    L0 AT%QX0.0: BOOL;
END_VAR
L0 := B0;

ConfigMode
保存工程文件后,菜单栏【Online】->【Choose Run-Time System】选中CX9020这台远程设备,然后点击Login登陆进入,按下F5,即可开始跑PLC程序。实验现象应该是由STM32扫描得到的键值bit0位如果是1的话,LED亮,bit0位如果是0的话,LED灭。
这种方式仅仅是模拟PLC进行测试,要想将PLC真正烧录进倍福控制器,并且配置PLC的点位跟物理点位进行link还需要到TwinCAT System Manager中进行设置。详细的TwinCAT教程我上传到这里:TwinCAT2教程TwinCAT3教程,有需要的朋友可以自行阅读。

总结

最后回顾总结一下,结束我们这个系列的教程。
本系列教程,我们首先全面地对STM32和LAN9252两款芯片的介绍,继而结合EtherCAT协会推出的SSC EtherCAT从站代码生成工具,然后以及有针对性地对生成的代码进行平台移植,从PIC32芯片的代码移植到了STM32的架构中去,最后我们通过对TwinCAT的简单介绍,在CX9020平台上对我们设计的整个从站系统进行了简单测试。
通过本系列教程,我们可以掌握以下几点:

  • STM32的基础用法,包括SPI,定时器,外部中断等,并学习了如何通过STM32CubeMX图形化生成HAL库开发环境。

  • LAN9252的基本用法,包括原理图,软件SDK配合SSC生成LAN9252的EtherCAT从站协议栈等用法。

  • EtherCAT从站协议栈的简单认知,我们了解了协议栈的相关接口,针对平台的移植其实就是将相应的接口在不同的平台上,合理地利用其硬件资源,调度起来。

  • TwinCAT2的简单使用。

    本系列教程就到此结束,期待以后有更好玩的系列教程跟大家分享。

STM32与LAN9252构建EtherCAT从站(五):STM32与LAN9252适配

  1. 一、 STM32与LAN9252构建EtherCAT从站(一):项目简介
  2. 二、 STM32与LAN9252构建EtherCAT从站(二):使用SSC生成EtherCAT协议栈和XML文件
  3. 三、 STM32与LAN9252构建EtherCAT从站(三):LAN9252的XML文件
  4. 四、 STM32与LAN9252构建EtherCAT从站(四):STM32配置SPI
  5. 五、 STM32与LAN9252构建EtherCAT从站(五):STM32与LAN9252适配
  6. 六、 STM32与LAN9252构建EtherCAT从站(六):TwinCAT2的使用和从站测试

STM32与LAN9252构建EtherCAT从站(五):STM32与LAN9252适配

这一章讲解STM32与LAN9252的全面适配。

1.硬件

既然是全面适配,那不得不略微提一下硬件连接,以下是相关区域的原理图:
lan1
lan2
可以看到,LAN9252与STM32通信一共用了7根线,4根SPI,3根外部中断,这跟我们上文STM32与LAN9252构建EtherCAT从站(四):STM32配置SPI讲解的内容是一致的。

2.软件

软件的适配主要包括以下几点:

  1. 协议栈移植,至少做到.c文件没有报错。上一章我们介绍了方法,后续的步骤留给各位自行完成,千万不要拿来主义。
  2. SPI驱动移植,上一章我们已经完成。
  3. 对接接口。将协议栈暴露给我们的几个函数,在代码相应的位置进行调用。
  4. 编写业务逻辑。根据第二章在Excel中设计的IO交互数据,在指定的函数中编写业务逻辑。

2.1对接接口

整个EtherCAT协议栈期望的我们的程序主体框架的伪代码如下:

void    APPL_AckErrorInd(UINT16 stateTrans) {
}
UINT16 APPL_StartMailboxHandler(void) {
    return ALSTATUSCODE_NOERROR;
}
UINT16 APPL_StopMailboxHandler(void) {
    return ALSTATUSCODE_NOERROR;
}
UINT16 APPL_StartInputHandler(UINT16 *pIntMask) {
    return ALSTATUSCODE_NOERROR;
}
UINT16 APPL_StopInputHandler(void) {
    return ALSTATUSCODE_NOERROR;
}
UINT16 APPL_StartOutputHandler(void) {
    return ALSTATUSCODE_NOERROR;
}
UINT16 APPL_StopOutputHandler(void) {
    return ALSTATUSCODE_NOERROR;
}
UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize){
    return ALSTATUSCODE_NOERROR;
}
void APPL_InputMapping(UINT16 *pData) {
}
void APPL_OutputMapping(UINT16 *pData) {
}
void APPL_Application(void) {
}

void EtherCatIRQ(){
    PDI_Isr();
}

void Sync0IRQ(){
    Sync0_Isr();
}

void Sync0IRQ(){
    Sync1_Isr();
}

void Timer1msIRQ(){
    ECAT_CheckTimer();
}

int main(){
    HW_Init();
    MainInit();
    while(1){
        MainLoop();
    }
}

其中,这些函数均由协议栈提供:

  • PDI_Isr():EtherCAT主中断响应,需要配置到外部中断中,跟LAN9252的44脚对应,下降沿触发。
  • Sync0_Isr():分布式时钟同步信号0,需要配置到外部中断中,跟LAN9252的18脚对应,下降沿触发。
  • Sync1_Isr():分布式时钟同步信号1,需要配置到外部中断中,跟LAN9252的34脚对应,下降沿触发。
  • ECAT_CheckTimer():1ms定时器溢出中断,我这边配置在STM32的TIM8的break interrupt上。
  • HW_Init():硬件初始化函数,MCU通过此函数感应LAN9252设备的存在与否。
  • MainInit():协议栈初始化函数。
  • MainLoop():协议栈主循环,主while(1)中尽量只放此函数,其他业务逻辑在APPL_Application(void)函数中编写,后面介绍。

这些函数需要根据自己的业务逻辑编写:

  • APPL_InputMapping(UINT16 *pData) 输入数据映射
  • APPL_OutputMapping(UINT16 *pData) 输出数据映射
  • APPL_Application(void) 主业务逻辑

这些函数保持默认即可,也可添加自己的业务逻辑:

  • APPL_AckErrorInd(UINT16 stateTrans) EtherCAT通信故障时回调
  • APPL_StartMailboxHandler(void) 邮箱消息接收前回调
  • APPL_StopMailboxHandler(void) 邮箱消息接收后回调
  • APPL_StartInputHandler(UINT16 *pIntMask) 输入映射前回调
  • APPL_StopInputHandler(void) 输入映射后回调
  • APPL_StartOutputHandler(void)输出映射前回调
  • APPL_StopOutputHandler(void) 输出映射后回调

最后一个APPL_GenerateMapping()函数有固定的模板,我们后面介绍。

2.2 中断接口

2.2.1 PDI_Isr()

这个函数来自于ecatappl.c文件,头文件定义取自ecatappl.h,我们在主程序中直接#include applInterface.h即可。

IRQ
将此函数配置到外部中断服务函数中去,我这边使用的是EXTI0_IRQHandler,并且根据习惯,我将这个服务函数从stm32f1xx_it.c中移动到main.c中去了,仅做参考。
EXT0
需要再次提醒的是,上一章已经说过,CubeMX生成的中断初始化函数,在gpio.c中,有以下代码:
gpio
正如我所说,设备上电后我们不可以直接调用HAL_NVIC_EnableIRQ函数将各种中断使能,因为此时协议栈相关数据都没初始化好,直接使能这些中断将导致严重的逻辑问题。因此上述代码后面的使能语句,都需要注释掉。

2.2.2 Sync0_Isr() 和 Sync1_Isr()

这个函数来自于ecatappl.c文件,头文件定义取自ecatappl.h,我们在主程序中直接#include applInterface.h即可。
我们将这个函数配置到EXTI9_5_IRQHandlerEXTI4_IRQHandler中:

void EXTI9_5_IRQHandler(void) {
    /* USER CODE BEGIN EXTI9_5_IRQn 0 */
    DISABLE_ESC_INT();
    Sync0_Isr();
    ENABLE_ESC_INT();
    /* USER CODE END EXTI9_5_IRQn 0 */
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
    /* USER CODE BEGIN EXTI9_5_IRQn 1 */

    /* USER CODE END EXTI9_5_IRQn 1 */
}

void EXTI4_IRQHandler(void) {
    /* USER CODE BEGIN EXTI4_IRQn 0 */
    DISABLE_ESC_INT();
    Sync1_Isr();
    ENABLE_ESC_INT();
    /* USER CODE END EXTI4_IRQn 0 */
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
    /* USER CODE BEGIN EXTI4_IRQn 1 */

    /* USER CODE END EXTI4_IRQn 1 */
}
2.2.3 ECAT_CheckTimer()

这个函数来自于ecatappl.c文件,头文件定义取自ecatappl.h,我们在主程序中直接#include applInterface.h即可。
我们将其配置到TIM8的break interrupt服务函数中去:

void TIM8_BRK_IRQHandler(void) {
    /* USER CODE BEGIN TIM8_BRK_IRQn 0 */
    ECAT_CheckTimer();
    /* USER CODE END TIM8_BRK_IRQn 0 */
    HAL_TIM_IRQHandler(&htim8);
    /* USER CODE BEGIN TIM8_BRK_IRQn 1 */

    /* USER CODE END TIM8_BRK_IRQn 1 */
}

2.3 主程序接口

2.3.1 HW_Init()

这个函数来自9252_HW.c, 在9252HW.h中有其定义,主程序中直接#include ecatslv.h即可。

2.3.2 HW_Init()

这个函数来自ecatappl.c, 在applInterface.h中有其定义,主程序中直接#include applInterface.h即可。

2.3.2 MainLoop()

同上。

2.4 业务逻辑接口

上面提到很多回调函数,在EtherCAT协议栈中,使用了extern关键字来调用那些函数,因此我们需要有这些函数的实体。

2.4.1 APPL_GenerateMapping()

这个函数是根据我们定义的过程通信数据,与内存中的数据进行映射的函数,这个函数不需要自己写,说实话如果靠我们自己写,要对EtherCAT协议栈深入理解才行。所幸SSC生成的代码中已经帮我写好了,但是并不归属于协议栈,在项目文件中有其实现,我这边项目命名为STM32_EtherCAT_Slave,因此在STM32_EtherCAT_Slave.c中可以找到,复制粘贴到main.c中即可。

2.4.2 APPL_InputMapping(UINT16 *pData)

这个函数是输入数据映射函数。这里的【输入】是相对于主站来说,也就是我们这里从站的输出数据。我们这里定义了输入数据是64个bit,因此这里就要将内存中64个bit的数据赋值给pData指针指向的首地址。
又因为我们在SSC配置的Excel中,输入数据定义的是InputS0x6000地址,因此在我的工程文件STM32_EtherCAT_SlaveObjects.h中就会有INPUTS0x6000这个变量的定义。
那么这个函数的实现也就不难了,深入理解C语言指针的同学一定不难看懂以下代码:

void APPL_InputMapping(UINT16 *pData) {
    *pData++ = (((UINT16 *) &INPUTS0x6000)[1]);
    *pData++ = (((UINT16 *) &INPUTS0x6000)[2]);
    *pData++ = (((UINT16 *) &INPUTS0x6000)[3]);
    *pData = (((UINT16 *) &INPUTS0x6000)[4]);
}
2.4.3 APPL_OutputMapping(UINT16 *pData)

同理,该函数这么编写:

void APPL_OutputMapping(UINT16 *pData) {
    ((UINT16 *) &OUTPUTS0x7000)[1] = (*pData++);
    ((UINT16 *) &OUTPUTS0x7000)[2] = (*pData++);
    ((UINT16 *) &OUTPUTS0x7000)[3] = (*pData++);
    ((UINT16 *) &OUTPUTS0x7000)[4] = (*pData);
}

3.总结

至此STM32与LAN9252对接的所有接口都完成了,编译无误的话可以试着下载到设备中去。观察看看LAN9252的RUN灯是否可以常亮。
其实移植的最主要难点还在于是否能将一开始KEIL的所有报错,有耐心地一一排除掉。到了这一步对接接口,就好比一个萝卜一个坑,在正确的位置调用正确的API就可以了。
下一章,我们将结束这篇系列教程,在TwinCAT2上配置从站,并验证我们编写的代码。

丁丁生于 1987.07.01 ,30岁,英文ID:newflydd
  • 现居住地 江苏 ● 泰州 ● 姜堰
  • 创建了 Jblog 开源博客系统
  • 坚持十余年的 独立博客 作者
  • 大学本科毕业后就职于 中国电信江苏泰州分公司,前两年从事Oracle数据库DBA工作,两年后公司精简技术人员,被安排到农村担任支局长(其本质是搞销售),于2016年因志向不合从国企辞职,在小城镇找了一份程序员的工作。
  • 在 Git OSChina 上积极参与开源社区
  •