文章目录

  • 0 前言
  • 1 Cortex-M 中断管理
    • 1.1 中断配置
    • 1.2 优先级分组配置
    • 1.3 FreeRTOS中断 PendSv和Systick中断优先级配置
  • 2 FreeRTOS的临界段代码保护和开关中断
    • 2.1 临界段代码保护
    • 2.2 中断临界段代码保护

0 前言

@ Author         :Dargon
@ Record Date    :2021/07/11
@ Reference Book : `FreeRTOS源码详解与应用开发`,`ARM Cortex-M3与Cortex-M4权威指南`,`B站正点原子FreeRTOS讲解视频`
@ Purpose        :学习正点原子的miniFly,该飞控基于FreeRTOS系统开发的,所以学习一下记录下关于RTOS系统的一些基本操作,大概了解系统的工作原理,如何创建,运行,切换任务等等基本操作流程。在此进行学习的记录。

1 Cortex-M 中断管理

1.1 中断配置

  • Cortex-M 内核中MCU提供一个用于中断管理的嵌套向量中断控制器,NVIC (Nested Vectored Interrupt Controller)
  • NVIC最多支持 240个IRQ 中断请求,一个不可屏蔽中断(不知道什么),一个systick(滴答定时器),和多个系统异常。
  • 管理这些寄存器的区域大都在控制块 NVIC 和 SCB 中,将其定义在结构体NVIC_Type 和SCB_Type中。
  • 从代码中看一下实际对应的内存地址
  • 位于文件core_cm4.h中
/* Memory mapping of Cortex-M4 Hardware */
#define SCS_BASE            (0xE000E000UL)                            /*!< System Control Space Base Address  */
#define SysTick_BASE        (SCS_BASE +  0x0010UL)                    /*!< SysTick Base Address               */
#define NVIC_BASE           (SCS_BASE +  0x0100UL)                    /*!< NVIC Base Address                  */
#define SCB_BASE            (SCS_BASE +  0x0D00UL)                    /*!< System Control Block Base Address  */#define SCnSCB              ((SCnSCB_Type    *)     SCS_BASE      )   /*!< System control Register not in SCB */
#define SCB                 ((SCB_Type       *)     SCB_BASE      )   /*!< SCB configuration struct           */
#define SysTick             ((SysTick_Type   *)     SysTick_BASE  )   /*!< SysTick configuration struct       */
#define NVIC                ((NVIC_Type      *)     NVIC_BASE     )   /*!< NVIC configuration struct          */
  • 可以计算出对应的 起始地址NVIC =SCS_BASE + 0x0100UL =0xE000E000UL + 0x0100UL =0xE000 E100UL
  • 可以计算出对应的 起始地址SCB =SCS_BASE + 0x0D00UL =0xE000E000UL + 0x0D00UL =0xE000 ED00UL
  • 下面看两个结构体 NVIC_Type 和SCB_Type的变量定义
  • 位于文件core_cm4.h中

typedef struct
{// --对应8*32 =256 对应240个外部中断__IO uint32_t ISER[8];                 /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register           */uint32_t RESERVED0[24];// --关于Offset =0x80情况 (24+8)*32/4 =128 Byte字节 =0x80,注意32位bit 是占用4 Byte字节__IO uint32_t ICER[8];                 /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register         */uint32_t RSERVED1[24];__IO uint32_t ISPR[8];                 /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register          */uint32_t RESERVED2[24];__IO uint32_t ICPR[8];                 /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register        */uint32_t RESERVED3[24];__IO uint32_t IABR[8];                 /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register           */uint32_t RESERVED4[56];__IO uint8_t  IP[240];                 /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */uint32_t RESERVED5[644];// --240+ 644=2816Byte =0xB00 Byte,0x300 +0xB00 =0xE00 ->Offset position__O  uint32_t STIR;                    /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register     */
}  NVIC_Type;

typedef struct
{// --一个uint32_t 对应 4 Byte 从ED00 开始 第一个就是ED00 -ED03 第二个就是ED04 -ED07,依次……__I  uint32_t CPUID;                   /*!< Offset: 0x000 (R/ )  CPUID Base Register                                   */__IO uint32_t ICSR;                    /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register                  */__IO uint32_t VTOR;                    /*!< Offset: 0x008 (R/W)  Vector Table Offset Register                          */// --SCB->AIRCR 8:10 bits 对应设置寄存器 2^3 =8组 在STM32中,我们选择组4 4个高位全是抢占优先级 对应16个优先级 // --对应着4个字节Byte 的偏移量 0xE000ED0C__IO uint32_t AIRCR;                   /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register      */__IO uint32_t SCR;                     /*!< Offset: 0x010 (R/W)  System Control Register                               */__IO uint32_t CCR;                     /*!< Offset: 0x014 (R/W)  Configuration Control Register                        */// --重点关注 ED18 -ED23 字节 优先级 ED22和ED23分别对应的 PendSV优先级和SYSTick优先级// --后面标注 (4-7, 8-11, 12-15) 可将4个8bit的优先级寄存器 当做32位寄存器进行配置// --配置FreeRTOS 中断的时候 直接是ED20-ED23 4个字节 ,对应配置内存地址 0xE000ED20// --对应8 bit 优先级寄存器,由于仅对高位是有效的,若设置优先级为0x02 ,则需要左移4位0x20……其余优先级设置类似__IO uint8_t  SHP[12];                 /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */__IO uint32_t SHCSR;                   /*!< Offset: 0x024 (R/W)  System Handler Control and State Register             */__IO uint32_t CFSR;                    /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register                    */__IO uint32_t HFSR;                    /*!< Offset: 0x02C (R/W)  HardFault Status Register                             */__IO uint32_t DFSR;                    /*!< Offset: 0x030 (R/W)  Debug Fault Status Register                           */__IO uint32_t MMFAR;                   /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register                      */__IO uint32_t BFAR;                    /*!< Offset: 0x038 (R/W)  BusFault Address Register                             */__IO uint32_t AFSR;                    /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register                       */__I  uint32_t PFR[2];                  /*!< Offset: 0x040 (R/ )  Processor Feature Register                            */__I  uint32_t DFR;                     /*!< Offset: 0x048 (R/ )  Debug Feature Register                                */__I  uint32_t ADR;                     /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register                            */__I  uint32_t MMFR[4];                 /*!< Offset: 0x050 (R/ )  Memory Model Feature Register                         */__I  uint32_t ISAR[5];                 /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register                   */uint32_t RESERVED0[5];__IO uint32_t CPACR;                   /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register                   */
} SCB_Type;
  • 重点关注外部中断优先级寄存器序列 __IO uint8_t IP[240]; 其偏移0x300,从绝对地址 0xE000 E100UL +0x300 =0xE000 E400UL ~0xE000 E4EFUL 对应着240个byte,也就是对应着240个外部中断的优先级寄存器,这里是可以进行设置的。
  • 系统异常优先级,重点关注 __IO uint8_t SHP[12];,对应着这几组 (4-7, 8-11, 12-15),从绝对地址 0xE000 ED00UL +0x018 =0xE000 ED18UL,我们只关注第PRI_14和PRI_15,分别对应,PendSV 的优先级和 Systick的优先级(这里在启动任务调度的时候,需要将这两个中断优先级配置为最低的),由于对应的32bit ,我们将4个8bit的寄存器当做一组32bit的寄存器来操作,所以对应的12-15 可以拼接成一个地址为0xE000 ED20UL的32bit的寄存器。
  • 进行配置该寄存器优先级的时候,直接操作地址0xE000 ED20UL即可,进行偏移<<16和偏移<<24即可

1.2 优先级分组配置

  • 介绍对于Cortex-M内核来说,优先级的编号越低,多对应的逻辑优先级越高,例如系统的复位,NMI,HardFault的中断优先级级别是-1 ,对应的逻辑优先级也是最高的。
  • 一般的是一个8bit的优先级配置寄存器来表达优先级,厂商一般都会精简优先级数,将低位的不使用,只使用高位进行优先级的表达,例如stm32 使用高4bit来表达优先级,所以2^4 =16 个优先级。
  • 优先级分为 抢占优先级和 子优先级,这是由于SCB结构体里面有个32bit的AIRCR(Application Interrupt and Reset Control Register)寄存器的bit8-bit10的3bits为优先级组的位段

  • 对应的STM32 选用的是分组位置是3 bit4-bit7表示抢占优先级,其余的位未使用,做裁剪,所以FreeRTOS中的配置为16个优先级
  • 对应的main函数初始化的优先级分组去看
// main.c
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4  --对应的16个抢占优先级// misc.h
#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority0 bits for subpriority */// misc.c
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{/* Check the parameters */assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
  • 将寄存器 SCB->AIRCR 的 bit8-bit10 或操作 011,就对应着初始化成 16个抢占优先级。

1.3 FreeRTOS中断 PendSv和Systick中断优先级配置

  • 先介绍几个关于FreeRTOS的中断的配置宏
***************************************************************************************************************/
/*                                FreeRTOS与中断有关的配置选项                                                  */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS#define configPRIO_BITS              __NVIC_PRIO_BITS // --对应分组 4 抢占优先级2^4 从0-15 共有16个优先级
#else#define configPRIO_BITS            4   // --对应的是 4位抢占优先级 宏定义为4
#endif#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY           15                //中断最低优先级
// ----优先级高于5的不能调用FreeRTOS的API了 配置5 应用于系统的开关中断,
// ----对应的都是操作寄存器 basepri =0 就是enable
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5               //系统可管理的最高中断优先级
#define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
  • 前面已经说过,STM32在出厂的时候,已经固定的选择了4个bit作为表达优先级,所以configPRIO_BITS定义为4

  • 4个bit对应的是高4位,需要左移4位,对应的配置内核优先级Pendsv 和systick为最低优先级(对应的数值最高为15),就是应该将这两个中断所对应的优先级寄存器,设置成0xf0,所以对应的configKERNEL_INTERRUPT_PRIORITY的define 如上。

  • 下面代码一步步配置PendSV的和Systick的优先级

  • 在port.c 文件中,函数BaseType_t xPortStartScheduler()
    该函数很重要,被任务调度器开始的调用,用来初始化内核相关的硬件,后面在关于FreeRTOS如何启动第一个任务的时候,会详细的进行分析,其中关于配置PendSV的和Systick的优先级代码如下

/* Make PendSV and SysTick the lowest priority interrupts. */// --配置PendSV 和 SysTick滴答定时器// --portNVIC_SYSPRI2_REG =对应配置内存地址 0xE000ED20 - 0xE000ED23// --进行配置的内存 最后2 Byte 对应着0xE000ED22 和0xE000ED23 2Byte// --portNVIC_PENDSV_PRI 值的设置 : 需要左移 16位 前面2字节 不是我们目标寄存器// --portNVIC_SYSTICK_PRI 值的设置 : 需要左移 24位 前面3字节 不是我们目标寄存器portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
  • portNVIC_SYSPRI2_REG寄存器 是在1.1中所提到的地址0xE000 ED20UL,看具体的代码里面的定义如下
  • 在port.c 文件中

/* Constants required to manipulate the core.  Registers first... */
#define portNVIC_SYSPRI2_REG                ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
  • portNVIC_PENDSV_PRI的定义,对应着1.1小节中推导的,PendSv和SysTick的优先级寄存器,位于高8位和次高8位。
  • 在port.c 文件中

// --在FreeRTOS内 PendSv和SysTick系统滴答定时器的中断优先级 全部设置为最低优先级
#define portNVIC_PENDSV_PRI                 ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
#define portNVIC_SYSTICK_PRI                ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )

至此,与中断有关的配置结束。

2 FreeRTOS的临界段代码保护和开关中断

2.1 临界段代码保护

  • 在开始任务中会写到
taskENTER_CRITICAL();           //进入临界区……taskEXIT_CRITICAL();            //退出临界区
  • task.h 文件中的宏定义

#define taskENTER_CRITICAL()        portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()#define taskEXIT_CRITICAL()          portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
  • portmacro.h 文件中的宏定义

/* Critical section management. */
extern void vPortEnterCritical( void );
extern void vPortExitCritical( void );#define portDISABLE_INTERRUPTS()              vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS()                 vPortSetBASEPRI( 0 )#define portENTER_CRITICAL()                    vPortEnterCritical()
#define portEXIT_CRITICAL()                     vPortExitCritical()#define portSET_INTERRUPT_MASK_FROM_ISR()        ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)    vPortSetBASEPRI(x)
  • port.c 文件中的定义
void vPortEnterCritical( void )
{portDISABLE_INTERRUPTS();uxCriticalNesting++;/* This is not the interrupt safe version of the enter critical function soassert() if it is being called from an interrupt context.  Only APIfunctions that end in "FromISR" can be used in an interrupt.  Only assert ifthe critical nesting count is 1 to protect against recursive calls if theassert function also uses a critical section. */if( uxCriticalNesting == 1 ){configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );}
}void vPortExitCritical( void )
{configASSERT( uxCriticalNesting );uxCriticalNesting--;if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS();}
}
  • 进入临界区相当于 调用portDISABLE_INTERRUPTS();,相当于调用vPortRaiseBASEPRI()

  • 退出临界区相当于 调用portDISABLE_INTERRUPTS();,相当于调用vPortSetBASEPRI( 0 )

  • 具体的开关中断是在对于寄存器 Basepri寄存器的操作,函数vPortRaiseBASEPRI(),向寄存器写入宏configMAX_SYSCALL_INTERRUPT_PRIORITY,那么优先级低于此宏的就会被关掉。

  • 函数vPortSetBASEPRI(),向寄存器写入0,所有中断就恢复了

  • portmacro.h 文件中,具体的汇编代码,对于函数名称为static类型的,前面有对于此函数的宏定义,实际上对外部使用的是,带有extern的形式的

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{__asm{/* Barrier instructions are not used as this function is only used tolower the BASEPRI value. */msr basepri, ulBASEPRI}
}static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;__asm{/* Set BASEPRI to the max syscall priority to effect a criticalsection. */msr basepri, ulNewBASEPRIdsbisb}
}

2.2 中断临界段代码保护

  • 和普通任务级别的是同理的

FreeRTOS学习记录 01--中断管理相关推荐

  1. FreeRTOS学习记录 05--任务调度器开启和切换

    文章目录 0 前言 1 任务调度器的开启 1.1 如何启动第一个任务的 2 任务的切换 2.1 PendSV 异常 2.2 两个事件引起PendSV 异常 2.3 PendSV 的中断服务函数 在这里 ...

  2. FreeRTOS学习记录

    FreeRTOS学习记录 前言 FreeRTOS学习记录 在STM32CubeMX中配置FreeRTOS 前言 本人小白,最近学习了FreeRTOS操作系统,打算做一点记录. 学习的过程中虽然做了点练 ...

  3. JavaScript学习记录01快速入门、基本语法、严格检查模式

    文章目录 JavaScript学习记录01快速入门.基本语法.严格检查模式 1.1什么是JavaScript 1.2认识JavaScript框架 1.3快速入门 1.4基本语法入门 1.5数据类型简介 ...

  4. 【ArcGIS学习记录01】--利用CRU TS数据集绘制降雨量分布图

    [ArcGIS学习记录01]–利用CRU TS数据集绘制降雨量分布图 注:仅作为本人的学习记录方便以后复习查阅. 一.介绍 CRU TS 是目前使用最广泛的气候数据集之一,由英国国家大气科学中心 (N ...

  5. FreeRTOS学习记录(四):任务、任务切换(难点)

    2022-04-23 依据:[野火]<FreeRTOS内核实现与应用开发实战指南> 目录 一.任务 二.创建任务 1.定义任务栈 2.定义任务函数 3.定义任务控制块 4.实现任务创建函数 ...

  6. FreeRTOS学习笔记<中断>

    中断概念 Cortex-M的NVIC最多支持240个IRQ(中断请求).1个不可屏蔽中断(NMI).1个Systick(滴答定时器)定时器中断和多个系统异常. Cortex-M处理器有多个用于管中断和 ...

  7. 初学者,FreeRTOS学习记录,配合STM32CubeMX(一)

    学习FreeRTOS之前,需要先了解RTOS,RTOS全称是Real Time Operating System,中文名是实时操作系统,实时操作系统是保证在一定时间限制内完成特定功能的操作系统.比如u ...

  8. FreeRTOS学习记录 02--任务篇

    文章目录 0 前言 1 任务基础知识 1.1 任务优先级 1.2 任务控制块TCB_t 1.3 任务的状态 2 API函数 2.1 任务创建 2.2 任务删除 2.3 任务阻塞 2.4 任务挂起 2. ...

  9. FreeRTOS学习记录 04--队列篇

    文章目录 0 前言 1 队列的基础知识 1.1 队列 Queue_t 1.2 队列初始化 Dynamic 2 API函数的实现 Application Programming Interface 2. ...

最新文章

  1. Spring框架系列之AOP思想
  2. 开启报名丨智源论坛 · 北大教授宋令阳:超材料感知与通信
  3. [CVPR2016]Learning Deep Feature Representations with Domain Guided Dropout for Person Re-id
  4. 对于存放数据库时带有html标签问题
  5. 如何在 C# 中使用匿名类型
  6. winmail计算机三级,2016年计算机三级网络技术机考模拟题(1)
  7. php mvc 路由,PHP手写MVC (五)—— 路由
  8. 求链式线性表的倒数第K项 (20 分)
  9. VS 查看dll接口
  10. innodb_file_format设置
  11. Redis集群原理和总结
  12. python微服务开发pdf_《微服务设计》中文完整版PDF电子书下载
  13. 20220年春招,秋招必问的1000道Java面试题及答案整理
  14. 弘辽科技:淘宝搜索流量是什么意思?如何提升流量?
  15. 五分钟带你了解《操作系统原理》
  16. Docker的深入浅出(入门新手篇)
  17. 虚拟场景+AR特效,世优科技助力京东手机华为新品发布会MR直播
  18. canvas制作钟表
  19. 四轴无人机那些事 番外篇 4 关于PWM模式的理解
  20. 推荐几个不错的DOTNET控件网址

热门文章

  1. 看风水用什么罗盘最好_风水罗盘放在什么位置效果最好?
  2. 【Android开发小项目】2、猜数字游戏 You win or you suck?
  3. 为MDaemon设置DMRAC验证
  4. 运行wrf报错:could not open xx albedo_modis/index
  5. 必看!TIKTOK TSP跨境服务商入驻详细流程
  6. TCP_IP 应用层
  7. C. Increase Subarray Sums
  8. html5黑板源码,支持移动端的HTML5 Canvas逼真黑板特效
  9. 树莓派4支持多大tf卡_什么是树莓派(Raspberry Pi)?一篇文章带你了解!
  10. 5.5 信号复归——一个强耦合模块的解耦过程