uC/OS-II(又名Micro C/OS)是基于嵌入式系统的完整的,可移植、可固化、可裁剪的可剥夺型实时内核,其已经广泛应用在航空飞行器、医疗设备、工业控制等可靠性和稳定性要求较高的场合。该内核的代码也是完全开源的,如果不做商业用途,完全免费。因此对于广大的嵌入式爱好者与工程师们而言,了解OS从uC/OS-II开始不失为一个很好的选择。
特权同学最近在一边狂啃邵贝贝翻译的《嵌入式实时操作系统uC/OS-II》,一边动手在NIOS II上做一些实践,加深理解和认识。
拿来自己设计的SF-NIOS2开发套件,使用第3个工程实例(《爱上FPGA开发》第6章的SOPC工程)进行了EDS上的uC/OS-II样板工程测试,对uC/OS-II有一个更感性的认识和体验。废话少说,简单的描述下整个实例过程,也算给手头拥有SF-NIOS2套件的朋友一个参考吧。
SOPC硬件修改
第3个SOPC工程硬件框图如图1所示。在此基础上,咱们需要添加一个10ms定时器,用于作为uC/OS-II的时钟节拍(Clock tick)。
图1
首先复制第3个工程实例,修改工程文件夹名为nios2ucosii,接着打开工程的SOPC Builder编辑界面,添加一个Interval Timer外设,设置该Timer的定时Period为10ms,如图2所示。
图2
修改该Timer外设名称为ucosii_timer。
图3
重新分配地址,如图4所示,点击SystemàAssign Base Addresses。接着点击右下角的Generate生成新的系统。
图4
完成SOPC新系统的Generate,接着重新编译Quartus II的project。自此,硬件的修改已经就绪。
软件工程创建
如图5所示,打开EDS后,点击FileàNewàNios II Application and BSP from Template新建模板工程。
图5
如图6所示,在新建工程向导中,选择SOPC Information File name为当前工程目录下的sopcinfo文件。Project name命名为myucosii_prj,选择Project template为Hello MicroC/OS II。最后点击Finish创建工程。
图6
新建工程出现在工程管理窗口后,右键单击myucosii_prj文件夹,选择NIOS IIàBSP Editor,如图7所示。
图7
确定Main页面中Common里面的stderr/stdin/stdout均为jtag_uart,sys_clk_timer为ucosii_timer即可。点击Generate更新设置。
图8
右键点击应用工程,选择Build Project进行软件工程编译。完成后Console窗口打印如图9所示的信息,可见这个uC/OS-II内核以及软件的HAL占用了大约101KB的存储空间,uC/OS-II其实还是很小的,只不过NIOS II各种外设的HAL比较大,不过也都是可以裁剪的。
图9
uC/OS-II运行调试
首先将Quartus II工程产生的sof硬件配置文件烧录到FPGA中。
接着如图10所示,在线运行uC/OS-II实例工程。
图10
这个uC/OS-II工程的实验目的只是创建两个task分别打印一串字符,正如readme所描述:
Readme - Hello MicroC/OS-II Hello Software Example
Hello_uosii is a simple hello world program running MicroC/OS-II. The
purpose of the design is to be a very simple application that just
demonstrates MicroC/OS-II running on NIOS II. The design doesn't account
for issues such as checking system call return codes. etc.
在NIOS II Console中,我们可以看到最终运行的效果,如图11所示,两个任务所打印的字符串”Hello from task1”和”Hello from task2”循环出现。
图11
主要实例源码如下:
#include
#include"includes.h"
/* Definition of Task Stacks */
#define TASK_STACKSIZE 2048
OS_STK task1_stk[TASK_STACKSIZE];
OS_STK task2_stk[TASK_STACKSIZE];
/* Definition of Task Priorities */
#define TASK1_PRIORITY 1
#define TASK2_PRIORITY 2
/* Prints "Hello World" and sleeps for three seconds */
void task1(void* pdata)
{
while (1)
{
printf("Hello from task1\n");
OSTimeDlyHMSM(0, 0, 3, 0);
}
}
/* Prints "Hello World" and sleeps for three seconds */
void task2(void* pdata)
{
while (1)
{
printf("Hello from task2\n");
OSTimeDlyHMSM(0, 0, 3, 0);
}
}
/* The main function creates two task and starts multi-tasking */
int main(void)
{
OSTaskCreateExt(task1,
NULL,
(void *)&task1_stk[TASK_STACKSIZE-1],
TASK1_PRIORITY,
TASK1_PRIORITY,
task1_stk,
TASK_STACKSIZE,
NULL,
0);
OSTaskCreateExt(task2,
NULL,
(void *)&task2_stk[TASK_STACKSIZE-1],
TASK2_PRIORITY,
TASK2_PRIORITY,
task2_stk,
TASK_STACKSIZE,
NULL,
0);
OSStart();
return 0;
}
源码中,一个标准的uC/OS-II工程,实际上应该是初始化时调用OSInit();函数,最后调用OSStart();函数。这里的main函数里虽然没有调用OSInit();函数,实际上在HAL后台外设初始化时候肯定调用了。中间是任务的创建,这里创建两个任务task1和task2,优先级分别为1和2,并且分配了相应的堆栈空间。在两个任务中,分别打印字符串”Hello from task1”和”Hello from task2”,字符串打印后调用OSTimeDlyHMSM(0, 0, 3, 0);函数做了3s的延时。如果修改这个延时时间,打印效果会发生改变,根据延时的情况,Console窗口出现的打印字样频率和速度会不一样。
NIOS II上的uC/OS-II移植,就这么简单。