如果还没看《移植 TencentOS-tiny 实时操作系统》,现去看,本篇文章是基于上篇介绍的工程项目修改而来。
一、硬件平台:STM32L051C8T6
概述
TencentOS tiny是面向物联网(IOT)领域的操作系统,由一个实现精简的实时操作系统(RTOS)内核,以及丰富的物联网组件组成。
任务管理
1、概述
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。
编程实例
1、在tos_config.h中,配置最大任务优先级TOS_CFG_TASK_PRIO_MAX:
#define TOS_CFG_TASK_PRIO_MAX 10u
2、配置每秒钟的系统滴答数TOS_CFG_CPU_TICK_PER_SECOND:
#define TOS_CFG_CPU_TICK_PER_SECOND 1000u
3、编写main.c示例代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "cmsis_os.h"
#include "stdio.h"
#include "tos_k.h" // 添加TencentOS tiny内核接口头文件
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define STK_SIZE_TASK_PRIO4 512 // 优先级为4的任务栈大小为512
#define STK_SIZE_TASK_PRIO5 1024 // 优先级为5的任务栈大小为1024
k_stack_t stack_task_prio4[STK_SIZE_TASK_PRIO4]; // 优先级为4的任务栈空间
k_stack_t stack_task_prio5[STK_SIZE_TASK_PRIO5]; // 优先级为5的任务栈空间
k_task_t task_prio4; // 优先级为4的任务体
k_task_t task_prio5; // 优先级为5的任务体
extern void entry_task_prio4(void *arg); // 优先级为4的任务体入口函数
extern void entry_task_prio5(void *arg); // 优先级为5的任务体入口函数
uint32_t arg_task_prio4_array[3] = { // 优先级为4的任务体入口函数入参
1, 2, 3,
};
char *arg_task_prio5_string = "arg for task_prio5"; // 优先级为5的任务体入口函数入参
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
static void dump_uint32_array(uint32_t *array, size_t len)
{
size_t i = 0;
for (i = 0; i < len; ++i) {
printf("%d\t", array[i]);
}
printf("\n\n");
}
void entry_task_prio4(void *arg)
{
uint32_t *array_from_main = (uint32_t *)arg; // 捕获调用者传入的参数
printf("array from main:\n");
dump_uint32_array(array_from_main, 3); // dump传入的参数(数组)
while (K_TRUE) {
printf("task_prio4 body\n"); // 任务运行体,不断打印这条信息
tos_task_delay(1000); // 睡眠1000个系统时钟滴答(以下记作systick),因为TOS_CFG_CPU_TICK_PER_SECOND为1000,也就是一秒钟会有1000个systick,因此睡眠1000个systick就是睡眠了1秒。
}
}
void entry_task_prio5(void *arg)
{
int i = 0;
char *string_from_main = (char *)arg;
printf("string from main:\n");
printf("%s\n\n", string_from_main); // 打印出调用者传入的字符串参数
while (K_TRUE) {
if (i == 2) {
printf("i = %d\n", i); // i为2时,挂起task_prio4,task_prio4停止运行
tos_task_suspend(&task_prio4);
} else if (i == 4) {
printf("i = %d\n", i); // i为4时,恢复task_prio4的运行
tos_task_resume(&task_prio4);
} else if (i == 6) {
printf("i = %d\n", i); // i为6时,删除task_prio4,task_prio4不再运行
tos_task_destroy(&task_prio4);
}
printf("task_prio5 body\n");
tos_task_delay(1000);
++i;
}
}
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
tos_knl_init(); // 初始化TencentOS tiny内核
// 创建一个优先级为4的任务
(void)tos_task_create(&task_prio4, "task_prio4", entry_task_prio4,
(void *)(&arg_task_prio4_array[0]), 4,
stack_task_prio4, STK_SIZE_TASK_PRIO4, 0);
// 创建一个优先级为5的任务
(void)tos_task_create(&task_prio5, "task_prio5", entry_task_prio5,
(void *)arg_task_prio5_string, 5,
stack_task_prio5, STK_SIZE_TASK_PRIO5, 0);
// 开始内核调度
tos_knl_start();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
4、运行效果,如下所示:
源码链接:Git