FreeRTOS静态任务的中断管理、时间片调度(尚硅谷学习笔记)

中断管理:

在STM32中,中断优先级是通过中断优先级配置寄存器的高4位 [7:4] 来配置的。因此STM32支持最多16级中断优先级,其中数值越小表示优先级越高,即更紧急的中断。(FreeRTOS任务调度的任务优先级相反,是数值越大越优先)

FreeRTOS可以与STM32原生的中断机制结合使用,但它提供了自己的中断管理机制,主要是为了提供更强大和灵活的任务调度和管理功能。

FreeRTOS中,将PendSV和SysTick设置最低中断优先级(数值最大,15),保证系统任务切换不会阻塞系统其他中断的响应。

FreeRTOS利用BASEPRI寄存器实现中断管理,屏蔽优先级低于某一个阈值的中断。比如: BASEPRI设置为0x50(只看高四位,也就是5),代表中断优先级在5~15内的均被屏蔽,0~4的中断优先级正常执行。

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

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

 实验目标:

学会FreeRTOS中断管理:

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

观察两个定时器的打印情况。

CubeMX两个定时器:

定时器TIM1设置为1s定时,中断优先级为6

定时器TIM2设置为1s定时,中断优先级为4

中断管理宏定义:

/*3. 中断嵌套行为相关配置 cm3内核:我们要求4个优先级位全部为抢占优先级位
最高优先级是 0
最低优先级是 15
*/
/* 设置 RTOS 内核自身使用的中断优先级。 一般设置为最低优先级, 不至于屏蔽其他优先级程序*/
#define configKERNEL_INTERRUPT_PRIORITY (15 << 4)
/* 设置了 调用中断安全的 FreeRTOS API 函数的最高中断优先级。 FreeRTOS 的管理的最高优先级 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (5 << 4)
/* 同上. 仅用于新版移植。 这两者是等效的。 */
#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY

 freertos_demo.c

/*
 * @Author: random
 * @Date: 2025-05-10 01:23:40
 * @Last Modified by: random
 * @Last Modified time: Do not Edit
 */
#include "freertos_demo.h"
/* freertos相关的头文件,必须的 */
#include "FreeRTOS.h"
#include "task.h"
/* 需要用到的其他头文件 */
#include "fun.h"

/* 启动任务的配置 */
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
StackType_t start_task_stack[START_TASK_STACK]; // 静态任务的任务栈,以数组形式存储
StaticTask_t start_task_tcb;                    // 静态任务的tcb结构体类型
void start_task(void *pvParameters);

/* 任务1的配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
StackType_t task1_stack[TASK1_STACK]; // 静态任务的任务栈,以数组形式存储
StaticTask_t task1_tcb;               // 静态任务的tcb结构体类型
void task1(void *pvParameters);

/* ==================== 静态创建方式,需要手动指定2个特殊任务的资源======================= */
/* 空闲任务的配置 */
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE]; // 静态任务的任务栈,以数组形式存储
StaticTask_t idle_task_tcb;                            // 静态任务的tcb结构体类型

/* 软件定时器任务的配置 */
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH]; // 静态任务的任务栈,以数组形式存储
StaticTask_t timer_task_tcb;                                // 静态任务的tcb结构体类型

/* 分配空闲任务的资源 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
                                   StackType_t **ppxIdleTaskStackBuffer,
                                   uint32_t *pulIdleTaskStackSize)
{
    *ppxIdleTaskTCBBuffer = &idle_task_tcb;
    *ppxIdleTaskStackBuffer = idle_task_stack;
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

/* 分配软件定时器任务的资源 */
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
                                    StackType_t **ppxTimerTaskStackBuffer,
                                    uint32_t *pulTimerTaskStackSize)
{
    *ppxTimerTaskTCBBuffer = &timer_task_tcb;
    *ppxTimerTaskStackBuffer = timer_task_stack;
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

/**
 * @description: 启动FreeRTOS
 * @return {*}
 */
void freertos_start(void)
{
    /* 1.创建一个启动任务 */
    start_task_handle = xTaskCreateStatic(
        (TaskFunction_t)start_task,       // 任务函数的地址
        (char *)"start_task",             // 任务名字符串
        (uint32_t)START_TASK_STACK,       // 任务栈大小,默认最小128,单位4字节
        (void *)NULL,                     // 传递给任务的参数
        (UBaseType_t)START_TASK_PRIORITY, // 任务的优先级
        (StackType_t *)start_task_stack,  // 任务栈的地址
        (StaticTask_t *)&start_task_tcb); // tcb的地址
    /* 2.启动调度器:会自动创建空闲任务和空闲任务(如果开启),静态创建的方式需要去实现2个分配资源的接口函数 */
    vTaskStartScheduler();
}

/**
 * @description: 启动任务:用来创建其他Task
 * @param {void} *pvParameters
 * @return {*}
 */
void start_task(void *pvParameters)
{
    /* 进入临界区:保护临界区里的代码不会被打断 */
    taskENTER_CRITICAL();

    /* 使用静态创建1个任务 */
    task1_handle = xTaskCreateStatic(
        (TaskFunction_t)task1,
        (char *)"task1",
        (configSTACK_DEPTH_TYPE)TASK1_STACK,
        (void *)NULL,
        (UBaseType_t)TASK1_PRIORITY,
        (StackType_t *)task1_stack,  // 任务栈的地址
        (StaticTask_t *)&task1_tcb); // tcb的地址
    /* 启动任务只需要执行一次即可,用完就删除自己 */
    vTaskDelete(NULL);

    /* 退出临界区 */
    taskEXIT_CRITICAL();
}

/**
 * @description: 任务一:判断按键KEY1是否按下,按下则删掉task1
 * @param {void} *pvParameters
 * @return {*}
 */
struct keys key[3] = {0}; // 定义按键结构体
void task1(void *pvParameters)
{
    while (1)
    {
        if (key[0].short_flag == 1)
        {
            /* 关中断 */
            printf(">>>>关中断.....\r\n");
            portDISABLE_INTERRUPTS();
            key[0].short_flag = 0;
        }
        else if (key[1].short_flag == 1)
        {
            /* 开中断 */
            printf(">>>>开中断.....\r\n");
            portENABLE_INTERRUPTS();
            key[1].short_flag = 0;
        }
        // HAL_Delay(500); //这种delay不会让任务进入阻塞态,不会让出cpu使用权
        /* 为了观察实验现象,不要调用freertos的延时函数,底层会去开关中断,影响现象 */
        // vTaskDelay(500);
        /* 使用HAL_Delay前提:HAL时钟修改成其他定时器,并且中断优先级高于freertos的管理范围 */
    }
}

 freertos_demo.h

/*
 * @Author: random
 * @Date: 2025-05-10 01:23:57
 * @Last Modified by: random
 * @Last Modified time: Do not Edit
 */

#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H

#include "main.h"
struct keys
{
  uchar key_judge;
  uchar key_sta;
  uint key_time;
  uchar short_flag;
  uchar long_flag;
};
extern struct keys key[];

void freertos_start(void);

#endif

 中断回调函数void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */
  if (htim->Instance == TIM1)
  {
    printf("TIM1优先级=6  \r\n");
  }
  if (htim->Instance == TIM2)
  {
    printf("TIM2优先级=4  \r\n");
  }
  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM4)
  {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
  if (htim->Instance == KEY_T)
  {
    key[0].key_sta = HAL_GPIO_ReadPin(KEY1_G, KEY1_P);
    key[1].key_sta = HAL_GPIO_ReadPin(KEY2_G, KEY2_P);
    key[2].key_sta = HAL_GPIO_ReadPin(KEY3_G, KEY3_P);
    for (int i = 0; i < 4; i++)
    {
      switch (key[i].key_judge)
      {
      case 0: // 检测按键
      {
        if (key[i].key_sta == 0)
        {
          key[i].key_judge = 1;
          key[i].key_time = 0;
        }
      }
      break;
      case 1: // 按键按下
      {
        if (key[i].key_sta == 0)
        {
          key[i].key_judge = 2;
          key[i].key_time = 0;
        }
        else
        {
          key[i].key_judge = 0;
        }
      }
      break;
      case 2: // 按键松开
      {
        if (key[i].key_sta == 1)
        {
          if (key[i].key_time < 70)
          {
            key[i].short_flag = 1;
          }
          if (key[i].key_time > 70)
          {
            key[i].long_flag = 1;
          }
          key[i].key_judge = 0;
        }
        else
        {
          key[i].key_time++;
        }
      }
      break;
      }
    }
  }
}

 main初始化

  /* 开启按键定时器TIM1-3 */
  HAL_TIM_Base_Start_IT(&htim3);
  HAL_TIM_Base_Start_IT(&htim1);
  HAL_TIM_Base_Start_IT(&htim2);
  /* 启动FreeRTOS */
  freertos_start();
  /* 注意:在进入freertos后面的代码,没机会执行 */

测试结果:

时间片调度:

在FreeRTOS中,同等优先级的任务会轮流分享相同的CPU时间,这个时间被称为时间片。在这里,一个时间片的长度等同于SysTick中断的周期。

实验目标:

理解FreeRTOS的时间片调度:

  • start_task:用来创建其他的2个任务。
  • task1:通过串口打印task1的运行次数,设置任务优先级为2。
  • task2:通过串口打印task2的运行次数,设置任务优先级为2。

为了更好观察现象,滴答定时器的中断频率设置为50ms中断一次(一个时间片)。

 freertos.c

/* 任务1的配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
StackType_t task1_stack[TASK1_STACK]; // 静态任务的任务栈,以数组形式存储
StaticTask_t task1_tcb;               // 静态任务的tcb结构体类型
void task1(void *pvParameters);

/* 任务2的配置 */
#define TASK2_STACK 128
#define TASK2_PRIORITY 2
TaskHandle_t task2_handle;
StackType_t task2_stack[TASK2_STACK]; // 静态任务的任务栈,以数组形式存储
StaticTask_t task2_tcb;               // 静态任务的tcb结构体类型
void task2(void *pvParameters);


/**
 * @description: 任务一:打印任务1执行次数
 * @param {void} *pvParameters
 * @return {*}
 */
void task1(void *pvParameters)
{
    uint16_t task1_count = 0;
    while (1)
    {
        /* 临界区避免printf执行一半被打断 */
        taskENTER_CRITICAL();
        printf("task1运行次数=%d..\r\n", ++task1_count);
        // vTaskDelay(500); //为了观察时间片调度,不使用freertos的延时函数
        HAL_Delay(10); // 使用该延时的前提:HAL时钟修改成其他定时器,并且中断优先级较高
        taskEXIT_CRITICAL();
    }
}

/**
 * @description: 任务二:打印任务2执行次数
 * @param {void} *pvParameters
 * @return {*}
 */
void task2(void *pvParameters)
{
    uint16_t task2_count = 0;
    while (1)
    {
        /* 临界区避免printf执行一半被打断 */
        taskENTER_CRITICAL();
        printf("task2运行次数=%d..\r\n", ++task2_count);
        // vTaskDelay(500); //为了观察时间片调度,不使用freertos的延时函数
        HAL_Delay(10); // 使用该延时的前提:HAL时钟修改成其他定时器,并且中断优先级较高
        taskEXIT_CRITICAL();
    }
}

 更改任务的优先级都为2,修改任务

宏定义:

#define configUSE_TIME_SLICING 1
#define configTICK_RATE_HZ ( ( TickType_t ) 20 )    //1s/20=50ms一个tick

实验现象:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值