(一) 任务管理
TencentOS tiny内核是单地址空间的可抢占式实时内核,TencentOS tiny内核不提供进程模型,任务对应线程的概念,是最小的调度运行体,也是最小的资源持有单位。
任务的本质是一个拥有独立栈空间的可调度运行实体,用户可以在任务的入口函数中编写自己的业务逻辑;多个任务之间可以通过系统提供的任务间通信机制进行同步或者信息传递等操作;每个任务都有优先级,高优先级任务可以抢占低优先级任务的运行。
API讲解
创建任务的系统api接口为tos_task_create,接口原型如下:
k_err_t tos_task_create(k_task_t *task,
char *name,
k_task_entry_t entry,
void *arg,
k_prio_t prio,
k_stack_t *stk_base,
size_t stk_size,
k_timeslice_t timeslice);
这里详细讲解此api参数意义:
• task
这是一个k_task_t类型的指针,k_task_t是内核的任务结构体类型。注意:task指针,应该指向生命周期大于待创建任务体生命周期的k_task_t类型变量,如果该指针指向的变量生命周期比待创建的任务体生命周期短,譬如可能是一个生命周期极端的函数栈上变量,可能会出现任务体还在运行而k_task_t变量已被销毁,会导致系统调度出现不可预知问题。
• name
指向任务名字符串的指针。注意:同task,该指针指向的字符串生命周期应该大于待创建的任务体生命周期,一般来说,传入字符串常量指针即可。
• entry
任务体运行的函数入口。当任务创建完毕进入运行状态后,entry是任务执行的入口,用户可以在此函数中编写业务逻辑。
• arg
传递给任务入口函数的参数。
• prio
任务优先级。prio的数值越小,优先级越高。用户可以在tos_config.h中,通过TOS_CFG_TASK_PRIO_MAX来配置任务优先级的最大数值,在内核的实现中,idle任务的优先级会被分配为TOS_CFG_TASK_PRIO_MAX - 1,此优先级只能被idle任务使用。因此对于一个用户创建的任务来说,合理的优先级范围应该为[0, TOS_CFG_TASK_PRIO_MAX - 2]。另外TOS_CFG_TASK_PRIO_MAX的配置值必需大于等于8。
• stk_base
任务在运行时使用的栈空间的起始地址。注意:同task,该指针指向的内存空间的生命周期应该大于待创建的任务体生命周期。stk_base是k_stack_t类型的数组起始地址。
• stk_size
任务的栈空间大小。注意:因为stk_base是k_stack_t类型的数组指针,因此实际栈空间所占内存大小为stk_size * sizeof(k_stack_t)。
• timeslice
时间片轮转机制下当前任务的时间片大小。当timeslice为0时,任务调度时间片会被设置为默认大小(TOS_CFG_CPU_TICK_PER_SECOND / 10),系统时钟滴答(systick)数 / 10。
编程实例
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "string.h"
#include "cmsis_os.h"
#include "tos.h"
//定义相关堆栈大小
#define KEY_STK_SIZE 512
#define LED_STK_SIZE 512
#define UART_STK_SIZE 512
//建立任务堆栈
k_stack_t stack_task_led[LED_STK_SIZE]; // 优先级为4的任务栈空间
k_stack_t stack_task_uart[UART_STK_SIZE]; // 优先级为5的任务栈空间
k_stack_t stack_task_key[KEY_STK_SIZE]; // 优先级为6的任务栈空间
//声明任务体
k_task_t led_task0; // 优先级为4的任务体
k_task_t key_task0; // 优先级为6的任务体
k_task_t uart_task0; // 优先级为5的任务体
char *uartin="helloworld";
static unsigned int count=0;
extern void led_task(void *arg); // 优先级为4的任务体入口函数
extern void uart_task(void *arg); // 优先级为5的任务体入口函数
extern void key_task(void *arg); // 优先级为5的任务体入口函数
void key_task(void *pdata)
{
int i=0,j=0;
u8 key;
while(1)
{
key=KEY_Scan(0); //按键扫描
switch(key)
{
case WKUP_PRES: //控制LED0,LED1互斥点亮
LED1=!LED1;
LED0=!LED1;
break;
case KEY2_PRES: //控制LED0翻转
LED0=!LED0;
break;
case KEY1_PRES: //控制LED1翻转
i=!i;
if(i==0)
{
printf("ledtaskoff\n");
tos_task_suspend(&led_task0);
}
else
{
printf("ledtaskon\n");
tos_task_resume(&led_task0);
}
break;
case KEY0_PRES:
j=!j;
if(j==0)
{
printf("uarttaskoff\n");
tos_task_destroy(&uart_task0);
}
else
{
printf("uarttaskon\n");
tos_task_resume(&uart_task0);
}
break;
}
}
}
void led_task(void *pdata)
{
LED_Init();
while(1)
{
LED0=!LED0;
tos_task_delay(1000);
}
}
void uart_task(void *pdata)
{
while(1)
{
printf("%s\n", pdata);
tos_task_delay(1000);
}
}
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
LCD_Init();
osKernelInitialize(); //TOS Tiny kernel initialize
(void)tos_task_create(&led_task0, //任务体指针
"led_task", //任务名
led_task, //任务函数入口
NULL , //入口参数
4, //任务优先级
stack_task_led, //任务堆栈起始地址
LED_STK_SIZE, //堆栈大小
0 //时间片大小
);
(void)tos_task_create(&uart_task0, //任务体指针
"uart_task", //任务名
uart_task, //任务函数入口
(void *)(uartin), //入口参数
5, //任务优先级
stack_task_uart, //任务堆栈起始地址
UART_STK_SIZE, //堆栈大小
0 //时间片大小
);
(void)tos_task_create(&key_task0, //任务体指针
"key_task", //任务名
key_task, //任务函数入口
NULL , //入口参数
6, //任务优先级
stack_task_key, //任务堆栈起始地址
KEY_STK_SIZE, //堆栈大小
0 //时间片大小
);
osKernelStart();//Start TOS Tiny
while(1)
{}
}
实验现象:
每隔一秒串口发送helloworld,每隔一秒灯闪烁一次
Key0按下,任务串口删除
KEY1按下,LED任务恢复挂起