前言
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 延時
有osDelay
和 osDelayUntil
兩個函數. 在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 Flags
是Event 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 */
}
編譯下載, 現象與上小節一樣.
微信公衆號
歡迎掃描二維碼關注本人微信公衆號, 及時獲取或者發送給我最新消息: