以太网PHY驱动软件配置
这里以Renesas提供的RZ/T2M工程样例“RZT2M_EtherCAT_RSK_rev0100”为例对PHY驱动的软件配置流程进行说明。此工程样例可以在Renesas提供的开发版上运行和调试。开发套件的使用文件《r20ut4939eg0050-rskrzt2m-usermanual_c.pdf》可以上Renesas官方网站上获取,开发板也可以申请购买或者是借用。
驱动配置的入口
void hal_entry (void) { fsp_err_t err; /* TODO: add your own code here */ /* Initialize EtherCAT SSC Port */ err = RM_ETHERCAT_SSC_PORT_Open(gp_ethercat_ssc_port->p_ctrl, gp_ethercat_ssc_port->p_cfg); if(FSP_SUCCESS != err) { __BKPT(0); /* Can't continue the stack */ } ... }
进入RM_ETHERCAT_SSC_PORT_Open(), 这个EtherCAT接口配置函数之后,可以看到EtherCAT Slave Controller的一些初始化配置,其中就包括了PHY的初始化:
/* Open Ether-Phy Driver */ for (i = 0; BSP_FEATURE_ESC_MAX_PORTS > i; i++) { p_ether_PHY_instance = (ether_PHY_instance_t *) p_extend->p_ether_PHY_instance[i]; if (NULL != p_ether_PHY_instance) { err = p_ether_PHY_instance->p_api->open(p_ether_PHY_instance->p_ctrl, p_ether_PHY_instance->p_cfg); } if (FSP_SUCCESS == err) { opened_PHY[i] = 1; } else { break; } }
PHY驱动配置相关数据结构解析
这里初始化的一个PHY实例是:
p_ether_PHY_instance,它是一个ether_PHY_instance_t类型的变量。
typedef struct st_ether_PHY_instance { ether_PHY_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance ether_PHY_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance ether_PHY_api_t const * p_api; ///< Pointer to the API structure for this instance } ether_PHY_instance_t;
其中ether_PHY_ctrl_t是指向PHY实例的控制结构体;
ether_PHY_cfg_t是指向实例配置的结构体指针;
ether_PHY_api_t是实例配置过程中需要调用到的函数方法所组成的结构体指针;
这个PHY的实例是在调用RM_ETHERCAT_SSC_PORT_Open()函数的时候形参传递进来的,也就是gp_ethercat_ssc_port。
ethercat_ssc_port_instance_t const * gp_ethercat_ssc_port = &g_ethercat_ssc_port0;
而gp_ethercat_ssc_port这个ethercat_ssc_port_instance_t类型的全局指针是指向一个常量,也就是下面代码中的g_ethercat_ssc_port0。
/* Instance structure to use this module. */ const ethercat_ssc_port_instance_t g_ethercat_ssc_port0 = { .p_ctrl = &g_ethercat_ssc_port0_ctrl, .p_cfg = &g_ethercat_ssc_port0_cfg, .p_api = &g_ethercat_ssc_port_on_ethercat_ssc_port };
可以看到g_ethercat_ssc_port0是一个常量结构体,它的成员变量分别是:
g_ethercat_ssc_port0_ctrl指向ethercat_ssc_port0控制结构体指针;
g_ethercat_ssc_port0_cfg指向ethercat_ssc_port0配置结构体指针;
g_ethercat_ssc_port_on_ethercat_ssc_port指向ethercat_ssc_port0配置方法的结构指针。
看到这里是不是有一种似成相识的感觉?g_ethercat_ssc_port0是对ethercat_ssc_port0这个外设的驱动的描述体,与前面PHY驱运的描述体“p_ether_PHY_instance”结构上很相似,其实工程样例中所有的外设驱动都可以使用类似的结构体去完成相应的初始化。比如说timer驱动描述结构体:
/** This structure encompasses everything that is needed to use an instance of this interface. */ typedef struct st_timer_instance { timer_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance timer_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance timer_api_t const * p_api; ///< Pointer to the API structure for this instance } timer_instance_t;
这种相似的驱动描述体其实就是工程样例驱动代码部分的大致框架所在,撑握了这个脉络即可以方便的看懂其它外设驱动的代码,也可以在以后的驱动开发过程中参考这种框架,提升代的通用性和可读性。
我们知道PHY的驱动是ethercat_ssc_port0外设驱动的子模块。因为要在RZ/T2M这个芯片上使能EtherCAT功能块,除了要完成芯片本身相关外设的初始化之外,还要完成与之对应的PHY的初始化。那么两者是如何关联在一起的呢?我们继续解读g_ethercat_ssc_port0这个全局结构体。可以看到g_ethercat_ssc_port0_cfg所指向的内容是配置ethercat_ssc_port0的描述体,如下所示:
/** Configuration parameters. */ typedef struct st_ethercat_ssc_port_cfg { uint32_t reset_hold_time; ///< PHY Reset signal hold time (ms) uint32_t reset_wait_time; ///< Wait time after PHY reset relase (us) uint32_t offset_address; ///< PHY offset PHYsical address IRQn_Type esc_cat_irq; ///< EtherCAT IRQ interrupt number uint8_t esc_cat_ipl; ///< EtherCAT interrupt priority IRQn_Type esc_sync0_irq; ///< EtherCAT Sync0 IRQ interrupt number uint8_t esc_sync0_ipl; ///< EtherCAT Sync0 interrupt priority IRQn_Type esc_sync1_irq; ///< EtherCAT Sync1 IRQ interrupt number uint8_t esc_sync1_ipl; ///< EtherCAT Sync1 interrupt priority ///< Callback provided when an ISR occurs void (* p_callback)(ethercat_ssc_port_callback_args_t * p_args); timer_instance_t const * p_timer_instance; ///< Pointer to Timer instance /** Placeholder for user data. Passed to the user callback in ethercat_ssc_port_callback_args_t. */ void const * p_context; void const * p_extend; ///< Placeholder for user extension. } ethercat_ssc_port_cfg_t;
对PHY的复位信号保持时间有描述,还有对EtherCAT中断有作描述,在此不展开讨论。其中p_extend成员是用户用于扩展控制的占位符。这也正是PHY驱动与ethercat_ssc_port0驱动关联的关键所在。代码赋于这个占位符是一个指向扩展配置的结构体指针。具体可以看看这个结构体的内容如下:
/** Extended configuration */ typedef struct s_ethercat_ssc_port_extend_cfg { ethercat_ssc_port_eeprom_size_t eeprom_size; ///< EEPROM memory size ethercat_ssc_port_txc_delay_t txc0; ///< Port 0 TXC delay time ethercat_ssc_port_txc_delay_t txc1; ///< Port 1 TXC delay time ethercat_ssc_port_txc_delay_t txc2; ///< Port 2 TXC delay time ether_PHY_instance_t const * p_ether_PHY_instance[BSP_FEATURE_ESC_MAX_PORTS]; ///< Pointer to ETHER_PHY instance } ethercat_ssc_port_extend_cfg_t; const ethercat_ssc_port_extend_cfg_t g_ethercat_ssc_port0_ext_cfg = { .eeprom_size = ETHERCAT_SSC_PORT_EEPROM_SIZE_UNDER_32KBIT, .txc0 = ETHERCAT_SSC_PORT_TXC_DELAY_00NS, .txc1 = ETHERCAT_SSC_PORT_TXC_DELAY_00NS, .txc2 = ETHERCAT_SSC_PORT_TXC_DELAY_00NS, .p_ether_PHY_instance[0] = #define FSP_NOT_DEFINED (1) #if (FSP_NOT_DEFINED == g_ether_PHY0) NULL, #else &g_ether_PHY0, #endif .p_ether_PHY_instance[1] = #if (FSP_NOT_DEFINED == g_ether_PHY1) NULL, #else &g_ether_PHY1, #endif .p_ether_PHY_instance[2] = #if (FSP_NOT_DEFINED == FSP_NOT_DEFINED) NULL, #else &FSP_NOT_DEFINED, #endif };
里面就对应有ether_PHY_instance_t类体的初始化值,这值的类型正好是PHY实例所对应的描体结构体如下代码所示,所以关联就产生了。
typedef struct st_ether_PHY_instance { ether_PHY_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance ether_PHY_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance ether_PHY_api_t const * p_api; ///< Pointer to the API structure for this instance } wh wether_PHY_instance_t;
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !