控制器模块的结构原理图如图3所示。整个控制器模块主要由控制器、422总线通信电路、1553B协议转换电路、隔离变压器电路、FPGA控制逻辑及ARINC429电平转换电路等组成。
FPGA控制逻辑用Verilog HDL在QuartusII环境下编程实现,主要完成以下功能:(1)产生对1553B协议芯片BU61580操作的所有控制信号;(2)产生对ARINC429接收模块和ARINC429发送模块操作的所有控制信号; (3)为1553B协议芯片BU61580、ARINC429接收模块和ARINC429发送模块产生各自的工作时钟,其中分频数N可通过软件进行赋值,实现可编程时钟模块;(4)建立FIFO模块,在数据传输中缓冲和存储数据;(5)实现ARINC429信号接收和发送,包括同步处理、字头检验、奇偶校验、串并转换和并串转换等。
4 实时操作系统的移植、构建和优化
将嵌入式实时内核μC/OS-II移植到DSP控制器TMS320LF2407上构建一个低成本的通用嵌入式实时软件平台,以进行系统软件的开发。引入μC/OS-II实时内核的目的是要以很小的系统代价,大大降低DSP系统软件开发的难度,同时使系统的实时性得到保证。
4.1 μC/OS-II移植的可行性分析
所谓移植,就是使一个实时内核能在某个处理器上运行[7]。因为C语言是跨平台的,各种编译器都支持,所以μC/OS-II和其他嵌入式操作系统一样,和处理器无关的代码主要用C语言写。虽然μC/OS-II系统的大部分源代码都是用C 语言实现的,但仍需要使用X86 汇编语言来完成一些与处理器、寄存器相关的代码[8]。这是因为μC/OS-II在读写处理器寄存器时只能通过汇编语言来实现。
要顺利移植μC/OS-II,处理器必须满足以下要求:(1)处理器的C编译器能产生重入代码,利用C语言就可以打开和关闭中断;(2)处理器支持中断,并能产生定时中断;(3)处理器支持足够的RAM(可能是几千字节)作为数据存储的硬件堆栈; (4)处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈或内存中的指令。
显然,采用的控制器TMS320LF2407及编译器CCS均可满足要求,因此完全可以在TMS320LF2407上移植嵌入式实时内核μC/OS-II。
4.2 μC/OS-II的代码结构和移植步骤
μC/OS-II的代码结构以及它与硬件的关系如图4所示,其全部代码可以分为三个部分[9]:
(1)与处理器无关的代码。包括SOFTWAREμC/OS-IISOURCE目录下的文件,主要提供μC/OS-II的系统服务、任务管理、任务间通信与内存管理等。这部分代码可完全移植到处理器上。
(2)与应用相关的代码。包括OS_CFG.H和INCLUDES.H头文件。OS_CFG.H文件包含μC/OS-II的初始化配置项,由一系列#define constant语句构成。INCLUDES.H是一个头文件,在所有.C文件的第一行被包含。用户可以通过编辑INCLUDES.H来增加自己的头文件,但是用户的头文件必须添加在头文件列表的最后。
(3) 与处理器相关的代码。即OS_CPU.H、OS_CPU_A.ASM及OS_CPU_C.C,绝大部分的移植工作都集中在这里。
移植μC/OS-II的具体步骤是:(1)在OS_CPU.H中设置一个常量来标识堆栈增长方向;(2)在OS_CPU.H中声明几个用于开关中断和任务切换的宏;(3)在OS_CPU.H中针对具体处理器的字长重新定义一系列数据类型;(4)在OS_CPU_A.ASM中改写4个汇编语言的函数:OSStartHighRdy()、OSCtxSw()、OSINTCtxSw()和OSTICkISR();(5)在OS_CPU_C.C中用C 语言重新编写6个简单的C函数: OSTaskStk Init()、OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskStatHook()和OSTimeTick Hook();(6)修改主头文件INCLUDES.H,将上面的三个文件和其他的头文件加入。
4.3 实时操作系统软件平台的优化
移植后的实时操作系统可根据具体硬件和性能需求进行优化,以获得更高的执行效率。
(1) 裁剪[9]
μC/OS-II具有源代码开放的优点,是一个可裁剪的操作系统。在实际应用时可根据需要对源代码进行取舍,去掉不需要的服务以及不需要的变量和函数,甚至可以根据需要改写相关函数。代码的削减可通过设置OS_CFG.H中的#define OS_×××_EN为0来实现。本系统中取消了不需要的邮箱服务、任务挂起等功能,使得代码非常简练,可靠性更好。此外,在μC/OS-II的源代码中,函数执行中有许多条件判断,作用是防止参数的错误传递。作为通用系统,这些条件判断是完全必要的,避免出现错误时系统崩溃。但作为具体的应用,只要在程序设计时保证参数传递的正确性,完全可以不用条件判断,所以在本系统程序设计时,取消了全部的条件判断,从而提高了函数的执行速度。
(2) 改进内存管理
μC/OS-II在内存管理上显得过于简单,其任务栈空间和内存分区的创建采用了定义全局数组的方法,即定义一维或二维的全局数组,在创建任务或内存分区时,将数组名作为内存地址指针传递给生成函数。这样实现起来固然简单,但是不够灵活有效。
编译器会将全局数组作为未初始化的全局变量,放到应用程序映像的数据段。数组大小是固定的,生成映像后不可能在使用中动态地改变。对于任务栈空间来说,数组定义大了会造成内存浪费,定义小了任务栈溢出会造成系统崩溃。对于内存分区,在不知道系统初始化后给用户留下了多少自由内存空间的情况下,很难定义内存分区所用数组的大小。可见,利用全局数组来分配内存空间是不合理的。另外,目前的μC/OS-II只支持固定大小的内存分区,容易造成内存浪费。所以应该改进以支持可变大小的内存分区。
因此,在本系统中采用如下方法来对内存进行管理:①系统初始化时,正确安排代码段和数据段的位置,从而确定用户自由空间的起始地址;②用目标板(LF2407)内存最高端地址减去起始地址得到用户自由空间的大小;③在自用空间中建立和使用内存分区,分配好任务堆栈、事件控制块和消息队列等各自内存大小。
(3) 堆栈的使用和管理
在μC/OS-II中,各任务的堆栈在逻辑上是相互独立的,这样在分配每一个任务堆栈区的大小时,不但要考虑本任务中的局部变量和函数嵌套所需要的堆栈空间,还要考虑系统中所可能发生的最大层数的中断嵌套所需的堆栈空间,从而要占用较多的RAM空间,在系统中有多个任务同时存在时尤其严重。如果对此考虑不足,则可能会出现运行中的任务堆栈空间不足、溢出的情形,从而导致系统崩溃。
针对上述问题,本系统采用以下方法使用和管理堆栈:各任务栈相互分离,且不考虑中断使用;另外分配一个工作栈,可满足所需堆栈空间最大的任务在最大可能层数的中断嵌套下使用。运行时,将当前任务的任务堆栈内容拷贝至工作栈中,在工作栈中运行;当发生任务切换时,先将工作栈中的有用内容保存到当前任务栈,然后将待运行任务的任务栈调入工作栈,在工作栈中运行。在此过程中,堆栈指针始终指在工作栈区域内。