FreeRTOS 定時器 延時 事件/線程Flag

前言

FreeRTOS STM32CubeMX配置 內存管理 任務管理
FreeRTOS 隊列 信號量 互斥量
這是前兩篇, 本篇繼續, 主要總結下FreeRTOS或者更確切的說是 CMSIS_RTOS2 的Timer(軟件定時器), Wait(延時), Event Flags(事件標誌)和Thread Flags(線程標誌).

Timer 軟件定時器

軟件定時器是FreeRTOS的一個組件, 雖不是那麼精確但可以處理週期性的動作, 可以創建一次定時和週期定時. cmsis_os2.h中聲明的定時器管理函數:

//  ==== Timer Management Functions ====

/// Create and Initialize a timer.
/// \param[in]     func          function pointer to callback function.
/// \param[in]     type          \ref osTimerOnce for one-shot or \ref osTimerPeriodic for periodic behavior.
/// \param[in]     argument      argument to the timer callback function.
/// \param[in]     attr          timer attributes; NULL: default values.
/// \return timer ID for reference by other functions or NULL in case of error.
osTimerId_t osTimerNew (osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr);

/// Get name of a timer.
/// \param[in]     timer_id      timer ID obtained by \ref osTimerNew.
/// \return name as NULL terminated string.
const char *osTimerGetName (osTimerId_t timer_id);

/// Start or restart a timer.
/// \param[in]     timer_id      timer ID obtained by \ref osTimerNew.
/// \param[in]     ticks         \ref CMSIS_RTOS_TimeOutValue "time ticks" value of the timer.
/// \return status code that indicates the execution status of the function.
osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks);

/// Stop a timer.
/// \param[in]     timer_id      timer ID obtained by \ref osTimerNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osTimerStop (osTimerId_t timer_id);

/// Check if a timer is running.
/// \param[in]     timer_id      timer ID obtained by \ref osTimerNew.
/// \return 0 not running, 1 running.
uint32_t osTimerIsRunning (osTimerId_t timer_id);

/// Delete a timer.
/// \param[in]     timer_id      timer ID obtained by \ref osTimerNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osTimerDelete (osTimerId_t timer_id);

用法和示例參考https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__TimerMgmt.html

週期性的定時行爲如下圖:
在這裏插入圖片描述
新建兩個任務:
在這裏插入圖片描述
添加兩個軟件定時器, 前者是一次性的, 後者是週期性的:
在這裏插入圖片描述
生成代碼, 補充freertos.c:

void Entry_Task1(void *argument)
{
  /* USER CODE BEGIN Entry_Task1 */
	osTimerStart(myTimer01Handle, 1000);
  /* Infinite loop */
  for(;;)
  {
		osDelay(1);
  }
  /* USER CODE END Entry_Task1 */
}

void Entry_Task2(void *argument)
{
  /* USER CODE BEGIN Entry_Task2 */
	osTimerStart(myTimer02Handle, 1000);
  /* Infinite loop */
  for(;;)
  {
		osDelay(1);
  }
  /* USER CODE END Entry_Task2 */
}

/* Callback01 function */
void Callback01(void *argument)
{
  /* USER CODE BEGIN Callback01 */
  HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
  /* USER CODE END Callback01 */
}

/* Callback02 function */
void Callback02(void *argument)
{
  /* USER CODE BEGIN Callback02 */
  HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
  /* USER CODE END Callback02 */
}

編譯運行, 可以看到LED1亮一次後就保持常亮, LED2每1s翻轉一次狀態.

Wait 延時

osDelayosDelayUntil 兩個函數. 在Include parameters配置選項卡中, 兩個均默認使能. 在cmsis_os2.h中的聲明:

//  ==== Generic Wait Functions ====

/// Wait for Timeout (Time Delay).
/// \param[in]     ticks         \ref CMSIS_RTOS_TimeOutValue "time ticks" value
/// \return status code that indicates the execution status of the function.
osStatus_t osDelay (uint32_t ticks);

/// Wait until specified time.
/// \param[in]     ticks         absolute time in ticks
/// \return status code that indicates the execution status of the function.
osStatus_t osDelayUntil (uint32_t ticks);

API及示例參考 https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__Wait.html
如果任務都沒有延時, 系統將在Runing和Ready兩個狀態切換.
如果任務恰好都處在osDelay狀態, 系統將運行在idle模式, 參考第一篇中的空閒任務.

創建兩個任務:
在這裏插入圖片描述
生成代碼, 補充freertos.c:

void Entry_Task1(void *argument)
{
  /* USER CODE BEGIN Entry_Task1 */
  /* Infinite loop */
  for(;;)
  {
		HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    	osDelay(1000);
  }
  /* USER CODE END Entry_Task1 */
}

void Entry_Task2(void *argument)
{
  /* USER CODE BEGIN Entry_Task2 */
  uint32_t tick;
  tick = osKernelGetTickCount();
  /* Infinite loop */
  for(;;)
  {
		HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin); 
		tick += 1000U;
		osDelayUntil(tick);
  }
  /* USER CODE END Entry_Task2 */
}

上面兩種延時等效, 兩個LED同時翻轉.

Event Flags

下面是兩個線程用event flags通信的簡單示例:
在這裏插入圖片描述
cmsis_os2.h中聲明的Event Flags管理函數:

//  ==== Event Flags Management Functions ====

/// Create and Initialize an Event Flags object.
/// \param[in]     attr          event flags attributes; NULL: default values.
/// \return event flags ID for reference by other functions or NULL in case of error.
osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr);

/// Get name of an Event Flags object.
/// \param[in]     ef_id         event flags ID obtained by \ref osEventFlagsNew.
/// \return name as NULL terminated string.
const char *osEventFlagsGetName (osEventFlagsId_t ef_id);

/// Set the specified Event Flags.
/// \param[in]     ef_id         event flags ID obtained by \ref osEventFlagsNew.
/// \param[in]     flags         specifies the flags that shall be set.
/// \return event flags after setting or error code if highest bit set.
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);

/// Clear the specified Event Flags.
/// \param[in]     ef_id         event flags ID obtained by \ref osEventFlagsNew.
/// \param[in]     flags         specifies the flags that shall be cleared.
/// \return event flags before clearing or error code if highest bit set.
uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags);

/// Get the current Event Flags.
/// \param[in]     ef_id         event flags ID obtained by \ref osEventFlagsNew.
/// \return current event flags.
uint32_t osEventFlagsGet (osEventFlagsId_t ef_id);

/// Wait for one or more Event Flags to become signaled.
/// \param[in]     ef_id         event flags ID obtained by \ref osEventFlagsNew.
/// \param[in]     flags         specifies the flags to wait for.
/// \param[in]     options       specifies flags options (osFlagsXxxx).
/// \param[in]     timeout       \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
/// \return event flags before clearing or error code if highest bit set.
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout);

/// Delete an Event Flags object.
/// \param[in]     ef_id         event flags ID obtained by \ref osEventFlagsNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osEventFlagsDelete (osEventFlagsId_t ef_id);

用例參考 https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__EventFlags.html

一個線程:

  • 可以用osEventFlagsWait等待event flags被設置, 進入BLOCKED狀態
  • 可以用osEventFlagsSet設置一個或多個flags
  • 可以用osEventFlagsClear清除自身或者其它線程的signals

注意每個信號有31個Event Flags

創建三個任務:
在這裏插入圖片描述
生成代碼, freertos.c中添加代碼:

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define BIT_0	( 1 << 0 )
#define BIT_1	( 1 << 1 )
#define BIT_2	( 1 << 2 )
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
osEventFlagsId_t led_evt_id;	//message queue id
/* USER CODE END Variables */

void Entry_Task1(void *argument)
{
  /* USER CODE BEGIN Entry_Task1 */
	uint32_t result;
  /* Infinite loop */
  for(;;)
  {
		result = osEventFlagsWait(led_evt_id, BIT_0, osFlagsWaitAny, osWaitForever); 
		if(result==BIT_0) {
			HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
		}
  }
  /* USER CODE END Entry_Task1 */
}

void Entry_Task2(void *argument)
{
  /* USER CODE BEGIN Entry_Task2 */
	uint32_t result;
  /* Infinite loop */
  for(;;)
  {
		result = osEventFlagsWait(led_evt_id, BIT_1 | BIT_2, osFlagsWaitAny, osWaitForever); 
		if(result == (BIT_1 | BIT_2)) {
			HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
		}
  }
  /* USER CODE END Entry_Task2 */
}

void Entry_Task3(void *argument)
{
  /* USER CODE BEGIN Entry_Task3 */
	led_evt_id = osEventFlagsNew(NULL);
  /* Infinite loop */
  for(;;)
  {
		osEventFlagsSet(led_evt_id, BIT_0);
    	osDelay(1000);
		osEventFlagsSet(led_evt_id, BIT_1 | BIT_2);
    	osDelay(1000);
  }
  /* USER CODE END Entry_Task3 */
}

Task_3創建了信號led_evt_id, 設置相應的位, Task_1和Task_2等待相應的位置位後才往下運行(會自動清除位). 編譯運行, 兩個LED都是2s的週期翻轉, LED1比LED2先亮1s.

注意osFlagsWaitAny可替換的參數爲:
在這裏插入圖片描述

Thread Flags

Thread FlagsEvent Flags的一個特別版, Event Flags可以用於多個線程的全局信號, 而Thread Flags僅僅發給單個的特定線程. 每個線程實例都可以接受therad flags, 無需額外分配therad flags對象, 意思是不用像上面event flags那樣還要創建一個led_evt_id.

cmsis_os2.h中聲明的Thread Flags 函數:

//  ==== Thread Flags Functions ====

/// Set the specified Thread Flags of a thread.
/// \param[in]     thread_id     thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
/// \param[in]     flags         specifies the flags of the thread that shall be set.
/// \return thread flags after setting or error code if highest bit set.
uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags);

/// Clear the specified Thread Flags of current running thread.
/// \param[in]     flags         specifies the flags of the thread that shall be cleared.
/// \return thread flags before clearing or error code if highest bit set.
uint32_t osThreadFlagsClear (uint32_t flags);

/// Get the current Thread Flags of current running thread.
/// \return current thread flags.
uint32_t osThreadFlagsGet (void);

/// Wait for one or more Thread Flags of the current running thread to become signaled.
/// \param[in]     flags         specifies the flags to wait for.
/// \param[in]     options       specifies flags options (osFlagsXxxx).
/// \param[in]     timeout       \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
/// \return thread flags before clearing or error code if highest bit set.
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout);

用例參考 https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__ThreadFlagsMgmt.html

仍然是上小節三個任務, freertos.c中添加代碼:

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define BIT_0	( 1 << 0 )
#define BIT_1	( 1 << 1 )
#define BIT_2	( 1 << 2 )
/* USER CODE END PM */

void Entry_Task1(void *argument)
{
  /* USER CODE BEGIN Entry_Task1 */
	uint32_t result;
  /* Infinite loop */
  for(;;)
  {
		result = osThreadFlagsWait(BIT_0, osFlagsWaitAny, osWaitForever); 
		if(result==BIT_0) {
			HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
		}
  }
  /* USER CODE END Entry_Task1 */
}

void Entry_Task2(void *argument)
{
  /* USER CODE BEGIN Entry_Task2 */
	uint32_t result;
  /* Infinite loop */
  for(;;)
  {
		result = osThreadFlagsWait(BIT_1 | BIT_2, osFlagsWaitAny, osWaitForever); 
		if(result == (BIT_1 | BIT_2)) {
			HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
		}
  }
  /* USER CODE END Entry_Task2 */
}

void Entry_Task3(void *argument)
{
  /* USER CODE BEGIN Entry_Task3 */
  /* Infinite loop */
  for(;;)
  {
		osThreadFlagsSet(Task1Handle, BIT_0);
    	osDelay(1000);
		osThreadFlagsSet(Task2Handle, BIT_1 | BIT_2);
    	osDelay(1000);
  }
  /* USER CODE END Entry_Task3 */
}

編譯下載, 現象與上小節一樣.

微信公衆號

歡迎掃描二維碼關注本人微信公衆號, 及時獲取或者發送給我最新消息:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章