当前位置: 首页 > 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_SIZEuint8 offset_ETH_PAD_SIZE = 0;//ETH_PAD_SIZE;#elseuint8 offset_ETH_PAD_SIZE = 0;#endifif(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 TyperawEthFrame[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_SIZElen += 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_SIZEpbuf_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_SIZEpbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endifLINK_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_SIZEpbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endifmemcpy(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_SIZEpbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endifLINK_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.dtcms.com/a/531946.html

相关文章:

  • 黑帽seo怎么做网站排名章丘网站定制
  • 最新多语言跨境商城系统源码 跨境电商系统 全开源
  • 如何解决PHP开发中的数据安全和加密存储
  • PHP Composer:高效的项目依赖管理工具
  • 网络攻防技术:防火墙技术
  • 旧版本附近停车场推荐系统demo,基于python+flask+协同推荐(基于用户信息推荐),开发语言python,数据库mysql,
  • 关于 CMS
  • 网站开发框架参考文献最新军事动态最新消息视频
  • 【Shell】流程控制
  • 设计模式-组合模式(Composite)
  • 景区建设网站的不足贵阳有做网站的公司吗?
  • 做网站有那几种末班网站维护员工作内容
  • 开源AI智能客服、AI智能名片与S2B2C商城小程序融合下的商家客服能力提升策略研究
  • 【FPGA】时序逻辑原理之D触发器与计数器原理
  • BLDC电机关键电气参数(R、L、磁链)的工程测量方法深度解析
  • NewStarCTF2025-Week4-Web
  • 主流多维表格产品深度解析:飞书、Teable、简道云、明道云、WPS
  • 怎么当网站站长网站建设都用那些软件
  • 装修中怎样避坑
  • MCoT在医疗AI工程化编程的实践手册(中)
  • RV1126 NO.33:OPENCV简介
  • [人工智能-大模型-84]:大模型应用层 - AI/AR眼镜:华为智能眼镜、苹果智能眼镜、Google Glass智能眼镜
  • 站长之家模板建立wordpress
  • WPF C# 视频播放器
  • Python中如何防止SQL注入攻击
  • 远程软件测评:关键维度评分与同类工具对比分析
  • 【HarmonyOS】动画—转场动效
  • Python函数定义与调用全解析:从基础语法到实战技巧
  • C++容器priority_queue
  • [linux仓库]打开线程的“房产证”:地址空间规划与分页式管理详解[线程·壹]