一,、STM32 (Cortex-M3) 中的優(yōu)先級概念 STM32(Cortex-M3)中有兩個優(yōu)先級的概念:搶占式優(yōu)先級和響應優(yōu)先級,,也把響應優(yōu)先級稱作“亞優(yōu)先級”或“副優(yōu)先級”,,每個中斷源都需要被指定這兩種優(yōu)先級,。 1. 何為占先式優(yōu)先級(pre-emption priority) 高占先式優(yōu)先級的中斷事件會打斷當前的主程序/中斷程序運行—搶斷式優(yōu)先響應,俗稱中斷嵌套,。 2. 何為副優(yōu)先級(subpriority) 在占先式優(yōu)先級相同的情況下,,高副優(yōu)先級的中斷優(yōu)先被響應; 在占先式優(yōu)先級相同的情況下,,如果有低副優(yōu)先級中斷正在執(zhí)行,, 高副優(yōu)先級的中斷要等待已被響應的低副優(yōu)先級中斷執(zhí)行結(jié)束后才能得到響應—非搶斷式響應(不能嵌套)。 3. 判斷中斷是否會被響應的依據(jù) 首先是占先式優(yōu)先級,,其次是副優(yōu)先級,; 占先式優(yōu)先級決定是否會有中斷嵌套; Reset,、NMI,、Hard Fault 優(yōu)先級為負(高于普通中斷優(yōu)先級)且不可調(diào)整。 4. 優(yōu)先級沖突的處理 具有高搶占式優(yōu)先級的中斷可以在具有低搶占式優(yōu)先級的中斷處理過程中被響應,,即中斷的嵌套,,或者說高搶占式優(yōu)先級的中斷可以嵌套低搶占式優(yōu)先級的中斷。 當兩個中斷源的搶占式優(yōu)先級相同時,,這兩個中斷將沒有嵌套關系,,當一個中斷到來后,如果正在處理另一個中斷,,這個后到來的中斷就要等到前一個中斷處理完之后才能被處理,。如果這兩個中斷同時到達,,則中斷控制器根據(jù)他們的響應優(yōu)先級高低來決定先處理哪一個,;如果他們的搶占式優(yōu)先級和響應優(yōu)先級都相等,則根據(jù)他們在中斷表中的排位順序決定先處理哪一個,。 5. Cortex-M3中對中斷優(yōu)先級的定義 既然每個中斷源都需要被指定這兩種優(yōu)先級,,就需要有相應的寄存器位記錄每個中斷的優(yōu)先級;在Cortex-M3中定義了8個比特位用于設置中斷源的優(yōu)先級,,這8個比特位可以有8種分配方式,,如下: 所有8位用于指定響應優(yōu)先級 這就是優(yōu)先級分組的概念。 6. stm32中對中斷優(yōu)先級的定義 Cortex-M3允許具有較少中斷源時使用較少的寄存器位指定中斷源的優(yōu)先級,,因此STM32把指定中斷優(yōu)先級的寄存器位減少到4位,,這4個寄存器位的分組方式如下: 第0組:所有4位用于指定響應優(yōu)先級 AIRC(Application Interrupt and Reset Register)寄存器中有用于指定優(yōu)先級的 4 bits。這4個bits用于分配preemption優(yōu)先級和sub優(yōu)先級,,在STM32的固件庫中定義如下: /* Preemption Priority Group */
NVIC_PriorityGroup_0 => 選擇第0組 接下來就是指定中斷源的優(yōu)先級,下面以一個簡單的例子說明如何指定中斷源的搶占式優(yōu)先級和響應優(yōu)先級: // 選擇使用優(yōu)先級分組第1組 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定響應優(yōu)先級別0
要注意的幾點是: 1. 如果指定的搶占式優(yōu)先級別或響應優(yōu)先級別超出了選定的優(yōu)先級分組所限定的范圍,,將可能得到意想不到的結(jié)果,; 2. 搶占式優(yōu)先級別相同的中斷源之間沒有嵌套關系; 3. 如果某個中斷源被指定為某個搶占式優(yōu)先級別,,又沒有其它中斷源處于同一個搶占式優(yōu)先級別,,則可以為這個中斷源指定任意有效的響應優(yōu)先級別。 二,、開關總中斷 在STM32/Cortex-M3中是通過改變CPU的當前優(yōu)先級來允許或禁止中斷,。 在STM32固件庫中(stm32f10x_nvic.c和stm32f10x_nvic.h) 定義了四個函數(shù)操作PRIMASK位和FAULTMASK位,,改變CPU的當前優(yōu)先級,,從而達到控制所有中斷的目的。 下面兩個函數(shù)等效于關閉總中斷: 下面兩個函數(shù)等效于開放總中斷: 上面兩組函數(shù)要成對使用,,但不能交叉使用。 例如: 第一種方法: 第二種方法: 常常使用: -------------------------------------------------------------------------------------------------
NVIC例程程序
/* Includes ------------------------------------------------------------------*/ #include "stm32f10x_lib.h"
NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; bool PreemptionOccured = FALSE; u8 PreemptionPriorityValue = 0; ErrorStatus HSEStartUpStatus;
/* Private function prototypes -----------------------------------------------*/ void RCC_Configuration(void); void GPIO_Configuration(void); void EXTI_Configuration(void); void Delay(vu32 nCount);
/* Private functions ---------------------------------------------------------*/
int main(void) { #ifdef DEBUG debug(); #endif
/* Configure the system clocks */ RCC_Configuration();
/* Configure GPIOs */ GPIO_Configuration();
/* Configures the EXTI Lines */ EXTI_Configuration();
#ifdef VECT_TAB_RAM /* Set the Vector Table base location at 0x20000000 */ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif
/* Configure one bit for preemption priority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //先占優(yōu)先級分組為1
/* Enable the EXTI0 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;// 外部中斷線0中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PreemptionPriorityValue; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
/* Enable the EXTI9_5 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
/* Configure the SysTick Handler Priority: Preemption priority and subpriority */ NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, !PreemptionPriorityValue, 0);
while (1) { if(PreemptionOccured != FALSE) { GPIO_WriteBit(GPIOC, GPIO_Pin_6, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_6))); Delay(0x5FFFF); GPIO_WriteBit(GPIOC, GPIO_Pin_7, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_7))); Delay(0x5FFFF); GPIO_WriteBit(GPIOC, GPIO_Pin_8, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_8))); Delay(0x5FFFF); GPIO_WriteBit(GPIOC, GPIO_Pin_9, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_9))); Delay(0x5FFFF); } } //GPIO_WriteBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin, BitAction BitVal)設置或者清除指定的數(shù)據(jù)端口位 }
void RCC_Configuration(void) { /* RCC system reset(for debug purpose) */ RCC_DeInit();
/* Enable HSE */ RCC_HSEConfig(RCC_HSE_ON);
/* Wait till HSE is ready */ HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS) { /* HCLK = SYSCLK */ RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */ RCC_PCLK2Config(RCC_HCLK_Div1);
/* PCLK1 = HCLK/2 */ RCC_PCLK1Config(RCC_HCLK_Div2);
/* Flash 2 wait state */ FLASH_SetLatency(FLASH_Latency_2); //FLASH_Latency用來設置FLASH存儲器延時時鐘周期數(shù)2 /* Enable Prefetch Buffer */ FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //使能或者失能預取指緩存
/* PLLCLK = 8MHz * 9 = 72 MHz */ //PLL的輸入時鐘 = HSE時鐘頻率, RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* Enable PLL */ RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } //當設置完成時,,RCC_FLAG_PLLRDY返回SET(1)
/* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */ while(RCC_GetSYSCLKSource() != 0x08) { } }
/* Enable GPIOA, GPIOB, GPIOC and AFIO Clocks */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); }
void GPIO_Configuration(void) { /* Configure PC6, PC7, PC8 and PC9 as output push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Configure GPIOA Pin0 as input floating */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure GPIOB Pin9 as input floating */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, &GPIO_InitStructure); }
void EXTI_Configuration(void) { /* Connect EXTI Line0 to PA.00 */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
/* Configure EXTI Line0 to generate an interrupt on falling edge */ EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);
/* Connect EXTI Line9 to PB.09 */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);
/* Configure EXTI Line9 to generate an interrupt on falling edge */ EXTI_InitStructure.EXTI_Line = EXTI_Line9; EXTI_Init(&EXTI_InitStructure); }
void Delay(vu32 nCount) { for(; nCount != 0; nCount--); }
#ifdef DEBUG void assert_failed(u8* file, u32 line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */ while (1) { } } #endif
1設計要求 設計一個中斷優(yōu)先級搶占的實例。設置三個中斷:EXTI0,、EXTI9和SysTick,,在EXTI9的中斷服務之程序 中實現(xiàn)EXTI1和SysTick的優(yōu)先級別的轉(zhuǎn)換,使之分別出現(xiàn):在EXTI1中斷時可以被SysTick搶占和不可以 被SysTick搶占這兩種狀態(tài),。 2 硬件電路設計 在評估板上按鍵Key與PB.9相連作為EXTI9,,按鍵Wakeup與PA.0相連作為EXTI1;LED1,、LED2,、LED3、LED4 分別與PC6,、PC7,、PC8、PC9相連,,用于顯示不同的優(yōu)先級搶占狀態(tài),。 3 軟件程序設計 根據(jù)設計任務要求,軟件程序主要內(nèi)容包括: (1) 配置兩根EXTI 外部中斷線(Line0 和 Line9),,在下降沿產(chǎn)生中斷,,并配置SysTick中斷。其中: EXTI0: 優(yōu)先級= PreemptionPriorityValue,,子優(yōu)先級=0,; EXTI9: 優(yōu)先級= 0,子優(yōu)先級= 1,; SysTick Handler:優(yōu)先級= !PreemptionPriorityValue,,子優(yōu)先級 SubPriority = 0; (2) 剛開始設置PreemptionPriorityValue=0,,即EXTI0優(yōu)先級比SysTick優(yōu)先級高,;在EXTI9中斷服務子 程序中,EXTI Line0和SysTick 的優(yōu)先級對換,; (3) 在EXTI0中斷服務子程序中,,SysTick中斷掛起位被置1,若SysTick優(yōu)先級比EXTI Line0 優(yōu)先級高,, 則EXTI 0中斷被搶占,,轉(zhuǎn)而去執(zhí)行SysTick中斷服務子程序;否則繼續(xù)執(zhí)行EXTI Line0中斷服務子程序,。 (4) 如果EXTI1被SysTick搶占則LED1,、LED2、LED3,、LED4循環(huán)閃爍,,如果EXTI1搶占SysTick則LED1、 LED2,、LED3,、LED4常亮。 4 運行過程 (1) 使用Keil uVision3 通過ULINK仿真器連接實驗板,,打開實驗例程NVIC_test子目錄下的NVIC.Uv2 例程,,編譯鏈接工程; (2) 點擊MDK 的Debug菜單,,選擇Start/Stop Debug Session項或Ctrl+F5鍵,,遠程連接目標板并下載 調(diào)試代碼到目標系統(tǒng)的RAM中; (3) 程序正常啟動運行后,,會有以下結(jié)果: 當?shù)谝淮伟l(fā)生EXTI9 中斷后(按下STM32板上Key按鈕),,SysTick中斷的優(yōu)先級比EXTI0中斷 優(yōu)先級高。因此當EXTI0中斷發(fā)生時(按下Wakeup按鈕),,將先執(zhí)行SysTick中斷服務子程序,, 發(fā)生搶占,變量PreemptionOccured 為真,,LED1-LED4開始閃爍,; 當?shù)诙伟l(fā)生EXTI9中斷后,SysTick中斷的占優(yōu)先級比EXTI0優(yōu)先級低,,因此當EXTI0中斷發(fā) 生時SysTick無法搶占,,變量PreemptionOccured 為假,LED1-LED4停止閃爍,; 每次EXTI9發(fā)生后,,SysTick和EXTI0就會發(fā)生優(yōu)先級轉(zhuǎn)換,出現(xiàn)前面2步的狀態(tài),。 |
|