EtherCAT-8 从站代码移植-FSMC

硬件平台:STM32F103 + AX58100

AX58100 提供两种过程数据接口 (PDI),Local Bus 接口和 SPI Slave 接口,可通过这些接口将 AX58100 连接到外部 MCU/以支持 EtherCAT 功能。

上一章内容写了SPI接口,本章对LocalBus接口进行介绍,和上章重复的部分就不在叙述。LocalBus为并行通讯方式,相对于SPI,通讯速率更高,最高可到100Mbit/s,适用于传输数量较大的情况。LocalBus支持8bit或16bit并行数据,与MCU的FSMC或FMC外设接口连接,本文以16bit数据传输、FSMC外设接口为例。

1、硬件连接

注意,AX58100的地址线LA0接地,LA1:13连接至MCU的ADR0:12。

2、MCU配置

2.1 时钟

2.2 FSMC

  • 片选 NE1~NE4任意选择
  • 内存类型:SRAM
  • 地址位数,AX58100有14根地址线,此处填14 bits
  • 数据位数:16bits
  • 非同步模式
  • 取消勾选Byte enable
  • Extended mode设置为Disabled,FSMC自动使用模式A对SRAM进行操作,SRAM的读写操作速度基本相同,所以读写操作可以使用相同的时序参数,无需使用扩展模式单独设置读时序和写时序
  • 时序,三个参数:地址建立时间、数据建立时间和总线翻转时间,需要结合芯片读写时序具体设置

2.2.1 FSMC控制SRAM的时序

模式A的读写时序,如下图。

该图表示一个存储器操作周期由地址建立周期(ADDSET)、数据建立周期(DATAST)以及2个HCLK周期组成。在地址建立周期中,地址线发出要访问的地址, 数据掩码信号线指示出要读取地址的高、低字节部分,片选信号使能存储器芯片;地址建立周期结束后读使能信号线发出读使能信号, 接着存储器通过数据信号线把目标数据传输给FSMC,FSMC把它交给内核。

写时序类似,区别是它的一个存储器操作周期仅由地址建立周期(ADDSET)和数据建立周期(DATAST)组成, 且在数据建立周期期间写使能信号线发出写信号,接着FSMC把数据通过数据线传输到存储器中。

2.2.2 localBus的时序要求

结合FSMC和localBus的时序要求,参数配置如下:

总线翻转时间:在NOR FLASH存储器中,地址线与数据线可以分时复用,总线转换周期就是指总线在这两种状态间切换需要的延时,防止冲突,控制其它存储器时这个参数无效,配置为0即可。

地址建立时间:

地址就绪至读使能的时间:TADR_BHE_setup (>0)

地址就绪至写使能的时间:TADR_BHE_DATA_setup(>6.5 ns) - TWR_active(>8.5 ns)

数据建立时间:

读使能至数据有效的时间:Tread(<300 ns) +TBUSY_to_DATA_valid(< -15 ns)

写使能至写使能结束的时间:TWR_active(>8.5 ns)

即 (ADDSET+1) > 0, 8.5ns < (DATAST+1) < 285ns ,FSMC的时钟周期是1/72Mhz = 13.9ns,所以设置ADDSET = 0,DATAST = 10即满足要求。

其他定时器和外部中断配置与上一章SPI接口一致。

3.软件接口

软件接口可以在ssc工具生成的mcihw.h/c(SSC->Hardware选项的MCI_HW置1)文件上修改。

mcihw.h

#ifndef _MCIHW_H_
#define _MCIHW_H_


/*-----------------------------------------------------------------------------------------
------
------    Includes
------
-----------------------------------------------------------------------------------------*/
#include  "esc.h"



/*-----------------------------------------------------------------------------------------
------
------    Defines and Types
------
-----------------------------------------------------------------------------------------*/
#define ESC_PDI_BASEADDR     ((UINT32)0x60000000)  //Bank1_SRAMx_ADDR



/*---------------------------------------------
-    hardware timer settings
-----------------------------------------------*/


/*---------------------------------------------
-    Interrupt and Timer defines
-----------------------------------------------*/


  #define HW_GetALEventRegister()                   (*(volatile UINT16 ESCMEM *)(ESC_PDI_BASEADDR+(ESC_AL_EVENT_OFFSET)))  /**< \brief Returns the first 16Bit of the AL Event register (0x220)*/
  #define HW_GetALEventRegister_Isr                 HW_GetALEventRegister  /**< \brief Returns the first 16Bit of the AL Event register (0x220).<br>Called from ISRs.*/

  #define HW_EscRead(pData,Address,Len)             ESCMEMCPY((MEM_ADDR *)(pData), (UINT16 *)(ESC_PDI_BASEADDR+((Address))), (Len)) /**< \brief Generic ESC (register and DPRAM) read access.*/
  #define HW_EscWrite(pData,Address,Len)            ESCMEMCPY((UINT16 *)(ESC_PDI_BASEADDR+(Address)), (MEM_ADDR *)(pData), (Len)) /**< \brief Generic ESC (register and DPRAM) write access.*/
  #define HW_EscReadIsr                             HW_EscRead  /**< \brief Generic ESC (register and DPRAM) read access.<br>Called for ISRs.*/
  #define HW_EscWriteIsr                            HW_EscWrite /**< \brief Generic ESC (register and DPRAM) write access.<br>Called for ISRs.*/

//#if ESC_32BIT_ACCESS
  #define HW_EscReadDWord(DWordValue, Address)      ((DWordValue) = (*(volatile UINT32 *)(ESC_PDI_BASEADDR+(Address))))  /**< \brief 32Bit ESC read access*/
  #define HW_EscWriteDWord(DWordValue, Address)     ((*(volatile UINT32 *)(ESC_PDI_BASEADDR+(Address))) = (UINT32)(DWordValue))  /**< \brief 32Bit ESC write access*/
  #define HW_EscReadDWordIsr                        HW_EscReadDWord  /**< \brief Interrupt specific 32Bit ESC read access*/
  #define HW_EscWriteDWordIsr                       HW_EscWriteDWord  /**< \brief Interrupt specific 32Bit ESC write access*/
//#elif ESC_16BIT_ACCESS
  #define HW_EscReadWord(WordValue, Address)        ((WordValue) = (*(volatile UINT16 *)(ESC_PDI_BASEADDR+(Address))))  /**< \brief 16Bit ESC read access*/
  #define HW_EscWriteWord(WordValue, Address)       ((*(volatile UINT16 *)(ESC_PDI_BASEADDR+(Address))) = (UINT16)(WordValue))  /**< \brief 16Bit ESC write access*/
  #define HW_EscReadWordIsr                         HW_EscReadWord  /**< \brief Interrupt specific 16Bit ESC read access*/
  #define HW_EscWriteWordIsr                        HW_EscWriteWord  /**< \brief Interrupt specific 16Bit ESC write access*/
//#else
//  #define HW_EscReadByte(ByteValue,Address)	      ((ByteValue) = (*(volatile UINT8 *)(ESC_PDI_BASEADDR+(Address)))) /**< \brief 8Bit specific ESC (register and DPRAM) read access.*/
//  #define HW_EscWriteByte(ByteValue, Address)     ((*(volatile UINT8 *)(ESC_PDI_BASEADDR+(Address))) = (UINT8)(ByteValue))  /**< \brief 8Bit specific ESC (register and DPRAM) write access.*/
//  #define HW_EscReadByteIsr	                      HW_EscReadByte
//  #define HW_EscWriteByteIsr                      HW_EscWriteByte
//#endif

#define HW_EscReadMbxMem(pData,Address,Len)      ESCMBXMEMCPY((MEM_ADDR *)(pData), (UINT16 ESCMEM *)(ESC_PDI_BASEADDR+((Address))), (Len))  /**< \brief The mailbox data is stored in the local uC memory therefore the default read function is used.*/
#define HW_EscWriteMbxMem(pData,Address,Len)     ESCMBXMEMCPY(((UINT16 ESCMEM *)(ESC_PDI_BASEADDR+((Address)))),(MEM_ADDR *)(pData), (Len))  /**< \brief The mailbox data is stored in the local uC memory therefore the default write function is used.*/



#endif //_MCIHW_H_

/* ECATCHANGE_START(V5.11) ECAT10*/
#if defined(_MCIHW_H_) && (_MCIHW_H_ == 1)
/* ECATCHANGE_END(V5.11) ECAT10*/
    #define PROTO
#else
    #define PROTO extern
#endif


/*-----------------------------------------------------------------------------------------
------
------    Global variables
------
-----------------------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------------------------
------
------    Global functions
------
-----------------------------------------------------------------------------------------*/
PROTO UINT8 HW_Init(void);
PROTO void  HW_Release(void);

#undef    PROTO
/** @}*/

mcihw.c

/*--------------------------------------------------------------------------------------
------
------    Includes
------
--------------------------------------------------------------------------------------*/
#include "ecat_def.h"

#include "ecatslv.h"


#include "mcihw.h"



#include "ecatappl.h"

#include  "gpio.h"
#include  "tim.h"

/*--------------------------------------------------------------------------------------
------
------    internal Types and Defines
------
--------------------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------------------------
------
------    SPI defines/macros
------
-----------------------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------------------------
------
------    Global Interrupt setting
------
-----------------------------------------------------------------------------------------*/
#define     ENABLE_GLOBAL_INT()       __enable_irq()
#define     DISABLE_GLOBAL_INT()      __disable_irq()
#define     ENABLE_AL_EVENT_INT       ENABLE_GLOBAL_INT()
#define     DISABLE_AL_EVENT_INT      DISABLE_GLOBAL_INT()
/*-----------------------------------------------------------------------------------------
------
------    ESC Interrupt
------
-----------------------------------------------------------------------------------------*/
//PDI_Isr
#define ENABLE_ESC_INT()    HAL_NVIC_EnableIRQ(EXTI0_IRQn)
#define DISABLE_ESC_INT()   HAL_NVIC_DienableIRQ(EXTI0_IRQn)
/*-----------------------------------------------------------------------------------------
------
------    SYNC0 Interrupt
------
-----------------------------------------------------------------------------------------*/
//Sync0_Isr
#define ENABLE_SYNCO_INT()    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn)
#define DISABLE_SYNCO_INT()   HAL_NVIC_DienableIRQ(EXTI9_5_IRQn)
//Sync1_Isr
#define ENABLE_SYNC1_INT()    HAL_NVIC_EnableIRQ(EXTI4_IRQn)
#define DISABLE_SYNC1_INT()   HAL_NVIC_DienableIRQ(EXTI4_IRQn)
/*-----------------------------------------------------------------------------------------
------
------    Hardware timer
------
-----------------------------------------------------------------------------------------*/
#define START_ECAT_TIMER()    HAL_TIM_Base_Start_IT(&htim3)
#define STOP_ECAT_TIMER()     HAL_TIM_Base_Stop_IT(&htim3)

#define HW_GetTimer()         __HAL_TIM_GET_COUNTER(&htim3)
#define ECAT_TIMER_INC_P_MS   1000  //1ms定时器的计数值
/*-----------------------------------------------------------------------------------------
------
------    Configuration Bits
------
-----------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------------
------
------    LED defines
------
-----------------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------------
------
------    internal Variables
------
----------------------------------------------------------------------------------


/*--------------------------------------------------------------------------------------
------
------    internal functions
------
--------------------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------------------
------
------    exported hardware access functions
------
--------------------------------------------------------------------------------------*/


/
/**
\return     0 if initialization was successful

\brief    This function intialize the Process Data Interface (PDI) and the host controller.
*
UINT8 HW_Init(void)
{
/*ECATCHANGE_START(V5.11) EL9800 2*/
    UINT32 intMask;
/*ECATCHANGE_END(V5.11) EL9800 2*/


    /* make sure it's localbus */ 
    do
    {
        intMask = 0x00;
        HW_EscReadByte(intMask, ESC_PDI_CONTROL_OFFSET);
    } while (intMask != 0x08);

/*ECATCHANGE_START(V5.11) EL9800 2*/
    do
    {
        intMask = 0x93;
        HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);
        intMask = 0;
        HW_EscReadDWord(intMask, ESC_AL_EVENTMASK_OFFSET);

    } while (intMask != 0x93);

    intMask = 0x00;

    HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);

/*ECATCHANGE_END(V5.11) EL9800 2*/

#if AL_EVENT_ENABLED
   ENABLE_ESC_INT();
#endif

#if DC_SUPPORTED
   ENABLE_SYNC0_INT();
   ENABLE_SYNC1_INT();
#endif

#if ECAT_TIMER_INT
    START_ECAT_TIMER();
#endif

#if INTERRUPTS_SUPPORTED
    /* enable all interrupts */
    ENABLE_GLOBAL_INT();
#endif

    return 0;
}


/
/**
 \brief    This function shall be implemented if hardware resources need to be release
        when the sample application stops
*
void HW_Release(void)
{
    DISABLE_GLOBAL_INT();
}



/** @} */

其他内容与上一章SPI接口一致。

<think>好的,我现在需要帮助用户解决如何将EtherCATSOEM移植STM32F767裸机环境中,并使用标准库进行外设时钟配置的问题。首先,我需要回顾用户提供的信息和之前的引用内容。用户提到使用的是STM32Cube_FW_F4_V1.26.2库和STM32CubeIDE 6.2.0,但目标芯片是STM32F767,属于F7系列,可能需要参考F7的库,但用户可能误用了F4的库信息。这一点需要注意,可能需要确认正确的库版本。 接下来,用户的问题涉及EtherCATSOEM的移植,这通常需要处理实时以太网通信,对时钟配置要求较高,特别是精确的时钟同步。STM32F767的外设包括以太网控制器(可能使用MAC或需要外接PHY芯片),因此需要正确配置相关外设的时钟,如SYSCLK、HCLK、PCLK1、PCLK2等,确保以太网外设的时钟频率符合要求。 首先,我需要考虑标准库和HAL库的区别。用户希望使用标准库,而STM32CubeMX默认生成的是HAL库代码。因此,可能需要手动配置时钟,或者找到标准库的时钟配置方法。标准库中通常通过SystemInit函数和system_stm32f7xx.c文件来配置时钟,用户可能需要修改此文件中的时钟树设置,如PLL参数、分频系数等,以匹配所需的频率。 然后,以太网外设的时钟配置是关键。STM32F767的以太网MAC需要25MHz或50MHz的时钟,通常由外部晶振提供,或者通过内部PLL分频得到。需要确认用户使用的PHY芯片类型,例如LAN8742等,并配置正确的RMII或MII接口时钟。对于RMII模式,需要提供50MHz的参考时钟,这可能来自PHY芯片或STM32的MCO输出。 接下来是SOEM的移植步骤。需要将SOEM的源码添加到项目中,并调整硬件抽象层(HAL)部分,特别是定时器、GPIO、SPI或以太网驱动。在裸机环境中,需要确保SOEM的任务调度和定时器中断能够正确运行,通常需要配置SysTick或硬件定时器来提供1ms的中断周期。 此外,内存管理也很重要,因为EtherCAT通信需要较大的缓冲区,可能需要调整堆栈大小,或者使用外部RAM。STM32F767具有较大的内存,但仍需合理分配。 在调试阶段,用户需要验证时钟配置是否正确,可以使用示波器测量相关引脚,或者通过软件读取时钟状态寄存器。同时,需要检查以太网的数据链路层通信是否正常,例如PHY的初始化、链路状态检测等。 最后,用户可能需要参考STM32F7的标准库文档,以及SOEM的官方移植指南,结合已有的STM32CubeMX配置作为参考,手动调整标准库的初始化代码。需要提醒用户注意标准库和HAL库之间的差异,特别是在外设初始化和中断处理方面。 可能遇到的问题包括时钟配置错误导致以太网通信失败、SOEM任务调度与裸机环境不兼容、内存不足等。需要逐步排查,从时钟配置开始,确保所有外设时钟正确,然后逐步集成SOEM的各个模块,进行功能测试。</think>将EtherCATSOEM移植STM32F767裸机环境需要分步骤完成,以下是基于标准库的外设时钟配置和移植要点: --- ### **一、时钟配置关键步骤** 1. **系统时钟初始化** - 在`system_stm32f7xx.c`中修改PLL参数,例如: $$ SYSCLK = \frac{HSE\_VAL \times PLLN}{PLLM \times PLLP} $$ 需确保主频达到216MHz(STM32F7最大值)[^1]。 - 配置AHB/APB分频器,确保以太网MAC时钟(通过AHB)满足要求: ```c RCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK = SYSCLK = 216MHz RCC_PCLK1Config(RCC_HCLK_Div4); // PCLK1 = 54MHz RCC_PCLK2Config(RCC_HCLK_Div2); // PCLK2 = 108MHz ``` 2. **以太网外设时钟** - 若使用RMII接口,需提供50MHz参考时钟: - 方案1:通过外部晶振直接输入到PHY芯片 - 方案2:使用STM32的MCO输出50MHz(需配置PLL和分频器) ```c RCC_MCO1Config(RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_4); // PLLCLK=200MHz时,输出50MHz ``` 3. **启用外设时钟** ```c RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE); ``` --- ### **二、SOEM移植核心步骤** 1. **源码集成** - 将SOEM源码中的`oshw.c`、`nicdrv.c`等文件加入工程。 - 修改硬件抽象层函数(如`oshw.c`中的定时器、GPIO操作)。 2. **定时器配置** - 配置SysTick或硬件定时器为1ms中断,用于SOEM周期任务: ```c SysTick_Config(SystemCoreClock / 1000); // 1ms中断 ``` 3. **以太网驱动适配** - 实现`ecx_setupnic()`和`ecx_close()`函数,调用标准库的ETH初始化: ```c ETH_InitTypeDef ETH_InitStruct; ETH_InitStruct.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; ETH_Init(&ETH_InitStruct); ``` --- ### **三、调试与验证** 1. 使用逻辑分析仪检查RMII接口的时钟和数据信号。 2. 通过SOEM的`simple_test`例程测试从通信。 3. 检查内存占用,必要时启用外部SDRAM(通过FSMC/FMC配置)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值