基于uCOS_II的以太网移植实例
- 格式:doc
- 大小:959.50 KB
- 文档页数:14
移植UCOS-II-V291到LPC1768(基于LPC17XX V2.0固件库)( 基于RealView MDK )Create:LMY@2012.08.01前言:我从2011年年初开始玩LPC1768,在公司做过几个LPC1768的小项目,都是基于UCOS-II-V2.86操作系统和LPC17XX V2.0固件库(还有的基于uIP1.0以态网协议栈),产品运行稳定,芯片级操作也十分方便(毕竟是基于固件库操作,不用管那些复杂的寄存器),在官网看到不知何时出了UCOS-II-V2.91,于是准备应用本版本编写今后的程序,移植时,有意留心写下这篇移植步骤,希望对初学者有帮助。
一、移植前的准备工作下载所需文件:1、在LPCWARE官网下载LPC175x_6x CMSIS-Compliant Standard Peripheral Firmware Driver Library (LPC175X,LPC176X固件驱动库),下载地址链接:/content/nxpfile/lpc175x6x-cmsis-compliant-standard-perip heral-firmware-driver-library-keil-iar-gnu进入网址选择lpc175x_6x_cmsis_driver_library.zip下载(也可以在NXP官网下载,但本人去年下载的固件库编译时出现N多警告,看起不爽,自己改又不怕改出问题)2、在UCOS-II官网下载UCOS-II-V291源码(上面描述为2.89,但实际内核为2.91),下载地址链接:/page/downloads/source_code进入网址选择UC/OS-II下载,如下:3、在UCOS-II官网下载UCOS-II移植到STM32的Ports文件(用于修改移植) /page/downloads/ports/st/stm32选择基于IAR的V2.92的版本下载,如下:以上三个文件大约65M,如无法下载。
UCOS II在PC上的移植网上移植教程有不少,不过对于初学者还是容易出问题,在这里将移植的详细过程记录如下,建议有兴趣的同学,找台电脑,从头试一遍,这样就算是入门了.一、准备工作在PC上移植ucos系统,因为ucos系统的源代码是c语言写的,因此编译C的软件必不可少。
在pc机上运行,还需要对pc的设备进行一空的控制,会用到汇编语言,因此汇编语言的编译软件也必不可少。
再有就是操作系统的源码,这些都准备好了,就可以进行移植了。
一些教材在移植是c编译环境选BORLAND C++ 4.5,汇编编译用TASM5.0,网络上能找到的移植方法基本都是基于这2个软件的。
这2个编译软件和操作系统源码可以通过网络下载。
图1 ucos移植的必备文件下载解压后,如图1所示。
下边开始安装,编译软件。
BORLAND C++和TASM5.0安装顺序不会影响到使用,在安装之前先来看下c盘的文件结构。
在图2中,c盘根目录下只有3个文件夹,当我们配置完成后,会多出4个文件夹。
图2 编译环境安装前c盘文件结构二、开始安装1.安装编译软件BORLAND C++ 4.5。
在BORLAND C++ 4.5安装文件包里找到找到install.exe文件并双击,默认的安装路径就C:\BC45。
因此安装时,可以用默认设置一直继续,安装过程如图3所示(注意安装包里还有一个setup文件,请不要用它来安装)。
图3 bc4.5安装界面2.安装汇编编译软件TASM5.0(1)这一步如果不小心,很容易安装不正确。
先在C盘建立一个名为TASM的文件夹,然后把TASM5.0安装文件里的所有文件都复制进去。
如图4所示,双击图4中的install 文件开始安装。
图4 TASM5.0安装(2)在弹出的界面按回车键继续,出现安装选择文件界面,将默认的A改为C如图5所示。
图5 修改盘符(3)按回车键继续,出现一个路径设置的界面,继续按回车键,出现安装配置界面如图6所示。
实验一uC/OS-II的移植1.实验目的(1)理解uCOS-II实时内核的工作原理;(2)熟悉uCOS-II在XS128上的移植过程;(3)掌握uCOS-II移植的细节。
2.实验任务(1)观察示例程序中的代码,体会实时操作系统与前后台程序的不同之处。
(2)完成由前后台程序编程到基于实时操作系统编程的思想转变。
3.预习要求(1)参考《嵌入式实时操作系统uCOS-II》(第2版),熟悉uCOS-II各模块的基本工作原理。
(2)参考《单片机与嵌入式系统开发方法》第9章内容以及《uCOS-II移植说明文档》。
熟悉uCOS-II在XS128上的移植过程。
4.实验步骤(1)打开示例程序,观察程序结构。
(2)识别出哪些是与硬件无关的文件,哪些是移植需要修改和添加的文件。
(3)打开OS_CPU.H文件,该文件定义CPU的数据类型,定义相关的宏。
打开OS_CPU_C文件,分析文件里各个函数的作用。
这两个文件是与CPU特性有关的文件。
(4)分别打开OS_CFG.H, INCLUDES.H. OS_CFG.H这三个文件,了解这三个文件的作用。
用户根据自己的应用系统来定制合适的内核服务功能.包括两个文件:OS_CFG.H, INCLUDES.H. OS_CFG.H是来配置内核, 用户根据需要对内核进行定制, 留下需要的部分, 去掉不需要的部分, 设置系统的基本情况. 比如系统可提供的最大任务数量, 是否定制邮箱服务, 是否需要系统提供任务挂起功能, 是否提供任务优先级动态改变功能等等;头文件INCLUDES.H为整个实时系统程序所需要的文件,包括了内核和用户的头文件。
(5)修改.prm文件中的中断向量,将其中的ROM_C000 = READ_ONLY DATA_NEAR IBCC_NEAR 0xC000 TO 0xFEFF;改为ROM_C000 = READ_ONLYDATA_NEAR IBCC_NEAR 0xC000 TO 0xEEFF;将结尾处原有的VECTOR 0 _Startup;改为VECTOR ADDRESS 0xEFFE _Startup;再添加上VECTOR ADDRESS 0xEFF6 OSCtxSw;VECTOR ADDRESS 0xEFF0 OSTickISR两个中断向量。
关于UC/OS-II的移植网上介绍的已经很多了,比较流行的几款处理器(例如ARM)在网上都可以直接下载移植好的代码。
由于最近选修了一门嵌入式系统的课,用的处理器是EPSON公司的S1C33系列,做实验的时候要进行操作系统的移植,这个周末花了一天半的时间学习了一下,因为毕业设计的时候做过ARM 上的移植,于是将两者比较了一下,给出一般的移植要点。
由于将来实验还要设计到GUI的移植以及文件系统的移植和网络协议的移植,我会将自己的学习笔记都记录下来。
大家下载到源码后,针对Intel 80x86的代码在uCOS-II\Ix86L目录下。
代码是80x86实模式,且在编译器大模式下编译的。
移植部分的代码可在下述文件中找到:OS_CPU.H, OS_CPU_C.C, 和OS_CPU_A.ASM。
大家可以参考这个例子,对它进行修改。
INCLUDES.H 是主头文件,在所有后缀名为.C的文件的开始都包含INCLUDES.H文件。
使用INCLUDES.H 的好处是所有的.C文件都只包含一个头文件,程序简洁,可读性强。
缺点是.C文件可能会包含一些它并不需要的头文件,额外的增加编译时间。
与优点相比,多一些编译时间还是可以接受的。
用户可以改写INCLUDES.H文件,增加自己的头文件,但必须加在文件末尾。
//////////////////////////////////////////////////////////一、(1)OS_CPU.H文件的移植(针对S1C33209)//////////////////////////////////////////////////////////OS_CPU.H 文件中包含与处理器相关的常量,宏和结构体的定义。
#ifdef OS_CPU_GLOBALS#define OS_CPU_EXT //全局变量#else#define OS_CPU_EXT extern#endif//////////////////////////////////////////////////////////由于不同的处理器有不同的字长,µC/OS-II的移植需要重新定义一系列的数据结构。
一步步移植uCOS-IIandLwIP(一)STM32F103ZE下移植uCOS-II and LwIP 汇总本文主要记录嵌入式实时操作系统uCOS-II(Ver 2.85)和轻量型TCP/IP协议栈LwIP(Ver 1.4.1)在32bit单片机STM32F103ZE上的移植过程,并列举几个simple examples说明两者工作原理。
本文的叙述原则是“用到什么知识点,就查阅相关资料”,对于其它延伸知识点不再概述。
所需物资:- 硬件开发平台,本平台网卡为DM9000A- uCOS-II(Ver 2.85)源码,可直接从Micrium官网下载uCOS 在cortex-M3上的移植例程uCOSII-ST-STM32F103ZE-SK - LwIP(Ver 1.4.1)源码,下载链接LwIP1.4.1一、Lwip移植TCP/IP协议分为网络链路层、网络层、传输层和应用层四个部分,网络链路层主要涉及底层硬件驱动的编写,另外三个上次协议一般采用协议栈的方式软件实现。
要实现与其它网络设备通信,首当其冲的是要移植好底层网卡驱动。
LwIP协议栈已经为我们提供了网络链路底层接口,我们要做到主要工作涉及到以下几个方面:- 单片机与网卡DM9000芯片的通信;- 完善LwIP协议栈文件ethernetif.c接口函数,该部分的难点在于实现LwIP规定的struct pbuf类型的数据包与网卡数据之间相互转换;- 上层软件协议的simple explain;1、网卡DM9000底层驱动的编写首先查阅DM9000的Datasheet(建议直接从芯片官网上查找,一般会有该芯片的Application note or Demo)本文主要是运用DM9000的16-bit mode,其总线形式类似与intel 8080总线,涉及读写指令和数据的控制引脚为CS#、IOW#、IOR#、CMD,数据总线引脚SD0~SD15,中断引脚INT。
一步步移植uCOS-IIandLwIP(四)二、uCOS-II移植嵌入式实时操作系统uCOS-II移植的核心在于任务切换时上下文环境的保存及恢复,针对Cortex-M3内核的单片机,其采用了PendSVHandler中断处理的方式解决这一核心问题。
我们要做的就是在任务切换及中断任务切换过程中开启触发PendSV中断,并在PendSVHandler中实现上下文环境的切换,即保存CPU内部寄存器值和恢复切换任务的相关信息。
为什么要使用PendSV中断实现上下文切换呢?参阅Cortex-M3的一段话。
另一个相关的异常是PendSV(可悬起的系统调用),它和SVC 协同使用。
一方面,SVC异常是必须在执行SVC指令后立即得到响应的(对于SVC异常来说,若因优先级不比当前正处理的高,或是其它原因使之无法立即响应,将上访成硬fault——译者注),应用程序执行SVC时都是希望所需的请求立即得到响应。
另一方面,PendSV则不同,它是可以像普通的中断一样被悬起的(不像SVC那样会上访)。
OS可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。
悬起PendSV 的方法是:手工往NVIC的PendSV悬起寄存器中写1。
悬起后,如果优先级不够高,则将缓期等待执行。
PendSV的典型使用场合是在上下文切换时(在不同任务之间切换)。
例如,一个系统中有两个就绪的任务,上下文切换被触发的场合可以是:- 执行一个系统调用-系统滴答定时器(SYSTICK)中断,(轮转调度中需要)让我们举个简单的例子来辅助理解。
假设有这么一个系统,里面有两个就绪的任务,并且通过SysTick异常启动上下文切换。
如图所示。
上图是两个任务轮转调度的示意图。
但若在产生SysTick 异常时正在响应一个中断,则SysTick异常会抢占其ISR。
在这种情况下,OS 是不能执行上下文切换的,否则将使中断请求被延迟,而且在真实系统中延迟时间还往往不可预知——任何有一丁点实时要求的系统都决不能容忍这种事。
两个任务分别控制一个led,位于PB4和PB5. PB4上的led每秒闪一次,PB5上的led每四秒闪一次。
#include"lf2407.h"#include"ucos_ii.h"OS_STK Main_TaskStk[64];OS_STK Sub_TaskStk[64];void Main_Task(void*data);void Sub_Task(void*data);void InitCPU(void){SCSR1 = 0x00fd;SCSR2 =(SCSR2|0x000b)& 0x000f;WDCR = 0x00e8;WSGR = 0x0040;MCRA = 0x0FFF;PBDATDIR = PBDATDIR|0x0F000;T2CON = 0x0000;GPTCONA = 0x0000;T2CNT = 0x0000;T2PR = 50000;T2CON = 0xd340;IMR = 0x0000;IFR = 0x003f;IMR = 0x0004;EVAIFRA = 0xFFFF;EVAIFRB = 0xFFFF;EVAIFRC = 0xFFFF;EVAIMRA = 0x0000;EVAIMRB = 0x0000;EVAIMRC = 0x0000;EVBIFRA = 0xFFFF;EVBIFRB = 0xFFFF;EVBIFRC = 0xFFFF;EVBIMRA = 0x0000;EVBIMRB = 0x0000;EVBIMRC = 0x0000;asm(" CLRC INTM");}int main(void){InitCPU();OSInit();OSTaskCreate(Main_Task,(void*)0,&Main_TaskStk[63], 0); OSStart();return 0;}void Main_Task(void*p_arg){EnableTick();OSTaskCreate(Sub_Task,(void*)0,&Sub_TaskStk[63],10);while(1){PBDATDIR |= 0x0010;OSTimeDlyHMSM(0, 0, 0, 500);PBDATDIR &= 0x0ffef;OSTimeDlyHMSM(0, 0, 0, 500);}}void Sub_Task(void*p_arg){while(1){PBDATDIR |= 0x0020;OSTimeDlyHMSM(0, 0, 2, 0);PBDATDIR &= 0x0ffdf;OSTimeDlyHMSM(0, 0, 2, 0);}}ucos-ii的移植及rtos下的应用API FOR 44B0要保证μC/OS-Ⅱ移植到微处理器后能正确运行;处理器需具备如下特性:1)处理器的c编译器支持可重入函数可重入的代码指的是一段代码(如一个函数)可以被多个任务同时调用,而不必担心会破坏数据。
TMS320F28335的uCOSⅡ移植TMS320F28335的uC-OSⅡ移植1、 uC-OSII 的原理uC-OSII 包括任务调度、时间管理、内存管理、资源管理(信号量、邮箱、消息队列)四⼤部分,没有⽂件系统、⽹络接⼝、输⼊输出界⾯。
它的移植只与 4 个⽂件相关:汇编⽂件(OS_CPU_A.ASM)、处理器相关C ⽂件(OS_CPU.H、OS_CPU_C.C)和配置⽂件(OS_CFG.H)。
有64 个优先级,系统占⽤8 个,⽤户可创建56 个任务,不⽀持时间⽚轮转。
它的基本思路就是“近似地每时每刻总是让优先级最⾼的就绪任务处于运⾏状态” 。
为了保证这⼀点,它在调⽤系统API 函数、中断结束、定时中断结束时总是执⾏调度算法。
原作者通过事先计算好数据,简化了运算量,通过精⼼设计就绪表结构,使得延时可预知。
任务的切换是通过模拟⼀次中断实现的。
uC-OSII ⼯作核⼼原理是:近似地让最⾼优先级的就绪任务处于运⾏状态。
操作系统将在下⾯情况中进⾏任务调度:调⽤API 函数( ⽤户主动调⽤), 中断( 系统占⽤的时间⽚中断OsTimeTick(),⽤户使⽤的中断)。
其整体整体思路如下。
(1)、在调⽤API 函数时,有可能引起阻塞,如果系统API 函数察觉到运⾏条件不满⾜,需要切换就调⽤OSSched()调度函数,这个过程是系统⾃动完成的,⽤户没有参与。
OSSched()判断是否切换,如果需要切换,则此函数调⽤OS_TASK_SW()。
这个函数模拟⼀次中断,好象程序被中断打断了,其实是OS 故意制造的假象,⽬的是为了任务切换。
既然是中断,那么返回地址(即紧邻OS_TASK_SW()的下⼀条汇编指令的PC 地址)就被⾃动压⼊堆栈,接着在中断程序⾥保存CPU寄存器(PUSHALL)……。
堆栈结构不是任意的,⽽是严格按照uC-OSII 规范处理的。
OS 每次切换都会保存和恢复全部现场信息(POPALL),然后⽤RETI 回到任务断点继续执⾏。
一步步移植uCOS-IIandLwIP(三)3、LwIP跑起来在前两小节中,我们详细介绍了lwip移植涉及的核心函数。
至于cc.h、cpu.h、lwipopts.h的中有关数据类型、字节对齐和调试信息的配置,网络资源很丰富,也基本上大同小异,本文就不做介绍。
需要说明的是,前两小节移植借鉴和参阅了网络上许多其他人的成果。
接下来我们要lwip跑起来,先上主程序://Other initiation do not listTimer_Config(); // 10ms interval for lwipLWIP_Init(); //lwip system initwhile(1){LWIP_Pkt_Handle();LWIP_Periodic_Handle(localtime);}各个子程序如下:uint32_t TCPTimer = 0;uint32_t ARPTimer =0;uint32_t DHCPfineTimer =0;uint32_t DHCPcoarseTimer;#define TCP_TMR_INTERVAL 250#define ARP_TMR_INTREVAL 5000#define DHCP_FINE_TIMER_MSECS 500#define DHCP_COARSE_TIMER_MSECS 6000struct netif dm9000if;void LWIP_Init(void){ip_addr_t ipaddr, netmask, gw;lwip_init();IP4_ADDR(&ipaddr,IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR 3);IP4_ADDR(&netmask,NETMASK_ADDR0,NETMASK_ADDR1, NETMASK_ADDR2,NETMASK_ADDR3);IP4_ADDR(&gw,GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_A DDR3);netif_add(&dm9000if,&ipaddr,&netmask,&gw,NULL,etherne tif_init,ethernet_input);netif_set_default(&dm9000if);netif_set_up(&dm9000if);}void LWIP_Pkt_Handle(void){ethernetif_input(&dm9000if); //read data from net interface }/*** @brief LwIP periodic tasks* @param localtime the current LocalTime value* @retval None*/void LWIP_Periodic_Handle( uint32_t localtime){#if LWIP_TCP/* TCP periodic process every 250 ms */if (localtime - TCPTimer >= TCP_TMR_INTERVAL){TCPTimer = localtime;tcp_tmr();}#endif/* ARP periodic process every 5s */if ((localtime - ARPTimer) >= ARP_TMR_INTERVAL){ARPTimer = localtime;etharp_tmr();}#ifdef USE_DHCP/* Fine DHCP periodic process every 500ms */if (localtime - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS) {DHCPfineTimer = localtime;dhcp_fine_tmr();if ((DHCP_state != DHCP_ADDRESS_ASSIGNED) && (DHCP_state != DHCP_TIMEOUT) &&(DHCP_state != DHCP_LINK_DOWN)){/* toggle LED1 to indicate DHCP on-going process */// STM_EVAL_LEDT oggle(LED1);/* process DHCP state machine */LwIP_DHCP_Process_Handle();}}/* DHCP Coarse periodic process every 60s */if (localtime - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS){DHCPcoarseTimer = localtime;dhcp_coarse_tmr();}#endif}LWIP_Init():完成lwip内部初始化及网口的注册;LWIP_Pkt_Handle(void):周期性查询网口是否接收到数据,也可采用中断的方式;LWIP_Periodic_Handle( uint32_t localtime):lwip内核需要周期性调用时间处理函数进行轮询操作,TIMER2提供10ms中断;下载程序后,使用windows的命令行工具ping硬件平台的IP地址,即 ping 192.168.0.10。
uCOS-II移值过程实例讲解我将uCOS-II 移植到了EPONS 的C33209的平台上,接下来我就基于我移植好的代码讲解如何将uCOS-II从一种MCU移植到另一种MCU。
首先介绍uCOS-II的文件,如下表:ucos_ii.hos_cfg.hos_cpu.hos_core.cos_dbg_r.cos_flag.cos_mbox.cos_mem.cos_mutex.cos_q.cos_sem.cos_task.cos_time.cucos_ii.cos_cpu_c.cos_cpu_a.asm其中我们和硬件平台相关的文件的文件名被加粗了,也就是说若要将uCOS-II移植到新的平台上只要关心被以上四个文件就行了。
当然你也可以根据需要再添加你自己的和平台相关的文件,事实上我也是这么做的。
在我移植的例子中就添加了四个和平台相关的文件,文件如下表:crt0.cdrv_rtc.cvector.cext.scrt0.c是用来初始化系统的比如说MCU的一些特殊寄存器、设置外围的总线接口,等。
drv_rtc.c是用来初始化系统中的一个RTC的,这个RTC可以为内核提供必要的基于时间片调度的时基。
同时提供了对RTC开始和停止的操作函数。
在我的例子中RTC会每秒产生32次中断。
vector.c顾名思义,它是系统上电后为系统提供矢量入口表的文件,当然也包括中断向量表。
ext.s是为uc/OS-II 提供OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()函数的具体实现以及在用户程序的中断函数出入时要调用的状态保护和状态恢复函数OS_SA VEALL ()和OS_RESTOREALL ()。
前面两个函数的功能是:OS_ENTER_CRITICAL()屏蔽中断;OS_EXIT_CRITICAL()恢复原来的中断使能状态。
1. os_cpu_a.asm的说明要想顺利的移植首先要了解uCOS-II的一些基本概念。
刚完成了uip在ucos下的移植,总结一下,其实uip部分的移植非常的简单,我整整花了2个星期,是因为cs8900的接收部分驱动程序一直没有处理好,由于uip收发都使用uip_buf,cs8900就一直处于要丢弃接收包的状态,而cs8900丢弃包似乎到现在我都没有做好!最后终于自己加了个环形缓冲区,让cs8900基本把rx的所有包收入内存,不作丢弃处理,之后就万事顺利了,希望有那位高人看到这个文章后赐我一个好点的cs8900驱动。
言归正传,uip的结构非常简单,网卡只需要接收的包填入uip_buf,设置uip_len的长度为包长。
然后在uip的轮询循环中检查uip_len是否>0(调用network_device_read()函数),如果uip_len>0说明收到了数据,循环中就分类处理各种协议的请求,如果程序有数据需要发送调用uip_send发送数据,它同样是把数据送入uip_buf,设置uip_len,主循环会在本次循环中把数据送入网卡(调用network_device_send()函数)。
因此uip与网卡的接口就network_device_read(), network_device_send()两个函数,一个缓冲uip_buf,一个数据uip_len.当然,真如我前面说的,为了提高效率你可以在中间加入自己的转存缓冲,否则很多包会被丢弃,你可能会看到网络里满是被丢弃和重发的包。
你的主程序循环需要被uip循环调用,这是通过把你的程序的主循环函数声明为宏UIP_APPCALL来实现的,uip_process里调用这个宏,也就执行了你的程序循环,因此你的程序主体不应该写的太长太繁琐,以免一次循环的时间过长。
由于uip不处理重发,你的程序必须知道之前发送了什么数据,程序处在什么状态,如果发送uip_rexmit(),程序就需要把上次送出的数据再发送一遍,实现重发。
我后面的例子里发送了不同的数据,是为了测试重发的频率。
STM32F107上移植声明XX如是说——尊重他人劳动成果,也是一种美德……转载请注明出处闲话稍说这篇笔记旨在讲解LwIP在uCOS-II的上的过程,着重讲述移植的整个流程,不拘泥于细节。
从头至尾一步一步的完成移植工作。
本篇目标是能够ping通目标板,PC机与目标机建立一个初步的连接,至于说其他的应用服务,暂时不予讲述。
友情提示:要想顺利完成LwIP移植,你需具备:1、有一定的C语言基础和数据结构的知识;2、熟悉uCOS-II,了解信号量,消息邮箱等通信方式;3、知道LwIP是何物,能用来干什么硬件平台STM32硬件平台某开发板或你自己设计的硬件我使用是自己的STM32F107VC+DP83848板子IDE环境EWARM-IAR V5.4uCos-II V2.86LwIP V1.3.2目录Day Day Up笔记之uCOS-II+LwIP在STM32F107上移植 (1)声明 (1)闲话稍说 (1)目录 (1)直奔主题 (3)1、巧妇备米 (3)1.1、下载STM32F107_ETH_LwIP例程 (3)1.2、基础工程的建立 (3)2、文件结构 (3)4.2、对应的函数原型声明: (11)4.3、在BSP_Init()中调用BSP_EthernetInit() (11)4.3、相关宏定义: (12)4.4、包含stm32_eth.h头文件: (12)5、编写操作系统模拟层代码 (12)5.1、sys_arch.txt的中文翻译 (12)5.2、编写模拟层代码 (16)A、在\LwIP\port下新建sys_arch.c文件,添加到工程的LwIP\port下 (16)B、在\LwIP\port下新建arch文件夹,新建一个sys_arch的.h头文件,还有cc.h (16)C、在cc.h下定义常用数据类型,服务于模拟层接口函数和底层协议。
添加如下代码: (16)D、留意到#include "cpu.h"没?这可不是uC/OS-II源码中的那个cpu.h, (18)D、编写sys_arch.h (18)E、编写信号量操作函数 (19)F、编写邮箱操作函数 (22)G、初始化sys_arch层 (25)H、编写sys_arch_timeouts函数 (26)I、编写thread_t sys_thread_new函数 (27)K、添加sys_arch.c所需的头文件及变量定义等 (28)5.3、组织编写LwIP接口函数 (29)A、新建两个文件,分别命名为:LwIP.c、LwIP.h。
一步步移植uCOS-IIandLwIP(二)2、ethernetif.c函数接口编写ethernetif.c文件主要涉及5个接口函数的编写:static void 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);err_t ethernetif_input(struct netif *netif);err_t ethernetif_init(struct netif *netif);(1)low_level_init(struct netif *netif)从函数的声明可以看出,我们的主要工作是对struct netif定义的netif初始化,查阅LwIP源文件找到struct netif结构体的定义(删除条件编译后的基本定义):struct netif {/** pointer to next in linked list */struct netif *next;/** IP address configuration in network byte order */ip_addr_t ip_addr;ip_addr_t netmask;ip_addr_t gw;/** This function is called by the network device driver* to pass a packet up the TCP/IP stack. */netif_input_fn input;/** This function is called by the IP module when it wants* to send a packet on the interface. This function typically * first resolves the hardware address, then sends the packet. */netif_output_fn output;/** This function is called by the ARP module when it wants* to send a packet on the interface. This function outputs* the pbuf as-is on the link medium. */netif_linkoutput_fn linkoutput;/** This field can be set by the device driver and could point* to state information for the device. */void *state;/** maximum transfer unit (in bytes) */u16_t mtu;/** number of bytes used in hwaddr */u8_t hwaddr_len;/** link level hardware address of this interface */u8_t hwaddr[NETIF_MAX_HWADDR_LEN];/** flags (see NETIF_FLAG_ above) */u8_t flags;/** descriptive abbreviation */char name[2];/** number of this interface */u8_t num;};其中typedef err_t (*netif_input_fn)(struct pbuf *p,struct netif *inp);typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr);typedef err_t (*netif_linkoutput_fn)(struct netif *netif,struct pbuf *p);在这里我们将DM9000底层的MAC地址及初始化写入该函数内,其代码如下:static void low_level_init(struct netif *netif){/* set MAC hardware address length */netif->hwaddr_len = ETHARP_HWADDR_LEN;/* set MAC hardware address */netif->hwaddr[0] = MAC_ADDR0;netif->hwaddr[1] = MAC_ADDR1;netif->hwaddr[2] = MAC_ADDR2;netif->hwaddr[3] = MAC_ADDR3;netif->hwaddr[4] = MAC_ADDR4;netif->hwaddr[5] = MAC_ADDR5;/* maximum transfer unit */netif->mtu = 1500;/* 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. */DM9000_Init();}(2) struct pbuf * low_level_input(struct netif netif)该函数主要是用来实现将网卡中接收到的数据转换为LwIP定义的struct pbuf类型的数据包,便于协议栈数据处理。
基于μC/OS_II的以太网移植实例目录第一章以太网移植准备工作 (2)1.1 硬件平台 (2)1.2 软件平台 (2)第二章以太网移植步骤 (4)2.1 文件结构以及文件说明 (4)2.2以太网文件移植 (5)2.3MDK中文件的导入 (5)2.4 程序中需要编辑的代码 (8)第三章以太网任务创建以及初始化流程 (11)3.1 以太网任务创建 (11)3.2 以太网初始化流程 (12)3.3 以太网数据收发流程 (12)第四章测试以太网连接以及任务间通信 (14)4.1Ping命令测试 (14)4.2 网络调试助手测试 (14)第一章以太网移植准备工作以移动基站的电表管理系统为背景,探讨基于μC/OS嵌入式系统的以太网移植方法。
移动基站电表管理系统终端的基本功能就是上位机通过以太网发送命令或数据给终端,终端收到后,再通过485通信对电表执行相应的动作,最后,终端把得到的信息处理后再次通过以太网上传回来。
这里重点是把以太网协议栈移植到程序中来,以创建一个以太网通信任务。
1.1 硬件平台硬件平台是主芯片为STM32F107VC的金牛开发板,开发板上已集成有以太网功能模块和RS485功能模块。
只是在开发板上RS-485与RS-232接口共用了微处理器的接收串口,需要设置JP4,且配置为1-2,如表1.1所示。
金牛开发板支持两种以太网接口模式,一种是MII接口模式,另一种是RMII接口模式。
这里选用MII接口模式,根据表1.2对跳线JP2、JP6、JP7、JP8、JP12进行相应的配置。
1.2 软件平台软件平台为RVMDK软件。
RVMDK是由ARM编译器RVCT与Keil的工程管理、调试仿真工具集成,RVMDK是业界最好的Cortex-M3开发工具之一,它拥有流畅的用户界面与强大的仿真功能,是一款非常强大的ARM微控制器开发工具。
移植前需要熟悉RVMDK软件的使用。
移植过程中需要用到如图1.1和图1.2所示文件,一个是基于μC/OS_II的移动基站电表管理系统终端程序,另一个是基于μC/OS_II系统以太网移植文件。
图1.1 电表系统终端程序文件夹图1.2 需要移植的以太网文件夹第二章 以太网移植步骤以太网的移植方法需要从三方面着手,首先分清电表管理系统终端文件和以太网文件层次结构;其次掌握以太网文件的移植过程以及在MDK 软件中如何把工程文件导入过程;最后明确以太网运行尚需添加的相应代码。
2.1 文件结构以及文件说明基于μC/OS_II 的移动基站电表管理系统终端程序主文件夹如图2.1所示,文件夹中包含了整个工程项目文件。
主文件夹中包含了如图2.2所示的四个文件夹。
Lis 和Obj 文件夹中是MDK 软件编译时生成的临时文件;MDK 文件夹中主要包含MDK 工程的启动文件;Source 文件夹包含了整个工程的源文件。
Source 文件夹中包含了如图2.3所示的四个文件夹。
App 文件夹中为用户的应用文件;CMSIS 文件夹主要包含STM32芯片内核启动文件;STM32F10x_StdPeriph_Driver 文件夹包含了STM32内部及外围器件的驱动文件;μC/OS_II 文件夹是μC/OS_II 系统源文件夹,包含了μC/OS_II 系统运行所需要的全部文件。
基于μC/OS_II 系统的以太网移植文件夹如图2.4所示,文件夹中包含了整个以太网协议栈文件和以太网驱动文件。
以太网主文件中包含了如图2.5所示的四个文件夹。
efsl 文件夹包含的文件功能是对输出文件的操作管理;STM32_ETH_Driver 文件夹包含STM32芯片中以太网的驱动文件;APP 文件夹中是用户对以太网初始化配置所写的文件;lwip-1.3.1文件夹包含整个以太网协议栈文件。
lwip-1.3.1文件夹包含了如图2.6所示的三个文件夹。
Doc 文件夹是作者写的一些说明性文本文件;port 文件夹包含以太网协议栈的接口文件;src 文件夹包含了以太网协议栈源文件。
图2.2 主文件夹中包含的文件夹 图2.3 Source 文件夹中包含的文件图2.4 以太网移植主文件夹 图2.5 主文件夹中包含的文件夹 图2.1 工程项目主文件夹src 文件夹中包含了如图2.7所示的四个源文件。
Api 文件夹中包含了以太网协议栈的应用接口文件;core 文件夹中包含了以太网协议栈最核心的文件,即以太网的内核文件;include 文件夹中是以太网协议栈的头文件。
2.2 以太网文件移植首先,将以太网文件夹中的efsl 、lwip-1.3.1、STM32_ETH_Driver 三个文件夹复制到Source 文件夹中。
复制过程如图2.8所示,复制后Source 文件夹中的文件如图2.9所示。
其次,将以太网文件夹中APP 文件夹里的四个文件复制到Source 文件夹中APP 文件夹里。
复制过程如图2.10所示,复制后APP 文件夹中的文件如图2.11所示。
2.3 MDK 中文件的导入首先,打开MDK 工程,进入文件添加界面,新建LWIP/Port 、LWIP/Source 、efsl 、图2.8 需要移植的文件夹 图2.9 Source 文件夹中的文件夹 图2.6 lwip-1.3.1文件夹中所包含的文件夹 图2.7 Src 文件夹中包含的文件夹 图2.10 以太网中APP 文件夹需要移植的文件 图2.11 Source 中APP 文件夹里的文件 图2.12 MDK 文件夹添加STM32_ETH_Driver 这四个文件夹,新建文件夹结果如图2.12所示。
其次,将刚刚移植过来的文件分类添加到各个工程文件夹中。
LWIP/Port 文件夹中添加文件结果如图2.13所示。
LWIP/Source 文件夹中依次添加如图2.14、图2.15、图2.16、图2.17所示文件。
ipv6、snmp 文件夹中的文件没用到,可以不添加这两个文件夹里面的文件,但如果需要可以选择添加。
LWIP/Source 文件夹中添加的文件结果如图2.18所示。
图2.14 Core 文件夹中需要添加的文件 图2.15 Ipv4文件夹中需要添加的文件 图2.16 netif 文件夹中需要添加的文件 图2.17 api 文件夹中需要添加的文件 图2.18 LWIP/Source 文件夹中添加后的文件 图2.13 LWIP/Port 文件夹需添加的文件Efsl文件夹中依次添加如图2.19、图2.20所示文件。
图2.19 efsl文件夹中需要调节的文件图2.20 efsl文件夹中需要调节的文件Efsl文件夹中添加的文件结果如图2.21所示。
图2.21 efsl文件中添加后的文件图2.22 STM32_ETH_Driver中需要添加的文件STM32_ETH_Driver文件夹中添加如图2.22所示文件,STM32_ETH_Driver文件夹中添加的文件结果如图2.23所示。
图2.23 STM32_ETH_Driver中添加后的文件图2.24 App文件夹中需要添加的文件App文件夹中添加如图2.24所示两个文件,APP文件夹中添加的文件结果如图2.25所示。
以太网移植文件全部添加到MDK工程文件夹中后,其MDK工程文件夹结构如图2.26所示,再点击全部保存后,以太网移植文件就全部导入到MDK工程文件夹中来了。
图2.25 App文件夹中添加后的文件图2.26 MDK工程文件结构以太网移植文件全部导入到MDK工程文件夹之后,最后,还需把以太网移植文件所用到的头文件的路径添加到工程中来。
其添加的头文件路径过程如图2.27所示。
图2.27 MDK中包含的头文件到这一步已经完成了以太网文件的导入,下面只需编译一下,检查文件的导入是否正常。
其编译结果如图2.28所示,零错误和零警告提示,表明编译通过。
但需值得注意的是要把Spi.c文件添加到STM32库里去,因为efsl对文件操作时,需要用到SPI中的函数。
图2.28 MDK编译结果2.4 程序中需要编辑的代码第一处:以太网接收数据是通过中断方式接收的,需要编写以太网中断函数。
其中断函数编写如图2.29所示。
中断函数中调用了如图2.30所示头文件中的函数,在中断文件中需要把下面两个头文件包含进来。
第二处:主函数中需对以太网硬件进行初始化,初始化位置如图2.31所示。
初始化函数需要自己写,由于程序中已经写好了,直接调用就行了,但值得注意的是需把如图2.32所示的头文件包含进来。
第三处:创建其它分任务之前,需要初始化LWIP 通信协议栈,初始化位置如图2.33所示。
LWIP 初始化函数需要自己写,由于程序中已经写好了,直接调用就行了,但值得注意的是需把如图2.34所示的头文件包含进来。
第四处:程序中用到了printf 函数(对字符串进行格式化输出),需在串口文件中添图2.29 以太网中断函数 图2.30 中断文件中需要包含的头文件 图2.31 以太网硬件初始化调用 图2.32 主文件中需要包含的头文件 图2.34 C 文件中需要包含的头文件 图2.33 LWIP 协议栈初始化调用加如图2.35所示一个函数。
此函数用到了C文件标准库,需要把标准库头文件包含进来并在工程设置选项中把“Use MicroLIB”勾上,其操作过程如图2.36、图2.37所示。
图2.35 fputc函数图2.36 C文件中需要包含的头文件图2.37 MDK选项中的设置到这一步,程序中需要修改的部分基本上完成。
最后,把工程编译一下,检查一下程序修改是否有错。
其编译结果如图2.38所示,零错误、零警告提示,表明程序修改无误。
图2.38 MDK编译结果第三章 以太网任务创建以及初始化流程在完成以太网移植且并对以太网硬件和协议栈进行了初始化之后,还不能进行以太网通信。
在μC/OS 系统中进行以太网的通信还必须先创建一个以太网任务并且把任务添加到μC/OS 系统中的任务列表中来。
以太网协议栈比较复杂,一下比较难理解其工作过程,这里也只是对以太网的通讯过程进行简单的描述,以求快速入门。
3.1 以太网任务创建以太网任务的创建分三步完成。
第一步:编写一个以太网任务函数。
函数功能能够创建一个新的TCP 连接;绑定IP 地址和端口号;监听TCP 的连接;等待TCP 连接。
如果有客服端请求连接则建立TCP 连接,否则继续等待TCP 连接。
编写的以太网任务函数如图3.1所示。
第二步:定义以太网任务堆栈空间大小和任务优先级,定义结果如图3.2、图3.3所示。
第三步:将创建的以太网任务添加到μC/OS 操作系统中的的任务列表中去,其添加结果如图3.4所示。
图3.2 以太网任务空间大小定义 图3.3 以太网任务优先级定义图3.1 以太网任务函数图3.4 以太网任务的添加任务添加完成之后,只需编译一下,检查一下以太网任务创建是否正确。