如果還沒看《移植 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