近日天气转凉偶感风寒百感交集遂赋诗一首年长头发稀未老人先贫。满目山河恨落花下津门。注当下裁员、降薪、和美帝那点事故曰满目山河恨借鉴杜少陵感时花溅泪恨别鸟惊心。工作背景其实这个方法曾福大佬已经写过了他是基于NXP芯片的MCAL做的我在他的博客下面把关键代码也贴上了。大家想看的话可以点击LWIP基于MCAL ETH Driver。之所以我今天写这个文章是看他写的我仍然有疑惑所以在巨人的肩膀上我把这个事再说透一些。我的Base工程是英飞凌提供的基于iLLD实现的LwIP工程。port文件夹下文件其实不多所以我重点是要看这两个.c文件里面对外暴露的接口。这里面只有2个暴露的接口聪明的你会发现怎么缺少了发送接口呢肿么肥事实际上low_level_output在ifx_netif_init中被绑定了所以low_level_output就没有再对外暴露的必要。总结下来我们要实现的最重要的3个函数就是err_t ifx_netif_init(struct netif *netif); err_t ifx_netif_input(struct netif *netif); static sint8 low_level_output(netif_t *netif, pbuf_t *p)先做到心中有数好了继续我们的工作。第一步编译通过叔考研期间快把谭浩强老爷子的C语言课本摸秃噜皮了至今仍能记住的一句话是自顶向下逐步细化模块化设计结构化编码。所以我们做事情要有先后顺序轻重缓急。由于iLLD和MCAL代码差别很大我的Base工程里面基于iLLD实现的很多函数在MCAL环境下都不能用所以我要先适配接口做法就是把各个函数功能屏蔽了只留个空壳一些非常必须的数据类型复制粘贴之外尽量不引入其他额外的东西。比如这样用一个宏把函数内容屏蔽了这样后期我用它就解开这个宏不用就留着供我参考。第二步接口适配MCAL接口iLLD接口EthIf_RxIndicationIfx_Lwip_pollReceiveFlags隶属于ifx_netif_input隶属于隶属于low_level_inputIrqEthernet_Initifx_netif_initEth_InitEthTrcv_InitEth_Transmitlow_level_outputEth_ProvideTxBuffer初始化函数对于init部分这个不用我多说都是初始化也很容易看懂你可以直接追加MCAL提供的Init接口也可以把IrqEthernet_Init/Eth_Init/EthTrcv_Init放在ifx_netif_init里面。这里注意一点我们看这个g_Lwip是个全局变量也就是对Lwip各项配置属性做个一个结构体所以这一块我们还是要保留的。后面有很多函数都是以g_Lwip作为参数比如接收函数这个iLLD函数周期性的去polling接收数据它跟MCAL中的EthIf_RxIndication被接收中断触发功能几乎是一样的。但是我们看Ifx_Lwip_pollReceiveFlags它下面的函数ifx_netif_input的参数g_Lwip.netif这是个什么玩意对比下接口差异MCAL接口iLLD接口我们对比这两种接口就会发现1. iLLD中的payload是通过遍历链表中获取的数据。2. iLLD接口中获取的是原始帧包含头。3. MCAL接口在EthIf_RxIndication接口之前已经完成了链表遍历并且把ETH头信息去掉了。所以我们在适配这两个接口的时候要把EthIf_RxIndication中的DataPtr数据头信息给还原回一个完整的ETH原始数据并且不用再做遍历描述符链表的操作了(MCAL代码已经做了)这样才能给ifx_netif_input用。EthIf_RxIndication适配//This MCAL API Replace the Ifx_Lwip_pollReceiveFlags void EthIf_RxIndication ( uint8 CtrlIdx, uint16 FrameType, boolean IsBroadcast, uint8 * PhysAddrPtr, uint8 * DataPtr, uint16 LenByte ) { //----ifx_netif_input //Trans CtrlIdx, FrameType, IsBroadcast, PhysAddrPtr, DataPtr, LenByte -- pbuf_t #if ETH_PAD_SIZE uint8 offset_ETH_PAD_SIZE 0;//ETH_PAD_SIZE; #else uint8 offset_ETH_PAD_SIZE 0; #endif if(IsBroadcast) { rawEthFrame[0offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[1offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[2offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[3offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[4offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[5offset_ETH_PAD_SIZE] 0xFF;// } else { rawEthFrame[0offset_ETH_PAD_SIZE] ethAddr.addr[0];// rawEthFrame[1offset_ETH_PAD_SIZE] ethAddr.addr[1];// rawEthFrame[2offset_ETH_PAD_SIZE] ethAddr.addr[2];// rawEthFrame[3offset_ETH_PAD_SIZE] ethAddr.addr[3];// rawEthFrame[4offset_ETH_PAD_SIZE] ethAddr.addr[4];// rawEthFrame[5offset_ETH_PAD_SIZE] ethAddr.addr[5];// } rawEthFrame[6offset_ETH_PAD_SIZE] PhysAddrPtr[0]; rawEthFrame[7offset_ETH_PAD_SIZE] PhysAddrPtr[1]; rawEthFrame[8offset_ETH_PAD_SIZE] PhysAddrPtr[2]; rawEthFrame[9offset_ETH_PAD_SIZE] PhysAddrPtr[3]; rawEthFrame[10offset_ETH_PAD_SIZE] PhysAddrPtr[4]; rawEthFrame[11offset_ETH_PAD_SIZE] PhysAddrPtr[5]; rawEthFrame[12offset_ETH_PAD_SIZE] FrameType 8;//Frame Type rawEthFrame[13offset_ETH_PAD_SIZE] FrameType 0xFF;//Frame Type (void)ifx_netif_input(CtrlIdx, FrameType, rawEthFrame, DataPtr, LenByte); }low_level_input适配static pbuf_t *low_level_input(uint8 CtrlIdx, uint8 * ethHeader, uint8 * DataPtr, uint16 LenByte) { //IfxGeth_Eth *ethernetif netif-state; pbuf_t *p; u16_t len; len LenByte 14; #if ETH_PAD_SIZE len ETH_PAD_SIZE; /* allow room for Ethernet padding */ #endif /* We allocate a pbuf chain of pbufs from the pool. */ p pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if (p ! NULL) { #if ETH_PAD_SIZE pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif //u8_t *src IfxGeth_Eth_getReceiveBuffer(ethernetif, IfxGeth_RxDmaChannel_0); memcpy(p-payload, ethHeader, 14);//把头信息还原出来 memcpy(((uint8 *)(p-payload)) 14, DataPtr, LenByte); /* We iterate over the pbuf chain until we have read the entire * packet into the pbuf. */ #if ETH_PAD_SIZE pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif LINK_STATS_INC(link.recv); } else { //TODO: drop packet(); LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); } return p; }这里再注意一点我把low_level_input的参数改了以便接收EthIf_RxIndication传来的数据。static pbuf_t *low_level_input(uint8 CtrlIdx, uint8 * ethHeader, uint8 * DataPtr, uint16 LenByte)这里再注意一点也是很容易出错的地方low_level_input中有为了数据对齐做的pad这里一定要注意去掉pad字符进行填充填充完再加上pad字符发送函数用MCAL接口Eth_TransmitEth_ProvideTxBuffer去实现low_level_output还是比较简单的。static sint8 low_level_output(netif_t *netif, pbuf_t *p) { err_enum_t res ERR_USE; BufReq_ReturnType buffRes; Std_ReturnType transRes; uint32 TmpBuffIdx; uint16 Tmp_Txlenghth1; uint8 *txBuf; uint16 frameType; boolean ifTxConfim 1; uint8 ethHeader[16]; uint8 desMac[16]; #if ETH_PAD_SIZE pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif memcpy(ethHeader, p-payload, 16); desMac[0] ethHeader[0]; desMac[1] ethHeader[1]; desMac[2] ethHeader[2]; desMac[3] ethHeader[3]; desMac[4] ethHeader[4]; desMac[5] ethHeader[5]; Tmp_Txlenghth1 p-len - 14; frameType (ethHeader[12] 8) ethHeader[13]; if(p-tot_len 2) { return ERR_USE; } if(p-if_idx 0xFF) { return ERR_USE; } //Eth_ProvideTxBuffer(0, TmpBuffIdx, TmpBuffPtr, Tmp_Txlenghth1); buffRes Eth_ProvideTxBuffer(0, TmpBuffIdx, txBuf, Tmp_Txlenghth1); if(BUFREQ_OK buffRes) { memcpy(txBuf, ((uint8 *)p-payload) 14, p-len-14); //Eth_Transmit(0, TmpBuffIdx, 0x0806u, TxConfirmation, Tmp_Txlenghth1, MacDestBrodaddress[0]); transRes Eth_Transmit(0, TmpBuffIdx, frameType, ifTxConfim, p-len - 14, desMac); //EthIf_TxConfirmation( uint8 CtrlIdx, uint32 BufIdx ) if(E_OK transRes) { //EthIf_TxConfirmation(0, ); } else { return ERR_USE; } } else { return ERR_USE; } #if ETH_PAD_SIZE pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif LINK_STATS_INC(link.xmit); return ERR_OK;时间戳接口1. 使用中断STM中断服务函数去做时间戳。2.不用中断的话即使不配置STM也可以用Mcal_DelayGetTick()来获取STM内的计数。但是叔这回就是倔了就非得用STM中断来做Ifx_Lwip_onTimerTick函数的逻辑几乎不用改变它会改变timer的值然后该timer再被Ifx_Lwip_pollTimerFlags周期性调用更新。好了接下来该看成果了OK!收工
AutoSAR实战教程--MCAL之ETH Driver移植集成LwIP以太网协议栈(英飞凌Tc3xx)
近日天气转凉偶感风寒百感交集遂赋诗一首年长头发稀未老人先贫。满目山河恨落花下津门。注当下裁员、降薪、和美帝那点事故曰满目山河恨借鉴杜少陵感时花溅泪恨别鸟惊心。工作背景其实这个方法曾福大佬已经写过了他是基于NXP芯片的MCAL做的我在他的博客下面把关键代码也贴上了。大家想看的话可以点击LWIP基于MCAL ETH Driver。之所以我今天写这个文章是看他写的我仍然有疑惑所以在巨人的肩膀上我把这个事再说透一些。我的Base工程是英飞凌提供的基于iLLD实现的LwIP工程。port文件夹下文件其实不多所以我重点是要看这两个.c文件里面对外暴露的接口。这里面只有2个暴露的接口聪明的你会发现怎么缺少了发送接口呢肿么肥事实际上low_level_output在ifx_netif_init中被绑定了所以low_level_output就没有再对外暴露的必要。总结下来我们要实现的最重要的3个函数就是err_t ifx_netif_init(struct netif *netif); err_t ifx_netif_input(struct netif *netif); static sint8 low_level_output(netif_t *netif, pbuf_t *p)先做到心中有数好了继续我们的工作。第一步编译通过叔考研期间快把谭浩强老爷子的C语言课本摸秃噜皮了至今仍能记住的一句话是自顶向下逐步细化模块化设计结构化编码。所以我们做事情要有先后顺序轻重缓急。由于iLLD和MCAL代码差别很大我的Base工程里面基于iLLD实现的很多函数在MCAL环境下都不能用所以我要先适配接口做法就是把各个函数功能屏蔽了只留个空壳一些非常必须的数据类型复制粘贴之外尽量不引入其他额外的东西。比如这样用一个宏把函数内容屏蔽了这样后期我用它就解开这个宏不用就留着供我参考。第二步接口适配MCAL接口iLLD接口EthIf_RxIndicationIfx_Lwip_pollReceiveFlags隶属于ifx_netif_input隶属于隶属于low_level_inputIrqEthernet_Initifx_netif_initEth_InitEthTrcv_InitEth_Transmitlow_level_outputEth_ProvideTxBuffer初始化函数对于init部分这个不用我多说都是初始化也很容易看懂你可以直接追加MCAL提供的Init接口也可以把IrqEthernet_Init/Eth_Init/EthTrcv_Init放在ifx_netif_init里面。这里注意一点我们看这个g_Lwip是个全局变量也就是对Lwip各项配置属性做个一个结构体所以这一块我们还是要保留的。后面有很多函数都是以g_Lwip作为参数比如接收函数这个iLLD函数周期性的去polling接收数据它跟MCAL中的EthIf_RxIndication被接收中断触发功能几乎是一样的。但是我们看Ifx_Lwip_pollReceiveFlags它下面的函数ifx_netif_input的参数g_Lwip.netif这是个什么玩意对比下接口差异MCAL接口iLLD接口我们对比这两种接口就会发现1. iLLD中的payload是通过遍历链表中获取的数据。2. iLLD接口中获取的是原始帧包含头。3. MCAL接口在EthIf_RxIndication接口之前已经完成了链表遍历并且把ETH头信息去掉了。所以我们在适配这两个接口的时候要把EthIf_RxIndication中的DataPtr数据头信息给还原回一个完整的ETH原始数据并且不用再做遍历描述符链表的操作了(MCAL代码已经做了)这样才能给ifx_netif_input用。EthIf_RxIndication适配//This MCAL API Replace the Ifx_Lwip_pollReceiveFlags void EthIf_RxIndication ( uint8 CtrlIdx, uint16 FrameType, boolean IsBroadcast, uint8 * PhysAddrPtr, uint8 * DataPtr, uint16 LenByte ) { //----ifx_netif_input //Trans CtrlIdx, FrameType, IsBroadcast, PhysAddrPtr, DataPtr, LenByte -- pbuf_t #if ETH_PAD_SIZE uint8 offset_ETH_PAD_SIZE 0;//ETH_PAD_SIZE; #else uint8 offset_ETH_PAD_SIZE 0; #endif if(IsBroadcast) { rawEthFrame[0offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[1offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[2offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[3offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[4offset_ETH_PAD_SIZE] 0xFF;// rawEthFrame[5offset_ETH_PAD_SIZE] 0xFF;// } else { rawEthFrame[0offset_ETH_PAD_SIZE] ethAddr.addr[0];// rawEthFrame[1offset_ETH_PAD_SIZE] ethAddr.addr[1];// rawEthFrame[2offset_ETH_PAD_SIZE] ethAddr.addr[2];// rawEthFrame[3offset_ETH_PAD_SIZE] ethAddr.addr[3];// rawEthFrame[4offset_ETH_PAD_SIZE] ethAddr.addr[4];// rawEthFrame[5offset_ETH_PAD_SIZE] ethAddr.addr[5];// } rawEthFrame[6offset_ETH_PAD_SIZE] PhysAddrPtr[0]; rawEthFrame[7offset_ETH_PAD_SIZE] PhysAddrPtr[1]; rawEthFrame[8offset_ETH_PAD_SIZE] PhysAddrPtr[2]; rawEthFrame[9offset_ETH_PAD_SIZE] PhysAddrPtr[3]; rawEthFrame[10offset_ETH_PAD_SIZE] PhysAddrPtr[4]; rawEthFrame[11offset_ETH_PAD_SIZE] PhysAddrPtr[5]; rawEthFrame[12offset_ETH_PAD_SIZE] FrameType 8;//Frame Type rawEthFrame[13offset_ETH_PAD_SIZE] FrameType 0xFF;//Frame Type (void)ifx_netif_input(CtrlIdx, FrameType, rawEthFrame, DataPtr, LenByte); }low_level_input适配static pbuf_t *low_level_input(uint8 CtrlIdx, uint8 * ethHeader, uint8 * DataPtr, uint16 LenByte) { //IfxGeth_Eth *ethernetif netif-state; pbuf_t *p; u16_t len; len LenByte 14; #if ETH_PAD_SIZE len ETH_PAD_SIZE; /* allow room for Ethernet padding */ #endif /* We allocate a pbuf chain of pbufs from the pool. */ p pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if (p ! NULL) { #if ETH_PAD_SIZE pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif //u8_t *src IfxGeth_Eth_getReceiveBuffer(ethernetif, IfxGeth_RxDmaChannel_0); memcpy(p-payload, ethHeader, 14);//把头信息还原出来 memcpy(((uint8 *)(p-payload)) 14, DataPtr, LenByte); /* We iterate over the pbuf chain until we have read the entire * packet into the pbuf. */ #if ETH_PAD_SIZE pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif LINK_STATS_INC(link.recv); } else { //TODO: drop packet(); LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); } return p; }这里再注意一点我把low_level_input的参数改了以便接收EthIf_RxIndication传来的数据。static pbuf_t *low_level_input(uint8 CtrlIdx, uint8 * ethHeader, uint8 * DataPtr, uint16 LenByte)这里再注意一点也是很容易出错的地方low_level_input中有为了数据对齐做的pad这里一定要注意去掉pad字符进行填充填充完再加上pad字符发送函数用MCAL接口Eth_TransmitEth_ProvideTxBuffer去实现low_level_output还是比较简单的。static sint8 low_level_output(netif_t *netif, pbuf_t *p) { err_enum_t res ERR_USE; BufReq_ReturnType buffRes; Std_ReturnType transRes; uint32 TmpBuffIdx; uint16 Tmp_Txlenghth1; uint8 *txBuf; uint16 frameType; boolean ifTxConfim 1; uint8 ethHeader[16]; uint8 desMac[16]; #if ETH_PAD_SIZE pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif memcpy(ethHeader, p-payload, 16); desMac[0] ethHeader[0]; desMac[1] ethHeader[1]; desMac[2] ethHeader[2]; desMac[3] ethHeader[3]; desMac[4] ethHeader[4]; desMac[5] ethHeader[5]; Tmp_Txlenghth1 p-len - 14; frameType (ethHeader[12] 8) ethHeader[13]; if(p-tot_len 2) { return ERR_USE; } if(p-if_idx 0xFF) { return ERR_USE; } //Eth_ProvideTxBuffer(0, TmpBuffIdx, TmpBuffPtr, Tmp_Txlenghth1); buffRes Eth_ProvideTxBuffer(0, TmpBuffIdx, txBuf, Tmp_Txlenghth1); if(BUFREQ_OK buffRes) { memcpy(txBuf, ((uint8 *)p-payload) 14, p-len-14); //Eth_Transmit(0, TmpBuffIdx, 0x0806u, TxConfirmation, Tmp_Txlenghth1, MacDestBrodaddress[0]); transRes Eth_Transmit(0, TmpBuffIdx, frameType, ifTxConfim, p-len - 14, desMac); //EthIf_TxConfirmation( uint8 CtrlIdx, uint32 BufIdx ) if(E_OK transRes) { //EthIf_TxConfirmation(0, ); } else { return ERR_USE; } } else { return ERR_USE; } #if ETH_PAD_SIZE pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif LINK_STATS_INC(link.xmit); return ERR_OK;时间戳接口1. 使用中断STM中断服务函数去做时间戳。2.不用中断的话即使不配置STM也可以用Mcal_DelayGetTick()来获取STM内的计数。但是叔这回就是倔了就非得用STM中断来做Ifx_Lwip_onTimerTick函数的逻辑几乎不用改变它会改变timer的值然后该timer再被Ifx_Lwip_pollTimerFlags周期性调用更新。好了接下来该看成果了OK!收工