当前位置: 首页 > 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/533465.html

相关文章:

  • 外出手痒怎么玩家里游戏?手机用UU远程玩家中电脑游戏
  • 林州网站建设公司个人域名备案后不能干什么
  • 网站泛目录怎么做网站建设中图片尺寸
  • SAP交货单过账接口(无批次)分享
  • CCF-GESP 等级考试 2024年3月认证C++四级真题解析
  • C++ 类的学习(五) 友元成员
  • 哪里做网站最便宜微信开发者工具下载官网下载
  • SpringBoot—配置文件分类 文件基本(数据)格式 获取数据 profile 内部(外部)配置顺序
  • 大做网站免费人脉推广
  • 手机模板的网站深圳营销网站
  • 第十一章:跃迁篇 - 集成 MCP,连接能力宇宙
  • 建设制作网站wordpress数据表格
  • 建立网站多少钱一年phpmyadmin做网站
  • html5网站布局教程网站建设座谈会上的发言
  • 【补题】 Codeforces Round 1039 (Div. 2) D. Sum of LDS
  • 自建站怎么接入支付个人买卖网站如何做
  • Linux小课堂: systemd核心功能详解
  • 专做网站wordpress 图像滑块插件
  • 郑州知名做网站中国市场网
  • 【技术深度】腾讯 IM 接入规范文档(基于区块链交易所 APP)
  • 什么是区块链?有哪些场景需要用到?
  • 该如何选择深圳网站建设公司菏泽网站建设公司
  • 基于AI Agent的数据资产自动化治理实验
  • Rust 并发编程进阶:线程模型、通道通信与异步任务对比分析
  • 南京一站式工程装饰装修网站做网站建设的名声很差吗
  • 三型布局的网站兴义网站网站建设
  • LangChain最详细教程之Model I/O(三)Output Parsers
  • 网站制作 万网营销型网站建设设计服务
  • 做网站前端用什么语言网站建设是什么专业
  • Photoshop - Photoshop 工具栏(17)铅笔工具