一:RTOS中的常用的用户函数说明如下:
1.1 osThreadId 函数说明如下所示:
/// Thread ID identifies the thread (pointer to a thread control block). /// \note CAN BE CHANGED: \b os_thread_cb is implementation specific in every CMSIS-RTOS. typedef TaskHandle_t osThreadId;
任务的ID,可以使用到其他函数说明中
1.2 任务的创建
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument);
如在上一篇帖子中,我们创建LED灯闪烁的任务:
任务创建的原型函数如下所示:
/*********************** Thread Management *****************************/
/**
* @brief Create a thread and add it to Active Threads and set it to state READY.
* @param thread_def thread definition referenced with \ref osThread.
* @param argument pointer that is passed to the thread function as start argument.
* @retval thread ID for reference by other functions or NULL in case of error.
* @note MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS.
*/
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
{
TaskHandle_t handle;
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {
handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
thread_def->buffer, thread_def->controlblock);
}
else {
if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
&handle) != pdPASS) {
return NULL;
}
}
#elif( configSUPPORT_STATIC_ALLOCATION == 1 )
handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
thread_def->buffer, thread_def->controlblock);
#else
if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
&handle) != pdPASS) {
return NULL;
}
#endif
return handle;
}在上个帖子中,应用如下所示:
首先定义任务的句柄如下:
osThreadId LED0Handle; osThreadId LED1Handle;
osThreadDef(LED0, StartTask02, osPriorityIdle, 0, 128); LED0Handle = osThreadCreate(osThread(LED0), NULL);
1.3 删除任务:
在STM32cube MX 软件配置的时候,已将删除任务的功能打开,我们只需要直接调用就可以了。
/**
* @brief Terminate execution of a thread and remove it from Active Threads.
* @param thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
* @retval status code that indicates the execution status of the function.
* @note MUST REMAIN UNCHANGED: \b osThreadTerminate shall be consistent in every CMSIS-RTOS.
*/
osStatus osThreadTerminate (osThreadId thread_id)
{
#if (INCLUDE_vTaskDelete == 1)
vTaskDelete(thread_id);
return osOK;
#else
return osErrorOS;
#endif
}1.4 开启任务调度:
在程序进入主程序之前,我们需要开启任务调度器,从而使rtos的任务正常的工作起来,在我们添加了LED0和LED1的两个任务到项目中,实际上系统还是处于未工作状态,没有实现任务调度,而且基本的空闲任务也没有实现,系统的时基定时器也没有正常工作。这两个任务都是在开启调度函数中实现的。
这时候,有的人会疑问?RTOS 中为什么要有空闲任务呢?没有这个空闲任务会有什么影响?
在RTOS中,一旦开启了任务调度,就必须保证系统中的存在一个任务处于工作状态,而且要求空闲的任务不能被挂起与删除,同事要求空闲任务的优先级是最低的,这样项目中的其他任务都可以打断空闲任务的运行。
/**
* @brief Start the RTOS Kernel with executing the specified thread.
* @param thread_def thread definition referenced with \ref osThread.
* @param argument pointer that is passed to the thread function as start argument.
* @retval status code that indicates the execution status of the function
* @note MUST REMAIN UNCHANGED: \b osKernelStart shall be consistent in every CMSIS-RTOS.
*/
osStatus osKernelStart (void)
{
vTaskStartScheduler();
return osOK;
}二:其他的API应用函数:
2.1 延时函数:
延时函数,用于阻塞延时使用,在任务中调用该函数时,任务进入阻塞状态,进入阻塞态的额任务将让出CPU资源。
cube Mx软件初始化时候,会自动使能该函数,用户无需配置。
当然也可以关闭:要想使用该函数必须在 Include parameters 中把 vTaskDelay 选择 Disabled 来失能。
osStatus osDelay (uint32_t millisec)
{
#if INCLUDE_vTaskDelay
TickType_t ticks = millisec / portTICK_PERIOD_MS;
vTaskDelay(ticks ? ticks : 1); /* Minimum delay = 1 tick */
return osOK;
#else
(void) millisec;
return osErrorResource;
#endif
}2.2 osThreadTerminate
osDelayUntil 基于系统的绝对时间进行延时,这意味着它会等待直到系统时钟达到某个特定的计数值。这种方式通常与 osKernelGetTickCount 函数配合使用,后者用于获取系统时钟的当前计数值。
在多线程环境中,即使设置了延时,也可能因为存在更高优先级的线程或中断服务例程(ISR)的回调处理而导致线程的执行被推迟。因此,计算线程被推迟的确切时间可能相当困难。尽管如此,使用 osDelayUntil 可以确保线程与预期的“匹配值”同步,这使得它成为实现长时间运行的周期性任务的推荐选择。
osStatus osDelayUntil (uint32_t *PreviousWakeTime, uint32_t millisec)
{
#if INCLUDE_vTaskDelayUntil
TickType_t ticks = (millisec / portTICK_PERIOD_MS);
vTaskDelayUntil((TickType_t *) PreviousWakeTime, ticks ? ticks : 1);
return osOK;
#else
(void) millisec;
(void) PreviousWakeTime;
return osErrorResource;
#endif
}函数程序如下所示:
void vTask_LED( void * pvParameters )
{
/* 用于保存上次时间。调用后系统自动更新 */
static portTickType PreviousWakeTime;
/* 设置延时时间,将时间转为节拍数 */
const portTickType TimeIncrement = pdMS_TO_TICKS(1000);
/* 获取当前系统时间 */
PreviousWakeTime = osKernelSysTick();
while (1)
{
/* 调用绝对延时函数,任务时间间隔为 1000 个 tick */
osDelayUntil( &PreviousWakeTime,TimeIncrement );
// 添加用户任务就看可以了。
}
}三:任务挂起与恢复
3.1 任务挂起
函数说明:挂起指定的任务,被挂起的任务不会占用CPU的资源,可以将一个线程挂起,使其进入阻塞状态,直到其他线程使用。
osThreadSuspend
osStatus osThreadSuspend (osThreadId thread_id)
{
#if (INCLUDE_vTaskSuspend == 1)
vTaskSuspend(thread_id);
return osOK;
#else
return osErrorResource;
#endif
}应用函数如下所示:
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
static void KEY_Task(void* parameter)
{
while (1)
{
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13) == 0)
{
osThreadSuspend(LED0Handle);/* 挂起 LED 任务 */
}
osDelay(50);/* 延时50个 tick */
}
}3.2 任务的恢复
函数说明:让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续运行。如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。
被唤醒的线程会立即运行,前提是它的优先级高于当前运行的线程。
osThreadResume
osStatus osThreadResume (osThreadId thread_id)
{
#if (INCLUDE_vTaskSuspend == 1)
if(inHandlerMode())
{
if (xTaskResumeFromISR(thread_id) == pdTRUE)
{
portYIELD_FROM_ISR(pdTRUE);
}
}
else
{
vTaskResume(thread_id);
}
return osOK;
#else
return osErrorResource;
#endif
}参考挂起的任务,可以实现恢复的调用
osThreadResume(LED0Handle);/* 恢复 LED0 任务! */
3.3 中断服务函数中恢复任务调度
使用 xTaskResumeFromISR()的时候有几个需要注意的地方:
当函数的返回值为 pdTRUE 时:恢复运行的任务的优先级等于或高于正在运行的任务,表明在中断服务函数退出后必 须进行一次上下文切换 , 使用 portYIELD_FROM_ISR() 进行上下文切换。当函数的返回值为 pdFALSE 时:恢复运行的任务的优先级低于当前正在运行的任务,表明在中断服务函数退出后不需 要进行上下文切换。
xTaskResumeFromISR() 通常被认为是一个危险的函数,因为它的调用并非是固定的,中断可能随时来来临。
所以xTaskResumeFromISR()不能用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失(任务还没有挂起,调用 xTaskResumeFromISR()函数是没有意义的,只能等下一次中断)。这种情况下,可以使用信号量或者任务通知来同步就可以避免这种情况。
我要赚赏金
