1. Task introduction
Regarding the introduction of the task, the previous article has a more detailed introduction, so I won’t explain too much here, you can refer to the following article: FreeRTOS Learning 2 (Task)_t_guest’s Blog-CSDN Blog
The main features of LiteOS can be summarized as follows:
- The task module of LiteOS can provide users with multiple tasks , realize the switching and communication between tasks , and help users manage business procedures.
- The task in LiteOS is a preemptive scheduling mechanism . High-priority tasks can interrupt low-priority tasks . Low-priority tasks can only be scheduled after high-priority tasks are blocked or completed. At the same time, time slice round-robin scheduling is supported .
- LiteOS tasks have 32 priorities (0-31) by default, the highest priority is 0 , and the lowest priority is 31.
2. Tasks
task status
Task status is usually divided into the following four types:
- Ready (Ready) : The task is in the ready list, only waiting for the CPU
- Running : The task is executing .
- Blocked (Blocked) : The task is not in the ready list . Including tasks are suspended, tasks are delayed, tasks are waiting for semaphores, read and write queues or wait for read and write events, etc.
- Exit state (Dead) : The task is finished , waiting for the system to reclaim resources .
noun introduction
Task ID : Returned to the user through parameters when the task is created, as a very important identification of the task
Task Priority : Priority identifies the order in which tasks are executed
Task entry function : the function that will be executed after each new task is scheduled
Task Control Block TCB : Each task contains a task control block (TCB-Task Control Block). TCB contains information such as task context stack pointer (stack pointer), task status, task priority, task ID, task name, and task stack size. TCB can reflect the running status of each task .
Task stack : Each task has an independent stack space , called task stack.
Task context : some resources used by the task during running , such as registers, etc., we call it task context. When the task is suspended, LitsOS will save the task context information of this task in its own task stack, so that after the task resumes, the context information at the time of suspension will be restored from the stack space, so as to continue execution. broken code.
Task switching : Task switching includes actions such as obtaining the highest priority task in the ready list, saving the context of the switched-out task, and restoring the context of the switched-in task.
Task State Migration
Ready state -> running state : After the task is created, it enters the ready state. When a task switch occurs, the task with the highest priority in the ready list is executed, thus entering the running state, but the task is still in the ready list at this moment.
Running state -> blocking state : the task is deleted from the ready list and enters the blocking state due to suspension, read semaphore waiting, etc.
Blocking state -> ready state (blocking state -> running state): After the blocked task is restored (task recovery, delay time timeout, read semaphore timeout or read semaphore, etc.), the restored task will is added to the ready list, thus changing from the blocking state to the ready state. At this time, if the priority of the restored task is higher than that of the running task, a task switch will occur, changing the task from the ready state to the running state.
Ready state -> blocking state : the task is suspended in the ready state, and then enters the blocking state.
Running state -> Ready state : After a higher priority task is created or restored, a task switch occurs and enters the ready list.
Running state -> Exiting state : The task is automatically deleted when the task is finished running, and the state changes from running to exiting.
Blocked -> Exited : The blocked task calls the delete interface, and the task status changes from blocked to exited.
3. Practical operation
The kernel code of LiteOS-m is encapsulated from the CMSIS-RTOS2 interface.
1. What is CMSIS-RTOS2 interface
CMSIS is the Cortex Microcontroller Software Interface Standard (Cortex Microcontroller Software Interface Standard). It is a set of standards that ARM and some compiler manufacturers and semiconductor manufacturers follow. It is a standard proposed by ARM specifically for the Cortex-M series. Under the agreement of this standard, ARM and chip manufacturers will provide some common API interfaces to access the Cortex core and some special peripherals, so as to reduce the money and time consumption of transplanting work such as replacing chips and development tools.
CMSIS-RTOS2 (CMSIS-RTOS API Version 2) is a general-purpose RTOS interface for Arm® Cortex®-M processors. Standardized APIs are provided for software components that require RTOS functionality.
CMSIS-RTOS2 is a general-purpose API, which has nothing to do with the underlying RTOS kernel. Programmers who write applications call CMSIS-RTOS2 API functions in user code, which can more easily transfer applications from one RTOS to another. Use The middleware of CMSIS-RTOS2 API can also avoid a lot of unnecessary porting work.
Official API Reference: Main Page
The source code files of the kernel in the SDK are in kernel/liteos_m/components/cmsis.
2. Common functions
osThreadNew
Function function :
Create a new task.
Function prototype :
osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
Parameters :
func : the callback function of the thread
argument : A pointer passed to the thread function as a startup argument . Usually NULL
attr : Thread attributes. Thread-related attributes are set here, including thread stack size, priority, and so on . Mainly look at the osThreadAttr_t data type.
typedef struct {
/** Thread name */
const char *name;
/** Thread attribute bits */
uint32_t attr_bits;
/** Memory for the thread control block */
void *cb_mem;
/** Size of the memory for the thread control block */
uint32_t cb_size;
/** Memory for the thread stack */
void *stack_mem;
/** Size of the thread stack */
uint32_t stack_size;
/** Thread priority */
osPriority_t priority;
/** TrustZone module of the thread */
TZ_ModuleId_t tz_module;
/** Reserved */
uint32_t reserved;
} osThreadAttr_t;
name | thread name pointer to a readable string with the thread object Default value: NULL |
attr_bits | Attribute bits, options for thread objects can be set. osThreadDetached(0): create thread in detached mode (default) osThreadJoinable(1): Create a thread in joinable mode |
cb_mem | memory control block location Points to the memory location of the thread control block object. Used when static memory allocation Default: NULL (dynamic memory allocation) |
cb_size | The size of memory provided for the control block The size of the memory block is passed along with cb_mem. Must be greater than or equal to the size of the thread control block. (used when static memory allocation) |
stack_mem | memory stack location A pointer to the memory location of the thread's stack, which must be 64-byte aligned. Used for static memory allocation. Default: NULL (dynamic memory allocation) |
stack_size | stack size Stack size specified by stack_mem. That is, the stack size allocated to the created thread |
priority | thread priority. Default: osPriorityNormal(24) Note: The priority here is different from that of liteos. Liteos priority is 31 the lowest and 0 the highest. Here 0 is the lowest and 38 is the highest . |
tz_module | TrustZone module identifier A thread context management identifier allocates context memory for a thread. The RTOS kernel running in a non-secure state calls the interface functions defined by the header file TZ_context.h. Safe to set to zero for threads that do not use safe calls at all. |
reserved | reserve Default: 0 |
return value :
Thread ID, which can be called by other functions.
Example :
attr.name = "thread1";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 1;
attr.priority = 25;
g_thread1_id = osThreadNew((osThreadFunc_t)thread1, NULL, &attr);
if (g_thread1_id == NULL)
{
LOG_E("Falied to create thread1!");
}
osThreadYield
Function function :
Passes control to the next thread of the same priority that is in the READY state . If there are no other threads of the same priority in state READY, the current thread continues execution and no thread switch occurs . osThreadYield does not set the thread to state BLOCKED . Therefore, even if a READY thread is available, a lower priority thread will not be scheduled . This function cannot be called from an interrupt service routine .
Function prototype :
osStatus_t osThreadYield(void)
Parameters :
none
return value :
osOK: success
Other values: fail
Example :
osThreadYield();
osThreadGetId
Function function :
get thread id
Function prototype :
osThreadGetId
Parameters :
none
return value :
thread ID
Example :
osThreadId_t temp_t2_id = osThreadGetId();
osThreadGetName
Function function :
Get the name of the thread. The name is set when the thread is created .
Function prototype :
const char *osThreadGetName(osThreadId_t thread_id)
Parameters :
thread_id: Thread ID. Obtained through osThreadGetId or osThreadNew .
return value :
ID of the thread. Returns NULL on error
Example :
osThreadId_t temp_t2_id = osThreadGetId();
const char *temp_name = osThreadGetName(temp_t2_id);
osThreadGetStackSize
Function function :
Get the total thread stack size
Function prototype :
uint32_t osThreadGetStackSize(osThreadId_t thread_id)
Parameters :
thread ID. Obtained through osThreadGetId or osThreadNew .
return value :
total thread stack size
Example :
osThreadId_t temp_t2_id = osThreadGetId();
osThreadGetStackSize(temp_t2_id);
osThreadGetStackSpace
Function function :
Get thread remaining stack size
Function prototype :
uint32_t osThreadGetStackSpace(osThreadId_t thread_id)
Parameters :
thread ID. Obtained through osThreadGetId or osThreadNew .
return value :
thread remaining stack size
Example :
osThreadId_t temp_t2_id = osThreadGetId();
osThreadGetStackSpace(temp_t2_id);
osKernelGetTickCount
Function function :
Get the Tick number of the system clock. Usually the Tick count period is 1ms .
Function prototype :
uint32_t osKernelGetTickCount(void)
Parameters :
none
return value :
number of ticks
Example :
osKernelGetTickCount()
osDelay
Function function :
Thread hang time.
Function prototype :
osStatus_t osDelay(uint32_t ticks)
Parameters :
Number of pending ticks. The real hang time is ticks*10ms
return value :
osOK: normal
osError: exception
Example :
osDelay(200);
osDelayUntil
Function function :
The thread is suspended until the number of ticks.
Note: After calling this function, the thread will hang until the number of ticks of the system reaches the set number of ticks . For example, if the number of ticks is set to 1000, the thread will query the current number of system ticks after running to the osDelayUntil function. If it is less than 1000, it will suspend and wait until the number of system ticks is equal to 1000 before continuing to execute.
Function prototype :
osStatus_t osDelayUntil(uint32_t ticks)
Parameters :
number of ticks
return value :
osOK: normal
osError: exception
Example :
osDelayUntil(1000);
osThreadTerminate
Function function :
Delete thread. After the thread terminates, all resources are returned to the system .
Note: This function cannot be called in interrupt service
Function prototype :
osStatus_t osThreadTerminate(osThreadId_t thread_id)
Parameters :
thread ID
return value :
osOK: success
Other values: exception. The meaning is as follows:
typedef enum {
/** Operation completed successfully */
osOK = 0,
/** Unspecified error */
osError = -1,
/** Timeout */
osErrorTimeout = -2,
/** Resource error */
osErrorResource = -3,
/** Incorrect parameter */
osErrorParameter = -4,
/** Insufficient memory */
osErrorNoMemory = -5,
/** Service interruption */
osErrorISR = -6,
/** Reserved. It is used to prevent the compiler from optimizing enumerations. */
osStatusReserved = 0x7FFFFFFF
} osStatus_t;
Example :
osThreadId_t temp_t2_id = osThreadGetId();
osStatus_t ret = osThreadTerminate(temp_t2_id);
4. Comprehensive examples
Here we create two threads, and print the stack size, name, and remaining stack size of the two threads respectively. And task 1 uses the task ID returned by osThreadNew when it is created , and task 2 uses the task ID obtained by osThreadGetId . See if the effect is the same.
#define LOG_I(fmt, args...) printf("<%8ld> - [APP]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...) printf("<%8ld>-[APP_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);
osThreadId_t g_thread1_id = NULL;
osThreadId_t g_thread2_id = NULL;
/*****任务一*****/
void thread1(void)
{
LOG_I("thread 1 start");
const char *temp_name = osThreadGetName(g_thread1_id);
int sum = 0;
osDelayUntil(1000);
while (1)
{
LOG_I("this is Thread 1,name:[%s],thread stack size:[%ld],left stack:[%ld],sum:%d", temp_name,osThreadGetStackSize(g_thread1_id),osThreadGetStackSpace(g_thread1_id),sum);
osDelay(100);
if(sum++ > 10)
break;
}
LOG_I("thread 1 break");
osThreadTerminate(g_thread1_id);
}
/*****任务二*****/
void thread2(void)
{
LOG_I("thread 2 start");
osThreadId_t temp_t2_id = osThreadGetId();
const char *temp_name = osThreadGetName(temp_t2_id);
while (1)
{
LOG_I("this is Thread 2,name:[%s],thread stack size:[%ld],left stack:[%ld]", temp_name,osThreadGetStackSize(temp_t2_id),osThreadGetStackSpace(temp_t2_id));
osDelay(100);
}
LOG_I("thread 2 end");
}
void Hello_World(void)
{
osThreadAttr_t attr;
LOG_I("hello!!!!!!!!!!!!!!!!!!!!!!!!!");
attr.name = "thread1";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 1;
attr.priority = 25;
g_thread1_id = osThreadNew((osThreadFunc_t)thread1, NULL, &attr);
if (g_thread1_id == NULL)
{
LOG_E("Falied to create thread1!");
}
else
{
LOG_I("thread1 id:0x%.8x",*(uint32_t *)(g_thread1_id));
}
attr.name = "thread2";
attr.stack_size = 1024 * 2;
g_thread2_id = osThreadNew((osThreadFunc_t)thread2, NULL, &attr);
if (g_thread2_id == NULL)
{
LOG_E("Falied to create thread2!");
}
else
{
LOG_I("thread2 id:0x%.8x",*(uint32_t *)(g_thread2_id));
}
}
SYS_RUN(Hello_World);
The following points can be seen from the results:
- After thread 1 starts running, it encounters osDelayUntil and hangs directly. Wait until the system ticks reaches 1000 before continuing to run.
- The thread ID used in thread 1 is the one returned by osThreadNew when the thread was created. The thread ID used in thread 2 is obtained through the osThreadGetId function. can achieve the expected effect.
- osThreadGetStackSize and osThreadGetStackSpace will print out the total stack size and remaining stack size respectively.
- After thread 1 runs 10 times, it deletes itself and releases all resources.
Reference link: