前言
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 */
}
编译下载, 现象与上小节一样.
微信公众号
欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息: