嵌入式电网监测仪的研制方案
扫描二维码
随时随地手机看文章
: μC/OS-II是一种适用于嵌入式系统的源码开放的占先式实时多任务操作系统。本文讨论了基于μC/OS-II嵌入式系统的网络通信实现,包括μC/OS-II实时操作系统、LwIP协议栈的移植和网络设备驱动程序的建立以及系统任务的调度。
嵌入式电网监测仪成功运用了S3C44BOX微处理器和RTL8019AS以太网控制器组成的硬件平台,并结合了嵌入式实时操作系统μC/OS-II和 LwIP协议栈。在实时操作系统和LwIP协议栈的驱动下,由微处理器实现数据采集、数据处理和采集模块的控制功能。嵌入式电网监测仪具有极高的性能价格比,可以直接取代常规电力变送器、测量指示仪表、电能计量仪表以及相关的辅助单元,具有安装方便、接线简单、维护方便,工程量小、现场可编程设置输入参数的特点。
1 μC/OS-II的移植
μC/OS-II 是一种基于优先级的抢占式多任务实时操作系统,包含了实时内核、任务管理、时间管理、任务间通信同步(信号量,邮箱,消息 队列)和内存管理等功能。它可以使各个任务独立工作,互不干涉,很容易实现准时而且无误执行,使实时应用程序的设计和扩展变得容易,使应用程序的设计过程大为减化。多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU 时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。μC/OS-II可以管理多达64个任务。由于它的作者占用和保留了8个任务,所以留给用户应用程序最多可有56个任务。赋予各个任务的优先级必须是不相同的。这意味着μC/OS-II不支持时间片轮转调度法(round-robin scheduli ng)。
μC /OS-II是一个完整的、可移植、可固化、可裁剪的占先式实时多任务内核。μC/OS-II绝大部分的代码是用ANSI的C语言编写的,包含一小部分汇编代码,使之可供不同架构的微处理器使用。至今,从8位到64位,μC/OS-II已在超过40种不同架构上的微处理器上运行。μC/OS-II已经在世界范围内得到广泛应用,包括很多领域, 如手机、路由器、集线器、不间断电源、飞行器、医疗设备及工业控制上。它适合小型控制系统,具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点,最小内核可编译至2KB。
移植μC/OS-II时,系统可以运行于用户模式(User Mode),也可以运行于管理模式(Supervision Mode),大部分的移植都运行于管理模式。如果需要,也可以设置为在用户模式运行,但进行处理模式的切换时必须由异常处理完成,这样操作要复杂一些。
下面介绍在本系统中进行μC/OS-II移植时需要解决的主要问题。
(1)OS_CPU.H修改
此修改主要是改变与处理器、编译器有关的数据类型和宏定义,S3C44BOX为32位微处理器,使用armcc编译器。Char类型长度8位,Short类型长度16位,Int和Long类型长度32位。ARM 寄存器都是32 位的,所以将堆栈数据类型OS_STK 声明为32位。所有的堆栈都必须使用OS_STK 声明。
将开关中断的宏OS_ENTER_CRITICAL和OS_EXIT_CRITICAL定义为OS_CPU_ASM.S中的函数ARMDisableINT和ARMEnableINT,用于屏蔽中断和开中断。
(2)OSTaskStkInit( )
OSTaskCreate( )和OSTaskCreateExt( )通过调用OSTask-StkInit( )来初始化任务的堆栈结构,因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。
μC/OS-II为每个任务建立堆栈,用于保存处理器的寄存器。其结构体定义OS_STK[17],任务堆栈空间由高至低依次保存着处理器工作模式(SVC模式)的pc、lr、r12、r11、r10……r1、r0、CPSR、SPSR。
μC/OS-II在OS_CPU_C.C中由任务堆栈初始化函数OSTaskStkInit,需要将任务栈内的CPSR 和SPSR 设为SVC 模式。
(3)OSCtxsw( )
用于任务级的上下文切换。当任务因为被阻塞而主动请求CPU调度时OSCtxsw( )被执行,此时的任务切换在非异常模式下进行。它的工作是先将当前任务的CPU现场保存到该任务堆栈中,然后获得最高优先级任务的堆栈指针,从该堆栈中恢复此任务的CPU现场,使之继续执行。这样就完成了一次任务切换。
(4)OSIntCtxSW( )
用于中断级的任务切换。若在时钟中断ISR中发现有高优先级任务等待的时钟信号到来,则在中断退出后并不返回被中断任务,而是直接调度就绪的高优先级任务执行,从而能够尽快地使高优先级的任务得到响应,保证系统的实时性能。
OSIntCtxSW( )完成的工作为:向S3C44BOX的INTCON寄存器写入值,将IRQ栈内保存的中断CPU寄存器的值写入被中断的任务栈,将就绪的高优先级的任务栈内容写入对应的CPU 寄存器。
(5)OSTickISR( )
时钟中断处理函数。其主要任务是负责处理时钟中断,调用系统实现OSTimeTick( )函数,如果有等待时钟信号的高优先级任务,则需要在中断级别上调度其执行。
OSTickISR( )是标准的中断服务程序,函数的入口写入ISR的中断向量表。其实现的过程是:向S3C44BOX 的0x18写入任意的数(0x18在ARM中是IRQ的中断入口地址),读取S3C44BOX的状态寄存器清除中断,保护CPU寄存器入栈,调用OSIntEnter( )对中断嵌套标志加1。调用中断服务程序OSTimeTick( ),调用OSIntExit( )判断是否需要任务切换,如果需要则调用OSIntCtxSW( )来进行任务切换。若没有调用任务切换函数OSCtxsw( ),则说明CPU寄存器入栈的工作已经在进入中断时完成。
2 LwIP协议栈的移植
lwip是瑞典计算机科学院的一个开源的TCP/IP协议栈实现。lwIP是TCP/IP协议栈的一个实现。lwIP协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让lwIP适用于资源有限的小型平台例如嵌入式系统。为了简化处理过程和内存要求,lwIP对API进行了裁减,可以不需要复制一些数据。LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,一般它只需要几百字节的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。
下面介绍在μC/OS-II操作平台上移植LwIP的主要步骤。
2.1 与CPU或编译器相关的include文件
/ src/arch/include/arch目录下cc.h、cpu.h、perf.h中,有一些与CPU或编译器相关的定义,如数据长度、字的高低位顺序等,这些参数应该与实现μC/OS-II时定义的数据长度等参数是一致的。
2.2 改写操作系统相关的函数
sys_arch.c中具有与操作系统相关的一些结构和函数,主要可以分为三个部分。
(1)进程间通信的函数
函数sys_sem_new( )、sys_sem_free( )、sys_sem_signal( )、sys_arch_sem_wait( )、sys_mbox_new( )、sys_mbox_free( )、sys_mbox_post( )、sys_arch_mbox_fetch( )的功能在μC/OS-II中基本都有,但要注意这里的mbox要用μC/OS-II中的消息队列实现。但是,μC/OS-II没有对消息队列中的消息进行管理,因此不能直接使用,必须在μC/OS-II的基础上重新实现。而有一些mbox只可能有一个消息,可以用邮箱实现。另外函数sys_sem_free( )和sys_mbox_free( )不易实现,可以采用从空闲队列中动态分配和回收的方法。
(2)sys_arch_timeout( )
LwIP中每个与外界网络连接的线程都有自己的timeout属性,即等待超时时间。这个属性表现为:每个线程都对应一个sys_timeout结构体队列,包括这个线程的timeout时间长度以及超时后应调用的timeout函数,该函数可以做一些释放连接和回收资源的工作。如果一个线程对应的sys_timeout为空(NULL),说明该线程会对连接做永久的等待。
(3)sys_thread_new( )
LwIP可以是单线程运行,即只有一个tcpip线程(tcpip_thread),负责处理所有的tcp/ucp连接,各种网络程序都通过tcpip线程与网络交互。但LwIP也可以多线程运行,以提高效率,降低编程复杂度。
创建新线程的函数为:
void sys_thread_new(void(*thread)(void*arg),void*arg)
在μC/OS-II中,没有线程(thread)的概念,只有任务(task)。它已经提供了创建新任务的系统API调用OSTask-Create。因此,只要把OSTaskCreate封装一下,就可以实现sys_thread_new。需要注意的是:LwIP中的thread并没有μC/OS-II中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。
2.3 lib_arch中库函数的实现
在ARM SDT 2.开发环境下,gcc编译器的lib库里已经有了LwIP协议栈中系统CPU或编辑器有关的外部函数:strlen( )、strcmp( )、bcopy( )、bzero( ),只需要编写htons( )、ntohs( )、htonl( )、ntohl( )即可。
3 网络设备驱动程序
基于RTL8019AS网络芯片驱动的编写,主要是进行相关寄存器的设置。LwIP协议栈中,网络接口层负责接收上层的IP数据报,装配成不完整的物理帧后复制到控制器片内RAM中,并通过控制器发送到传输介质上,发送时由控制器装配成完整的物理帧;或者将控制器中缓存的接收到的物理帧先复制到系统内存,然后抽出IP数据报,交给IP层进行处理。修改ethernetif.c文件,实现底层的输入输出。
RTL8019AS是一种全双工即插即用的以太网控制器,它在一块芯片上集成了RTL8019内核和一个16KB的SDRAM存储器。它兼容RTL8019控制软件和NE2000 8bit或16bit的传输,支持UTP,AUI,BNC和PNP自动检测模式,支持外接闪烁存储器读写操作,支持I/O口地址的完全解码,具有LED指示功能。
3.1 网卡初始化函数
void ethernetif_init(struct netif*netif)用于初始化网卡,在程序启动之初被调用。这里主要完成网卡的复位操作以及通过对各个寄存器赋值,确定网卡的工作方式等。
3.2 网卡发送函数
函数err_t ethernetif_output(struct netif*netif,struct pbuf*p,struct ip_addr*ipaddr)为IP层传来的IP报文加上以太网包头并通过网络接口发送。RTL8019AS使用远端DMA将封装好的以太网包写到RTL8019AS内部的双口RAM的发送缓冲环中,然后启动本地DMA,网卡自动发送缓冲环里的数据到以太网。
发送过程有三个步骤:数据包的封装;通过远程DMA将数据包送到数据发送缓冲区;通过RTL8019的本地DMA将数据送入FIFO进行发送。
3.3 网卡接收函数
函数void ethernetif_input(struct netif*netif)从网络接口接收以太网数据包并把其中的IP报文向IP层发送。网卡对于以太网上目的地为该网卡的包会自动启动本地DMA接收数据,并存放在RTL8019AS芯片内部RAM的接收缓冲环中,然后以中断的方式通知CPU。此时该函数使用远端DMA接收数据到系统的RAM当中。
3.4 中断处理函数
void ethernetif_isr(void)处理网卡相关的中断,RTL8019AS接收到数据后,就通过中断入口把接收数据的工作交给函数ethernetif_input()来处理。
在实时多任务环境中,一般采用中断方式处理RTL8019AS的收发。图1是一个典型中断处理程序(ISR)的流程。当主程序响应RTL8019AS的中断时,ISR的入口将根据读取的中断状态寄存器(ISR)的值来确定程序的走向。
4 系统任务
图2是基于μC/OS-II的嵌入式系统的框架结构。按电网监测系统所要实现的功能,整个系统划分为二个中断程序和五个并行存在的任务层。
中断程序按其优先级从高到低分别是网络通信和数据采集。将系统的任务按其优先级从高到低顺序排列的次序是:系统监控任务,键盘扫描任务,任务之间的通信,数据运算统计处理任务,液晶显示任务。
在该电网远程监测系统软件中,系统监视任务优先权最高,最先进入运行态。该任务分别查询每一个被监视的任务是否向其发送消息。按优先权级别顺序,键盘扫描任务将由就绪态转为运行态。在该任务将要执行完毕时,向系统监视任务发送消息,然后执行延时函数将自身转为挂起态,交出CPU使用权,让其他任务得以执行。如果没有,则进入挂起态,再次等待其他被监视任务的运行消息。系统按任务优先级继续执行优先级高的就绪态任务,依次类推。
中断发生时,系统将强行剥夺运行态任务时CPU的使用权,将它转入中断态并保存相关数据到堆栈区之后,执行中断服务程序。在中断返回时,系统返回函数将重新进行任务调度,将优先权最高的就绪态任务转为运行态。