FreeRTOS中断

FreeRTOS的中断管理

‌‌‌  在STM32中,中断优先级是通过中断优先级配置寄存器的高4位 [7:4] 来配置的。因此STM32支持最多16级中断优先级,其中数值越小表示优先级越高,即更紧急的中断。(任务调度的任务优先级相反,是数值越大越优先)
‌‌‌  FreeRTOS可以与STM32原生的中断机制结合使用,但它提供了自己的中断管理机制,主要是为了提供更强大和灵活的任务调度和管理功能。
‌‌‌  FreeRTOS中,将PendSV和SysTick设置最低中断优先级(数值最大,15),保证系统任务切换不会阻塞系统其他中断的响应。
‌‌‌  FreeRTOS利用BASEPRI寄存器实现中断管理,屏蔽优先级低于某一个阈值的中断。比如: BASEPRI设置为0x50(只看高四位,也就是5),代表中断优先级在5–15内的均被屏蔽,0–4的中断优先级正常执行。这个也就是5–15的优先级被FreeRTOS管着,0~4还是归stm32管着。
‌‌‌  在这里插入图片描述

‌‌‌  在中断服务函数中调用FreeRTOS的API函数需注意:

  1. 中断服务函数的优先级需在FreeRTOS所管理的范围内,阈值由configMAX_SYSCALL_INTERRUPT_PRIORITY指定。
  2. 建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理。
  3. 在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数。

FreeRTOS的开关中断

‌‌‌  关中断即关掉中断,中断不起作用了,一个正在执行的程序不会因为突发事件被打断。
‌‌‌  开中断即打开中断,中断重新起作用。
‌‌‌  FreeRTOS 开关中断函数其实是宏定义,在 portmacro.h 中有定义,如下:

#define portDISABLE_INTERRUPTS()                  vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS()                   vPortSetBASEPRI( 0 )

‌‌‌  为什么要有关中断和开中断?
‌‌‌  简单理解为,不想让某一个执行的程序被打断,这个执行的程序一旦被打断(比如初始化程序),初始化都还没执行完毕,再去执行其他,又怎么会执行成功呢?

FreeRTOS的临界段代码

‌‌‌  学过操作系统的都知道,临界资源的概念即同一个时间段只允许一个进程访问的资源,对临界资源必须互斥地访问。
‌‌‌  临界资源要实现互斥访问,逻辑上分为四个部分:
‌‌‌  - 进入区,检查是否可以进入临界区,理解为上锁,而FreeRTOS中上锁就是关中断。
‌‌‌  - 临界区
‌‌‌  - 退出区,解锁即开中断。
‌‌‌  - 剩余区
‌‌‌  临界段代码,又称为临界区,指的是那些必须在不被打断的情况下完整运行的代码段。
不知道也没关系。你将临界段当成打游戏最激烈的那段时间,你不希望有人打扰,所以你选择锁门和戴上耳机,这个相当于关中断。关掉之后,接下来的时间谁都不能打扰我直到游戏完毕,然后取掉耳机打开门(开中断),饱餐一顿。
‌‌‌  在FreeRTOS中,进入临界段代码时需要关闭中断,在处理完临界段代码后再重新开启中断。
‌‌‌  FreeRTOS系统本身包含许多临界段代码,并对其进行了保护。在编写用户程序时,有些情况下也需要添加临界段代码以确保代码的完整性。
‌‌‌  与临界段代码保护有关的函数有 4 个:
1、taskENTER_CRITICAL() :进入临界段。
2、taskEXIT_CRITICAL() :退出临界段。
‌‌‌3、taskENTER_CRITICAL_FROM_ISR() :进入临界段(中断级)。
4、taskEXIT_CRITICAL_FROM_ISR():退出临界段(中断级)。
‌‌‌  进入和退出临界段是成对使用的。
‌‌‌  前面说过,进入临界区要上锁,也就是关中断,在freertos的代码中可以表现。
‌‌‌ 在这里插入图片描述

FreeRTOS中断管理实验

任务要求:

  • 设置管理的优先级范围:5~15。
  • 使用两个定时器,一个优先级为4,一个优先级为6。
  • 两个定时器每1s,打印一段字符串,当关中断时,停止打印,开中断时持续打印。

在动态创建任务的基础上进行修改的。
在Hal库添加两个定时器
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
添加完定时器,记得重新注释掉stm32f1xx_it.c的SVC_Handler和PendSV_Handler函数。这个是为了用FreeRTOS实现的函数

定时器中断函数

首先,在main函数中书写定时器中断函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2)
    {
       printf("TIM2优先级为4,运行中...\r\n");
    }
    else if(htim->Instance == TIM3)
    {
       printf("TIM3优先级为6,运行中...\r\n");
    }
}

记得开启中断,否则不会接收到

 /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim2);  
  HAL_TIM_Base_Start_IT(&htim3);  
  /* USER CODE END 2 */

添加配置文件

在FreeRTOSConfig.h里面添加

/* 中断嵌套行为配置 */
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS 4
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15                  /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5                   /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY                 ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY           configMAX_SYSCALL_INTERRUPT_PRIORITY

在这里插入图片描述

添加后编译会出现
在这里插入图片描述
这是因为多重宏定义FreeRTOSConfig.h已经存在了相同的宏,只需要注释一下
在这里插入图片描述

函数具体实现

默认FreeRTOS官方文件已经配置完毕。
任务宏定义

/* 启动任务的参数配置 */
#define START_STACK_DEPTH 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);
/* 任务1的参数配置 */
#define TASK1_STACK_DEPTH 128
#define TASK1_TASK_PRIORITY 2
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

启动函数和启动任务

void FreeRTOS_Start(void)
{
    /* 1.创建启动任务 */
    xTaskCreate((TaskFunction_t) Start_Task,
                (char *)  "Start_Task", 
                (configSTACK_DEPTH_TYPE) START_STACK_DEPTH,
                (void *)  NULL,
                (UBaseType_t)START_TASK_PRIORITY ,
                (TaskHandle_t * ) &start_task_handler );
    /* 2.启动调度器 */
    vTaskStartScheduler();
}
//启动任务
void Start_Task(void* pvParameters )
{
    //进入临界区
    //vPortEnterCritical();
    taskENTER_CRITICAL();
    /* 创建任务 1*/
    xTaskCreate((TaskFunction_t) Task1,
                (char *)  "Task1", 
                (configSTACK_DEPTH_TYPE) TASK1_STACK_DEPTH,
                (void *)  NULL,
                (UBaseType_t)TASK1_TASK_PRIORITY ,
                (TaskHandle_t * ) &task1_handler );
    /* 启动任务只需要执行一次,用完就删 */
    vTaskDelete(NULL);
    //退出临界区
    //vPortExitCritical();
    taskEXIT_CRITICAL();
}

任务1函数实现:

void Task1(void* pvParameters )
{
    uint32_t delay=0;
    while (1)
    {
       delay=50000000;
       /* 关中断:观察tim的打印情况 */
       printf("关中断..............");
       portDISABLE_INTERRUPTS();
       /* 加个延时,观察 */
       while(delay--);
       /* 开中断:观察tim的打印情况 */
       printf("开中断..............");
       portENABLE_INTERRUPTS();
    }
    
}

完整代码

#include "freertos_demo.h"
/* freertos相关的头文件 */
#include "FreeRTOS.h"
#include "task.h"

/* 任务需要用到 */
#include "LED.h"
#include "Key.h"
#include "usart.h"


/* 启动任务的参数配置 */
#define START_STACK_DEPTH 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);
/* 任务1的参数配置 */
#define TASK1_STACK_DEPTH 128
#define TASK1_TASK_PRIORITY 2
TaskHandle_t task1_handler;
void Task1(void *pvParameters);


/**
* @description:入口函数,创建启动任务,启动调度器
* @return{}
*/
void FreeRTOS_Start(void)
{
    /* 1.创建启动任务 */
    xTaskCreate((TaskFunction_t) Start_Task,
                (char *)  "Start_Task", 
                (configSTACK_DEPTH_TYPE) START_STACK_DEPTH,
                (void *)  NULL,
                (UBaseType_t)START_TASK_PRIORITY ,
                (TaskHandle_t * ) &start_task_handler );
    /* 2.启动调度器 */
    vTaskStartScheduler();
}


/**
* @description:启动任务,创建其他任务
* @return{}
*/
void Start_Task(void* pvParameters )
{
    //进入临界区
    //vPortEnterCritical();
    taskENTER_CRITICAL();
    /* 创建任务 1*/
    xTaskCreate((TaskFunction_t) Task1,
                (char *)  "Task1", 
                (configSTACK_DEPTH_TYPE) TASK1_STACK_DEPTH,
                (void *)  NULL,
                (UBaseType_t)TASK1_TASK_PRIORITY ,
                (TaskHandle_t * ) &task1_handler );
    /* 启动任务只需要执行一次,用完就删 */
    vTaskDelete(NULL);
    //退出临界区
    //vPortExitCritical();
    taskEXIT_CRITICAL();
}
/**
* @description:两个定时器每1s,打印一段字符串,当关中断时,停止打印,开中断时持续打印。
* @return{}
*/
void Task1(void* pvParameters )
{
    uint32_t delay=0;
    while (1)
    {
       delay=50000000;
       /* 关中断:观察tim的打印情况 */
       printf("关中断..............");
       portDISABLE_INTERRUPTS();
       /* 加个延时,观察 */
       while(delay--);
       /* 开中断:观察tim的打印情况 */
       printf("开中断..............");
       portENABLE_INTERRUPTS();
    }
    
}

结果展示

在这里插入图片描述
下载程序后,定时器每s进一次中断,因为定时器3的优先级高,所以先打印定时器2,然后打印定时器3。
进入关中断后,因为FreeRTOS只管理5~15的优先级中断,所以定时器3被关掉了,定时器2不归FreeRTOS管理,只打印定时器2。
等到一段时间后开中断,两个定时器都开始工作。

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值