一、简介
1.FreeRTOS简介
RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性。而Free显而易见体现的是其免费性。总的来说这是一个免费的嵌入式实时操作系统。
其特点是:免费开源、可剪裁(独立性强,适应范围广)、简单、优先级/任务不限(但是受到不同开发环境和硬件的限制,一般受限)、支持三种方式的任务调度。
与裸机的区别:裸机的应用程序整体来看放在整个大循环里,很多时候资源浪费即“空等待”;而RTOS是多个优先级相同的任务每个任务执行一个时间片(时间长度可以调节),来回切换,最终效果是所有优先级相同的任务同时进行,而且有任务被“阻塞”时,会释放cpu资源。
2.任务调度简介
一共支持三种方式:
1)抢占式调度:主要是针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可以抢占优先级低的任务。
2)时间片调度:主要针对优先级相同的任务,当多个任务的优先级相同时, 任务调度器会在每一次系统时钟节拍到的时候切换任务。
3)协程式调度:当前执行任务将会一直运行,同时高优先级的任务不会抢占低优先级任务 FreeRTOS现在虽然还支持,但是官方已经表示不再更新协程式调度。仅作了解。
3.任务状态简介
FreeRTOS总共存在4种任务状态:
运行态:正在占据CPU资源,正在运行的任务。每一时刻只存在一个任务处于运行态。
就绪态:简单概括,此任务若准备工作都已经做完,即可进入就绪态。
阻塞态:如果一个任务因延时或等待外部事件发生,那么这个任务就处于阻塞态。
挂起态:类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用解挂函数vTaskResume() 才可以进入就绪态。
注意挂起和删除的区别,删除是将此任务从任务堆栈中完全移除,挂起只是暂时停止运行。
所有的任务想进入运行态必须要先进入就绪态,而从运行态可以进入任何状态。如下图表示:
在FreeRTOS中有许多Include可以配置,主要是使能一些功能。包括任务挂起、恢复、中断恢复等等,在需要时使能即可。
二、HAL库配置
1.时钟树的配置
参考:STM32 hal库使用笔记(一)GPIO的使用—流水灯_乱码小伙的博客-CSDN博客
由于FreeRTOS的系统定时器要用滴答定时器,而CubeMX不推荐共用,所以需要修改Timebase Source的来源于其他的定时器。
2.FreeRTOS的配置
2.1 动态任务的配置(代码对应3.1)
实验目标:添加两个任务,分别是task1、task2,task1中实现LED0的定时翻转,task2中实现LED1的定时翻转。当按键1按下时,删除任务2。
使用CubeMX配置后,会自动剪裁FreeRTOS,只保留必要文件,由于只是实验动态任务,所有大部分默认即可。
添加任务1和任务2,注意,这两个任务的级别可以相同也可以不同,因为LED翻转会有阻塞态,会释放CPU资源给另一个LED。
配置完成后生成代码即可。
2.2 静态任务的配置
静态任务的创建与静态任务的创建的最大区别就是,静态任务需要用户自己分配任务堆栈,步骤麻烦一些,不过利用CubeMX,只需修改任务创建方式即可。
任务控制块的作用:用来保存任务信息,在任务切换时保存任务状态,方便恢复现场。
其实验效果和原理与动态基本一致,就不再演示实验效果和代码部分。
关于任务删除有几点补充:在一个任务中删除的任务要不是这个任务本身,要不就是其他的任务,如果是其他任务,则会在本任务中将被删除的任务释放;若是任务本身,则需要在空闲任务中删除本任务,所以FreeRTOS会自动创建空闲任务。(这是针对的动态任务,静态任务需要用户进行删除。)
2.3 任务挂起和恢复的配置(包括中断恢复,对应代码3.2)
实验目的:配置两个任务和一个外部中断,由于用LED灯来显示的话与上个任务的现象差不多,所以改用串口来检测,按键1在任务中挂起任务,按键2在任务中恢复任务,按键3在中断中恢复任务。
串口的配置参考:STM32 hal库使用笔记(二)中断—串口中断_乱码小伙的博客-CSDN博客
外部中断的配置参考:STM32 hal库使用笔记(二)中断—外部中断_乱码小伙的博客-CSDN博客
先说一些中断恢复的注意问题,为了任务调度时任务的优先级清楚、明晰、好分配所以CubeMX会自动将中断分组设为4,这样就没有响应优先级的冲突,由抢占优先级起到决定作用。而想在中断中使用API函数,中断的优先级必须在FreeRTOS的管理范围内,这个范围可以配置,默认是5-15。如果优先级分组/优先级不是如上所说的配置,在中断中调用API函数时会发生很多问题,老版本会不产生作用,但是我的实测是直接卡死,所以还是建议按照分组和优先级的规则进行配置。
在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数。
Config的配置同上就行,再就是确保以下的两个函数使能即可(才能打开对应的寄存器)。
2.4 任务中断管理配置(代码参考3.3)
实验目的:检验中断管理的作用,属于FreeRTOS管理的范围内(5~15)的中断,可以利用portDISABLE_INTERRUPTS()函数进行关闭。利用串口进行检验,将定时器2、3的抢占优先级配为4和6,建立一个任务,在任务中利用按键1和按键2来开启和关闭中断,串口打印信息进行反馈。
定时器配置参考:STM32 hal库使用笔记(二)中断—定时器中断_乱码小伙的博客-CSDN博客
按键配置参考:STM32 hal库使用笔记(一)GPIO的使用—按键控制LED_乱码小伙的博客-CSDN博客
CubeMX的配置同上,打开两个函数的使能,配置一个任务即可。
三、代码编写
3.1
只需要编写任务即可。
任务1:
void StartTask02(void const * argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0);
if(myTask03Handle!=NULL){
vTaskDelete(myTask03Handle);
myTask03Handle=NULL;
}
}
}
osDelay(500);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
k++;if(k==4)printf("task1正在运行\r\n");
if(k>7)k=0;
//uxTaskGetStackHighWaterMark(myTask02Handle);
}
/* USER CODE END StartTask02 */
}
任务2:
void StartTask03(void const * argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(500);
if(k==7)printf("task2正在运行\r\n");
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
}
/* USER CODE END StartTask03 */
}
串口部分不用配置,删除即可。
实现现象:
任务的建立和删除实验
3.2
编写任务1和任务2,以及外部中断更新回调函数即可。
任务1:
void StartTask02(void const * argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
osDelay(500);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
count1++;
if(count1==9)
{
printf("-----task1正在运行-----\r\n");
count1=0;
}
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0);
vTaskSuspend(myTask03Handle);
printf("!!!--------task2被挂起--------!!!\r\n");
}
}
else if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==0)
{
while(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0);
vTaskResume(myTask03Handle);
printf("!!!--------task2已在任务中恢复--------!!!\r\n");
}
}
}
/* USER CODE END StartTask02 */
}
任务2:
void StartTask03(void const * argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(500);
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
count2++;
if(count2==5)
{
printf("-----task2正在运行-----\r\n");
count2=0;
}
}
/* USER CODE END StartTask03 */
}
中断回调函数,注意抢占优先级必须在5~15:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
BaseType_t xYieldRequired;
if(GPIO_Pin == KEY_UP_Pin)
{
xYieldRequired = xTaskResumeFromISR( myTask03Handle );
if(xYieldRequired == pdTRUE )//需要任务切换
{
portYIELD_FROM_ISR( xYieldRequired );
}
printf("!!!----已经在中断中恢复task2----!!!");
}
}
有个问题需要说明,printf函数占时较长,在中断中不宜使用,下个实验已经更换。
实现现象:
3.3
同理,只需要编写任务逻辑和中断更新回调函数即可。
任务编写:
void StartTask02(void const * argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0);
portDISABLE_INTERRUPTS();
printf("!!!--------中断关闭--------!!!\r\n");
}
}
else if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==0)
{
while(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0);
portENABLE_INTERRUPTS();
printf("!!!--------中断开启--------!!!\r\n");
}
}
}
/* USER CODE END StartTask02 */
}
中断回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM2)
{
uint8_t MyArray[] = {"定时器2正在运行\r\n"};
HAL_UART_Transmit(&huart1, MyArray, sizeof(MyArray), 10000);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
if (htim->Instance == TIM3)
{
uint8_t MyArray2[] = {"定时器3正在运行\r\n"};
HAL_UART_Transmit(&huart1, MyArray2, sizeof(MyArray2), 10000);
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
}
/* USER CODE END Callback 1 */
}
实验现象:
可以看到,中断关闭,在管理范围内的定时器3已经停止工作,不在管理范围内的定期器2仍在工作。文章来源:https://www.toymoban.com/news/detail-761354.html
欢迎大家交流和指正!!!不胜欣喜!!!文章来源地址https://www.toymoban.com/news/detail-761354.html
到了这里,关于STM32 hal库使用笔记之FreeRTOS—任务创建、删除,任务挂起、恢复,任务中断管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!