热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TICortexM3串口转以太网例程分析3-----lwIP1.3.2移植

TICortexM3串口转以太网例程上层应用的基础是lwIP,版本是V1.3.2。对于lwIP,陌生的同学可以到网上查查,它是是瑞士的Adam编写的一个开源TCPIP协

       TI Cortex M3串口转以太网例程上层应用的基础是lwIP,版本是V1.3.2 。对于lwIP,陌生的同学可以到网上查查,它是是瑞士的Adam编写的一个开源TCP/IP协议。既然串口转以太网例程的基础是lwIP,那么还是看看lwIp是如何移植到TI的Cortex M3硬件中的吧。此为分割线-------

         移植概述可以参看博客的这篇文章,以下基本按照这个格式来看看具体的移植代码。http://blog.csdn.net/zhzht19861011/article/details/6615965

1.cc.h文件

         这个文件主要设置lwIP内部使用的数据类型,比如u8_t、u32_t等。lwIP可以移植到32位、16位甚至是8位构架的微控制器,由于移植的硬件平台以及编译器的不同,这些数据类型是要移植者根据自己的硬件和编译器特性来自行设置的。比如int类型变量,在8位和16位控制器中多表示2字节,但在32位微处理器中却表示4个字节,若是连这些基本数据类型都没有设置正确的话,就谈不上移植了。下面看cc.h的源代码:

#ifndef __CC_H__
#define __CC_H__

typedef unsigned    char    u8_t;     //基本数据类型设置
typedef signed      char    s8_t;
typedef unsigned    short   u16_t;
typedef signed      short   s16_t;
typedef unsigned    long    u32_t;
typedef signed      long    s32_t;
typedef u32_t               mem_ptr_t;

#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif

#if defined(__arm__) && defined(__ARMCC_VERSION)         //以下主要设置不同编译器的结构体数据的对齐,lwIP需要
    //
    // Setup PACKing macros for KEIL/RVMDK Tools
    //
    #define PACK_STRUCT_BEGIN __packed
    #define PACK_STRUCT_STRUCT 
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
#elif defined (__IAR_SYSTEMS_ICC__)
    //
    // Setup PACKing macros for IAR Tools
    //
    #define PACK_STRUCT_BEGIN
    #define PACK_STRUCT_STRUCT
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
    #define PACK_STRUCT_USE_INCLUDES
#else
    //
    // Setup PACKing macros for GCC Tools
    //
    #define PACK_STRUCT_BEGIN
    #define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
#endif

#ifdef DEBUG
extern void __error__(char *pcFilename, unsigned long ulLine);
#define LWIP_PLATFORM_ASSERT(expr)      \
{                                       \
    if(!(expr))                         \
    {                                   \
        __error__(__FILE__, __LINE__);  \
    }                                   \
}
#else
#define LWIP_PLATFORM_ASSERT(expr)
#endif

#endif /* __CC_H__ */


2.以太网硬件初始化、与硬件密切相关的数据接收、发送函数

         虽然Adam为便于lwIP协议栈的移植做了大量的工作,但因为网卡的多样性和新网卡的不断出现,Adam不可能为每一个网卡都写一个驱动。因此,与网卡硬件相关的代码就留给程序员来编写了。其实Adam在lwIP协议栈中已经写好了一个与硬件密切相关的移植代码框架,它位于lwIP-1.3.2/src/netif/ethernetif.c中。Stellaris串口转以太网移植代码也基本上是参照这个代码框架来编写的。Stellais串口转以太网模块与硬件密切相关的移植代码位于stellarisif.c中。这里面的代码主要是三部分:lwIP协议栈和以太网硬件初始化函数、lwIP协议栈将数据发送到网络接口上的输出函数以及从Stellaris以太网硬件读取数据并送给lwIP协议栈的输入函数。

2.1 lwIP协议栈和以太网硬件初始化

       在移植代码stellarisif.c中,对lwIP协议栈和以太网硬件初始化的函数是:

                       err_t stellarisif_init(structnetif *netif)

这个函数先是设置与协议栈有关的底层操作,指定底层接收回调函数等,接着对实际网络接口芯片进行初始化,设置硬件的工作方式,开放中断等。源代码如下所示:

/**
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function stellarisif_hwinit() to do the
 * actual setup of the hardware.
 * 此在程序开始的时候被调用,用来设置网络接口.他调用stellarisif_hwinit()函数
 * 来完成以太网硬件的设置.
 * This function should be passed as a parameter to netif_add().
 * 这个函数作为一个参数传递给netif_add()函数.
 * @param netif the lwip network interface structure for this ethernetif
 * @return ERR_OK if the loopif is initialized
 *         ERR_MEM if private data couldn't be allocated
 *         any other err_t on error
 */
err_t
stellarisif_init(struct netif *netif)
{
  LWIP_ASSERT("netif != NULL", (netif != NULL));

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";		   //初始化接口主机名字
#endif /* LWIP_NETIF_HOSTNAME */

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 1000000);	 //初始化snmp变量

  netif->state = &stellarisif_data;		  //指向以太网接口的私有数据,包括pbuf数据链和MAC地址
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */
  netif->output = etharp_output;	 		//IP层将一包数据发往网络接口时调用此函数
  netif->linkoutput = stellarisif_output; 	//ARP模块将一包数据发往网络接口时调用此函数

  stellarisif_data.ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);  //初始化MAC地址
  stellarisif_data.txq.qread = stellarisif_data.txq.qwrite = 0;		  //初始化pbuf数据链
  stellarisif_data.txq.overflow = 0;

  /* initialize the hardware */
  stellarisif_hwinit(netif);	 //初始化Stellaris以太网硬件

  return ERR_OK;
}

1. netif->output = etharp_output;用于将一包数据发送到网络接口,由IP层调用。这个函数最终会调用netif->linkoutput来将数据发送到网络接口。

2. netif->linkoutput = stellarisif_output;用于将一包数据发送到网络接口,有ARP模块调用。程序员需根据自己的硬件平台来编写该函数。后面会讲到该函数。

3.stellarisif_hwinit(netif):初始化以太网硬件,还是有必要看看该函数的,代码如下所示,不再单独解释,可以看注释。

/**
 * In this function, the hardware should be initialized.
 * Called from stellarisif_init().
 *
 * @param netif the already initialized lwip network interface structure
 *        for this ethernetif
 */
static void
stellarisif_hwinit(struct netif *netif)
{
  u32_t temp;
  //struct stellarisif *stellarisif = netif->state;

  /* 设置以太网硬件MAC地址长度 */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* 设置以太网硬件地址 */
  EthernetMACAddrGet(ETH_BASE, &(netif->hwaddr[0]));

  /* 最大发送单元 */
  netif->mtu = 1500;

  /* 使能网卡的功能,允许网卡广播、ARP功能和允许硬件链路连接该网卡*/
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

  /* Do whatever else is needed to initialize interface. */
  /* 禁止所有以太网中断 */
  EthernetIntDisable(ETH_BASE, (ETH_INT_PHY | ETH_INT_MDIO | ETH_INT_RXER |
     ETH_INT_RXOF | ETH_INT_TX | ETH_INT_TXER | ETH_INT_RX));
  temp = EthernetIntStatus(ETH_BASE, false);
  EthernetIntClear(ETH_BASE, temp);

  /* 初始化以太网控制器 */
  EthernetInitExpClk(ETH_BASE, SysCtlClockGet());

  /*
   * 配置以太网控制器正常运行.
   * - 使能 TX 全双工模式
   * - 使能 TX 填充
   * - 使能 TX CRC 生成
   * - 使能 RX 组播接收
   */
  EthernetConfigSet(ETH_BASE, (ETH_CFG_TX_DPLXEN |ETH_CFG_TX_CRCEN |
    ETH_CFG_TX_PADEN | ETH_CFG_RX_AMULEN));

  /* 使能以太网控制器的发送器和接收器 */
  EthernetEnable(ETH_BASE);

  /* 使能以太网中断 */
  IntEnable(INT_ETH);

  /* 使能以太网TX和RX数据包中断 */
  EthernetIntEnable(ETH_BASE, ETH_INT_RX | ETH_INT_TX);
}


2.2 Stellaris以太网硬件底层数据发送函数
        上面也已经讲到了,ARP模块发送数据到网络接口会调用netif->linkoutput函数;IP层发送数据到网络接口会调用netif->output函数,但IP层实际的数据发送函数任然是netif->linkoutput,在以太网初始化中,已经为linkoutput指针赋值:netif->linkoutput=stellarisif_output;因此,移植lwIP时程序员需要编写的Stellaris以太网硬件底层数据发送函数正是stellarisif_output()函数。先来看以下它的源码:

/**
 * This function with either place the packet into the Stellaris transmit fifo,
 * or will place the packet in the interface PBUF Queue for subsequent
 * transmission when the transmitter becomes idle.
 * 这个函数要么将数据包放到Stellaris发送缓存FIFO中,要么把数据包放到PBUF队列,等待
 * 发送器变空之后载发送。
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return ERR_OK if the packet could be sent
 *         an err_t value if the packet couldn't be sent
 *
 */
static err_t
stellarisif_output(struct netif *netif, struct pbuf *p)
{
  struct stellarisif *stellarisif = netif->state;
  SYS_ARCH_DECL_PROTECT(lev);

  /**
   * This entire function must run within a "critical section" to preserve
   * the integrity of the transmit pbuf queue.
   *
   */
  SYS_ARCH_PROTECT(lev);

  /**
   * Bump the reference count on the pbuf to prevent it from being
   * freed till we are done with it.
   *
   */
  pbuf_ref(p);

  /**
   * If the transmitter is idle, and there is nothing on the queue,
   * send the pbuf now.
   *
   */
  if(PBUF_QUEUE_EMPTY(&stellarisif->txq) &&
    ((HWREG(ETH_BASE + MAC_O_TR) & MAC_TR_NEWTX) == 0)) {
    stellarisif_transmit(netif, p);
  }

  /* Otherwise place the pbuf on the transmit queue. */
  else {
    /* Add to transmit packet queue */
    if(!enqueue_packet(p, &(stellarisif->txq))) {
      /* if no room on the queue, free the pbuf reference and return error. */
      pbuf_free(p);
      SYS_ARCH_UNPROTECT(lev);
      return (ERR_MEM);
    }
  }

  /* Return to prior interrupt state and return. */
  SYS_ARCH_UNPROTECT(lev);
  return ERR_OK;
}

1. SYS_ARCH_DECL_PROTECT(lev);、SYS_ARCH_PROTECT(lev);、SYS_ARCH_UNPROTECT(lev);由于Stellaris以太网在发送数据的时候必须处在临界区,既不能被中断打扰,因此,上面几个宏是用来关闭和打开总中断的。SYS_ARCH_DECL_PROTECT(lev)用来定义一个32位变量lev,这个变量用来保存当前中断使能信息;SYS_ARCH_PROTECT(lev)用来关闭中断;SYS_ARCH_UNPROTECT(lev)用来打开中断。

2. pbuf_ref(p);将参数结构体pbuf的ref域加一。这个域统计有多少个指针指向这个pbuf,这些指针可能是应用程序的指针、协议栈的指针或者数据链中的pbuf->next指针,只有ref为0时,才可以释放这个pbuf。关于结构体pbuf的详细介绍参见博客:http://blog.csdn.net/zhzht19861011/article/details/6591252

3. stellarisif_transmit(netif, p):发送数据。


2.3  Stellaris以太网硬件底层数据过程
当网络上一包数据到达以太网控制器后,以太网控制器会置为接收中断,在中断服务函数中,调用

stellarisif_receive函数(需程序员根据具体硬件自行编写)来接收一个数据包,再通过

ethernet_input函数分析接收到的数据包的类型,比如类型为0x8000,则为IP帧,会调用ip_input

函数来将收到的这个IP数据包送往lwIP协议栈的高层。Stellaris以太网硬件具体中断函数分析见博客

:http://blog.csdn.net/zhzht19861011/article/details/6221699

3.lwipopts.h文件

...









推荐阅读
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 本题旨在通过给定的评级信息,利用拓扑排序和并查集算法来确定全球 Tetris 高手排行榜。题目要求判断是否可以根据提供的信息生成一个明确的排名表,或者是否存在冲突或信息不足的情况。 ... [详细]
  • 本文介绍了Linux系统中的文件IO操作,包括文件描述符、基本文件操作函数以及目录操作。详细解释了各个函数的参数和返回值,并提供了代码示例。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
  • 磁盘健康检查与维护
    在计算机系统运行过程中,硬件或电源故障可能会导致文件系统出现异常。为确保数据完整性和系统稳定性,定期进行磁盘健康检查至关重要。本文将详细介绍如何使用fsck和badblocks工具来检测和修复文件系统及硬盘扇区的潜在问题。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 深入了解 Windows 窗体中的 SplitContainer 控件
    SplitContainer 控件是 Windows 窗体中的一种复合控件,由两个可调整大小的面板和一个可移动的拆分条组成。本文将详细介绍其功能、属性以及如何通过编程方式创建复杂的用户界面。 ... [详细]
  • 在跨浏览器开发中,一个常见的问题是关于如何在鼠标悬停时显示图片提示信息。本文深入探讨了 IE 浏览器对 IMG 元素 alt 属性的特殊处理,并提供了最佳实践建议。 ... [详细]
  • 树链问题的优化解法:深度优先搜索与质因数分解
    本文介绍了一种通过深度优先搜索(DFS)和质因数分解来解决最长树链问题的方法。我们通过枚举树链上的最大公约数(GCD),将所有节点按其质因子分类,并计算每个类别的最长链,最终求得全局最长链。 ... [详细]
  • 查找最小值的操作是很简单的,只需要从根节点递归的遍历到左子树节点即可。当遍历到节点的左孩子为NULL时,则这个节点就是树的最小值。上面的树中,从根节点20开始,递归遍历左子 ... [详细]
  • 在进行QT交叉编译时,可能会遇到与目标架构不匹配的宏定义问题。例如,当为ARM或MIPS架构编译时,需要确保使用正确的宏(如QT_ARCH_ARM或QT_ARCH_MIPS),而不是默认的QT_ARCH_I386。本文将详细介绍如何正确配置编译环境以避免此类错误。 ... [详细]
author-avatar
同步管理精英
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有