EtherCAT基于SOEM建立主站程序
https://blog.csdn.net/weixin_43956732/article/details/106565370?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
simple_test.c解读 基于SOEM创建主站步骤 主要函数介绍 ecx_init函数 ecx_config_init函数 ecx_config_map_group函数 ecx_SDOwrite函数 ecx_SDOread函数 ecx_statecheck函数 simple_test.c文件 基于SOEM创建主站步骤 参照EtherCAT状态机,可以为如下步骤
1 初始化SOEM,将socket绑定到ifname,调用 ecx_init 2 枚举并初始化从站,调用ecx_config_init函数 3 建立从站pdo与IOMap映射,调用ecx_config_map_group函数 4 等待所有从站运行到安全状态(SAFE_OP),调用ec_statecheck函数 5 切换到运行状态(OP state),通过发送一个有效过程数据实现 //等待所有从站进入运行状态 /* wait for all slaves to reach OP state */ do { ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); ec_statecheck(0, EC_STATE_OPERATIONAL, 50000); } while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL)); 1 2 3 4 5 6 7 8 9 主要函数介绍 ecx_init函数 功能:初始化SOEM,将socket绑定到ifname ecx_init函数定义在ethercatmain.h文件,其有两种参数形式,由于大部分函数都是这两种格式,所以后文将不做区分处理。
int ec_init(const char * ifname); int ecx_init(ecx_contextt *context, const char * ifname); 1 2 函数方法在ethercatmain.c中定义如下
/** Initialise lib in single NIC mode * @param[in] context = context struct * @param[in] ifname = Dev name, f.e. "eth0" * @return >0 if OK */ int ecx_init(ecx_contextt *context, const char * ifname) { return ecx_setupnic(context->port, ifname, FALSE); } 1 2 3 4 5 6 7 8 9 ecx_setupnic函数调用的oshw/linux/nicdrv.h文件,实现方法太长,本文不介绍。
/** Basic setup to connect NIC to socket. * @param[in] port = port context struct * @param[in] ifname = Name of NIC device, f.e. "eth0" * @param[in] secondary = if >0 then use secondary stack instead of primary * @return >0 if succeeded */ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) {...} 1 2 3 4 5 6 7 8 ecx_config_init函数 功能:枚举并初始化从站,返回发现从站的个数 定义在ethercatconfig.h文件,实现方法如下
/** Enumerate and init all slaves. * * @param[in] context = context struct * @param[in] usetable = TRUE when using configtable to init slaves, FALSE otherwise * @return Workcounter of slave discover datagram = number of slaves found */ int ecx_config_init(ecx_contextt *context, uint8 usetable) {...} 1 2 3 4 5 6 7 8 ecx_config_map_group函数 功能:将所有从站pdo映射到IOmap 定义在ethercatconfig.h文件,实现方法如下
/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs * in sequential order (legacy SOEM way). * * * @param[in] context = context struct * @param[out] pIOmap = pointer to IOmap * @param[in] group = group to map, 0 = all groups * @return IOmap size */ int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group) {...} 1 2 3 4 5 6 7 8 9 10 11 ecx_SDOwrite函数 功能:写入SDO数据 定义在ethercatcoe.h文件,实现方法如下
/** CoE SDO write, blocking. Single subindex or Complete Access. * * A "normal" download request is issued, unless we have * small data, then a "expedited" transfer is used. If the parameter is larger than * the mailbox size then the download is segmented. The function will split the * parameter data in segments and send them to the slave one by one. * * @param[in] context = context struct * @param[in] Slave = Slave number * @param[in] Index = Index to write * @param[in] SubIndex = Subindex to write, must be 0 or 1 if CA is used. * @param[in] CA = FALSE = single subindex. TRUE = Complete Access, all subindexes written. * @param[in] psize = Size in bytes of parameter buffer. * @param[out] p = Pointer to parameter buffer * @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM * @return Workcounter from last slave response */ int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex, boolean CA, int psize, void *p, int Timeout) {...} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ecx_SDOread函数 功能:读取SDO数据 定义在ethercatcoe.h文件,实现方法如下
/** CoE SDO read, blocking. Single subindex or Complete Access. * * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes * then a "expedited" response is returned, otherwise a "normal" response. If a "normal" * response is larger than the mailbox size then the response is segmented. The function * will combine all segments and copy them to the parameter buffer. * * @param[in] context = context struct * @param[in] slave = Slave number * @param[in] index = Index to read * @param[in] subindex = Subindex to read, must be 0 or 1 if CA is used. * @param[in] CA = FALSE = single subindex. TRUE = Complete Access, all subindexes read. * @param[in,out] psize = Size in bytes of parameter buffer, returns bytes read from SDO. * @param[out] p = Pointer to parameter buffer * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM * @return Workcounter from last slave response */ int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex, boolean CA, int *psize, void *p, int timeout) {...} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ecx_statecheck函数 功能:检测从站状态 定义在ethercatmain.h文件,实现方法如下
/** Check actual slave state. * This is a blocking function. * To refresh the state of all slaves ecx_readstate()should be called * @param[in] context = context struct * @param[in] slave = Slave number, 0 = all slaves (only the "slavelist[0].state" is refreshed) * @param[in] reqstate = Requested state * @param[in] timeout = Timeout value in us * @return Requested state, or found state after timeout. */ uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout) {...} 1 2 3 4 5 6 7 8 9 10 11 simple_test.c文件 /** \file * \brief Example code for Simple Open EtherCAT master * * Usage : simple_test [ifname1] * ifname is NIC interface, f.e. eth0 * * This is a minimal test. * * (c)Arthur Ketels 2010 - 2011 */
#include <stdio.h> #include <string.h> #include <inttypes.h>
#include "ethercat.h"
#define EC_TIMEOUTMON 500
char IOmap[4096]; //映射空间 OSAL_THREAD_HANDLE thread1; int expectedWKC; boolean needlf; volatile int wkc; boolean inOP; uint8 currentgroup = 0;
void simpletest(char *ifname) { int i, j, oloop, iloop, chk; needlf = FALSE; inOP = FALSE;
printf("Starting simple test\n");
/* initialise SOEM, bind socket to ifname */ if (ec_init(ifname)) //初始化soem,将主站绑定到网口 { printf("ec_init on %s succeeded.\n",ifname); /* find and auto-config slaves */
if ( ec_config_init(FALSE) > 0 ) //配置从站 { printf("%d slaves found and configured.\n",ec_slavecount);
ec_config_map(&IOmap); //主站内存空间与从站内存空间映射
ec_configdc(); //配置
printf("Slaves mapped, state to SAFE_OP.\n"); /* wait for all slaves to reach SAFE_OP state */ ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4); //运行到安全运行状态 //将主站输入输出字节上下限设置为1和8 oloop = ec_slave[0].Obytes; if ((oloop == 0) && (ec_slave[0].Obits > 0)) oloop = 1; if (oloop > 8) oloop = 8; iloop = ec_slave[0].Ibytes; if ((iloop == 0) && (ec_slave[0].Ibits > 0)) iloop = 1; if (iloop > 8) iloop = 8;
printf("segments : %d : %d %d %d %d\n",ec_group[0].nsegments ,ec_group[0].IOsegment[0],ec_group[0].IOsegment[1],ec_group[0].IOsegment[2],ec_group[0].IOsegment[3]);
//工作计数 printf("Request operational state for all slaves\n"); expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC; printf("Calculated workcounter %d\n", expectedWKC); ec_slave[0].state = EC_STATE_OPERATIONAL;
//激活从站输出 /* send one valid process data to make outputs in slaves happy*/ ec_send_processdata();
//接收数据 ec_receive_processdata(EC_TIMEOUTRET); /* request OP state for all slaves */
//写入状态 ec_writestate(0); chk = 200;
//等待所有从站进入运行状态 /* wait for all slaves to reach OP state */ do { ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); ec_statecheck(0, EC_STATE_OPERATIONAL, 50000); } while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL));
//进入运行状态 if (ec_slave[0].state == EC_STATE_OPERATIONAL ) { printf("Operational state reached for all slaves.\n"); inOP = TRUE; /* cyclic loop */ for(i = 1; i <= 10000; i++) { ec_send_processdata(); wkc = ec_receive_processdata(EC_TIMEOUTRET);
if(wkc >= expectedWKC) { printf("Processdata cycle %4d, WKC %d , O:", i, wkc);
for(j = 0 ; j < oloop; j++) { printf(" %2.2x", *(ec_slave[0].outputs + j)); }
printf(" I:"); for(j = 0 ; j < iloop; j++) { printf(" %2.2x", *(ec_slave[0].inputs + j)); } printf(" T:%"PRId64"\r",ec_DCtime); needlf = TRUE; } osal_usleep(5000);
} inOP = FALSE; } //未进入运行状态 else { printf("Not all slaves reached operational state.\n"); ec_readstate(); //读取状态 for(i = 1; i<=ec_slavecount ; i++) { if(ec_slave[i].state != EC_STATE_OPERATIONAL) { printf("Slave %d State=0x%2.2x StatusCode=0x%4.4x : %s\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode)); } } } printf("\nRequest init state for all slaves\n"); ec_slave[0].state = EC_STATE_INIT; //初始化所有从站 /* request INIT state for all slaves */ ec_writestate(0); } else { printf("No slaves found!\n"); } printf("End simple test, close socket\n"); /* stop SOEM, close socket */ ec_close(); } else { printf("No socket connection on %s\nExecute as root\n",ifname); } }
//检测 OSAL_THREAD_FUNC ecatcheck( void *ptr ) { int slave; (void)ptr; /* Not used */
while(1) { if( inOP && ((wkc < expectedWKC) || ec_group[currentgroup].docheckstate)) { if (needlf) { needlf = FALSE; printf("\n"); } /* one ore more slaves are not responding */ ec_group[currentgroup].docheckstate = FALSE; ec_readstate(); for (slave = 1; slave <= ec_slavecount; slave++) { if ((ec_slave[slave].group == currentgroup) && (ec_slave[slave].state != EC_STATE_OPERATIONAL)) { ec_group[currentgroup].docheckstate = TRUE; if (ec_slave[slave].state == (EC_STATE_SAFE_OP + EC_STATE_ERROR)) { printf("ERROR : slave %d is in SAFE_OP + ERROR, attempting ack.\n", slave); ec_slave[slave].state = (EC_STATE_SAFE_OP + EC_STATE_ACK); ec_writestate(slave); } else if(ec_slave[slave].state == EC_STATE_SAFE_OP) { printf("WARNING : slave %d is in SAFE_OP, change to OPERATIONAL.\n", slave); ec_slave[slave].state = EC_STATE_OPERATIONAL; ec_writestate(slave); } else if(ec_slave[slave].state > EC_STATE_NONE) { if (ec_reconfig_slave(slave, EC_TIMEOUTMON)) { ec_slave[slave].islost = FALSE; printf("MESSAGE : slave %d reconfigured\n",slave); } } else if(!ec_slave[slave].islost) { /* re-check state */ ec_statecheck(slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET); if (ec_slave[slave].state == EC_STATE_NONE) { ec_slave[slave].islost = TRUE; printf("ERROR : slave %d lost\n",slave); } } } if (ec_slave[slave].islost) { if(ec_slave[slave].state == EC_STATE_NONE) { if (ec_recover_slave(slave, EC_TIMEOUTMON)) { ec_slave[slave].islost = FALSE; printf("MESSAGE : slave %d recovered\n",slave); } } else { ec_slave[slave].islost = FALSE; printf("MESSAGE : slave %d found\n",slave); } } } if(!ec_group[currentgroup].docheckstate) printf("OK : all slaves resumed OPERATIONAL.\n"); } osal_usleep(10000); } }
int main(int argc, char *argv[]) { printf("SOEM (Simple Open EtherCAT Master)\nSimple test\n");
if (argc > 1) { /* create thread to handle slave error handling in OP */ // pthread_create( &thread1, NULL, (void *) &ecatcheck, (void*) &ctime); osal_thread_create(&thread1, 128000, &ecatcheck, (void*) &ctime); /* start cyclic part */ simpletest(argv[1]); } else { printf("Usage: simple_test ifname1\nifname = eth0 for example\n"); }
printf("End program\n"); return (0);
———————————————— 版权声明:本文为CSDN博主「雪花飞龙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_43956732/article/details/106565370 |