热门标签 | 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文件

...









推荐阅读
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • Linux设备驱动程序:异步时间操作与调度机制
    本文介绍了Linux内核中的几种异步延迟操作方法,包括内核定时器、tasklet机制和工作队列。这些机制允许在未来的某个时间点执行任务,而无需阻塞当前线程,从而提高系统的响应性和效率。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 深入解析TCP/IP五层协议
    本文详细介绍了TCP/IP五层协议模型,包括物理层、数据链路层、网络层、传输层和应用层。每层的功能及其相互关系将被逐一解释,帮助读者理解互联网通信的原理。此外,还特别讨论了UDP和TCP协议的特点以及三次握手、四次挥手的过程。 ... [详细]
  • 解决Anaconda安装TensorFlow时遇到的TensorBoard版本问题
    本文介绍了在使用Anaconda安装TensorFlow时遇到的“Could not find a version that satisfies the requirement tensorboard”错误,并提供详细的解决方案,包括创建虚拟环境和配置PyCharm项目。 ... [详细]
  • GPIO实现I2C从机的设计[2]
    在上篇文章中,实现了GPIO模拟I2C从机的初步设计,但在实际的使用过程中,发现了一些问题,为了解决实际传输过程中发生的数据传输错误问题,在本篇文章中,会根据实际测试情况对上篇的代码做一些修改。 ... [详细]
  • 新增和更新价目表行--目的:在已有的价目表头基础上,增加行信息--限制:该api有些问题,如果强制增加头信息,会有很多问题,所以该例子只是在已有头信息基础上,增加行信息 ... [详细]
  • Use从句的语法一、Use从句列出了所有使用到的单元的名称(不是源文件的名称)。Use从句出现在以下位置:-->.dpr文件的工程名称之后。-->.pas文 ... [详细]
  • 概要Clientsshouldnotbeforcedtodependuponinterfacesthattheydonotuse.不能强迫用户去依赖那些他们不使用的接 ... [详细]
  • 原创我的网址http:blog.csdn.nethsuyuan如有错误请指正八.类对于面向对象语言来说,类是重头戏,C#中类的使用和C++中类似,在细节方面有些 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • This post discusses an issue encountered while using the @name annotation in documentation generation, specifically regarding nested class processing and unexpected output. ... [详细]
  • 深入理解TCP/IP协议中的MTU与MSS及以太网数据帧
    本文详细探讨了TCP/IP协议中MTU(最大传输单元)和MSS(最大分段大小)的概念及其在以太网数据帧中的应用。通过分析这些关键参数的工作机制,帮助读者更好地理解网络通信中的数据包处理过程。 ... [详细]
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社区 版权所有