ucos操作系统在ARM上的移植.docx

上传人:b****8 文档编号:29725231 上传时间:2023-07-26 格式:DOCX 页数:25 大小:223.44KB
下载 相关 举报
ucos操作系统在ARM上的移植.docx_第1页
第1页 / 共25页
ucos操作系统在ARM上的移植.docx_第2页
第2页 / 共25页
ucos操作系统在ARM上的移植.docx_第3页
第3页 / 共25页
ucos操作系统在ARM上的移植.docx_第4页
第4页 / 共25页
ucos操作系统在ARM上的移植.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

ucos操作系统在ARM上的移植.docx

《ucos操作系统在ARM上的移植.docx》由会员分享,可在线阅读,更多相关《ucos操作系统在ARM上的移植.docx(25页珍藏版)》请在冰豆网上搜索。

ucos操作系统在ARM上的移植.docx

ucos操作系统在ARM上的移植

UC/OS-II嵌入式系统在ARM上的移植

UC/OS-II操作系统是一款完全公开的源代码,它非常精简,整个操作系统的代码只有几千行,是专门针对于嵌入式开发而产生的一款代码。

它有几个特点,分别是可移植性(Portable)、可固化(ROMable)、可裁剪(Scalable)、多任务、可确定性、任务栈、系统服务、中断管理、稳定性可靠性。

UC/OS-II主要就是一个内核,由ANSIC语言编写而成。

负责任务管理和任务调度,没有文件系统和界面系统。

它的代码是公开的,系统的实时性强、移植性好、可多任务。

UC/OS-II作为基于优先级的抢占式多任务的实时操作系统,包含了实时内核、任务管理、时间管理、任务间通信同步和内存管理的功能。

它使得任务的独立性,不相互干涉,非常的准时和高效,且易于设计和扩展。

UO/OS-II共有16个内核文件,11个与CPU类型无关,就是说可以直接使用不需要修改。

还有3个内核文件与CPU有关系,要根据需要作出相应的改动。

剩下的两个内核文件和具体的应用有关。

如图所示UC/OS-II的16个内核文件的层次。

 

μC/OS-II内核文件

多任务操作的核心是系统调度器,利用TCB来管理任务调度功能。

它的主要功能是保存任务的当前态、优先级、等待事件、代码起始地址、初始堆栈指针等。

程序的设计关键就是确定划分多任务的问题,以及任务优先级和任务通信。

优先级的意思是每个任务都是无限循环的,有运行态度、就绪态、休眠态、挂起态和中断五种状态。

当有高一级优先级的任务就绪后,低优先级立即停止运行,转为挂起态或就绪态。

这就是可剥夺型的内核。

当中断一个高优先级任务,中断时挂起,中断结束后任务继续运行,并立即剥夺低优先级的任务。

对于这种可剥夺型内核,CPU的使用时可以确定的,可优化任务级响应。

在很多单片机或ARM板上很容易就可以移植UC/OS-II。

当然本次设计使用的TQ2440,也可以完美的移植它。

移植程序在网上都可以找得到,所以设计中就不做解释了。

本次设计实现的是串口协议和网口协议组合成的一个数据网关。

其主要的流程图如下所示:

 

 

如图所示可以很清楚的看出内核操作系统对于整个系统的控制过程,下面我们要介绍一下UC/OS-II里面的一些程序。

 

用户应用程序任务定义代码如下:

voidMyTask(void*pdata)//开始定义用户任务

{

for(;;)

{

...

}

}

 

voidmain()

{

TargetInit();//完成初始化目标开发板。

OSInit();//完成初始化UCOS-II。

OSTimeSet(0);//完成初始化系统时基。

OSTaskCreate(MainTask,(void*)0,&MainTaskStk[MainTaskStkLengh-1],MainTaskPrio);//开始创建系统初始任务。

OSStart();//整个任务开始。

return0;

}

voidTask0(void*pdata)//主任务建立:

taks0和task1

{

#ifOS_CRITICAL_METHOD==3/*分配CPU状态寄存器*/

OS_CPU_SRcpu_sr;

#endif

OS_ENTER_CRITICAL();

Timer0Init();//初始化报时信号

ISRInit();//初始化中断优先级

OS_EXIT_CRITICAL();

OSPrintfInit();//用户任务给串口

DM9000_init();

OSStatInit();

while

(1)

{

OSPrintf("\nEnterMainTask\n");

//测试Dm9000

//打印DM9000寄存器

OSTimeDly(OS_TICKS_PER_SEC);

}

}

需要注意的是,μC/OS-II的应用程序要使用空闲任务OSTaskldle(),而它是不可删除的系统文件。

下面开始对应用程序进行移植了。

本次设计是在ARM开发板上实现串口、网口数据转换的的功能。

在TQ2440上有串口和网口。

通信过程简单的描述就是PC串口发送数据给ARM板,ARM接到串口数据后,从网口再传给PC。

相反也是同样的道理,由网口发送数据,有ARM控制由串口发回数据。

首先是TCP/IP协议的移植。

在编写移植程序前,有必要解释一下基本的协议栈作用和意义。

`就常见的网络通信方式一般来说有两种:

1、UART-RS232,此时只需要pc上有串口调试助手即可。

2、TCP/IP,这时候和普通pc与pc通信一样可以用socket套接字编程也可用别人写好的软件侦听。

而现在我们要实现的是串口控制单片机与PC机的通信,在这里用PC来代替以太网。

接下来还要介绍一下以太网接口。

以太网技术如今已经相当的成熟了,其相应的网络产品价格低廉、技术完善。

而数据总线如今越来越难以满足人们日益提升的需要,这时以太网控制网络技术得到了快速的发展,并形成了现场总线的新标准。

加上国内大部分局域网是以太网,给予以太网实现现场总线有了雄厚的物质基础。

以太网的接口就是以太网同信的基础,是通信介质通信的中间处理部件,实现报文的发送与接收功能,位于TCP/IP协议栈的数据链路层。

每一个以太网接口(有时候也叫网卡),在连通后就可以随时的发送和接收网络上的数据,执行EEE802.3标准。

TCP/IP对应的ISO结构如图所示:

 

TCP/IP协议栈

而单片机与计算机的TCP/IP协议的实现也有很大的不同,原本在计算机里编写的程序可以不考虑代码的大小和效率,但在嵌入式开发板上都要考虑到这些问题。

在操作系统、内存分配、指针、参数传递、协议支持以及硬件接口的设计方面有些不同。

首先就是操系统,嵌入式的特点之一就是简洁高效,有很强的专业功能。

相对而言计算机上的操作系统的侧重点就是兼容性,所以资源要求全面支持,所以很复杂。

其次就是内存上的分配,计算机像windows系统它的内存分配是动态的,而在单片机上却不能同样如此,应为RAM的容量所限,所以其中存放以太网的数据包是固态的。

由于ARM相对于单片机的能力而言有了很大的提升,所以可以突破单片机的一些约束。

如下图所示,TCP/IP协议栈中的内容,从上往下分别对应应用层、传输层、网络层和网络接口层。

 

 

TCP/IP协议栈

在本次设计中采用Lwip协议栈来实现ARM与以太网的连接。

完成移植后,需要介绍一下以太网的初始化过程和数据收发过程。

LWIP的初始化要在UC/OS-II之后,在其它任务创建之前。

因为LWIP要对以太网协议栈初始化以及新线程的建立,LWIP初始化如图所示。

LWIP程序可以实现很多功能,在本设计中没有实现他的全部功能。

而简单的PING通需要DM9000来实现。

以太网的接收是通过中断方式,如果有数据进入以太网中断函数。

其主要任务就是读取和分析数据包。

如果数据有效则在Tcpip_input()函数把数据发送出去,并在LWIP初始化创建的线程中就可以得到此消息。

然后通过ip_input()函数进入传输层后,再把把数据传到应用层。

具体流程如下图所示:

 

为什么要选择DM9000网络驱动器呢?

如今嵌入式中大量使用10/100M的以太网卡,实际上并不实用。

因为它不能既满足快速传输速率又满足成本控制。

这时,DM9000作为一种综合低成本的单一快速以太网控制芯片就有了很高的实用价值。

它具有通用的接口,设计简单,可满足不同系统的软件驱动。

DM9000程序的体系结构可以分为网络协议接口、设备接口层、功能层及媒介层。

下面是DM9000的部分驱动程序的编写。

定义DM9000:

#include"config.h"

#defineDM_ADD(*((volatileunsignedshort*)0x20000300))

#defineDM_CMD(*((volatileunsignedshort*)0x20000304))

#definePrintfOSPrintf

 

uint8Buffer[1000];//定义了一个1000字节的接收发送缓冲区

uint8host_mac_addr[6]={0xff,0xff,0xff,0xff,0xff,0xff};//主机的MAC地址

uint8mac_addr[6]={0x0a,0x1b,0x2c,0x3d,0x4e,0x5f};//开发板{0a,1b,2c,3d,4e,5f},这实际上是一个软地址,在本设计中不需要特指。

 

uint8ip_addr[4]={192,168,1,6};

uint8host_ip_addr[4]={192,168,1,100};

uint16packet_len;//接收、发送数据包的长度,单位为字节

 

uint8arpsendbuf[60]={

0xff,0xff,0xff,0xff,0xff,0xff,//以太网目标地址

0x0a,0x1b,0x2c,0x3d,0x4e,0x5f,//以太网源地址

0x08,0x06,//帧类型:

ARP帧

0x00,0x01,//硬件类型:

以太网

0x08,0x00,//协议类型:

IP协议

0x06,//硬件地址长度:

6字节

0x04,//协议地址长度:

4字节

0x00,0x00,//操作:

ARP请求

0x0a,0x1b,0x2c,0x3d,0x4e,0x5f,//发送端硬件地址

192,168,1,6,//发送端协议地址

0x00,0x00,0x00,0x00,0x00,0x00,//接收端硬件地址

192,168,1,100//接收端协议地址

};

 

#defineDM9KS_ID0x90000A46

#defineDM9KS_VID_L0x28

#defineDM9KS_VID_H0x29

#defineDM9KS_PID_L0x2A

#defineDM9KS_PID_H0x2B

#defineDM9KS_BASE_ADDR_ETH00x20000000//nGCS4

#defineDM9KS_Index(*((volatileunsignedshort*)(DM9KS_BASE_ADDR_ETH0+0x300)))

#defineDM9KS_Data(*((volatileunsignedshort*)(DM9KS_BASE_ADDR_ETH0+0x304)))

 

接下是来对DM9000进行初始化的程序部分:

voidDM9000_init(void)

{

uint32i;

//Test_DM9000AE();

IOSetInit();//设置中断EINIT7

//初始化设置步骤:

1

dm9000_reg_write(DM9000_GPCR,0x01);//设置GPCR(1EH)bit[0]=1,使DM9000的GPIO3为输出。

dm9000_reg_write(DM9000_GPR,0x00);//GPRbit[0]=0使DM9000的GPIO3输出为低以激活内部PHY。

udelay(5000);//延时2ms以上等待PHY上电。

//初始化设置步骤:

2

dm9000_reg_write(DM9000_NCR,0x03);//软件复位

udelay(3000);//延时20us以上等待软件复位完成

dm9000_reg_write(DM9000_NCR,0x00);//复位完成,设置正常工作模式。

dm9000_reg_write(DM9000_NCR,0x03);//第二次软件复位,为了确保软件复位完全成功。

此步骤是必要的。

udelay(3000);

dm9000_reg_write(DM9000_NCR,0x00);//以上完成了DM9000的复位操作

//初始化设置步骤:

3

dm9000_reg_write(DM9000_NSR,0x2c);//清除各种状态标志位

dm9000_reg_write(DM9000_ISR,0xbf);//清除所有中断标志位

//以上清除标志位

//初始化设置步骤:

4

dm9000_reg_write(DM9000_RCR,0x39);//接收控制

dm9000_reg_write(DM9000_TCR,0x00);//发送控制

dm9000_reg_write(DM9000_BPTR,0x3f);

dm9000_reg_write(DM9000_FCTR,0x38);//接收FIFO门限3k8k

dm9000_reg_write(DM9000_FCR,0xff);

dm9000_reg_write(DM9000_SMCR,0x00);

//以上是功能控制

//初始化设置步骤:

5

for(i=0;i<6;i++)

dm9000_reg_write(DM9000_PAR+i,mac_addr[i]);//mac_addr[]自己定义一下吧,6个字节的MAC地址

//以上存储MAC地址(网卡物理地址)到芯片中去,这里没有用EEPROM,所以需要自己写进去

//初始化设置步骤:

6

dm9000_reg_write(DM9000_NSR,0x2c);//清除各种状态标志位

dm9000_reg_write(DM9000_ISR,0x3f);//清除所有中断标志位

//初始化设置步骤:

7

dm9000_reg_write(DM9000_IMR,0x81);//中断使能

//中断使能(或者说中断屏蔽),即开启我们想要的中断,关闭不想要的,这里只开启的一个接收中断

udelay(10000);//延时2ms以上等待PHY上电。

//PrintfDM9000Reg();

Printf("DM9000初始化完毕\r\n");

}

接下来是数据包的接收和发送流程图和程序:

DM9000收到封包,置于接收内存的0C00h-3FFFh。

若超过最大值时,会自动把位置移到0C0h0位置。

每个封包有4字节是存放一些与封包相关的信息。

若内存的第1字节值为“01h”,表明封包已经在内存中了。

在读取其它字节前,先要确定第2个字节,则是这个封包的相关信息。

如下图是DM9000接收封包流程:

 

DM9000接收封包流程

如图可以看出,DM9000从网络中接到一个数据包后,在数据包前面加了4个字节,分别为“01H”、“status”、“LENL”(包长的低8位)、“LENH”(包长的高8位)。

这4个字节来确定数据包的状态,“01H”表示接下来的是有效数据包,“00H”则表示没有数据包,其它值则表示没有正确初始化,并重新初始化。

若数据包长度小于60字节,则DM9000芯片会自动为不足的字节补上0。

同时,在接收到的包后程序还会自动添加4个CRC校验字节。

于是,接收到的数据包至少为64字节。

也根据TCP/IP协议从首部字节中找出有效字节数,这就是TCP/IP协议栈的功能了。

具体程序如下:

//接受数据包

//参数:

datas为接收到是数据存储位置(以字节为单位)

//返回值:

接收成功返回数据包类型,不成功返回0

uint32receivepacket(uint8*datas)

{

uint16i,tmp,status,len;

uint8ready;

uint32st;

ready=0;//希望读取到"01H"

status=0;//数据包状态

len=0;//数据包长度

if(dm9000_reg_read(DM9000_ISR)&0x01)

{

dm9000_reg_write(DM9000_ISR,0x01);//清除接收中断标志位

}

ready=dm9000_reg_read16(DM9000_MRCMDX);//第一次读取,一般读取到的是00H

Printf("ready1=%x\r\n",ready);

if((ready&0x0ff)!

=0x01)

{

ready=dm9000_reg_read16(DM9000_MRCMDX);//第二次读取,总能获取到数据

Printf("ready2=%x\r\n",ready);

if((ready&0x01)!

=0x01)

{

if((ready&0x01)!

=0x00)//若第二次读取到的不是01H或00H,则表示没有初始化成功

{

dm9000_reg_write(DM9000_IMR,0x80);//屏蔽网卡中断

DM9000_init();//重新初始化

dm9000_reg_write(DM9000_IMR,0x81);//打开网卡中断

}

return0;

}

}

//status=dm9000_reg_read16(DM9000_MRCMD);

DM_ADD=DM9000_MRCMD;

st=DM_CMD;

status=st;

len=64;//DM_CMD;

Printf("st=%xstatus=%xlen=%x\r\n",st,status,len);

if((len<1522))//!

(status&0xbf)&&

{

for(i=0;i

{

udelay(20);

tmp=DM_CMD;

datas[i]=tmp&0x0ff;

datas[i+1]=(tmp>>8)&0x0ff;

}

}

elsereturn0;

if(len>1000)return0;

//if((HON(ETHBUF->type)!

=ETHTYPE_ARP)&&(HON(ETHBUF->type)!

=ETHTYPE_IP))

//return0;

packet_len=len;

returnlen;

}

 

在发送封包之前,需要将其中的数据存放在DM9000传送内存0000h~0BFFh。

当超过0BFFh时,位置自动回到0000h的位置。

封包的数据存放在MWCND中,芯片会吧数据自动的存入传送内存的。

其封包的大小存放在低字节的TXPLL和高字节的TXPLH中。

然后把TCRbit()设置成1,开始传送封包。

完成后,将是否完成的信息存入TSRI、TSRII中。

其顺序为TSRI->TERII->TSRI循环。

故需按照NSRbit-3来判断完成进度。

 

以下是发送数据包的流程图和程序:

 

DM9000发送封包流程

以上是发送数据包,过程很简单。

//发送数据包

//参数:

datas为要发送的数据缓冲区(以字节为单位),length为要发送的数据长度(两个字节)。

voidDM9000_sendPcket(uint8*datas,uint32length)

{

uint32len,i;

uint8tmp;

Printf("发送数据\r\n");

dm9000_reg_write(DM9000_IMR,0x80);//先禁止网卡中断,防止在发送数据时被中断干扰

len=length;//把发送长度写入

/*这两句是将要发送数据的长度告诉DM9000的寄存器*/

dm9000_reg_write(DM9000_TXPLH,(len>>8)&0x0ff);

dm9000_reg_write(DM9000_TXPLL,len&0x0ff);

DM_ADD=DM9000_MWCMD;

//将要发送的数据写到DM9000的内部SRAM中的写FIFO中

for(i=0;i

{

udelay

(2);

DM_CMD=datas[i]|(datas[i+1]<<8);

}

dm9000_reg_write(DM9000_TCR,0x01);//发送数据到以太网上

while

(1)//等待数据发送完成

{

uint8data;

data=dm9000_reg_read(DM9000_TCR);//DM9000_NSR

if((data&0x01)==0x00)break;

}

tmp=dm9000_reg_read(DM9000_NSR);

if((tmp&0x01)==0x04)

{

if((dm9000_reg_read(DM9000_TSR1)&0xfc)==0x00)

Printf("TSR1成功\r\n");

else

Printf("TSR1失败r\n");

}

else

{

if((dm9000_reg_read(DM9000_TSR2)&0xfc)==0x00)

Printf("TSR2成功\r\n");

else

Printf("TSR2失败r\n");

}

dm9000_reg_write(DM9000_NSR,0x2c);//清除状态寄存器,由于发送数据没有设置中断,因此不必处理中断标志位

dm9000_reg_write(DM9000_IMR,0x81);//DM9000网卡的接收中断使能

Printf("发送数据完成\r\n");

}

 

TQ2440串口初始化程序:

staticintwhichUart=0;

voidUart_Init(intpclk,intbaud)

{

inti;

if(pclk==0)

pclk=PCLK;

rUFCON0=0x0;//UARTchannel0FIFOcontrolregister,FIFOdisable

rUFCON1=0x0;//UARTchannel1FIFOcontrolregister,FIFOdisable

rUFCON2=0x0;//UARTchannel2FIFOcontrolregister,FIFOdisable

rUMCON0=0x0;//UARTchaneel0MODEMc

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1