1. 程式人生 > 其它 >STM32 中斷總結(1)

STM32 中斷總結(1)

技術標籤:STM32stm32嵌入式微控制器

前言

由於個人部落格被攻擊,現逐漸將部落格內容搬運至CSDN,本文原寫於2020年4月。

中斷是微控制器的核心之一,stm32具有強大的中斷,幾乎每個外設都可設中斷。

補充知識

  • 中斷和異常向量表(見st官方stmF10x英文資料手冊),共60個可設定中斷
  • NVIC:巢狀向量中斷控制器,控制著整個晶片中斷相關的功能,它跟核心緊密耦合,是核心裡面的一個外設。但是各個晶片廠商在設計晶片的時候會對 Cortex-M3 核心裡面的 NVIC 進行裁剪,把不需要的部分去掉,所以說 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一個子集。
  • 《STM32F10xxx Cortex-M3程式設計手冊》

NVIC

結構體定義

在這裡插入圖片描述

優先順序

概況

在NVIC中 中斷優先順序暫存器 NVIC_IPRx用來配置優先順序,該暫存器寬度為8bit,但是在F103中只使用了高4bit,這4bit又分組為搶佔優先順序和子優先順序。如果多個終端同時響應,依次比較搶佔優先順序、子優先順序和硬體中斷編號(越小越先)。

分組

優先順序的分組由核心外設 SCB 的應用程式中斷及復位控制暫存器 AIRCR 的PRIGROUP[10:8]位決定, F103 分為了 5 組,具體如下圖:主優先順序=搶佔優先順序

優先順序分組

對應位於misc.h和misc.c中函式 NVIC_PriorityGroupConfig() 實現
優先順序分組真值表

步驟

配置步驟

  1. 使能外設某個中斷,具體由每個外設的相關中斷使能位控制;
  2. 初始化NVIC_InitTypeDef結構體,設定搶佔優先順序和子優先順序,使能中斷請求。
  3. 編寫中斷服務函式

解釋:

對於步驟二:程式碼如下


typedef struct
{
uint8_t NVIC_IRQChannel; // 中斷源

uint8_t NVIC_IRQChannelPreemptionPriority; //搶先優先順序

uint8_t NVIC_IRQChannelSubPriority; //子優先順序

FunctionalState NVIC_IRQChannelCmd; //中斷使能或者失能
} NVIC_InitTypeDef;

對於中斷源的配置特此說明:不同的中斷中斷源不一樣,寫錯不報錯。成員過多,此處不再展示,具體參考stm32f103.h裡的IRQn_Type結構體定義。


對於步驟三:在 在啟動檔案

startup_stm32f10x_hd.s 中預先為每個中斷都寫了一箇中斷服務函式,只是這些中斷函式都是為空,為的只是初始化中斷向量表。實際的中斷服務函式都需要重新編寫, 為了方便管理中斷服務函式統一寫在 stm32f10x_it.c 這個庫檔案中 .

EXTI

這部分不打算寫很詳細,講起來太多了,寫個大致 。主要涉及程式碼編寫流程。主要方便自個程式設計作為參考

簡單說,EXTI全稱外部中斷/事件控制器。管理了20箇中斷線,具有邊沿檢測器,可實現輸入訊號上升沿or下降沿的檢測。可實現對每個中斷線進行單獨配置。


硬體流程圖

在這裡插入圖片描述

中斷線/實踐線

每個GPIO都可以被設定為輸入線,見表:

在這裡插入圖片描述

EXTI結構體

EXTI初始化結構體(stm32f10x_exti.c,stm32f10x_exti.h)


typedef struct
{
uint32_t EXTI_Line; //中斷/事件線

EXTIMode_TypeDef EXTI_Mode; //模式

EXTITrigger_TypeDef EXTI_Trigger; //觸發型別

FunctionalState EXTI_LineCmd;//使能
}EXTI_InitTypeDef;

說明:

  • EXTI_Line見上表

  • XTIMode_TypeDef EXTI_Mode可選擇產生中斷還是事件

  • EXTITrigger_TypeDef EXTI_Trigger選擇上升沿or下降沿

程式設計

步驟

  1. 初始化GPIO
  2. 初始化EXTI
  3. 初始化NVIC
  4. 編寫中斷服務函式

NVIC配置


static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC為優先順序組1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中斷源:按鍵1 */
  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
  /* 配置搶佔優先順序 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子優先順序 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中斷通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  /* 配置中斷源:按鍵2,其他使用上面相關配置 */  
  NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
  NVIC_Init(&NVIC_InitStructure);
}

GPIO設定和EXTI終端配置


void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;

/*開啟按鍵GPIO口的時鐘*/
RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);

/* 配置 NVIC 中斷*/
NVIC_Configuration();

/*--------------------------KEY1配置-----------------------------*/
/* 選擇按鍵用到的GPIO */
GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
/* 配置為浮空輸入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

/* 選擇EXTI的訊號源 */
GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE);
EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;

/* EXTI為中斷模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中斷 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中斷 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

/*--------------------------KEY2配置-----------------------------*/
/* 選擇按鍵用到的GPIO */
GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
/* 配置為浮空輸入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

/* 選擇EXTI的訊號源 */
GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE);
EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;

/* EXTI為中斷模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 下降沿中斷 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
/* 使能中斷 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}

[/highlight]

EXTI中斷服務函式

[highlight lanaguage="C"]

void KEY1_IRQHandler(void)
{
//確保是否產生了EXTI Line中斷
if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET)
{
// LED1 取反
LED1_TOGGLE;
//清除中斷標誌位
EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
}
}

void KEY2_IRQHandler(void)
{
//確保是否產生了EXTI Line中斷
if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET)
{
// LED2 取反
LED2_TOGGLE;
//清除中斷標誌位
EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
}
}