文中截圖摘自《Cortex_M3權威指南》
core_cm3.h
1 Memory Map
/* Memory mapping of Cortex-M3 Hardware */
#define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
#define ITM_BASE (0xE0000000) /*!< ITM Base Address */
#define CoreDebug_BASE (0xE000EDF0) /*!< Core Debug Base Address */
#define SysTick_BASE (SCS_BASE + 0x0010) /*!< SysTick Base Address */
#define NVIC_BASE (SCS_BASE + 0x0100) /*!< NVIC Base Address */
#define SCB_BASE (SCS_BASE + 0x0D00) /*!< System Control Block Base Address */
#define InterruptType ((InterruptType_Type *) SCS_BASE) /*!< Interrupt Type Register */
#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 */
#define ITM ((ITM_Type *) ITM_BASE) /*!< ITM configuration struct */
#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */
#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1)
#define MPU_BASE (SCS_BASE + 0x0D90) /*!< Memory Protection Unit */
#define MPU ((MPU_Type*) MPU_BASE) /*!< Memory Protection Unit */
#endif
從圖5.2可以看到, CM3的SCS的基地址爲0xE000E000。
2 SCB
core_cm3.c
/** @addtogroup CMSIS_CM3_SCB CMSIS CM3 SCB
memory mapped structure for System Control Block (SCB)
@{
*/
typedef struct
{
__I uint32_t CPUID; /*!< Offset: 0x00 CPU ID Base Register */
__IO uint32_t ICSR; /*!< Offset: 0x04 Interrupt Control State Register */
__IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register */
__IO uint32_t AIRCR; /*!< Offset: 0x0C Application Interrupt / Reset Control Register */
__IO uint32_t SCR; /*!< Offset: 0x10 System Control Register */
__IO uint32_t CCR; /*!< Offset: 0x14 Configuration Control Register */
__IO uint8_t SHP[12]; /*!< Offset: 0x18 System Handlers Priority Registers (4-7, 8-11, 12-15) */
__IO uint32_t SHCSR; /*!< Offset: 0x24 System Handler Control and State Register */
__IO uint32_t CFSR; /*!< Offset: 0x28 Configurable Fault Status Register */
__IO uint32_t HFSR; /*!< Offset: 0x2C Hard Fault Status Register */
__IO uint32_t DFSR; /*!< Offset: 0x30 Debug Fault Status Register */
__IO uint32_t MMFAR; /*!< Offset: 0x34 Mem Manage Address Register */
__IO uint32_t BFAR; /*!< Offset: 0x38 Bus Fault Address Register */
__IO uint32_t AFSR; /*!< Offset: 0x3C Auxiliary Fault Status Register */
__I uint32_t PFR[2]; /*!< Offset: 0x40 Processor Feature Register */
__I uint32_t DFR; /*!< Offset: 0x48 Debug Feature Register */
__I uint32_t ADR; /*!< Offset: 0x4C Auxiliary Feature Register */
__I uint32_t MMFR[4]; /*!< Offset: 0x50 Memory Model Feature Register */
__I uint32_t ISAR[5]; /*!< Offset: 0x60 ISA Feature Register */
} SCB_Type;
3 NVIC
寄存器定義
typedef struct
{
__IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */
} NVIC_Type;
ISER&ICER
中斷的使能與除能分別使用各自的寄存器來控制——這與傳統的,使用單一比特的兩個狀態來表達使能與除能是不同的。CM3中可以有240對使能位/除能位(SETENA位/CLRENA位),每個中斷擁有一對。這240個對子分佈在8對32位寄存器中(最後一對沒有用完)。欲使能一箇中斷,我們需要寫1到對應SETENA的位中;欲除能一箇中斷,你需要寫1到對應的CLRENA位中。如果往它們中寫0,則不會有任何效果。
ISPR&ICPR
如果中斷髮生時,正在處理同級或高優先級異常,或者被掩蔽,則中斷不能立即得到響應。此時中斷被懸起。中斷的懸起狀態可以通過“中斷設置懸起寄存器(SETPEND)”和“中斷懸起清除寄存器(CLRPEND)”來讀取,還可以寫它們來手工懸起中斷。
懸起寄存器和“解懸”寄存器也可以有8對,其用法和用量都與前面介紹的使能/除能寄存器完全相同,見表8.2。
IP[240]
IABR[8]
每個外部中斷都有一個活動狀態位。在處理器執行了其ISR的第一條指令後,它的活動位就被置1,並且直到ISR返回時才硬件清零。由於支持嵌套,允許高優先級異常搶佔某個ISR。然而,哪怕中斷被搶佔,其活動狀態也依然爲1(請仔細琢磨前文講到的“直到ISR返回時才清零)。活動狀態寄存器的定義,與前面講的使能/除能和懸起/解懸寄存器相同,只是不再成對出現。它們也能按字/半字/字節訪問,但他們是隻讀的,如表8.4所示
STIR
NVIC 函數/宏
優先級分組設置
下表爲AIRCR的寄存器,AIRCR位於SCB中。
- NVIC_SetPriorityGrouping這個函數實現就非常簡單了,同時將VECTKEY和PRIGROUP就能完成優先級組的設置。
- NVIC_GetPriorityGrouping是讀出優先級組設置的函數,實現很簡單,直接讀AIRCR的PRIGROUP字段即可。
static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */
reg_value = SCB->AIRCR; /* read old register configuration */
reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */
reg_value = (reg_value |
(0x5FA << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << 8)); /* Insert write key and priorty group */
SCB->AIRCR = reg_value;
}
static __INLINE uint32_t NVIC_GetPriorityGrouping(void)
{
return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */
}
使能/除能中斷
NVIC_EnableIRQ設置ISER中的某一箇中斷,計算方法也很簡單,就是簡單的bitmap操作。
NVIC_DisableIRQ設置ICER中的某一箇中斷,計算方法與NVIC_EnableIRQ一樣。
static __INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */
}
static __INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)
{
NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */
}
中斷掛起/清除
以下三個函數非常分別獲取中斷掛起狀態,設置中斷掛起,清除中掛起。實際上就是去操作NVIC的ISPR和ICPR寄存器,看過上面寄存器定義看到這個代碼就很好理解了。
static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
{
return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */
}
static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */
}
static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */
}
激活中斷狀態
NVIC_GetActive操作NVIC中的IABR寄存器,其操作與其他NVIC的寄存器操作一樣,類似bitmap。
static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
{
return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */
}
設置中斷優先級
優先級設置會根據是IRQ還是系統內部異常設置不同的寄存器,如果是IRQ中斷,設置NVIC的IP寄存器。否則設置SCB中SHP寄存器。下圖就是SHP的寄存器,SHP位於SCB中。
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if(IRQn < 0) {
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
}
static __INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn)
{
if(IRQn < 0) {
return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M3 system interrupts */
else {
return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */
}
Encode/Decode優先級
NVIC_EncodePriority的計算方法如下:先根據優先級組設置PriorityGroup得到優先級是如何分佈,即多少位表示搶佔優先級,多少位表示次優先級。然後根據得到的位數分佈分別將搶佔優先級和次優先級填入一個字節中。
NVIC_DecodePriority 正好相反,根據優先級值和優先級組設置獲取搶佔優先級和次優先級。
static __INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
{
uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */
uint32_t PreemptPriorityBits;
uint32_t SubPriorityBits;
PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp;
SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS;
return (
((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) |
((SubPriority & ((1 << (SubPriorityBits )) - 1)))
);
}
static __INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority)
{
uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */
uint32_t PreemptPriorityBits;
uint32_t SubPriorityBits;
PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp;
SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS;
*pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1);
*pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1);
}
系統復位
這個看AIRCR寄存器的定義就可以知道往AIRCR中的SYSRESETREQ就可以知道,往該位中寫入1就能觸發一次芯片復位。
static __INLINE void NVIC_SystemReset(void)
{
SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
while(1); /* wait until reset */
}
4 SysTick
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
core_cm3.c
這個文件裏的實現都是封裝了一些C語言沒法操作的東西,代碼都非常簡單。
設置棧
__ASM uint32_t __get_PSP(void)
{
mrs r0, psp
bx lr
}
__ASM void __set_PSP(uint32_t topOfProcStack)
{
msr psp, r0
bx lr
}
__ASM uint32_t __get_MSP(void)
{
mrs r0, msp
bx lr
}
__ASM void __set_MSP(uint32_t mainStackPointer)
{
msr msp, r0
bx lr
}
設置PRIMASK
__ASM uint32_t __get_PRIMASK(void)
{
mrs r0, primask
bx lr
}
__ASM void __set_PRIMASK(uint32_t priMask)
{
msr primask, r0
bx lr
}
設置BASEPRI
__ASM uint32_t __get_BASEPRI(void)
{
mrs r0, basepri
bx lr
}
/**
* @brief Set the Base Priority value
*
* @param basePri BasePriority
*
* Set the base priority register
*/
__ASM void __set_BASEPRI(uint32_t basePri)
{
msr basepri, r0
bx lr
}