STMCU小助手
发布时间:2022-11-17 16:56
|
1. Cortex-M中断 1.1 简介 中断由硬件产生,当中断产生后CPU会中断当前的流程转而去处理中断服务,Cortex-M内核MCU提供了用于中断管理的嵌套向量中断控制器(NVIC)。NVIC最多支持240个中断请求(IRQ)、1个不可屏蔽中断(NMI)、1个滴答定时器中断(Systick)和多个系统异常。 1.2 管理方式 Cortex-M处理器有多个用于管理中断和异常的可编程寄存器,位于NVIC和系统控制块(SCB)中,CMSIS将这些寄存器定义为结构体。打开core_cm3.h,有如下两个结构体:
NVIC和SCB位于系统控制空间(SCS)内,SCS的地址从0XE000E000开始,SCB和NVIC的地址在core_cm3.h中定义如下:
1.3 优先级分组定义 高优先级的中断可以抢占低优先级的中断,这就是中断嵌套。有些中断是固定优先级的,比如复位、NMI、HardFault,这些中断的优先级是负数,即最高优先级。 由于大多数芯片会精简设计,实际优先级数会比较少。STM32只有16级优先级。在设计芯片时会裁掉优先级的几个低端有效位,以减少优先级数,如下图所示:
上图中,Bit0~Bit4的返回值总是零。对于3个位的情况,可使用的优先级就是8个:0x00、0x20、0x40、0x60、0x80、0xA0、0xC0和0xE0。注意,STM32选择了4位作为优先级! 为了使抢占机能变得可控,Cortex-M处理器把256个优先级按位分为高低两段:抢占优先级和亚优先级,NVIC中有一个寄存器叫“应用程序中断及复位控制寄存器AIRCR”,其中有个位段名为优先级组:
PRIGROUP是优先级分组,把优先级分为两个位段:MSB所在位段(左)对应抢占优先级,LSB所在位段(右)对应亚优先级:
在msic.h中有定义:
可以看出有5个分组,如果选择分组4,即NVIC_PriorityGroup_4,那4位优先级都是抢占优先级,没有亚优先级。 由于FreeRTOS的中断配置没有处理亚优先级这种情况,所以必须配置为组4! 1.4 优先级设置 每个外部中断有一个对应的优先级寄存器,每个寄存器占8位,因此最大宽度是8位,最小是3位。4个相邻优先级寄存器组成一个32位寄存器。根据优先级分组设置,优先级可分为高、低两个位段,即抢占优先级和亚优先级。优先级寄存器可按字节访问,也可按半字/字来访问,有意义的优先级寄存器数目由厂商确定:
根据四个相邻寄存器可拼成一个32位寄存器,因此地址0xE000_ED20~0xE000_ED23这四个寄存器可拼成一个地址为0xE000ED20的32位寄存器。 FreeRTOS在设置PendSV和SysTick的中断优先级时是直接操作地址0xE000_ED20。 1.5 用于中断屏蔽的特殊寄存器 这里重点关注PRIMASK、FAULTMASK和BASEPRI三个寄存器: 1)PRIMASK和FAULTMASK寄存器 在应用中,需要暂时屏蔽所有的中断,执行一些对时序要求严格的任务,这时要用PRIMASK寄存器,它用于禁止除NMI和HardFalut外的所有异常和中断,汇编编程时用CPS(修改处理器状态)指令修改PRIMASK寄存器:
PRIMASK寄存器还可以通过MRS和MSR指令访问:
FAULTMASK比PRIMASK更厉害,它可以连HardFault都屏蔽掉,FAULTMASK会在退出时自动清零,汇编编程时用CPS指令:
用MRS和MSR指令:
2)BASEPRO寄存器 当只要屏蔽优先级低于某个阈值的中断时,可用BASEPRI寄存器,向BASEPRI写0会停止屏蔽中断。若屏蔽优先级不高于0x60的中断,可用如下汇编编程:
如要取消BASEPRI对中断的屏蔽:
FreeRTOS开关中断就是操作BASEPRI寄存器实现的! 2. FreeRTOS中断配置宏 1)configPRIO_BITS 设置MCU使用几位优先级,STM32使用4位,则宏为4! 2)configLIBRARY_LOWEST_INTERRUPT_PRIORITY 设置最低优先级。由于STM32优先级使用4位,且STM32配置为分组4,即4位都是抢占优先级。那么最低优先级即为15。所以此宏为15。不同MCU值不同,要看所用的MCU架构。 3)configKERNEL_INTERRUPT_PRIORITY 设置内核中断优先级,宏定义如下:
左移(8 - configPRIO_BITS) 位,即左移4位。因为STM32使用4位作为优先级,且该4位是高4位,因此要左移4位! configKERNEL_INTERRUPT_PRIORITY用来设置PendSV和滴答定时器的中断优先级,在port.c中有如下定义:
PendSV优先级设置是configKERNEL_INTERRUPT_PRIORITY左移16位, SysTick优先级设置是configKERNEL_INTERRUPT_PRIORITY左移24位。 因为,PendSV和SysTick的中断优先级操作是0xE000_ED20地址,一次写入的是32位数据,SysTick和PendSV的优先级寄存器对应这个32位数据的最高8位和次高8位,所以是左移16位和左移24位。 PendSV和SysTick优先级在port.c文件里的xPortStartScheduler()函数里设置:
上述代码中,PendSV和SysTick优先级设置是直接向地址portNVIC_SYSPRI2_REG写入优先级数据,portNVIC_SYSPRI2_REG是个宏,在port.c中定义如下:
可知,portNVIC_SYSPRI2_REG地址是0xE000ED20,同时可知在FreeRTOS中PendSV和SysTick的中断优先级是最低的。 4) configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 设置FreeRTOS系统可管理的最大优先级,也就是BASEPRI寄存器的那个阈值优先级,这里设为5(不强制),即高于5的优先级不归FreeRTOS管理。 5)configMAX_SYSCALL_INTERRUPT_PRIORITY 由configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移4位而来。设置好以后,低于此优先级的中断可安全的调用FreeRTOS的API函数,高于此优先级的中断FreeRTOS是不能禁止的,中断服务函数也不能调用FreeRTOS的API函数。 以STM32为例,有16个优先级,0为最高优先级,15为最低优先级,配置如下: configMAX_SYSCALL_INTERRUPT_PRIORITY == 5; configKERNEL_INTERRUPT_PRIORITY == 15。 结果如下图:
3. FreeRTOS开关中断 FreeRTOS开关中断函数为portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS(),位于portmacro.h中:
可以看出开关中断实际上是通过函数vPortRaiseBASEPRI()和vPortSetBASEPRI(0)来实现的:
函数vPortSetBASEPRI()是向寄存器BASEPRI写入一个值,portENABLE_INTERRUPTS()是开中断,它传递一个0值给vPortSetBASEPRI(),即开中断。 函数vPortRaiseBASEPRI()是向寄存器BASEPRI写入宏configMAX_SYSCALL_INTERRUPT_PRIORITY,那么优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断就会被屏蔽。 4. 临界段代码 临界段代码也称临界区,指那些必须完整运行,不能被打断的代码段。FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS系统本身就有很多临界段代码,这些代码都加了临界段代码保护。 FreeRTOS与临界段代码保护有关的函数有4个:taskENTER_CRITICAL()、taskEXIT_CRITICAL()、taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR(),这四个函数是宏定义,在task.h文件中。前两个是任务级的临界段代码保护,后两个是中断级的临界段代码保护。 4.1 任务级临界段代码保护 taskENTER_CRITICAL()和taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个是成对使用的,在task.h文件里如下:
portENTER_CRITICAL()和portSET_INTERRUPT_MASK_FROM_ISR()也是宏定义,在portmacro.h文件里:
函数vPortEnterCritical()和vPortExitCritical()位于port.c文件中:
上述代码可知,在进入函数vPortEnterCritical()以后会首先关闭中断,给变量uxCriticalNesting加1,uxCriticalNesting是全局变量,记录临界段嵌套次数。函数vPortExitCritical()是退出临界段调用的,将uxCriticalNesting减1,只有当uxCriticalNesting为0时才会调用函数portENABLE_INTERRUPTS()使能中断。这样,只有所有临界段代码都退出后才会使能中断! 任务级临界段代码保护案例:
注意临界区代码一定要精简,因为进入临界区会关闭中断,导致优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断得不到及时的响应! 4.2 中断级临界段代码保护 函数 taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY! 这两个函数位于task.h文件中:
portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR( x )位于portmacro.h文件中:
vPortSetBASEPRI(x)前面已经说明,ulPortRaiseBASEPRI()位于也portmacro.h文件中:
中断级临界代码保护案例:
———————————————— 版权声明:天亮继续睡 |
HRTIM 指南
使用 STM32 的 DWT 单元监控内存
DAC,COMP,HRTIM Fault 功能的使用
一个软件引起的 LSE 驱动不良的问题
STM32 GUI LTDC 最大像素时钟评估方法
STM32 Cordic 运算速度评估
全新OpenSTLinux 6.1版本发布
ST-LINK Utility介绍、下载、安装和使用教程
【2025·STM32峰会】GUI解决方案实训分享1-对LVGL咖啡机例程的牛刀小试以及问题排查
OpenBLT移植到STM32F405开发板
微信公众号
手机版