当前位置: 首页 > news >正文

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_RxIndication

Ifx_Lwip_pollReceiveFlags

隶属于ifx_netif_input
隶属于隶属于low_level_input

IrqEthernet_Init

ifx_netif_init

Eth_Init

EthTrcv_Init

Eth_Transmit

low_level_output

Eth_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[0+offset_ETH_PAD_SIZE] = 0xFF;// rawEthFrame[1+offset_ETH_PAD_SIZE] = 0xFF;// rawEthFrame[2+offset_ETH_PAD_SIZE] = 0xFF;// rawEthFrame[3+offset_ETH_PAD_SIZE] = 0xFF;// rawEthFrame[4+offset_ETH_PAD_SIZE] = 0xFF;// rawEthFrame[5+offset_ETH_PAD_SIZE] = 0xFF;// } else { rawEthFrame[0+offset_ETH_PAD_SIZE] = ethAddr.addr[0];// rawEthFrame[1+offset_ETH_PAD_SIZE] = ethAddr.addr[1];// rawEthFrame[2+offset_ETH_PAD_SIZE] = ethAddr.addr[2];// rawEthFrame[3+offset_ETH_PAD_SIZE] = ethAddr.addr[3];// rawEthFrame[4+offset_ETH_PAD_SIZE] = ethAddr.addr[4];// rawEthFrame[5+offset_ETH_PAD_SIZE] = ethAddr.addr[5];// } rawEthFrame[6+offset_ETH_PAD_SIZE] = PhysAddrPtr[0]; rawEthFrame[7+offset_ETH_PAD_SIZE] = PhysAddrPtr[1]; rawEthFrame[8+offset_ETH_PAD_SIZE] = PhysAddrPtr[2]; rawEthFrame[9+offset_ETH_PAD_SIZE] = PhysAddrPtr[3]; rawEthFrame[10+offset_ETH_PAD_SIZE] = PhysAddrPtr[4]; rawEthFrame[11+offset_ETH_PAD_SIZE] = PhysAddrPtr[5]; rawEthFrame[12+offset_ETH_PAD_SIZE] = FrameType >> 8;//Frame Type rawEthFrame[13+offset_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_Transmit+Eth_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!收工!

http://www.cnnetsun.cn/news/1358585.html

相关文章:

  • 深入理解incbin:解析INCBIN和INCTXT宏的工作原理与实现
  • 如何快速构建Swift GraphQL API:Kitura与Apollo Server完整集成指南
  • 高效实战:PlantUML编辑器从入门到架构可视化全指南
  • Youtu-Parsing开源镜像免配置部署:supervisor开机自启+日志监控完整指南
  • DAMO-YOLO国产化适配实践:昇腾/海光平台移植可行性验证
  • AI 净界多场景实战:宠物、人物、商品图的统一抠图方案
  • Fish Speech 1.5企业级部署案例:金融IVR语音系统降本提效实践
  • MGeo中文地址解析模型效果展示:多模态预训练带来的地图语义对齐能力
  • 弦音墨影开箱即用教程:无需CUDA手动编译,GPU显存自动适配方案
  • FLUX.小红书极致真实V2效果实测:低光照室内场景下暗部细节保留能力
  • ClawdBot详细步骤:从docker run到Dashboard访问的全流程解析
  • Qwen2.5-VL-7B-Instruct入门指南:视觉-语言对齐原理与提示词设计技巧
  • Leather Dress Collection开源镜像:预装ComfyUI节点支持12LoRA可视化工作流编排
  • 栈与队列经典算法题精讲(一):循环队列·有效括号·面试高频原题全解析
  • 屠龙刀法32--国产系统下如何导出Oracle的AWR报告
  • 修改表结构后报错:字段级的结构更改(转换表ZTQME016A)
  • 动态规划_最长递增子序列_C++
  • ssm+java2026年毕设社区疫情统计分析系统【源码+论文】
  • 华为OD机试真题精讲:数据单元的变化替换(Python/Java/C++多语言实现)
  • Chrome Remote Desktop介绍(谷歌远程桌面软件、远程控制、屏幕共享、Chrome远程)
  • 把数据交给松鼠,把安全留给自己(二):异地同步——把第二份数据放在灾害够不到的地方
  • SAP零售行业商品主数据增强全解析:MM41配置与ALE增强实战
  • 银河麒麟V10 SP1离线环境搭建全攻略:从Java8到Node.js的避坑指南
  • FinalShell连接WSL Ubuntu的3种方法:从基础到高级(含SSH自启动配置)
  • 比迪丽LoRA模型在Agent智能体中的应用:自主创作与迭代
  • FireRedASR Pro性能基准测试:对比不同GPU型号下的转写速度与成本
  • Vue3实战:如何优雅地从静态页面URL中提取参数(附完整代码)
  • VMware虚拟机安装macOS完全指南:Unlocker工具实战攻略
  • 一分钟讲透:c++新特性string_view
  • Fish-Speech-1.5在网络安全教学中的语音辅助应用