最近在做一些关于一些驱动向TI板子的移植集成的事情,涉及操作系统为RTOS实时操作系统,在这里简单的记录一下。本文主要叙述如何进行多任务的编程,以及我在编程中遇到的一些不解。
- Task组成部分:任务函数,任务参数(属性),任务堆栈.
实际上Task是sys/bios提供的一种并发编程手段,也是实时操作系统必备要素之一,
和我们常见到的线程是一个道理,换了个说法。
- Task类型:硬中断HWI,软中断SWI,任务Task,空闲任务IDLE.
- 优先级排列:硬中断HWI>软中断SWI>任务Task>空闲任务IDLE.
举个例子:
同时存在四种任务,优先处理硬中断;
如果只存在一种类型任务,优先处理该类型中优先级高的任务;
如果只存在一种类型任务,且优先级相同,任务调度取决于系统调度程序;
(特殊情况除外)
- 四种类型任务释义:
硬中断:实时环境中,它是响应外部触发的异步事件(中断)-----优先级不由sys/bios管理,由cpu的特性决定。
虽然优先级最高,当然也可以被中断。
软中断:软件中断服务例程 ------优先级可以达到 32 级,默认只有 16 级。
SWI 可以被更高优先级的 SWI 和 HWI 抢占,但是 SWI 是不会阻塞的。
任务:用户任务,运行过程中,可以被阻塞直到必要的资源可用;
TASK 要求每个任务要有自己独立的栈空间。
SYS/BIOS 提供了一些机制用于 TASK 间的同步和通信,它们有信号量(Semphore),事件(Events),队列(Queue)和邮箱(Mailboxes)。
优先级也可以达到 32 级,默认是 16 级。
Task 优先级数值越大,其优先级就越高,0 是优先级最低的。Task_setPri 函数动态的更改。新的优先级必须在 1 到 TnumPriorities – 1 之间。当新优先级数低于当前优先级时,可能会发生任务的切换。
空闲任务:优先级最低的任务。
当 SYS/BIOS 没有比空闲任务更高的任务运行时,SYS/BIOS 就会执行空闲任务,并且是连续执行,直到有更高的任务进入就绪状态。
其运行在Task任务 0 优先级上。
- 创建一个task过程:
#include <app.h>
#include <utils/console_io/include/app_log.h>
#include <xdc/runtime/Error.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <app_ipc_rsctable.h>
static Void appMain(UArg arg0, UArg arg1){ //任务函数
//do something
printf("this is thread function\n");
}
void StartupEmulatorWaitFxn (void){
volatile uint32_t enableDebug = 0;
do{
}while (enableDebug);
}
static uint8_t gTskStackMain[4*1024] //任务使用的栈空间
__attribute__ ((section(".bss:taskStackSection")))
__attribute__ ((aligned(4096)))
;
int main(void)
{
Task_Params tskParams;
Error_Block eb;
Task_Handle task;
/* This is for debug purpose - see the description of function header */
StartupEmulatorWaitFxn();
Error_init(&eb);
Task_Params_init(&tskParams);
tskParams.arg0 = (UArg) NULL;
tskParams.arg1 = (UArg) NULL;
tskParams.priority = 10u; //指定任务优先级
tskParams.stack = gTskStackMain; //指定任务使用的堆栈
tskParams.stackSize = 4096; //指定堆栈的大小
task = Task_create(appMain, &tskParams, &eb); //动态创建一个task
/*
You can continue to create other tasks from here......
*/
if(NULL == task){
BIOS_exit(0);
}
BIOS_start(); //运行到这里程序不会退出,相当于是个死循环,操作系统将会调度执行任务
return 0;
}
- SDK版本以及引用路径:
/psdk_rtos_auto_j7_06_01_00_15/bios_6_76_03_01/packages/ti/sysbios/knl
- 如何在程序中获取任务的信息以及状态:
// bios_6_76_03_01/packages/ti/sysbios/knl/Task.h:105:struct ti_sysbios_knl_Task_Stat ; 任务信息声明处
//解释几个我用到的成员:
struct ti_sysbios_knl_Task_Stat {
xdc_Int priority; //优先级
xdc_Ptr stack; //使用的堆栈地址
xdc_SizeT stackSize; //使用的堆栈的大小
xdc_runtime_IHeap_Handle stackHeap;
xdc_Ptr env;
ti_sysbios_knl_Task_Mode mode;//休眠态,就绪态,运行态,挂起态,被中断态
xdc_Ptr sp;
xdc_SizeT used; //已经使用了的堆栈空间大小
};
如何在一个任务中动态获取当前任务的状态信息:
Task_Stat statbuf;
Task_stat(Task_self(),&statbuf);
printf("In appMain task,stack buff used = %d\n",statbuf.used);//查看当前任务已经使用的
printf("In appMain task,stack total size = %d\n",statbuf.stackSize);//查看当前任务分配的总stack
printf("In appMain task,task priority =%d\n",statbuf.priority); //查看当前任务的优先级
- 关于任务堆栈:
大小:
尽量使用自定义数组来充当任务执行时的堆栈,不要依靠于系统的堆栈,如果内存不足,将发生致命错误;
关于给任务分配多大内存为合适,我查看了一些资料,有说到一个任务最少需要分配512个字节,这是理论值,或者说这个任务的函数是一个空函数,啥都不干,这512是用来满足上下文的保存的,不符合实际开发,如果条件允许,简单的函数给予2k,稍微复杂的给予4k.(具体值还请各位尝试,这里作为参考)
注意:我这里是跑在R5F mcu2_0上的,和使用的SDK版本也有关系。
对齐方式:(不添加对齐应该也可以运行)
我理解cpu为了提高取指速率,需要进行内存对齐。
一般而言,选择对齐数num * n = stackSize,其中n%2 == 0,num可能更满足要求,如
static uint8_t TskStackMain[4*1024]
attribute ((section(".bss:taskStackSection")))
attribute ((aligned(4096)));
这个我并没有找到官方的文档的来解释说明,仅仅作为一种猜测,当然不是凭空猜测,是经过实验的。
- 任务阻塞方式:
Task_sleep():使用:Task_sleep(5300 / Clock_tickPeriod);// 延时5.3ms Clock_tickPeriod定义在ti/sysbios/knl/Clock.h
Task_yield():使得任务主动让出自己的时间片
Semaphore_pend():信号量,获取临界资源
- 同步方式:
信号量、邮箱、事件等,如需要可以参考官方文档。实际上原理和用法与多线程同步方式一个道理,只不过换了个名字罢了。
最后说一下我的遇到几个很奇怪的现象:
1.替换固件,板子断电重启,系统运行的结果是前一次,为什么上一次数据没有被擦除?
2.相同优先级的任务存在某一个任务一直运行,不释放cpu的现象,只好通过一些接口
让他主动释放,并没有像一些文档中描述的,类似于时间片轮转?
3.为什么我在main()函数中打印的log不能查看到,只能看到在任务中打印的log?
这里猜测可能是SDK版本的原因,也可能是板子还是不够成熟吧