EtherCAT主站SOEM源码解析----分布时钟DC

本文介绍EtherCAT分布式时钟(DC)的工作原理及其在SOEM中的实现过程,包括DC从站的识别、时间锁存、偏移量及传输延时的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        分布时钟(DC,Dsitributed Clock)可以使所有EtherCAT设备使用相同的系统时间,从而控制各设备任务的同步执行。

 

1、DC从站

       支持分布式时钟的从站称为DC从站,同步原理如下所示:

 

其中,

Tref: 参考时间,网络中第一个DC从站的时间

Tsys_local(n): 第n个从站的本地系统时间副本(同步后等于Tref),这个时间用来产生同步信号和锁存信号时间标记,供从站微处理器使用。

Tdleay(n): 从第一个DC从站到从站n的传输延时。

Toffset(n):从站n的时间和参考时间的初始偏移量。

其中,Toffset(n)和Tdelay(n)在初始化时由主站测量,并写入从站对应寄存器中。Tref在运行过程中由主站使用FRMW命令定期更新。

SOEM中配置同步时钟的步骤如下:

2、获取从站DC特征信息

      在/soem/EthercatConfig.c 的ecx_config_init(ecx_contextt *context, uint8 usetable)函数中,获取从站信息,判断从站是否为DC从站。

         if ((etohs(ecx_FPRDw(context->port, configadr, ECT_REG_ESCSUP, EC_TIMEOUTRET3)) & 0x04) > 0)  /* Support DC? */ 
         {   
            context->slavelist[slave].hasdc = TRUE;  
         }
         else
         {
            context->slavelist[slave].hasdc = FALSE;
         }

3、锁存端口时间

       在计算时间偏差Toffset(n)和传输延时之前,主站需要发送一个广播命令BWR,写所有从站端口0的接收时间寄存器0x0900,将所有从站捕捉数据帧第一个前导位到达每个端口的本地时间保存到寄存器0x0900~0x090F, 每个端口使用4个字节。

       具体代码在/soem/Ethercatdc.c的函数ecx_configdc(ecx_contextt *context)中:

       ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);  /* latch DCrecvTimeA of all slaves */


4、计算初始时间偏移

      读取数据帧处理单元接收时间,与主站的系统时间比较,差值作为Toffset(n)写入从站寄存器0x0920.  计算代码为:

 

        (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);//前导位到达的本地时间
         /* use it as offset in order to set local time around 0 + mastertime */
         hrt = htoell(-etohll(hrt) + mastertime64);//初始偏移量
         /* save it in the offset register */
         (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);

        

      SOEM只在初始化时使从站时间同步主站时间,若要运行后使主从时间同步,需要定期修正主站时间或从站时间   。


5、计算传输延时

      传输延时的计算比较复杂,与总线拓扑密切相关,计算原理图如下:

 


       假设系统有5个从站,拓扑如上图所示,以计算从站4的传输延时Tdelay(4)为例。

       Tdelay(4) = deltaT +  Tdelay(2)  +  dt2  //deltaT为图中黄色粗箭头所示时间

       而deltaT = (dt3-dt1)/2。 假设数据帧经过图中两段黄色粗箭头的时间相等。

  

       dt3为数据帧从Slave2端口2离开到返回的时间,计算代码为:

            dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
                  ecx_porttime(context, parent, 
                    ecx_prevport(context, parent, context->slavelist[i].parentport));

 

      dt1为从站4后续从站的传输时间,计算代码为:

            /* current slave has children */
            /* those childrens delays need to be substacted */
            if (context->slavelist[i].topology > 1)
            {
               dt1 = ecx_porttime(context, i, 
                        ecx_prevport(context, i, context->slavelist[i].entryport)) -
                     ecx_porttime(context, i, context->slavelist[i].entryport);
            }


      dt2为从站2其它接口所接从站的传输时间,计算代码为:

           /* current slave is not the first child of parent */
            /* previous childs delays need to be added */
            if ((child - parent) > 1)
            {
               dt2 = ecx_porttime(context, parent, 
                        ecx_prevport(context, parent, context->slavelist[i].parentport)) -
                     ecx_porttime(context, parent, context->slavelist[parent].entryport);
            }


      计算传输延时并写入从站寄存器0x0928,代码为:

            /* calculate current slave delay from delta times */
            /* assumption : forward delay equals return delay */
            context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
               context->slavelist[parent].pdelay;
            ht = htoel(context->slavelist[i].pdelay);
            /* write propagation delay*/
            (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);


6、广播参考时钟

         在运行过程中,主站使用命令FRMW读取第一个DC从站的时间并写入到后续DC从站,达到时间同步的目的。

               if(first)//第一个DC从站
               {
                  context->DCl = sublength;
                  /* FPRMW in second datagram */
                  context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
                                           context->slavelist[context->grouplist[group].DCnext].configadr,
                                           ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
                  first = FALSE;
               }


            







<think>好的,我现在需要帮助用户将EtherCAT主站SOEM)移植到STM32F767的裸机环境中。首先,我需要明确用户的需求。他们使用的是STM32CubeIDE和STM32CubeMX 6.2.0,库版本是STM32Cube_FW_F4_V1.26.2,但目标芯片是STM32F767,这属于F7系列,可能需要确认库的兼容性。不过用户提到的是F4库,可能是个笔误,需要留意。 首先,移植SOEM到STM32F767需要了解SOEM的基本要求。SOEM是一个开源的EtherCAT主站协议栈,通常运行在Linux环境下,移植到裸机环境需要处理操作系统相关的部分,比如内存管理、定时器、网络驱动等。因此,我需要考虑如何替换这些依赖。 接下来是外设配置。STM32F767的以太网外设是关键,需要正确配置MAC和PHY。用户需要确认使用哪个PHY芯片,比如LAN8742,然后在CubeMX中配置RMII或MII接口。时钟配置非常重要,特别是ETH时钟的来源,比如来自PLL,必须确保时钟频率正确,否则通信会失败。参考用户提到的时钟配置[^1],可能需要调整PLL参数以满足ETH的要求。 中断配置方面,以太网需要处理接收和发送中断,可能还需要定时器中断用于EtherCAT的周期性任务。在裸机环境中,中断服务函数必须高效,避免长时间阻塞,否则会影响EtherCAT的实时性。 然后是SOEM本身的移植。需要实现oshw.c中的硬件抽象层,比如网络发送接收函数、定时器函数和内存分配。可能需要修改SOEM的代码,替换掉Linux特有的系统调用,比如gettimeofday,用STM32的定时器代替。此外,SOEM可能需要一个精确的时钟来同步从站,因此高精度定时器的配置也是关键。 内存管理方面,STM32F767有足够的RAM吗?EtherCAT通信需要一定的缓冲区,需确保分配足够的内存,同时避免内存泄漏。可能需要调整链接脚本,分配适当的内存区域。 还需要考虑实时性。EtherCAT要求严格的周期时间,因此主循环的结构需要优化,确保及时处理EtherCAT帧。可能需要使用定时器中断来触发周期任务,而不是依赖轮询。 调试部分,如何验证移植是否成功?用户可能需要通过日志输出或者LED指示来检查EtherCAT状态机的变化,比如使用串口打印调试信息,或者利用调试器查看变量状态。 此外,用户可能遇到的问题包括时钟配置错误导致以太网无法通信,中断优先级设置不当导致实时性不足,或者内存不足导致运行时错误。需要逐一排查这些可能性,提供相应的解决建议。 最后,生成相关问题时,需要考虑用户可能进一步的需求,比如如何优化性能、如何处理特定的从站设备、如何进行实时性测试等。</think>### EtherCAT主站移植到STM32F767裸机环境的步骤 #### 一、硬件基础配置 1. **外设初始化** - 通过STM32CubeMX配置以下模块: - **ETH外设**:选择RMII接口(需与PHY芯片匹配),例如LAN8742 PHY配置 - **GPIO**:配置ETH相关引脚(REF_CLK/CRS_DV/RXD0-RXD1/TXD0-TXD1等) - **DMA**:启用ETH接收/发送DMA通道,优化数据传输效率 - 生成代码时勾选"LWIP"库(需手动替换为SOEM协议栈) 2. **时钟配置关键点** - ETH时钟必须从PLL输出获得,STM32F767的ETH要求RMII接口REF_CLK为50MHz - 在CubeMX中配置: $$ PLL\_N = 432,\quad PLL\_M = 25,\quad PLL\_P = 2 $$ $$ HCLK = 216MHz,\quad APB1 = 54MHz,\quad APB2 = 108MHz $$ #### 二、SOEM协议栈移植 1. **代码结构修改** ```c /* 添加硬件抽象层 */ // oshw_stm32.c void oshw_setup(void) { ethernet_init(); // 自定义ETH初始化 timer_setup(); // 1ms精度定时器 } // 重写网络收发函数 int oshw_send(uint8_t *buf, int len) { HAL_ETH_Transmit(&heth, buf, len, 1000); } ``` 2. **关键适配点** - 替换`oshw.c`中的时间函数(使用Systick或硬件定时器) - 修改`nicdrv.c`直接调用ETH HAL库函数 - 调整`ecatcoe.h`中的PDO映射缓存区大小(根据从站配置) #### 三、中断与实时性优化 1. **中断配置模板** ```c // stm32f7xx_it.c void ETH_IRQHandler(void) { HAL_ETH_IRQHandler(&heth); /* SOEM处理 */ ecat_poll(); // EtherCAT状态机 } // 定时器中断配置(周期1ms) void TIM2_IRQHandler(void) { __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); ecat_checktimer(); // SOEM时间基准 } ``` 2. **优先级设置建议** | 中断源 | 优先级 | 说明 | |----------------|--------|-----------------------| | ETH | 0 | 最高级实时响应 | | SYSTICK | 1 | 系统时钟基准 | | DMA | 2 | 数据传输 | #### 四、验证流程 1. **基础测试序列** - 使用示波器测量PHY的REF_CLK是否稳定50MHz - 通过`ecat_init()`返回值确认主站初始化状态 - 使用Wireshark抓包验证EtherCAT帧的正确性 2. **状态监测代码示例** ```c while(1) { ecat_poll(); if(ecat_slave[0].state != prev_state) { printf("Slave状态变化: 0x%02X\n", ecat_slave[0].state); prev_state = ecat_slave[0].state; } } ``` #### 五、常见问题解决 - **PHY链路不稳定**:检查复位电路,确认`LAN8742_Init()`中配置了自动协商 - **SOEM报超时错误**:检查DMA描述符对齐方式,确保缓存区32字节对齐 - **周期抖动过大**:优化中断处理时间,使用`DC-Sync`模式需要硬件定时器支持
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值