【STM32+cubemx】0023 HAL库开发:EN28J60移植LwIP协议栈(裸机环境)

LwIP是瑞典计算机科学院(SICS)的Adam Dunkels开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用。相比我们前面讲的uIP,占用资源要稍多一些,但是功能更加完善。

LwIP有三种编程接口:RAW/Callback API、Netconn API、Socket API,三者易用性依次提高、执行效率依次降低。无操作系统时,只能使用RAW/Callback API接口。本节我们学习LwIP在裸机环境(无操作系统)下的移植

本节的例子使用的开发环境是cubemx 6.1.0,pack版本是1.8.0;硬件使用的是stm32f103VET6和ENC28J60;软件方面,LwIP协议栈使用的是1.4.1版。

1)cubemx工程配置

(与上一节的配置内容是完全一样的)

先看一下硬件连接图:

ENC28J60这个芯片可实现10M的以太网通信,与MCU是使用SPI接口,接在SPI1口上,使用软件控制CS线,另外还连接了RST复位引脚、INT中断引脚。

配置SPI接口时,如下图:

选择全双工,配置为速率为不大于20M(因为ENC28J60的SPI接口时钟最大20M)

配置GPIO,PB7为输出CS、PE1为输出RST、PA1为输入INT:

把堆栈设置大一些,因为后面在代码中会申请比较大的变量:

到这里,cubemx里的设置就已经完成了,现在可以生成keil的代码工程了。

2)keil中的代码编写

(本文中的所有代码均可在文末关注公众号获取)

先下载lwip相关的源代码,就是lwip-1.4.1.zip和contrib-1.4.1。

其中lwip-1.4.1.zip是lwip实现的源代码,contrib-1.4.1是移植的范例,可以作为我们移植到stm32平台的参考。

lwip-1.4.1.zip解压缩后,在src文件夹中,得到以下目录,这就是lwip的源码目录:

a)添加LwIP源代码

我们把他们都拷贝到工程目录下,然后在keil工程中创建几个分组,以便分类管理源代码文件,如下图所示:

在各分组下添加源码文件:

Lwip-netif中,添加网卡的驱动源文件,包括enc28j60的驱动函数、lwip目录下netif目录中的网卡驱动;

Lwip-core中,添加lwip目录下的core和ipv4中的全部源文件;

Lwip-api中,添加lwip目录下的api中的全部源文件;

Lwip-arch和Lwip-app中的函数需要我们自己编写,实际上,也可以参考contrib-1.4.1.zip里的代码来实现。

b)编写arch相关的源代码

建立一个arch目录,包含这几个文件:cc.h、cpu.h、perf.h、sys_arch.c和sys_arch.h,都添加到Lwip-arch分组下。这几个文件中的代码主要实现如下:

cc.h中,实现了数据类型的定义、编译及调试信息的选项:

cpu.h中,定义了处理器为小端模式:

sys_arch.c中定义了一个返回时间计数的函数,sys_arch.h文件中对这个函数进行了声明。

其中的lwip_localtime变量我们要添加到定时器中断中,使得它每ms加1,作为lwip的计时器。如下所示,添加到了stm32f1xx_it.c文件中的systick中断服务程序中:

perf.h中定义了两个lwip系统测量和统计的宏,我们不使用,可以定义为空的:

c)编写网卡驱动源代码

enc28j60的驱动函数前几节我们讲过,直接将enc28j60.c 和 spi_enc28j60.c两个文件拿来使用即可。

这里我们还需要改写ethernetif.c文件中的3个函数:

static err_t    low_level_init(struct netif *netif) //网卡初始化

static err_t    low_level_output(struct netif *netif, struct pbuf *p) //网卡发送数据

static struct pbuf * low_level_input(struct netif *netif) //网卡接收数据

首先是low_level_init网卡初始化的函数,主要实现MAC地址初始化,数据包长度定义,

这里对28j60的初始化直接调用enc28j60.c中实现的enc28j60Init函数。

对发送函数low_level_output的改写,核心是标记的几句:

lwip网卡结构体中,要发送的数据是以链表的形式存放的;我们先将所有需要发送的数据通过memcpy函数拷贝到mysendbuf数组中,再通过enc28j60PacketSend发送出去。

一般来说,mysendbuf这个缓冲区以动态的形式分配比较省内存,但是为了方便理解和实现,本例子这里mysendbuf定义为全局变量数组。

对接收函数low_level_input的改写,主要代码如下所示:

先通过enc28j60PacketReceive从网卡中读取最大长度(1500)的数据,数据存放在全局数组myrecvbuf中,实际读取的数据长度存放在len中;

然后将myrecvbuf拷贝到lwip网卡结构的链表中。

改写完成后,将ethernetif.c文件开始和末尾的#if 0 和 #endif注释掉(默认这个文件是不被编译的)。

为了便于理解,我们这里的例子,临时数据存放是使用的是全局变量数组mysendbuf和myrecvbuf,没有使用动态内存分配,所以,暂时不需要实现动态内存分配的功能。

d)编写lwip的配置头文件

LwIP的配置由lwipopts.h头文件决定。LwIP的默认配置在opt.h文件中,该文件中包括了所有的参数默认配置,是以条件编译的形式实现的。用户如果想要修改,则可以在lwipopts.h来重新定义,就能更改配置。

本文需要在lwipopts.h中定义的的几个基本参数如下:

到这里,lwip的移植工作基本完成了,之后就是编写应用程序了。

e)应用程序编写

主要有三个函数:

lwip_comm_init();    初始化函数;

lwip_pkt_handle();    //处理接收数据的函数,需要及时调用;

lwip_periodic_handle();    //处理lwip状态的函数,需周期调用;

各函数的实现如下:

lwip_comm_init();函数主要实现了lwip内核的初始化、设置ip地址、初始化网卡(netif_add实现)、打开网口等操作:

lwip_pkt_handle只是对之前讲过的ethernetif_input函数再做了一次封装:

lwip_periodic_handle用于处理定时刷新的lwip任务:

 

Main函数中如下调用:

主循环之前调用初始化函数,主循环中,不断调用lwip_pkt_handle和lwip_periodic_handle。

到这里主要的测试函数就实现完了,这个函数其实只是实现了网卡的初始化,应用程序里没有对数据包做任何处理,只能测试ping功能。

3)运行测试程序

将代码编译、下载到开发板运行,开发板通过网线连接到计算机。

计算机端设置IP地址为192.168.1.18(只要和开发板的ip地址192.168.1.15在一个网段就行)。

测试网络是否连通:

在终端上输入ping 192.168.1.15:

可以看到ping有回复,说明网络已经通了,移植成功了。

4)修改为中断形式

上述的例子中,对网卡数据的处理是轮询式的,在主循环中调用lwip_pkt_handle,比较耗费资源,而且如果主循环中任务较多时会处理不及时;修改为中断形式,只在enc28j60收到数据时进行处理会比较合适。

要更改为中断方式,需要修改enc28j60的初始化函数,打开enc28j60的各种中断;将lwip_pkt_handle的处理放到中断服务程序中;进入中断后还要清各个中断的标志位。可以按下面的方法:

开启enc28j60中断的代码,在enc28j60Init函数中,标记的一行:

由于enc28j60的中断线我们接在PA1上,所以,在PA1的中断服务程序EXTI1_IRQHandler中,添加如下代码;在收到中断后,先判断enc28j60的中断种类,再作相应处理。

可以看到,原来在主循环中调用的lwip_pkt_handle函数,我们放到了接收数据时处理:

这样就实现了中断形式的处理。测试程序仍然和之前的一样,可以ping网络进行测试。

好了,本节在enc28j60平台上,无操作系统环境下移植LwIP的内容就讲到这里了。

本节移植过程参考了正点原子的《STM32F1 LWIP开发手册(ENC28J60版)》、《嵌入式网络那些事-STM32物联实战》(朱升林-2015年版)等资料:

欢迎关注我的公众号,可留言“资料”获取上述源码和参考资料:

  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值