FreeRTOS:中断配置

news2024/10/7 10:20:32

目录

  • 一、Cortex-M 中断
    • 1.1中断简介
    • 1.2中断管理简介
    • 1.3优先级分组定义
    • 1.4优先级设置
    • 1.5用于中断屏蔽的特殊寄存器
      • 1.5.1PRIMASK 和 FAULTMASK 寄存器
      • 1.5.2BASEPRI 寄存器
  • 二、FreeRTOS 中断配置宏
    • 2.1configPRIO_BITS
    • 2.2configLIBRARY_LOWEST_INTERRUPT_PRIORITY
    • 2.3configKERNEL_INTERRUPT_PRIORITY
    • 2.4configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
    • 2.5configMAX_SYSCALL_INTERRUPT_PRIORITY
  • 三、FreeRTOS 开关中断
  • 四、FreeRTOS 中断测试实验
    • 4.1实验要求
    • 4.2实验设计
    • 4.3程序代码
    • 4.4实验程序运行结果

一、Cortex-M 中断

1.1中断简介

中断是微控制器一个很常见的特性,中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务, Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。

Cotex-M3 的 NVIC 最多支持 240 个 IRQ(中断请求)、1 个不可屏蔽中断(NMI)、1 个Systick(滴答定时器)定时器中断和多个系统异常。

1.2中断管理简介

Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器, 这些寄存器大多数都在NVIC 和系统控制块(SCB)中, CMSIS 将这些寄存器定义为结构体。以 STM32F103 为例,打开core_cm3.h,有两个结构体, NVIC_Type 和 SCB_Type。

1.3优先级分组定义

当多个中断来临的时候处理器应该响应哪一个中断是由中断的优先级来决定的,高优先级的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断,这个就是中断嵌套。 Cortex-M 处理器的有些中断是具有固定的优先级的,比如复位、 NMI、HardFault,这些中断的优先级都是负数,优先级也是最高的。

Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,最多有 128 个抢占等级,但是实际的优先级数量是由芯片厂商来决定的。但是,绝大多数的芯片都会精简设计的,以致实际上支持的优先级数会更少,如 8 级、 16 级、 32 级等, 比如 STM32 就只有 16 级优先级。在设计芯片的时候会裁掉表达优先级的几个低端有效位,以减少优先级数,所以不管用多少位来表达优先级,都是 MSB 对齐的,下图就是使用三位来表达优先级。
在这里插入图片描述
在上图 中, Bit0~Bit4 没有实现,所以读它们总是返回零,写入它们的话则会忽略写入的值。因此,对于 3 个位的情况,可是使用的优先级就是 8 个: 0X00(最高优先级)、 0X20、0X40、0X60、 0X80、 0XA0、 0XC0 和 0XE0。 注意,这个是芯片厂商来决定的!不是我们能决定的,比如 STM32 就选择了 4 位作为优先级!

优先级配置寄存器是 8 位宽的,为什么却只有 128 个抢占等级? 8 位不应该是 256 个抢占等级吗?为了使抢占机能变得更可控, Cortex-M 处理器还把 256 个优先级按位分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级), NVIC 中有一个寄存器是“应用程序中断及复位控制寄存器(AIRCR)”, AIRCR 寄存器里面有个位段名为“优先级组”,如表所示:
在这里插入图片描述
表中 PRIGROUP 就是优先级分组, 它把优先级分为两个位段: MSB 所在的位段(左边的)对应抢占优先级, LSB 所在的位段(右边的)对应亚优先级,如下表所示
在这里插入图片描述
在看一下 STM32 的优先级分组情况,我们前面说了 STM32 使用了 4 位,因此最多有 5 组优先级分组设置,这 5 个分组在 msic.h 中有定义,如下:

#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */

可以看出 STM32 有 5 个分组,但是一定要注意! STM32 中定义的分组 0 对应的值是 7!如果我们选择分组 4,即 NVIC_PriorityGroup_4 的话,那 4 位优先级就都全是抢占优先级了,没有亚优先级,那么就有 0~15 共 16 个优先级。而移植 FreeRTOS 的时候我们配置的就是组 4,如图所示:
在这里插入图片描述
如果使用 ALIENTEK 的基础例程的话默认配置的组 2,所以在将基础例程中的外设驱动移植到 FreeRTOS 下面的时候需要修改优先级配置。 主要是 FreeRTOS 的中断配置没有处理亚优先级这种情况,所以只能配置为组 4,直接就 16 个优先级,使用起来也简单!

1.4优先级设置

每个外部中断都有一个对应的优先级寄存器,每个寄存器占 8 位,因此最大宽度是 8 位,但是最小为 3 位。 4 个相临的优先级寄存器拼成一个 32 位寄存器。如前所述,根据优先级组的设置,优先级又可以分为高、低两个位段,分别抢占优先级和亚优先级。 STM32 我们已经设置位组 4,所以就只有抢占优先级了。优先级寄存器都可以按字节访问,当然也可以按半字/字来访问,有意义的优先级寄存器数目由芯片厂商来实现,如表 所示:

中断优先级寄存器阵列 中断优先级寄存器阵列 (地址: 0xE000_E400~0xE000_E4EF):
在这里插入图片描述
系统异常优先级阵列 系统异常优先级阵列 (地址: 0XE000_ED18~0xE000_ED23):
在这里插入图片描述
上 面 说 了 , 4 个 相 临 的 寄 存 器 可 以 拼 成 一 个 32 位 的 寄 存 器 , 因 此 地 址0xE000_ED20~0xE000_ED23 这四个寄存器就可以拼接成一个地址为 0xE000_ED20 的 32 位寄存器。 这一点很重要! 因为 FreeRTOS 在设置 PendSV 和 SysTick 的中断优先级的时候都是直接操作的地址 0xE000_ED20。

1.5用于中断屏蔽的特殊寄存器

1.5.1PRIMASK 和 FAULTMASK 寄存器

在许多应用中,需要暂时屏蔽所有的中断一执行一些对时序要求严格的任务,这个时候就可以使用 PRIMASK 寄存器, PRIMASK 用于禁止除 NMI 和 HardFalut 外的所有异常和中断,汇编编程的时候可以使用 CPS(修改处理器状态)指令修改 PRIMASK 寄存器的数值:

CPSIE I; //清除 PRIMASK(使能中断)
CPSID I; //设置 PRIMASK(禁止中断)

PRIMASK 寄存器还可以通过 MRS 和 MSR 指令访问,如下:

MOVS R0, #1
MSR PRIMASK, R0 ;//将 1 写入 PRIMASK 禁止所有中断

以及:

MOVS R0, #0
MSR PRIMASK, R0 ;//将 0 写入 PRIMASK 以使能中断

UCOS 中的临界区代码代码保护就是通过开关中断实现的(UCOSIII 也可以使用禁止任务调度的方法来实现临界区代码保护,这里不讨论这种情况),而开关中断就是直接操作 PRIMASK寄存器的,所以在 UCOS 中关闭中断的时候时关闭了除复位、 NMI 和 HardFault 以外的所有中断!
FAULTMASK 比 PRIMASK 更狠, 它可以连 HardFault 都屏蔽掉,使用方法和 PRIMASK 类似, FAULTMASK 会在退出时自动清零。

汇编编程的时候可以利用 CPS 指令修改 FAULTMASK 的当前状态

CPSIE F ;清除 FAULTMASK
CPSID F ;设置 FAULTMASK

还可以利用 MRS 和 MSR 指令访问 FAULTMASK 寄存器:

MOVS R0, #1
MSR FAULTMASK, R0 ;1 写入 FAULTMASK 禁止所有中断

以及:

MOVS R0, #0
MSR FAULTMASK, R0 ;0 写入 FAULTMASK 使能中断

1.5.2BASEPRI 寄存器

PRIMASK 和 FAULTMASK 寄存器太粗暴了,直接关闭除复位、 NMI 和 HardFault 以外的其他所有中断,但是在有些场合需要对中断屏蔽进行更细腻的控制, 比如只屏蔽优先级低于某一个阈值的中断。那么这个作为阈值的优先级值存储在哪里呢?在 BASEPRI 寄存器中,不过如果向 BASEPRI 写 0 的话就会停止屏蔽中断。 比如,我们要屏蔽优先级不高于 0X60 的中断,则可以使用如下汇编编程:

MOV R0, #0X60
MSR BASEPRI, R0

注意! FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!

二、FreeRTOS 中断配置宏

2.1configPRIO_BITS

此宏用来设置 MCU 使用几位优先级, STM32 使用的是 4 位,因此此宏为 4!

2.2configLIBRARY_LOWEST_INTERRUPT_PRIORITY

此宏是用来设置最低优先级,前面说了,STM32 优先级使用了 4 位,而且 STM32 配置的使用组 4,也就是 4 位都是抢占优先级。 因此优先级数就是 16 个,最低优先级那就是 15。 所以此宏就是 15,注意!不同的 MCU 此值不同,具体是多少要看所使用的 MCU 的架构,本文只针对 STM32 讲解!

2.3configKERNEL_INTERRUPT_PRIORITY

此宏用来设置内核中断优先级, 此宏定义如下:

#define configKERNEL_INTERRUPT_PRIORITY
( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

宏 configKERNEL_INTERRUPT_PRIORITY 为 , 宏configLIBRARY_LOWEST_INTERRUPT_PRIORITY 左移 8-configPRIO_BITS 位,也就是左移 4位。为什么要左移 4 位呢?前面我们说了, STM32 使用了 4 位作为优先级,而这 4 位是高 4位,
因 此 要 左 移 4 位 才 是 真 正 的 优 先 级 。 当 然 了 也 可 以 不 用 移 位 , 直 接 将 宏configLIBRARY_LOWEST_INTERRUPT_PRIORITY 定义为 0XF0! 不过这样看起来不直观。
宏 configKERNEL_INTERRUPT_PRIORITY 用来设置 PendSV 和滴答定时器的中断优先级,port.c 中有如下定义:

#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) <<
16UL )
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) <<
24UL )

可 以 看 出 , portNVIC_PENDSV_PRI 和 portNVIC_SYSTICK_PRI 都 是 使 用 了 宏configKERNEL_INTERRUPT_PRIORITY , 为 什 么 宏 portNVIC_PENDSV_PRI 是 宏configKERNEL_INTERRUPT_PRIORITY 左移 16 位呢?宏 portNVIC_SYSTICK_PRI 也同样是左移 24 位。 PendSV 和 SysTcik 的中断优先级设置是操作 0xE000_ED20 地址的, 这样一次写入的是个 32 位的数据, SysTick 和 PendSV 的优先级寄存器分别对应这个 32位数据的最高 8 位和次高 8 位,不就是一个左移 16 位,一个左移 24 位了。
PendSV 和 SysTick 优先级是在哪里设置的呢?在函数 xPortStartScheduler()中设置,此函数在文件 port.c 中,函数如下:

BaseType_t xPortStartScheduler( void )
{
    configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY );
    configASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
    configASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
    #if( configASSERT_DEFINED == 1 )
    {
        volatile uint32_t ulOriginalPriority;
        volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * )
        ( portNVIC_IP_REGISTERS_OFFSET_16 +
        portFIRST_USER_INTERRUPT_NUMBER );
        volatile uint8_t ucMaxPriorityValue;
        ulOriginalPriority = *pucFirstUserPriorityRegister;
        *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
        ucMaxPriorityValue = *pucFirstUserPriorityRegister;
        configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY &
        ucMaxPriorityValue ) );
        ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY &
        ucMaxPriorityValue;
        ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
        while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
        {
            ulMaxPRIGROUPValue--;
            ucMaxPriorityValue <<= ( uint8_t ) 0x01;
        }
        ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
        ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
        *pucFirstUserPriorityRegister = ulOriginalPriority;
    }
    #endif /* conifgASSERT_DEFINED */
    portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; //设置 PendSV 中断优先级
    portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; //设置 SysTick 中断优先级
    vPortSetupTimerInterrupt();
    uxCriticalNesting = 0;
    prvStartFirstTask();
    return 0;
}

上述代码中红色部分就是设置 PendSV 和 SysTick 优先级的,它们是直接向地址portNVIC_SYSPRI2_REG 写入优先级数据, portNVIC_SYSPRI2_REG 是个宏,在文件 port.c 中由定义,如下:

#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )

可以看到宏 portNVIC_SYSPRI2_REG 就是地址 0XE000ED20!同时也可以看出在 FreeRTOS中 PendSV 和 SysTick 的中断优先级都是最低的!

2.4configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

此宏用来设置 FreeRTOS 系统可管理的最大优先级,也就是我们在 1.5 中讲解BASEPRI 寄存器说的那个阈值优先级,这个大家可以自由设置,这里我设置为了 5。也就是高于 5 的优先级(优先级数小于 5)不归 FreeRTOS 管理!

2.5configMAX_SYSCALL_INTERRUPT_PRIORITY

此宏是 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 左移 4 位而来的,原因和宏 configKERNEL_INTERRUPT_PRIORITY 一样。此宏设置好以后,低于此优先级的中断可以安全的调用 FreeRTOS 的 API 函数,高于此优先级的中断 FreeRTOS 是不能禁止的,中断服务函数也不能调用 FreeRTOS 的 API 函数!

以 STM32 为例,有 16 个优先级, 0 为最高优先级, 15 为最低优先级,配置如下:
● configMAX_SYSCALL_INTERRUPT_PRIORITY5
● configKERNEL_INTERRUPT_PRIORITY
15
结果如图所示:
在这里插入图片描述
由于高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的优先级不会被 FreeRTOS 内核屏蔽,因此那些对实时性要求严格的任务就可以使用这些优先级,比如四轴飞行器中的壁障检测。

三、FreeRTOS 开关中断

FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS(),这两个函数其实是宏定义,在 portmacro.h 中有定义,如下:

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

可以看出开关中断实际上是通过函数 vPortSetBASEPRI(0)和 vPortRaiseBASEPRI()来实现的,这两个函数如下:

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
    __asm
    {
    msr basepri, ulBASEPRI
    }
}
    /*-----------------------------------------------------------*/
    static portFORCE_INLINE void vPortRaiseBASEPRI( void )
    {
        uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
        __asm
        {
            msr basepri, ulNewBASEPRI
            dsb
            isb
        }
    }

函数 vPortSetBASEPRI()是向寄存器 BASEPRI 写入一个值,此值作为参数 ulBASEPRI 传递进来, portENABLE_INTERRUPTS()是开中断,它传递了个 0 给 vPortSetBASEPRI(),根据我们前面讲解 BASEPRI 寄存器可知,结果就是开中断。
函 数 vPortRaiseBASEPRI() 是 向 寄 存 器 BASEPRI 写 入 宏configMAX_SYSCALL_INTERRUPT_PRIORITY , 那 么 优 先 级 低 于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断就会被屏蔽!

四、FreeRTOS 中断测试实验

4.1实验要求

使用两个定时器,一个优先级为 4,一个优先级为 5,两个定时器每隔 1s 通过串口输出一串字符串。然后在某个任务中关闭中断一段时间,查看两个定时器的输出情况。

4.2实验设计

本实验设计了两个任务 start_task()和 interrupt_task(), 这两个任务的任务功能如下:start_task():创建另外一个任务。interrupt_task() : 中 断 测 试 任 务 , 任 务 中 会 调 用 FreeRTOS 的 关 中 断 函 数portDISABLE_INTERRUPTS()来将中断关闭一段时间。

4.3程序代码

任务设置

#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 256 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define INTERRUPT_TASK_PRIO 2 //任务优先级
#define INTERRUPT_STK_SIZE 256 //任务堆栈大小
TaskHandle_t INTERRUPTTask_Handler; //任务句柄
void interrupt_task(void *p_arg); //任务函数

main()函数

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4
    delay_init(); //延时函数初始化
    uart_init(115200); //初始化串口
    LED_Init(); //初始化 LED
    TIM3_Int_Init(10000-1,7200-1); //初始化定时器 3,定时器周期 1S
    TIM5_Int_Init(10000-1,7200-1); //初始化定时器 5,定时器周期 1S
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task, //任务函数
    (const char* )"start_task", //任务名称
    (uint16_t )START_STK_SIZE, //任务堆栈大小
    (void* )NULL, //传递给任务函数的参数
    (UBaseType_t )START_TASK_PRIO, //任务优先级
    (TaskHandle_t* )&StartTask_Handler); //任务句柄
    vTaskStartScheduler(); //开启任务调度
}

任务函数

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL(); //进入临界区
    //创建中断测试任务
    xTaskCreate((TaskFunction_t )interrupt_task, //任务函数 (1)
    (const char* )"interrupt_task", //任务名称
    (uint16_t )INTERRUPT_STK_SIZE, //任务堆栈大小
    (void* )NULL, //传递给任务函数的参数
    (UBaseType_t )INTERRUPT_TASK_PRIO, //任务优先级
    (TaskHandle_t* )&INTERRUPTTask_Handler); //任务句柄
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL(); //退出临界区
}
//中断测试任务函数
void interrupt_task(void *pvParameters)
{
    static u32 total_num=0;
    while(1)
    {
        total_num+=1;
        if(total_num==5) (2)
        {
            printf("关闭中断.............\r\n");
            portDISABLE_INTERRUPTS(); //关闭中断 (3)
            delay_xms(5000); //延时 5s (4)
            printf("打开中断.............\r\n"); //打开中断
            portENABLE_INTERRUPTS(); (5)
        }
        LED0=~LED0;
        vTaskDelay(1000);
    }
}

(1)、创建一个任务来执行开关中断的动作,任务函数为 interrupt_task()。
(2)、当任务 interrupt_task()运行 5 次以后关闭中断。
(3) 、 调 用 函 数 portDISABLE_INTERRUPTS() 关 闭 中 断 。 优 先 级 低 于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断都会被关闭,高于的不会受任何影响。
(4)、调用函数 delay_xms()延时 5S,此函数是对 delay_us()的简单封装, delay_xms()会用来模拟关闭中断一段时间,此函数不会引起任务调度!
(5)、调用函数 portENABLE_INTERRUPTS()重新打开中断。
● 中断初始化及处理过程

//通用定时器 3 中断初始化
//这里时钟选择为 APB1 的 2 倍,而 APB1 为 36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器 3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    //定时器 TIM3 初始化
    TIM_TimeBaseStructure.TIM_Period = arr; //自动重装载值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //定时器分频
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的 TIM3 中断,允许更新中断
    //中断优先级 NVIC 设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4; //先占优先级 4 级 (1)
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 寄存器
    TIM_Cmd(TIM3, ENABLE); //使能 TIMx
}
    //通用定时器 5 中断初始化
    //这里时钟选择为 APB1 的 2 倍,而 APB1 为 36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器 5!
void TIM5_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //时钟使能
    //定时器 TIM5 初始化
    TIM_TimeBaseStructure.TIM_Period = arr; //自动重装载值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //定时器分频
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
    TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的 TIM5 中断,允许更新中断
    //中断优先级 NVIC 设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM5 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5; //先占优先级 5 级 (2)
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 寄存器
    TIM_Cmd(TIM5, ENABLE); //使能 TIM5
}
    //定时器 3 中断服务函数
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
    {
        printf("TIM3 输出.......\r\n"); (3)
    }
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
//定时器 5 中断服务函数
void TIM5_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET) //溢出中断
    {
        printf("TIM5 输出.......\r\n"); (4)
    }
    TIM_ClearITPendingBit(TIM5,TIM_IT_Update); //清除中断标志位
}

(1)、设置定时器 3 的抢占优先级为 4,高于configMAX_SYSCALL_INTERRUPT_PRIORITY,因此在调用函数portDISABLE_INTERRUPTS()关闭中断的时候定时器 3 是不会受影响的。
(2)、设置定时器 5 的抢占优先级为 5,等于configMAX_SYSCALL_INTERRUPT_PRIORITY,因此在调用函数portDISABLE_INTERRUPTS()关闭中断的时候定时器 5 中断肯定会被关闭的。
(3)和(4)、定时器 3 和定时 5 串口输出信息。

4.4实验程序运行结果

编译并下载代码到开发板中,打开串口调试助手查看数据输出,结果如图所示:

在这里插入图片描述

从图可以看出,一开始没有关闭中断,所以 TIM3 和 TIM5 都正常运行,红框所示部分。当任务 interrupt_task()运行了 5 次以后就关闭了中断,此时由于 TIM5 的中断优先级为 5,等于 configMAX_SYSCALL_INTERRUPT_PRIORITY,因此 TIM5 被关闭。但是, TIM3 的中断优先级高于 configMAX_SYSCALL_INTERRUPT_PRIORITY,不会被关闭,所以 TIM3 正常运行,绿框所示部分。 中断关闭 5S 以后就会调用函数 portENABLE_INTERRUPTS()重新打开中断,重新打开中断以后 TIM5 恢复运行, 蓝框所示部分。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/507679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Class 01 - R语言介绍及部分功能演示

Class 01 - R语言介绍及部分功能演示 教程介绍功能演示数据导入 R语言&Python对比在数据科学中的不同优势 教程介绍 在本章中&#xff0c;将学习到编程的新概念&#xff0c;以及如何使用R语言来做数据的分析。 我们先谈谈计算机编程吧&#xff0c;计算机编程是指向计算机…

计算机网络学习笔记-数据链路层

目录 概述 三个功能 封装成帧 透明传输 差错检测 点对点协议PPP 三个组成部分 帧格式 透明传输 工作状态 使用广播信道的局域网 以太网 两个主要标准 高速以太网&#xff08;速率 > 100Mb/s&#xff09; MAC层&#xff1a; 集线器 交换机 自学习和转发帧 生…

tkinter+re实现百度网盘分享链接自动跳转

目录 一、背景 二、需求分析 三、实现 三、打包 四、测试 一、背景 相信大家使用百度网盘的时候都会去一些链接网站吧&#xff0c;比如下面这个网站 这个网站具体实现的功能是&#xff1a;管理员上传百度网盘链接&#xff0c;用户点击复制链接按钮可以将百度网盘的分享链…

线性结构-数组

数组(Array)是最简单的数据结构&#xff0c;是由有限个相同类型的变量或对象组成的有序集合。因为数组中各元素之间是按顺序线性排列的&#xff0c;所以数组是一种线性数据结构。 数组是一类物理空间和逻辑形式都连续的线性数据结构&#xff1a; 数组用唯一的名字标识&#xf…

输出数字的位数(C语言)以及逆序输出

#include <stdio.h>int main() {int N;int i 0;scanf("%d",&N);int a[5];int j;while(N > 0){a[i] N%10;i;N N/10;}printf("这个数字是%d位数\n",i); for(j 0;j < i;j){printf("%d",a[j]);} } 原题如下&#xff1a;

并发编程08:原子操作类

文章目录 8.1 基本类型原子类8.1.1 常用API简介8.1.2 Case 8.2 数组类型原子类8.2.1 常用API简介8.2.2 Case 8.3 引用类型原子类8.4 对象的属性修改原子类8.4.1 使用目的8.4.2 使用要求8.4.3 Case 8.5 原子操作增强类原理深度解析8.5.1 常用API8.5.2 面试题8.5.3 点赞计数器8.5…

读书笔记-《ON JAVA 中文版》-摘要15[第十五章 异常]

文章目录 第十五章 异常1. 异常概念2. 基本异常2.1 基本异常2.2 异常参数 3. 异常捕获3.1 try 语句块3.2 异常处理程序3.3 终止与恢复 4. 自定义异常4.1 自定义异常 5. 异常声明6. 异常捕获6.1 捕获所有异常6.2 多重捕获6.3 栈轨迹6.4 重新抛出异常6.5 精准的重新抛出异常 6.6 …

ExpressGridPack Crack快速电子表格

ExpressGridPack Crack快速电子表格 ExpressEditors库 外壳对话框-对话框窗体不会出现在活动监视器中。 TdxVisualRefinements.PPadding属性对dxTokenEdit没有影响。 Express库 TdxVisualRefinements.PPadding属性对dxTokenEdit没有影响。 ExpressQuantumTreeList套件 TcxTreeL…

二层交换机和三层交换机到底区别在哪?

你好&#xff0c;这里是网络技术联盟站。 今天我们谈谈二层交换机和三层交换机。 二层交换机的概念和特点 二层交换机是一种工作在数据链路层的网络设备&#xff0c;主要功能是根据数据帧中的MAC地址进行转发&#xff0c;并将这些MAC地址与对应的端口记录在自己内部的一个地…

IDA常用宏定义函数

一.引言 做题目遇到了几个神奇的函数. SDWORD1(x), SDWORD2(x), SHIDWORD(x) 通过查询得知是IDA的宏定义函数 宏定义本身类似字符串替换,假设#define x 666 只是编译器在预处理阶段进行宏展开,将所有的x替换为666,然后再进行编译 二.IDA宏定义头文件 可以在路径\IDA_Pro_7.7…

Dubbo2.7 纯注解使用+ Nacos + Springboot 整合集成

Dubbo2.7 纯注解使用 NacosSpringboot 环境准备篇相关依赖nacos准备代码编写服务提供者服务使用者整体结构图 结果 常规操作篇服务分组服务版本参数传递泛化调用参数校验只订阅延迟暴露服务端异步回调多协议复用多注册中心本地存根 服务治理篇超时时间重试并发控制权限控制服务…

css04笔记

目录 盒子模型 5.7 外边距折叠现象 – ① 合并现象 5.8 外边距折叠现象 – ② 塌陷现象 5.9 行内元素的margin和padding无效情况 一、结构伪类选择器 &#xff08;了解&#xff09;nth-of-type结构伪类选择器 二、伪元素 三、标准流 四、浮动 浮动的代码&#xff1a; …

用 Pygal 模拟掷骰子

这篇博客&#xff0c;我们将学习使用 python可视化包 Pygal 来生成矢量图形文件。针对于需要在尺寸不同的屏幕上显示的图表具有很大用处。因为它们可以自动缩放&#xff0c;以此来适合观看者的屏幕。 . 在这个项目中&#xff0c;我们将对掷骰子的结果进行分析。掷6面的常规骰子…

<Linux> 基础IO(文件操作、文件描述符fd、重定向)

基础IO&#xff08;文件操作、文件描述符fd、重定向&#xff09; 文章目录 基础IO&#xff08;文件操作、文件描述符fd、重定向&#xff09;一、回顾C和C的文件操作二、C语言文件IO1.什么是当前路径&#xff1f;2.C语言文件接口汇总3.默认打开的三个流 三、系统文件IO1.open2.c…

真题详解(索引长度计算)-软件设计(七十一)

真题详解(哈希冲突)-软件设计&#xff08;七十)https://blog.csdn.net/ke1ying/article/details/130566800 在面向对象系统中&#xff0c;一个类定义了大体相似的对象&#xff0c;这些对象共享_____。 属性和行为。 &#xff08;属性就是状态&#xff09; 数据库主要分为用户…

shapefile.js实现shp数据的上传与展示

概述 shapefile是常见的矢量数据格式&#xff0c;但是由于其文件组成结构很难在webgis上直接展示。本文通过express和compressing实现打包后shapefile文件的上传&#xff0c;并结合shapefile.js实现shapefile数据的转换展示。 实现效果 实现代码 1. 后端实现 router.post(/…

Android 引入hunter-debug监测代码运行时函数耗时和参数及返回值,Java(1)

Android 引入hunter-debug监测代码运行时函数耗时和参数及返回值&#xff0c;Java&#xff08;1&#xff09; &#xff08;1&#xff09;在工程的根build.gradle文件里面添加cn.quinnchen.hunter:hunter-debug-plugin引用&#xff1a; buildscript {repositories {mavenCentra…

SAP CAP篇三:定义Model

SAP CAP篇一:快速创建一个Service&#xff0c;基于Java的实现 SAP CAP篇二&#xff1a;为Service加上数据库支持 文章目录 理解CAP的ModelDomain-Driven DesignKISSBasic TypesCommon Reuse TypecuidmanagedtemporalCountry, Currency, LanguagecodeList Assocation & Comp…

匹配算法之 匈牙利算法详解

参考&#xff1a; 算法学习笔记(5)&#xff1a;匈牙利算法漫谈匈牙利算法匈牙利算法、KM算法匈牙利算法&#xff08;二分图&#xff09;通俗易懂小白入门&#xff09;二分图最大匹配——匈牙利算法多目标跟踪之数据关联&#xff08;匈牙利匹配算法和KM算法&#xff09;【小白学…

手把手教你使用gtest写单元测试

开源框架&#xff1a;gtest&#xff0c;它主要用于写单元测试&#xff0c;检查真自己的程序是否符合预期行为。这不是QA&#xff08;测试工程师&#xff09;才学的&#xff0c;也是每个优秀后端开发codoer的必备技能。 本期博文内容及使用的demo&#xff0c;参考&#xff1a; …