Qsys与uCOS学习笔记3:Hello uC/OS-II uC/OS-II(又名Micro C/OS)是基于嵌入式系统的完整的,可移植、可固化、可裁剪的可剥夺型实时内核,其已经广泛应用在航空飞行器、医疗设备、工业控制等可靠性和稳定性要求较高的场合。该内核的代码也是完全开源的,如果不做商业用途,完全免费。因此对于广大的嵌入式爱好者与工程师们而言,了解OS从uC/OS-II开始不失为一个很好的选择。
之前是使用特权同学自己的SF-NIOS2开发套件进行了EDS上的uC/OS-II样板工程测试,为了当前学习笔记的持续性,这里重新就DE2-115板重新整理一个Hello uC/OS-II实例的创建和演示。
Qsys组件添加
在一个工程实例基础上,添加一个Interval Timer外设,设置该Timer的定时Period为10ms,用于作为uC/OS-II的时钟节拍(Clock tick),如图1所示。
图1
修改该Timer外设名称为ucosii_timer。将它的clk和reset信号分别连接到clk组件的clk和clk_reset信号上,将它的Avalon从机接口s1连接到nios_qsys_0的data_master上,并将它的irq连接到nios_qsys_0的d_irq上。
图2
自动分配地址,点击SystemàAssign Base Addresses。自动分配中断向量号,点击SystemàAssign Interrupt Numbers,接着点击Generate生成新的系统。
完成Qsys新系统的Generate,接着重新编译Quartus II的project。自此,硬件的修改已经就绪。
软件工程创建
如图3所示,打开EDS后,点击FileàNewàNios II Application and BSP from Template新建模板工程。
图3
如图4所示,在新建工程向导中,选择SOPC Information File name为当前工程目录下的sopcinfo文件。Project name命名为ucosii_swprj,选择Project template为Hello MicroC/OS II。最后点击Finish创建工程。
图4
新建工程出现在工程管理窗口后,右键单击ucosii_swprj文件夹,选择NIOS IIàBSP Editor,如图5所示。
图5
如图6所示,确定Main页面中Common里面的stderr/stdin/stdout均为jtag_uart,ucosii_timer为sys_clk_timer即可。点击Generate更新设置。
图6
右键点击应用工程,选择Build Project进行软件工程编译。完成后Console窗口打印如图7所示的信息,可见这个uC/OS-II内核以及软件的HAL占用了大约94KB的存储空间,uC/OS-II其实还是很小的,只不过NIOS II各种外设的HAL比较大,不过也都是可以裁剪的。
图7
uC/OS-II运行调试
首先将Quartus II工程产生的sof硬件配置文件烧录到FPGA中。
接着应用工程上点击右键弹出菜单选择Run asàNios II Hardware,在线运行uC/OS-II实例工程。
这个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中,我们可以看到最终运行的效果,如图8所示,两个任务所打印的字符串”Hello from task1”和”Hello from task2”循环出现。
图8
主要实例源码如下:
#include <stdio.h>
#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工程,如图9所示,初始化时调用OSInit();函数;接着调用OSTaskCreate();或OSTaskCreateExt();函数创建用户任务;最后调用OSStart();函数运行任务。这里的main函数里虽然没有出现OSInit();函数,但实际上在HAL后台外设初始化时候肯定调用了。中间是任务的创建,这里创建两个任务task1和task2,优先级分别为1和2,并且分配了相应的堆栈空间。在两个任务中,分别打印字符串”Hello from task1”和”Hello from task2”,字符串打印后调用OSTimeDlyHMSM(0, 0, 3, 0);函数做了3s的延时。如果修改这个延时时间,打印效果会发生改变,根据延时的情况,Console窗口出现的打印字样频率和速度会不一样。
图9
前面提到我们的实例其实是有OSInit();函数存在的,而且是在系统的main();函数之前就调用了。这里也探个究竟,也算是给大家讲讲NIOS II的软件执行顺序吧。NIOS II软件在上电后其实并非如同一帮的裸奔的嵌入式软件一样直接从main();函数开始执行程序,而是先执行HAL里面的alt_main();函数,这个函数在bsp工程下的HALàsrc里面,找到名为alt_main();的函数便是。
图10
打开alt_main.c源代码文件后,如图11所示,alt_main();函数中执行了Qsys系统的几乎所有可用外设的初始化,因为我们这个模板工程用的是uC/OS-II的例子,所以必有其初始化,图11中的ALT_OS_INIT();便是。如果右击该函数,选择Open Declaration,则我们便能看到这样的定义:
#define ALT_OS_INIT() OSInit();
图11
再来简单的熟悉一下这个模板工程中所涉及的几个函数,包括OSInit();函数、OSTaskCreate();函数、OSTaskCreateExt();函数、OSStart();函数。
OSInit();函数
void OSInit (void)
在使用uC/OS-II的任何功能之前,必须调用该函数。该函数建立了两个任务:空闲任务——在所有其他任务均未就绪时运行;统计任务——计算CPU的利用率。此外,该函数也对uC/OS-II所有的变量和数据结构进行初始化。
OSTaskCreate();函数
INT8U OSTaskCreate (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio)
除了OSInit();函数执行时为系统建立的2个基本任务(优先级最低的2个任务),uC/OS-II建议用户另外保留2个优先级最低的任务和4个优先级最高的任务,为了将来的内核升级只用,当然了,其实也可以不保留。所以,通常来讲,用户至少可以建立56个任务。
OSTaskCreate();函数有4个入口参数,其含义分别为:task是指向任务代码的指针;p_arg是任务开始执行时,传递给任务的参数的指针;ptos是分配给任务的堆栈的栈顶指针;prio是分配给任务的优先级。
OSTaskCreateExt();函数
INT8U OSTaskCreateExt (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio,
INT16U id,
OS_STK *pbos,
INT32U stk_size,
void *pext,
INT16U opt)
OSTaskCreateExt();函数其实是OSTaskCreate();函数的扩展,前4个参数的定义用法完全一致,后面5个入口参数的定义为:id是为要建立的任务创建一个特殊标志符,主要是保留为了将来内核升级使用,当前只要将此参数值设置成和任务的优先级一样就行;pbos指向任务堆栈栈底的指针,用于堆栈的检验;stk_size用于指定堆栈的容量;pext指向用户附加的数据域的指针,用来扩展任务的任务控制块OS_TCB;opt用于设定OSTaskCreateExt();的选项,指定是否允许堆栈的检验,是否将堆栈清0,任务是否要进行浮点操作等。
OSStart();函数
void OSStart (void)
该函数负责从任务就绪表中找出用户建立的优先级最高的任务控制块,并开始执行这个任务。调用该函数后,软件就将控制权交给了uC/OS-II的内核,开始运行多任务。在调用该函数之前,必须先建立一个任务,否则,应用程序将会崩溃。
NIOS II上的uC/OS-II移植,其实就这么简单。当然了,如果要不断的深入进去,一定大有学问。
|
|