Cortex-M 中断

          中断由 硬件 产生,当中断产生,CPU会中断当前程序 去执行 中断服务.

          Cortex-M 内核 提供 一个 中断管理 的 嵌套向量中断控制器 (NVIC)

          Cortex-M3 M4 的NVIC 最多支持 240个IRQ(中断请求) ,1 个不可屏蔽中断 (NMI), 1 个 点滴定时器(Systick) 中断,多个系统异常

中断管理

       Cortex-M 有多个用于管理中断和异常的可编辑寄存器,大多寄存器在 NVIC,系统控制块(SCB)中,硬件抽象层(CMSIS
)把寄存器封装为结构体,在 core_cm4.h 中 NVIC_Type ,SCB_Type

 /* Defines 'read / write' structure member permissions 定义“读/写”结构成员权限 */
#define    __IOM    volatile 

/* Defines 'write only' structure member permissions 定义“只写”结构成员权限 */        
#define    _OM      volatile          

typedef struct
{
    __IOM uint32_t ISER[8U];          // 0x000  Interrupt Set Enable Register 中断设置启用寄存器
          uint32_t RESERVED0[24U];    //保留字节
    __IOM uint32_t ICER[8U];          // 0x080 Interrupt Clear Enable Register 中断清除启用寄存器
          uint32_t RESERVED1[24U];    
    __IOM uint32_t ISPR[8U];         // 0x100 Interrupt Set Pending Register 中断设置挂起寄存器
          uint32_t RESERVED2[24U];
    __IOM uint32_t ICPR[8U];        // 0x180 Interrupt Clear Pending Register 中断清除挂起寄存器
          uint32_t RESERVED3[24U];
    __IOM uint32_t IABR[8U];        // 0x200 Interrupt Active bit Register 中断激活位寄存器
          uint32_t RESERVED4[56U];

    // 0x300 Interrupt Priority Register (8Bit wide) 中断优先级寄存器 8位宽
    __IOM uint8_t IP[240U];    
    
          uint32_t RESERVED5[644U];
    __OM uint32_t STIR;            // 0xE00 Software Trigger Interrupt Register 软件触发中断寄存器
}NVIC_Type;
/* Defines 'read only' structure member permissions 定义“只读”结构成员权限*/
#define    __IM    volatile const

typedef struct
{
    __IM  uint32_t CPUID;    /* 0x000 CPUID Base Register CPUID基寄存器*/

    /* 0x004 Interrupt Control and State Register 中断控制和状态寄存器*/
    __IOM uint32_t ICSR;
     
    __IOM uint32_t VTOR;     /* 0x008 Vector Table Offset Register 向量表偏移寄存器*/

    /* 0x00C Application Interrupt and Reset Control Register 应用中断和复位控制寄存器*/
    __IOM uint32_t AIRCR;    
    __IOM uint32_t SCR;      /* 0x010 System Control Register 系统控制寄存器*/
    __IOM uint32_t CCR;       /* 0x014 Configuration Control Register 配置控制寄存器*/

/* 0x018 System Handlers Priority Registers 系统处理程序优先级寄存器(4-7, 8-11, 12-15)*/
    __IOM uint8_t  SHP[12U]; 

   /* 0x024 System Handler Control and State Register 系统处理程序控件和状态寄存器*/
    __IOM uint32_t SHCSR;   
  
    __IOM uint32_t CFSR;      /* 0x028 (R/W) Configurable Fault Status Register */
    __IOM uint32_t HFSR;       /* 0x02C (R/W)  HardFault Status Register */
    __IOM uint32_t DFSR;       /* 0x030 (R/W)  Debug Fault Status Register */
    __IOM uint32_t MMFAR;      /* 0x034 (R/W)  MemManage Fault Address Register */
    __IOM uint32_t BFAR;       /* 0x038 (R/W)  BusFault Address Register */
    __IOM uint32_t AFSR;       /* 0x03C (R/W)  Auxiliary Fault Status Register */
    __IM  uint32_t PFR[2U];    /* 0x040 (R/ )  Processor Feature Register */
    __IM  uint32_t DFR;        /* 0x048 (R/ )  Debug Feature Register */
    __IM  uint32_t ADR;        /* 0x04C (R/ )  Auxiliary Feature Register */
    __IM  uint32_t MMFR[4U];   /* 0x050 (R/ )  Memory Model Feature Register */
    __IM  uint32_t ISAR[5U];   /* 0x060 (R/ )  Instruction Set Attributes Register */
          uint32_t RESERVED0[5U];
    __IOM uint32_t CPACR;       /* 0x088 (R/W)  Coprocessor Access Control Register */
} SCB_Type;

        NVIC , SCB 都位于 系统控制空间(SCS),SCS 地址从 0XE000E000

// System Control Space Base Address 系统控制空间基址
#define    SCS_BASE    (0xE000E000UL)

// NVIC Base Address NVIC基址
#define    NVIC_BASE   (SCS_BASE + 0x0100UL)

// System Control Block Base Address 系统控制块基址
#define    SCB_BASE    (SCS_BASE + 0x0D00UL)

// System control Register not in SCB
#define    SCnSCB      ((SCnSCB_Type *)SCS_BASE)

//  SCB configuration struct SCB配置结构
#define    SCB         ((SGB_Type *)SCB_BASE)

// NVIC configuration struct NVIC配置结构
#define    NVIC        ((NVIC_Type *)NVIC_BASE)

  优先级分组定义

         当多个中断来临,响应那个中断由中断优先级决定,高优先级(优先级编号小)首先响应

         中断嵌套 :高优先级可以 打断 低优先级

         Cortex-M 有固定的优先级的中断,如:复位、 NMI、HardFault

         优先级的 具体个数 由 芯片厂家 决定

typedef enum IRQn
{
    /******  Cortex-M4 Processor Exceptions Numbers *************/  
    NonMaskableInt_IRQn     = -14,    /*!< 2 Non Maskable Interrupt                */
    MemoryManagement_IRQn   = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt */
    BusFault_IRQn           = -11,    /*!< 5 Cortex-M4 Bus Fault Interrupt         */
    UsageFault_IRQn         = -10,    /*!< 6 Cortex-M4 Usage Fault Interrupt       */
    SVCall_IRQn             = -5,     /*!< 11 Cortex-M4 SV Call Interrupt          */
    DebugMonitor_IRQn       = -4,     /*!< 12 Cortex-M4 Debug Monitor Interrupt    */
    PendSV_IRQn             = -2,     /*!< 14 Cortex-M4 Pend SV Interrupt          */ 
    SysTick_IRQn            = -1,     /*!< 15 Cortex-M4 System Tick Interrupt      */
    //...                              
} IRQn_Type;

       优先级由MSB对齐 ,下图为 3 位

          优先级就是 8 个: 0X00(最高优先级)、 0X20( 00100000 )、 0X40(01000000)、0X60(01100000)、 0X80、 0XA0、 0XC0 和 0XE0

         优先级按位分为 抢占优先级(分组优先级) ,亚优先级(子优先级)

typedef struct
{
    //...
  /* 0x00C (R/W) Application Interrupt and Reset Control Register 应用中断和复位控制寄存器*/
    __IOM uint32_t AIRCR;   
    //...
} SCB_Type;

     AIRCR 寄存器设置优先级分组

     PRIGROUP 分为  MSB 所在的位段(左边的)对应抢占优先级, LSB 所在的位段(右边的)对应亚优先级

/*!< 0 bits for pre-emption priority 4 bits for subpriority 0位表示先占优先级 4位表示子优先级*/
#define    NVIC_PriorityGroup_0     ((uint32_t)0x700) 

/*!< 1 bits for pre-emption priority 3 bits for subpriority */
#define    NVIC_PriorityGroup_1     ((uint32_t)0x600) 

/*!< 2 bits for pre-emption priority 2 bits for subpriority */
#define    NVIC_PriorityGroup_2     ((uint32_t)0x500) 
 
/*!< 3 bits for pre-emption priority 1 bits for subpriority */
#define    NVIC_PriorityGroup_3     ((uint32_t)0x400) 

/*!< 4 bits for pre-emption priority 0 bits for subpriority */
#define    NVIC_PriorityGroup_4     ((uint32_t)0x300) 

 

     FreeRTOS 的中断配置没有处理 亚优先级 这种情况,所以只能配置为组 4,直接就 16 个优先级
 

int main(void)
{
	/* Configure the hardware ready to run the test 配置硬件以准备运行测试 */
	prvSetupHardware();

	/* Start standard demo/test application flash tasks. The LED flash tasks 
           are always created.  The other tasks are only created if 
           mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY is set to 0  
           启动标准演示/测试应用程序闪存任务。始终创建LED闪烁任务,只有当 
           mainCREATE_SIMPLE_LED_FLASHER_DEMO_only设置为0时,才会创建其他任务 */
	vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY );

	/* The following function will only create more tasks and timers if
	   mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY is set to 0  
           以下函数仅在以下情况下创建更多任务和计时器
           mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY 设置为 0*/
	prvOptionallyCreateComprehensveTestApplication();

	/* Start the scheduler. 启动调度程序*/
	vTaskStartScheduler();

	/* If all is well, the scheduler will now be running, and the following line
	   will never be reached.  If the following line does execute, then there was
	   insufficient FreeRTOS heap memory available for the idle and/or timer tasks
	   to be created.  
           如果一切正常,调度程序现在将运行,并且永远无法到达下一行。如果执行以下行,
           则空闲和/或计时器任务的可用空闲RTOS堆内存不足*/
	for( ;; );
}

static void prvSetupHardware( void )
{
	/* Setup STM32 system (clock, PLL and Flash configuration) */
	SystemInit();

	/* Ensure all priority bits are assigned as preemption priority bits. 
           确保所有优先级位都被分配为抢占优先级位*/
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

	/* Setup the LED outputs. */
	vParTestInitialise();

	/* Configure the button input.  This configures the interrupt to use the
	lowest interrupt priority, so it is ok to use the ISR safe FreeRTOS API
	from the button interrupt handler. */
	STM_EVAL_PBInit( BUTTON_USER, BUTTON_MODE_EXTI );
}

  优先级设置

             优先级寄存器可以按 字节访问


     中断优先级寄存器阵列

     系统异常优先级阵列

      设置 PendSV 和 SysTick 的中断优先级的时候都是 直接操作的地址  0xE000_ED20。

 中断屏蔽的特殊寄存器

           PRIMASK 寄存器

                        PRIMASK 用于禁止除 NMI 和 Hard***t 外的所有异常和中断

                       汇编编程,可以使用 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 以使能中断

                    开关中断就是直接操作 PRIMASK寄存器
 

          FAULTMASK 寄存器

                          FAULTMASK 连 HardFault 都屏蔽掉,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 使能中断

          BASEPRI 寄存器

                     向 BASEPRI 写 0 ,就会停止屏蔽中断

//屏蔽优先级不高于 0X60 的中断

MOV R0, #0X60
MSR BASEPRI, R0

//取消 BASEPRI 对中断的屏蔽

MOV R0, #0
MSR BASEPRI, R0

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

   FreeRTOS 中断配置宏

              设置 MCU 使用几位优先级

/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
	/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
	#define configPRIO_BITS       		__NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		4        /* 15 priority levels */
#endif

              设置最低优先级

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			0xf

          设置内核中断优先级
 

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY  ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 
                                           (8 - configPRIO_BITS) )

              设置 FreeRTOS 系统可管理的最大优先级

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	5

 

 

FreeRTOS 开关中断

      开关中断函数

//..\FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU\portmacro.h

#define    portDISABLE_INTERRUPTS()  	vPortRaiseBASEPRI()

//开中断
#define    portENABLE_INTERRUPTS()	vPortSetBASEPRI(0)
#ifndef portFORCE_INLINE

  #define portFORCE_INLINE __forceinline
#endif

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
    // 低 于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断就会被屏蔽
    uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
}

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}

 

临界段代码
   

            临界段代码也叫做临界区,指 那些必须完整运行,不能被打断的代码段,临界区代码一定要精简,快进快出。

            任务级临界段代码保护

// FreeRTOS\Source\include\task.h
/**
 * task. h
 *
 * Macro to mark the start of a critical code region.  Preemptive context
 * switches cannot occur when in a critical region.
 *
 * NOTE: This may alter the stack (depending on the portable implementation)
 * so must be used with care!
 *
 * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL
 * \ingroup SchedulerControl
 * 进入临界段
 */
#define taskENTER_CRITICAL()		portENTER_CRITICAL()

/**
 * task. h
 *
 * Macro to mark the end of a critical code region.  Preemptive context
 * switches cannot occur when in a critical region.
 *
 * NOTE: This may alter the stack (depending on the portable implementation)
 * so must be used with care!
 *
 * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL
 * \ingroup SchedulerControl
 * 退出临界段
 */
#define taskEXIT_CRITICAL()			portEXIT_CRITICAL()
// ..\FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU\portmacro.h 
/* Critical section management. */
extern void vPortEnterCritical( void );
extern void vPortExitCritical( void );

#define    portENTER_CRITICAL()	vPortEnterCritical()
#define    portEXIT_CRITICAL()	vPortExitCritical()

 

// FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU\port.c
void vPortEnterCritical( void )
{
    BaseType_t xRunningPrivileged = xPortRaisePrivilege();

	portDISABLE_INTERRUPTS();  //关闭中断
	uxCriticalNesting++;       // 全局变量,临界段嵌套次数

	vPortResetPrivilege( xRunningPrivileged );
}

void vPortExitCritical( void )
{
    BaseType_t xRunningPrivileged = xPortRaisePrivilege();

	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();  /使能中断,所有的临界段代码都退出以后才会使能中断
	}
	vPortResetPrivilege( xRunningPrivileged );
}

 

     中断级临界段代码保护

              中断级别临界段代码保 护 ,是用在中断服务程序中

/**
 * task. h
 *
 * Macro to mark the start of a critical code region.  Preemptive context
 * switches cannot occur when in a critical region.
 *
 * NOTE: This may alter the stack (depending on the portable implementation)
 * so must be used with care!
 *
 * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL
 * \ingroup SchedulerControl
 */
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()

/**
 * task. h
 *
 * Macro to mark the end of a critical code region.  Preemptive context
 * switches cannot occur when in a critical region.
 *
 * NOTE: This may alter the stack (depending on the portable implementation)
 * so must be used with care!
 *
 * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL
 * \ingroup SchedulerControl
 */
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

 

//..\FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU\portmacro.h 

#define portSET_INTERRUPT_MASK_FROM_ISR()	ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)


static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		mrs ulReturn, basepri        //读BASEPRI的值,保存在ulReturn
		msr basepri, ulNewBASEPRI    //把ulNewBASEPRI的值,写入到 BASEPRI
		dsb
		isb
	}

	return ulReturn;        //退出临界区代码保护后使用此值
}

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}