LWIP移植说明及程序分析
新哥NUAA(技术交流279141909) 2015年8月 说在前面
LWIP,这个轻量级的TCP/IP协议,说来惭愧,把我虐得够惨的,今天算是第三次接触lwip了,第一次是认真看了别人移植可以ping通的程序,后来我也参照野火的以太网文档一步步的移植了一次,但不管怎么调都有bug!bug!bug!一气之下就不想再碰了!!这两天想想又很不甘心,于是乎,我又开始移植起来~~~
移植要点
这次也首先是基于无操作系统的移植,在后期再移植基于ucou2的网络协议。那么在这里我就首先带领那些炙热于lwip协议但又因网上的移植资料残缺不全而苦无移植良策的同志们来一次彻底的lwip移植吧(好长,说的我好累)!
本文档是基于stm32f103+ENC28J06的移植(如果是stm32f107就不是这种方法了,107处理器自带MAC控制器,只需要外接一个PHY控制器和以太网变压器就可以实现以太网功能。)我发现上一次移植是照葫芦画瓢,完全没有理解移植的要点,在这里我简单的总结一下移植要点和出现bug的原因吧。
移植要点1:复制 opt.h文件到新建的lwipopt.h文件,并按照处理器资源与实际需求修改。(虽然lwipopt.h是有opt.h文件修改得到的,但opt.h文件仍然有用,里面涉及到的条件编译在协议的正常运行中是必要的,在keil4中要同样包含在路径中。换句话说,只要在其他文件里没有对opt.h中条件编译的对象进行重定义,那么就要以opt.h中的条件编译为准,如果读者删掉opt.h后可以看到很多bug)。
移植要点二:编写enc28j60的驱动文件(含头文件)。(函数模块包括读控制寄存器、向以太网控制器写入命令、读取buff的数据、向buffer写入数据、选定将要操作的bank、读取控制寄存器的内容、写入数据到控制寄存器、向PHY写入控制参数、初始化以太网控制器、发送数据包函数、接收数据包函数)。以上括号中的函数可以查看enc28j60的datasheet自己写,也可以网上下载借鉴别人写好的。
移植要点三:编写网络处理函数netconfig.c(与对应的netconfig.h)。主要是初始化lwip协议栈、以及做一些系统时钟中断等处理。这一句将enc28j6与lwip连接起来:netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input); ethernetif_init, ethernet_input是lwip协议中neif与外设接口的函数。 移植要点四:修改路径为\\LWIP\\lwip-1.3.2\\lwip-1.3.2\\src\\netif中ethernetif.c文件。原协议栈中ethernetif.c只提供了一些子函数的模板,要实现通信功能还要根据网卡(enc28j60)进一步修改。需修改的子函数有:网卡初始化函数(low_level_init)、底层发送数据函数(low_level_output)、底层接收数据函数(low_level_input)、数据输入处理函数(ethernetif_input)。 对以上四个要点进行正确的配置后,就完成了移植步骤的一大半了。但移植的最大问题往往不是出在这儿。往往是读者对keil的不熟悉,比方说头文件路径是不是正确的包含了,这往往是出现bug的根本原因所在。
移植过程
好了,说完以上要注意的要点后,其实我们对lwip的移植过程也有一个大体的了解。那接下来我们就真正手把手去移植lwip吧,在移植的过程中呢,带读者简要分析一下程序,
就当做学习一下嵌入式及编程咯。
Step1:
在移植的时候,我们也常常需要利用contrib-1.3.0.zip 中的文件。解压后,在
\\contrib-1.3.2_\\contrib\\ports 目录下有一些针对特定平台移植时使用的文件,选择进入其中一个目录,如\\contrib-1.3.0_\\contrib\\ports old\\6502\\include\\arch。
我们需要用到的是cc.h、perf.h(不需修改)和sys_arch.h (可以暂不移植,根据操作系统而定)文件,通常把它复制出来存放到自己工程中的arch 文件夹中。cc.h 包含了LwIP 对于基本数据类型的定义。perf.h文件是和系统统计和测量有关的头文件,sys_arch.h 定义了与系统有关的信号量、邮箱及线程(这里是基于无操作系统的移植暂时不需要对其进行更改)。其中cc.h要根据编译器平台与处理器平台等进行修改。还有一个lwipopt.h文件也要根据\\LWIP\\lwip-1.3.2\\lwip-1.3.2\\src\\include\\lwip中的opt.h文件进行修改。 (其实lwiopts.h起名无所谓,只要将opt.h中包含的头文件lwipopts.h改为你新建的.h文件一致就可以了,但里面的内容配置包含用户对协议栈内核的参数配置,要根据实际情况进行更改,有时候严格按照文档对应来更改也会出错,可能要删除opt.t中的条件编译,尽量简洁明朗一点,其中
#define NO_SYS 1
#define LWIP_NETCONN 0
这两个是很重要的!定义错了或不定义都会导致bug!!)
Step2:
在完成以上的三个文件的移植后,就已经完成一小部分工作量,虽然很简单,但一定要细心哦亲!接下来看一下enc28j60的驱动文件吧。这部分文件批量购买的话芯片公司会提供源代码,也可以通过论坛等途径下载到的,需要考验读者的信息搜索能力啦! /****************************************************
* 函数名:main.c * 描述:main * 输入:无 * 输出:无
****************************************************/ #include \
__IO uint32_t LocalTime = 0; int main(void) { uart_init(9600); //串口初始化为9600 ENC_SPI_Init(); SysTick_Init(); //延时函数初始化 /*初始化完成之后,通过下面的心跳函数 直接通过结构体neif->input(ethernet_input())进行对函数的访问了 说明: ethernet_input()和ethernetif_input这两个函数是有区别的, ethernetif_input是调用ethernet_input函数的,但是初始化neif_add结构体的时候还是ethernet_input这个函数。*/ LwIP_Init();
httpd_init(); while ( 1 ) { /*轮询*/ LwIP_Periodic_Handle(LocalTime); } }
在以上程序LwIP_Init()中有一句程序语句为:
netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input); 其中ethernetif_init, ethernet_input将LWIP协议与enc28j60结合起来了,实现了网卡的网络功能。ethernetif_init中的low_level_init( netif )是网卡初始化函数(包括mac地址写入网卡及网卡的初始化。mac地址结构为00:00:00:00:00:00:00,48位,其中前三个为芯片生产商定义的,后面三个可以认为定义,如果前面的数据随意改动了再编译的话,结果一想而知:有bug!!) 下面就结合enc28j60的datasheet分析一下以太网的初始化程序: /**************************************************** * 函数名:enc28j60Init
* 描述:初始化以太网控制器 * 输入:MAC地址 * 输出:无
****************************************************/ void enc28j60Init(u8 * macaddr) {
/* CS端口为输出 */ 01// ENC28J60_RSTH();
/* 禁止ENC28J60 */ 02 ENC28J60_CSH();
/* ENC28J60软件复位该函数可以改进*/
03 enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
/* 设置接收缓冲区起始地址该变量用于每次读取缓冲区时保留下一个包的首地址 */ 04 NextPacketPtr = RXSTART_INIT;
/* 设置接收缓冲区起始指针*/
05 enc28j60Write(ERXSTL, RXSTART_INIT & 0xFF); 06 enc28j60Write(ERXSTH, RXSTART_INIT >> 8); /* 设置接收缓冲区读指针*/
07 enc28j60Write(ERXRDPTL, RXSTART_INIT & 0xFF); 08 enc28j60Write(ERXRDPTH, RXSTART_INIT >> 8); /* 设置接收缓冲区结束指针 */
09 enc28j60Write(ERXNDL, RXSTOP_INIT & 0xFF); 10 enc28j60Write(ERXNDH, RXSTOP_INIT >> 8); /* 设置发送缓冲区起始指针 */
11 enc28j60Write(ETXSTL, TXSTART_INIT & 0xFF); 12 enc28j60Write(ETXSTH, TXSTART_INIT >> 8); /* 设置发送缓冲区结束指针 */
13 enc28j60Write(ETXNDL, TXSTOP_INIT & 0xFF);
14 enc28j60Write(ETXNDH, TXSTOP_INIT >> 8);
/* 使能单播过滤使能CRC校验使能格式匹配自动过滤*/
15 enc28j60Write(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN); 16 enc28j60Write(EPMM0, 0x3f); //格式匹配字节 17 enc28j60Write(EPMM1, 0x30); //格式匹配字节 18 enc28j60Write(EPMCSL, 0xf9); //格式匹配字节 19 enc28j60Write(EPMCSH, 0xf7); //格式匹配字节
/* 使能MAC接收允许MAC发送暂停控制帧当接收到暂停控制帧时停止发送*/
20 enc28j60Write(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); /* 退出复位状态 */
21 enc28j60Write(MACON2, 0x00);
/* 用0填充所有短帧至60字节长并追加一个CRC 发送CRC使能帧长度校验使能 MAC全双工使能*/
/* 提示由于ENC28J60不支持802.3的自动协商机制,所以对端的网络卡需要强制设置为全双工 */
22 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX );//MACON3_HFRMLEN /* 填入默认值 */
23 enc28j60Write(MAIPGL, 0x12); /* 填入默认值 */
24 enc28j60Write(MAIPGH, 0x0C); /* 填入默认值 */
25 enc28j60Write(MABBIPG, 0x12); /* 最大帧长度 */
26 enc28j60Write(MAMXFLL, MAX_FRAMELEN & 0xFF); 27 enc28j60Write(MAMXFLH, MAX_FRAMELEN >> 8); /* 写入MAC地址 */
28 enc28j60Write(MAADR5, macaddr[0]); 29 enc28j60Write(MAADR4, macaddr[1]); 30 enc28j60Write(MAADR3, macaddr[2]); 31 enc28j60Write(MAADR2, macaddr[3]); 32 enc28j60Write(MAADR1, macaddr[4]); 33 enc28j60Write(MAADR0, macaddr[5]); /* 配置PHY为全双工 LEDB为拉电流 */
34 enc28j60PhyWrite(PHCON1, PHCON1_PDPXMD);
/* 半双工回环禁止 */
35 enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); /* 返回BANK0 */
36 enc28j60SetBank(ECON1);
/* 使能中断全局中断接收中断接收错误中断 */
37 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); /* 接收使能位 */
38 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); }
程序分析:(以此为例带读者学习一下如何看datasheet编写驱动程序)
02ENC28J60_CSH();是一个宏定义,#define ENC28J60_CSH() (GPIOB->ODR |= 1<<12),表
示将PB12置为高电平,因为在硬件接线时PB12接ENC28J60的CS端(片选),高电平表示禁止使用此网卡。
04~14 设置接收/发送缓冲区,根据datasheet的地址查询,分离出高低地址。
分离低地址:(以起始指针为例)enc28j60Write(ERXSTL, RXSTART_INIT & 0xFF);保留低8位即可; 分离高地址:enc28j60Write(ERXSTH, RXSTART_INIT >> 8);将地址右移8位,保留高八位即可。 其中,ERXSTL与ERXSTH都是宏定义,如下: #define ERXSTL (0x08|0x00) #define ERXSTH (0x09|0x00)
Datasheet中bank0的地址映射如下图所示。因为是bank0所以(0x08|0x00)。若是bank1,则(0x08|0x20)。
15 enc28j60Write(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN);请详细看
一下过滤器ERXFCON,后面几个都是它的数据位,都宏定义了,查看后可知ERXFCON被赋值为1011 0000。具体表示什么意义可以详细查看datasheet。如下图所示:(ERXFCON的地址映射位于bank1的0x18h。)#define ERXFCON (0x18|0x20)
其他的就不逐条解释了,都大同小异,看datasheet就可搞定一切。在完成网卡的驱动程序时,将网卡和mcu相连接的程序也要写好。因为硬件是怎么连接的也会影响驱动程序怎么写的对吧!但在此重申一下,自己动手写这部分的驱动文件还是挺费事的,建议小伙伴们发挥自己信息搜索能力尽量下载或拷贝吧!这都是用的比较成熟的了,好了废话不多说了,接下来我
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库LWIP移植说明及程序分析在线全文阅读。
相关推荐: