<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
摘要:本文主要探讨μC/OS-II在配电自动化远方终端中的应用情况,给出了将μC/OS-II移植至TMS<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />320F2407A中的详细代码,并讨论了应用软件的整体设计思路和主要任务的详细流程,最后介绍了软件整体性能的测试方法。
关键词:实时操作系统,μC/OS-II,配电自动化远方终端,多任务,嵌入式系统
配电自动化远方终端(DATU)是配电自动化系统(DAS)的基础设备,是配电变压器远方终端(TTU)、馈线远方终端(FTU)和中压监控单元等几种设备的统称。这几种设备除少数高端功能不同以外,其余功能均大致相似,主要包括数据采集与处理、监控、保护和远方通信等功能。DATU是一种实时性很强的嵌入式系统,正常情况下它负责采集相应设备的运行情况,如电压、电流的数值以及开关的分合状态等,并将上述信息通过通信网络传向远方的配电自动化主站,同时还可以根据主站下发的命令进行远方操作[1]。在配电网发生故障时,DATU还要完成故障检测、记录故障参数和微机保护等功能。
在传统的嵌入式系统中,应用程序一般是由一个主程序和几个中断服务子程序(ISR)组成。主程序一般是一个无限的循环,在循环中调用相应的函数完成相应的数据处理,ISR负责处理时间相关性很强的关键操作。因为ISR提供的信息一直要等到主程序循环到负责处理这个信息的那段程序的时候才能得到处理,所以传统系统的实时性比较差,最坏情况下的实时响应时间可能是整个循环的执行时间[2]。如果采用嵌入式实时操作系统μC/OS-II,则可以大大缩短实时响应时间,接近理论上能达到的最好水平;此外,在嵌入式应用中使用μC/OS-II,通过将应用程序分解为多个任务,可以简化应用软件的设计;同时良好的多任务设计,有助于提高系统的稳定性与可靠性[2]。
1 μC/OS-II的移植
所谓移植就是使μC/OS-II能在某个CPU中运行。虽然大部分的μC/OS-II代码是用C语言写的,但仍需要开发者用汇编语言写一些与CPU相关的代码,这是因为μC/OS-II在访问CPU寄存器时只能通过汇编语言来实现。μC/OS-II与CPU相关的代码包含在三个文件中:OS_CPU.H,OS_CPU_C.C,OS_CPU_A.ASM。
1.1 移植的前提条件
我们的DATU所采用的CPU是美国德州仪器公司(TI)生产的一种高性能16位定点DSP——TMS320LF2407A(以下简称为F2407)。其定点运算速度最高可达每秒40兆条指令,片内除具有2.5K字的RAM和32K字的FLASH存储器外,还有A/D、SPI、CAN等多种外设资源[3]。
μC/OS-II能否移植至某种CPU中,这种CPU必须满足以下要求:该CPU的C编译器支持内嵌汇编语句;能由硬件产生定时中断;支持软件堆栈;有将堆栈指针(SP)和其它寄存器存储到内存中的指令[2]。通过查看F2407的手册可以得知它完全满足以上条件,所以将μC/OS-II移植到F2407中是完全可行的。
1.2 OS_CPU.H
OS_CPU.H包括一系列用#define定义的与处理器相关的数据类型、宏和常量,如关中断、开中断、任务切换和堆栈增长方式等。需要注意的是,由于INTM不受加载状态寄存器指令LST的影响,也就是说,即便可以将ST0保存至堆栈或内存中,也无法使用LST指令从堆栈或内存中恢复INTM的值,故而只能使用直接关中断的方式(方式一)来保护临界区代码[2]。
1.3 OS_CPU_C.C
OS_CPU_C.C中包括一系列C语言函数:OSTaskStkInit()和若干个Hook函数。其中唯一必要的是OSTaskStkInit(),其它几个函数可以不包含任何代码[2]。OSTickISR()是时钟节拍中断的ISR,因为Code Composor支持用C语言编写ISR,而且也支持内嵌汇编语句,所以我们把用C语言编写的OSTickISR()也放在OS_CPU_C.C中。
1.3.1 OSTaskStkInit()
OSTaskStkInit()在创建任务时被OSTaskCreate()或OSTaskCreateExt()调用来初始化任务的堆栈结构。图1显示了在F2407中需要放到任务堆栈中的寄存器及其顺序。
高地址内存 | <-栈底 |
…… |
|
硬件堆栈2-8级 | <-SP |
辅助寄存器AR2-AR7 |
|
辅助寄存器AR0 | ▲ 堆 |
临时寄存器 | ▲ 栈 |
乘积寄存器 | | 增 |
累加器低16位 | | 长 |
累加器高16位 | | 方 |
状态寄存器0 | | 向 |
状态寄存器1 |
|
任务返回地址 |
|
任务参数 |
|
…… |
|
低地址内存 | <-栈顶 |
图1 任务的堆栈结构 |
OSTaskStkInit()的代码如下,其四个参数中,task是任务的起始地址,pdata是传给任务的数据指针,ptos是最初的SP,opt没有用到。函数返回的是全部入栈操作完成后的新的SP。
OS_STK *OSTaskStkInit(void (*task),
void *pdata, OS_STK *ptos, INT16U opt)
{
*ptos++ = (OS_STK)pdata;/*任务参数*/
*ptos++ = (OS_STK)0;/*空闲 */
*ptos++ = (OS_STK)0x27FC;/*ST1*/
*ptos++ = (OS_STK)0x2600;/*ST0*/
*ptos++ = (OS_STK)0;/*ACCH*/
*ptos++ = (OS_STK)0;/*ACCL*/
*ptos++ = (OS_STK)0;/*PH*/
*ptos++ = (OS_STK)0;/*PL*/
*ptos++ = (OS_STK)0;/*临时寄存器*/
*ptos++ = (OS_STK)0;/*辅助寄存器0*/
……………/*辅助寄存器AR2-AR7*/
*ptos++ = (OS_STK)task;/*硬件堆栈2*/
……………/*硬件堆栈3-8*/
return ptos;
}
1.3.2 OSTickISR()
OSTickISR()的主要任务就是调用函数OSTimeTick(),其作用是给每个需要延时的任务的延时时间OSTCBDly减1(如果该项不为零的话)。当某个任务的OSTCBDly减到了零,这个任务就进入了就绪态[2]。具体代码如下:
interrupt void timer()/*时钟节拍中断*/
{
*IFR |= 0x0004;
OSIntEnter();
if(OSIntNesting = = 1){
/*OSTCBCur->OSTCBStkPtr = SP;*/
asm(" LDPK _OSTCBCur");
asm(" LAR AR3, _OSTCBCur");
asm(" MAR *, AR3");
asm(" SAR AR1, *");
asm(" LACL *");
asm(" SUB #1h");
asm(" SACL *,0,AR1");
}
OSTimeTick();
OSIntExit();
}
1.4 OS_CPU_A.ASM
OS_CPU_A.ASM中包括几个汇编语言函数:OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()。需要注意的是,我们在OSCtxSw()中直接调用了F2407的集成开发环境Code Composor 4.0提供的两个运行库函数I$$SAVE和I$$REST,I$$SAVE的作用是将全部寄存器保存到任务堆栈中,I$$REST的作用是从任务堆栈中恢复全部CPU寄存器,并执行中断返回指令。
OSIntCtxSw()是在ISR中被调用的,由于所有的CPU寄存器都已经被ISR正确地保存到了被中断任务的堆栈之中,并且如果该ISR是中断嵌套的第一层,则ISR已经将SP保存到被中断任务的任务控制块(TCB)中了。因此除了不需要再保存CPU寄存器和堆栈指针外,OSIntCtxSw()完全可以采用OSCtxSw()中的大部分代码,实际上本文中的中断级切换就是直接跳转到OSCtxSw()中的标号为_OSIntCtxSw的那一行的。
2 应用软件
2.1 软件整体设计思路
将μC/OS-II引入嵌入式系统之后,应用程序的设计就变得非常简单了。尽管DATU的功能比较繁多,但我们可以将比较复杂的程序层次化,按照功能划分为多个任务,各个任务可以分别编写,但要事先设计好任务间的通信方式,这样程序将更容易开发与维护[2]。根据DATU应完成的功能将应用软件划分为十个用户任务和四个ISR,按照任务的重要性和是否具有硬实时性来分配优先级,优先级数值越低,任务的优先级越高。所有给任务分配的优先级都是在用户自定义头文件userdef.h中用宏来定义的;由于保护处理任务需要进行FFT运算,这需要大量的局部变量空间,故而该任务的堆栈非常大,为2048字节,其余任务均为128字节。所有任务的编号、名称和功能描述如表1所示。
2.2 任务介绍
任务其实就是一个简单的无限循环的程序,该程序可以认为CPU完全只属于它自己。在循环中必须调用延时或等待邮箱消息等函数,以便将CPU控制权交给其它的任务。在上述10个任务中,比较复杂的就是保护处理任务。它根据A/D中断子程序中采集到的瞬时值计算出电流的基波及二次谐波分量,结合相应的电压信号,判断是否有线路故障,如是则启动故障后录波,然后向主站上报故障信息。除此之外,该任务还能配合通信任务进行远方或当地定值整定。任务流程从略。
3 系统性能测试
3.1 CPU使用率
μC/OS-Ⅱ提供一个计算CPU使用率的统计任务OSTaskStat()。该任务每秒钟运行一次,计算当前的CPU使用率。如果应用程序打算使用统计任务,必须在初始化时建立一个唯一的任务,并且只在这个任务中调用OSStatInit()。换句话说,在调用系统启动函数OSStart()之前,必须先建立一个任务,在这个任务中调用统计初始化函数OSStatInit(),然后再建立应用程序中的其它任务。
3.2 任务堆栈使用情况
有时候决定任务实际所需的堆栈大小是很有必要的,这样我们就可以避免为任务分配过多的堆栈空间。μC/OS-II提供的函数OSTaskStkChk()可以为开发者提供这种有价值的信息。每调用一次该函数,就会执行一次堆栈检验。每次在调用OSTaskStkChk()的时候,可能会得到不同的空闲空间数。应该让应用程序运行足够长的时间,才有可能经历最坏的堆栈使用情况,这样才能得到正确的信息,我们就可以据此重新设置堆栈的最终容量了[2]。
4 结束语
DATU实际上是一种非常典型的嵌入式系统,将μC/OS-II引入该系统中是本产品的一大特色。实测证明,采用μC/OS-II大大提高了系统的实时性,特别是提高了遥信处理和保护处理等较高优先级任务的响应速度。在满足系统对实时性的整体要求的基础上,μC/OS-II所采用的基于优先级的调度策略可以最大限度地满足最关键的任务,如遥信变位处理的响应时间远少于部颁标准。同时以μC/OS-II作为整个软件体系的基础,非常方便应用软件的模块化设计。各个任务之间除通过μC/OS-II提供的函数进行通信之外,没有其他的联系途径,这种松耦合结构提高了整个软件的可靠性。
参考文献
1. 刘健,倪建立,邓永辉.配电自动化系统[M].北京:中国水利水电出版社,2000
2. (美)拉伯罗斯 著,邵贝贝 等译:嵌入式实时操作系统μC/OS-II(第二版)[M].北京:北京航空航天大学出版社
技术支持:13148818895 0755-83690800/075583662100 余焕丽
文章评论(0条评论)
登录后参与讨论