geren2014 发表于 2020-5-18 18:22:03

【MCU实战经验】+stm32f4 + uCOS-III(V3.0.3) + LWIP(V1.4.0)


之前手里有一块stm32f407的discovery。最近想测试一下F407 的Ethernet MAC外设。实现mcu网络通信功能的方案有很多种,包括SPI接口的ENC28J60,成本很低,而且源码很丰富,但是总觉得用spi搞以太网毕竟不够直接;还可以用W5100/w5200(没记错的话这个芯片好像是W5100+STM32F103集成在一起了,ST可能把103的核卖给那个韩国公司了?)系列以及CH395等硬件集成了以太网TCP/IP协议的芯片开发,与mcu的接口依然是spi,这种方式开发非常简单,不需要非常了解与网络相关的协议即可;最后一种利用mcu的MAC配合一个PHY实现网络功能。最后我选的是最后一种方案,一来多学点东西,二来非常好奇ST的mac怎么工作(第一次接触stm32的时候在103和107之间徘徊了很久,怎奈鱼和熊掌不可兼得)。
硬件配置:stm32f407discovery + 微雪DP83848模块。(RMII接口,没接DP的中断,汗)
http://www.stmcu.org.cn/module/forum/data/attachment/forum/201403/30/6175b848c91263dfdd39082ec26b1f0b.jpg


ETH_MDIO ---------------------------------------------------> PA2
ETH_MDC ----------------------------------------------------> PC1
ETH_PPS_OUT ---------------------------------------------> PB5
ETH_RMII_REF_CLK--------------------------------------> PA1
ETH_RMII_CRS_DV ---------------------------------------> PA7
ETH_RMII_RXD0 -------------------------------------------> PC4
ETH_RMII_RXD1 -------------------------------------------> PC5
ETH_RMII_TX_EN ------------------------------------------> PB11
ETH_RMII_TXD0 --------------------------------------------> PB12
ETH_RMII_TXD1 --------------------------------------------> PB13
DP83848_INT------------------------------------------------->PB14
其中,ETH_PPS_OUT可以不接,硬件也可以不设置,没记错的话,这个管脚和八线的以太网有关,详情问百度。以太网时钟采用的是模块上50M的有源晶振,通过PA1参考时钟和F407同步。中断引脚可以连接也可以不连接,影响不大。另外,在图中用的是杜邦线连接的两个模块,导致最后的实现有10%~20%的丢包率,所以以后能开板还是开板吧,毕竟50M的速率还是要考虑一下信号的感受。后来加工了一下连接了PHY的INT引脚(没有会很蛋疼),不过这个引脚是OD的,要把407的内部IO设置成上拉。
开始用固件库配置好底层之后,想不移植TCP/IP协议栈直接用。直接从mac接收数据然后处理,发现处理一般的TCP传输还可以(其实不可靠),处理HTTP的时候,只要网页里有图片,就很容易挂掉(网页里显示的内容重复很多,版面很乱,可见tcp没有正确处理重传机制真的不行)。后来看lwip的源码才知道tcp的复杂性(lwip用了一半的代码量实现tcp),这个东西要用个定时器来处理超时重发等等机制保证数据传输的完整可靠。tcp的状态机在老衲五木的LWIP源码详解里有介绍。看了之后有一种望尘莫及,连尘土都看不到的感觉。

在移植前请确保MAC和PHY都已经移植好了,我使用的官方的DP83848,所以这点省略了,其他phy的移植大同小异都是通过两个stm32F4提供的库函数通过MDIO和MDC接口配置访问的,这两个函数是ETH_ReadPHYRegister和ETH_WritePHYRegister。这是因为MII/RMII(介质无关接口)对任何phy芯片的管理接口是统一的,事实上,IEEE规定了一些通用的寄存器定义用于实现网络的基本功能,这些公用寄存器和phy自定义的寄存器都是通过MDIO和MDC接口访问控制。如果是使能了自动协商,就要保证在硬件初始化之前网络硬件连接是ok的,否则F407的以太网初始化函数ETH_Init会因为连接自动协商超时返回失败。
对于tcpip协议的具体实现,这里做了简化。如果想更详细的了解其实现过程,讲了tcpip的具体实现细节。包括arp,icmp,dns的介绍。ip包格式,ip分包;tcp协议包格式,传输过程(连接建立和终止,三次握手,SYN,SYN/ACK,SYN,四步终止,fin ack),分包,重传,流控制,拥塞控制,快速恢复等内容。
软件配置:uC/OS-III V3.03 + lwip1.4.0
uC/OS移植注意事项:移植ucos-III的博客一抓一大把,这里就不多说了,只是要注意下fpu的支持问题(需要处理一下cpu部分汇编代码,任务切换的时候多保存几个寄存器。 在使用FPU的情况下,异常到来的时候,CORTEX-M4会将部分FPU的寄存器也一起自动入栈,包括一个未知的字,一个FPSCR寄存器,以及S15-S0,所以如果要使UCOS支持FPU,就必须配合M4的这种入栈/出栈行为相应地修改任务堆栈的结构以及任务切换时的入栈/出栈汇编代码,摘自阿莫论坛,修改的基于STM32F407的官方版UCOS III源码,增加了FPU浮点支持,具体可以看这个帖子),支持fpu的话要添加__FPU_PRESENT = 1和 __FPU_USED = 1(mdk4.72版本),不搞这个会进硬fault,另外在用的时候注意任务堆栈的大小,这个如果太小也会进硬fault,还一个就是中断的问题,ucos-iii的中断可以用它自己移植好的中断,也可以用st官方的中断函数,关键是要保证中断函数和st启动文件里的中断ISR名字一致。
lwip1.4.0说明:lwip是一种tcp/ip协议栈,与uip相比,功能相对全面。ROM和RAM占用资源较多。使用方式灵活。uip适合8、16位单片机使用,资源占用少,但是主函数会写很长。lwip主要有三种访问方式:RAW_API方式;LWIP_API方式;BSD套接字方式。三种方式各有特点,RAW_API(单线程方式):使用回调函数方式实现,占用资源最少。但实现略复杂,效率不高,据老衲五木说。LWIP_API(多线程方式):使用简单灵活,但是需要OS配合进行多线程操作,lwip会使用OS的信号量,邮箱等进行进程同步,移植复杂。BSD套接字方式,适应软件socket编程,与LWIP_API方式类似,但是lwip进行了封装,更像socket编程了,但是lwip的支持不足,需要用户直接访问lwip协议栈以完善套接字功能。另外LWIP1.4.0相对于之前的版本多了一个timers.c文件处理和定时器相关的所有内容,使得lwip的源码和app的代码分割比以前清晰了。所以在不用OS的情况下,要用sys_check_timeouts()函数代替之前版本在main.c里循环调用的LwIP_Periodic_Handle函数,这个函数主要负责检查相关定时器的超时事件的处理,同时需要实现一个sys_now的函数,用来获得当前的时间,这个时间是在systick中断函数里累加的值,而在LWIP_API(多线程方式)中是通过对信号量的超时判断,来实现LWIP与os时间上的同步的。相对于LWIP1.4.1来说,1.4.0缺少了对ICMP校验和的宏判断,导致使用F407的mac的硬件校验和会ping不通,需要对lwip的源码做简单的修改。需要注意的是,LWIP的源代码(文件名为lwip-x.x.x)和应用实例(contrib-x.x.x)是需要分别下载的。官方的源码包文件结构如下:
http://www.stmcu.org.cn/module/forum/data/attachment/forum/201404/02/d3d6713ffe10342918ab69dffc47272e.jpg
如果需要移植lwip的话,doc中的两个文件sys_arch.txt 和rawapi.txt需要阅读。src中主要是lwip的源码,其中的ethernetif.c文件提供了软件移植的模板,需要自己根据网卡进行修改实现,包括ethernetif_init、ethernetif_input、low_level_output、low_level_input、low_level_init的实现模板和注释。changelog包括维护的bug,与之前版本的差异、upgrading包括新版本的新特性。不加后缀可能是为了linux的缘故吧,这些文件基本都是文本文件。
在lwip的contrib-x.x.x文件夹下,一般只有两个文件夹,一个是app,一个是port。其中app主要是使用lwip协议栈实现的具体应用。下图为app文件夹下的内容。
http://www.stmcu.org.cn/module/forum/data/attachment/forum/201404/02/3e37f5dc26f35fe4711d282de97e3b8f.jpg
其中httpserver和httpserver_raw分别为使用多线程API函数形式的和使用单线程raw回调函数形式的web服务器的简单实现。stm32官方的以太网例程就是对这两个文件夹进行了扩充和改写实现的。下图是httpserver_raw文件夹的内容,rar文件是我压缩的,可以忽略。用过stm32官方以太网例程的同学,应该很熟悉这几个文件吧~
http://www.stmcu.org.cn/module/forum/data/attachment/forum/201404/02/3d1bfb44f25d578803f260aff8084681.jpg
httpserver文件夹下只有三个文件,httpserver-netconn.c/.h文件和一个readme文件。通过对比httpserver-netconn.c和httpd.c文件,你就会明白我为什么要用多线程LWIP_API实现网络通信了。
下面再来说下contrib-x.x.x的另一部分,port部分。这个部分是和平台密切相关的,文件构成各异。但是一般都包括一个include文件夹、一个sys_arch模块的实现,一个ethernetif.c.h模块,以及一个雷打不动的lwipopts.h配置文件。include文件夹中一般包括很多头文件,但它们其实都很简单,比如cpu.h看着很复杂的感觉,其实里面只定义了一个cpu大小端的宏。需要注意的文件并不多,主要是cc.h文件,用于定义lwip会用到的数据类型,以及禁止字对齐编译器指令,这样就可以将数据包中指定格式的内容强制转换为结构体访问了,提高处理效率。sys_arch.h文件用于定义信号量,邮箱,线程等与系统相关的数据类型。ethernetif.c.h就是之前说的要根据实际网卡实现的5个函数。sys_arch.c就是实现与线程以及信号量,邮箱相关的函数。对于stm32这种官网支持比较全面的芯片来说,port只要从官网上下载就可以了。对于资料比较少的芯片,工作量相应的就比较大,这也是我为什么总喜欢用stm32的一个原因。
这样我们就把要移植的部分顺了一下。为下面的移植工作确定了目标。不过我还是不太敢往下写lwip和uC/OS-III的移植。请看下回:大胆发言,小心求证。
ucos


http://www.stmcu.org.cn/module/forum/data/attachment/forum/201403/30/99ec2dd10ca6bad12c7335889ab15402.jpg


http://www.stmcu.org.cn/module/forum/data/attachment/forum/201403/30/1b20e577119af7fdf95bc15ae5c436f2.jpg


http://www.stmcu.org.cn/module/forum/data/attachment/forum/201403/30/99ec2dd10ca6bad12c7335889ab15402.jpg


http://www.stmcu.org.cn/module/forum/data/attachment/forum/201403/30/5cdadbcaa85eb1c4ab18f171204a4fc3.jpg


http://www.stmcu.org.cn/module/forum/data/attachment/forum/201403/30/f201b02e8f2d54e3e8be587189774ac4.jpg

**** Hidden Message *****


kunchen 发表于 2020-5-19 09:32:55

谢谢分享

Sonnyu 发表于 2020-7-20 09:04:23

感谢分享

hdyangqy 发表于 2021-6-1 14:03:53

有没有源代码

marco5512 发表于 2022-2-8 13:24:05

謝謝分享

sunwumcu 发表于 2022-3-18 11:28:59

谢谢分享!

eefocus_3828453 发表于 2022-3-30 13:20:10

学习

eefocus_3929274 发表于 2023-6-16 10:58:22

围观

daitouzhang 发表于 2023-12-13 15:09:27

遇到问题了,来看看有没有解决办法

eefocus_4059614 发表于 2024-10-31 17:48:36

学习
页: [1]
查看完整版本: 【MCU实战经验】+stm32f4 + uCOS-III(V3.0.3) + LWIP(V1.4.0)