STM32 external interrupt (based on STM32F103 library function version)

Description: This article aims to analyze the external interrupt of STM32 in detail to realize the key-triggered external interrupt. It includes five parts: "programming process", "program code", "code analysis", "principle analysis" and "summary".

1. Programming process

To realize STM32 external interrupt, according to the basic process, the initial idea should focus on port configuration and interrupt service function, which can be divided into four parts:
①Initialize GPIO;
②Initialize EXTI;
③Initialize NVIC;
④Configure interrupt service function.

Second, the program code

/** 
  * @brief  GPIO Init structure definition(<stm32f10x_gpio.h>库已包含)  
  */
typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */
  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */
  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

/** 
  * @brief  EXTI Init Structure definition (<stm32f10x_exti.h>库已包含) 
  */

typedef struct
{
  uint32_t EXTI_Line;               /*!< Specifies the EXTI lines to be enabled or disabled.
                                         This parameter can be any combination of @ref EXTI_Lines */
  EXTIMode_TypeDef EXTI_Mode;       /*!< Specifies the mode for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef */
  EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef */
  FunctionalState EXTI_LineCmd;     /*!< Specifies the new state of the selected EXTI lines.
                                         This parameter can be set either to ENABLE or DISABLE */ 
}EXTI_InitTypeDef;

/**
  *@brief   NVIC_Configuration实现NVIC配置
  *
  */
static void NVIC_Configuration()
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);           //配置NVIC优先级分组为1

    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;        //中断源:[9:5],位于“stm32f10x.h”中
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级:1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        //子优先级:1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //使能中断通道
    NVIC_Init(&NVIC_InitStructure);
}
/** 
  * @brief  KEY_Configuration按键配置 
  */
void KEY_Configuration()
{
    GPIO_InitTypeDef GPIO_InitStructure;                      //定义GPIO_InitTypeDef结构体

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | \
                            RCC_APB2Periph_AFIO, ENABLE);     //开启GPIOB和复用功能时钟                   
    GPIO_DeInit(GPIOB);                                       //将外设GPIOB寄存器重设为缺省值(强制或释放APB2外设复位)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;                 //选择GPIO_Pin_8
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;             //选择上拉输入模式
    GPIO_Init(GPIOB, &GPIO_InitStructure);                    //初始化以上参数
}
/** 
  * @brief  EXTI_Configuration配置EXTI
  */
void EXTI_Configuration()
{
    KEY_Configuration();

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8); //选择EXTI信号源

    EXTI_InitTypeDef EXTI_InitStructure;                                                                                            
    EXTI_InitStructure.EXTI_Line = EXTI_Line8;               //中断线选择
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;      //EXTI为中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  //下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                //使能中断
    EXTI_Init(&EXTI_InitStructure); 

    NVIC_Configuration();                                     //配置NVIC中断                                                                                    
}
/** 
  * @brief  中断服务函数
  */
void EXTI9_5_IRQHandler(void)                       //EXTI_Line:9...5
{
  if(EXTI_GetITStatus(EXTI_Line8)!= RESET)  
  {  
    EXTI_ClearITPendingBit(EXTI_Line8);
    if(ledstate == 0)
    {
        ledstate = 1;   //标志位
        LEDON;          //LED开启
    }
    else if(ledstate == 1)
    {
        ledstate = 0;  //标志位
        LEDOFF;        //LED关闭
    }
  }   
}

Note: At that time, you only need to call the EXTI_Configuration(); function in the main function.

Three, code analysis

1. Initialize GPIO:
① Turn on GPIOX clock and multiplexed clock:

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

②Initialize GPIO mode and corresponding pins:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;                 //选择GPIO_Pin_8
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;             //选择上拉输入模式
GPIO_Init(GPIOB, &GPIO_InitStructure);                    //初始化以上参数

2. Initialize EXTI:
①Select EXTI signal source:

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8);

② Determine the interrupt line, interrupt mode, trigger mode and enable:

EXTI_InitTypeDef EXTI_InitStructure;                                                                                            
EXTI_InitStructure.EXTI_Line = EXTI_Line8;               //中断线选择
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;      //EXTI为中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;                //使能中断
EXTI_Init(&EXTI_InitStructure); 

3. Initialize NVIC:
①Configure NVIC priority grouping:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

② Determine the interrupt source, priority (preemption priority and sub-priority), enable:

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;        //中断源:[9:5],位于<stm32f10x.h>中
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级:1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        //子优先级:1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //使能中断通道
NVIC_Init(&NVIC_InitStructure);

4. Configure the interrupt service function:
① Determine whether to enter the interrupt:

 if(EXTI_GetITStatus(EXTI_Line8)!= RESET)
 {

 }

②Interrupt behavior processing:

if(ledstate == 0)
{
    ledstate = 1;   //标志位
    LEDON;          //LED开启
}
else if(ledstate == 1)
{
    ledstate = 0;  //标志位
    LEDOFF;        //LED关闭
}

③Clear position:

EXTI_ClearITPendingBit(EXTI_Line8);

Four, principle analysis

1. RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); function: (from both the code base and the firmware library manual)

First , the library is defined like this:

/**
  * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB2ENR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2ENR &= ~RCC_APB2Periph;
  }
}

Secondly , the firmware library manual writes:
write picture description here
that is, this function implements "enable or disable the APB2 peripheral clock" .

Finally , analyze the internal structure of the function RCC_APB2PeriphClockCmd(…,…):
①The following two codes are used to check the correctness of parameters.

assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
assert_param(IS_FUNCTIONAL_STATE(NewState));

Among them, the function prototype of assert_param(...); is in "stm32f10x_conf.h", as follows. Its prototype takes the form of #ifdef/#else/#endif, called "assertion mechanism", that is

  • If USE_FULL_ASSERT is not defined, or USE_FULL_ASSERT is 0, this sentence is not executed and is an empty sentence.
  • Conversely, if USE_FULL_ASSERT is defined as 1, "((expr) ? (void)0 : assert_failed((uint8_t *) _ FILE_ , _ LINE __))" is executed. Where expr is "IS_RCC_APB2_PERIPH(RCC_APB2Periph)" in this example, which is a code that judges whether expr is 0 at compile time.

However, it should be noted here that _ FILE_ and _ LINE_ are macro definitions in standard library functions.

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Uncomment the line below to expanse the "assert_param" macro in the 
   Standard Peripheral Library drivers code */
/* #define USE_FULL_ASSERT    1 */

/* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT

/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr: If expr is false, it calls assert_failed function which reports 
  *         the name of the source file and the source line number of the call 
  *         that failed. If expr is true, it returns no value.
  * @retval None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

The function prototype of assert_failed is as follows. You can add a processing function in while(1), such as printf, to indicate the number of files and lines where the error is located.

void assert_failed(uint8_t* file, uint32_t line)
{ 
    while (1)
    {
    }
}

②The second point: RCC->APB2ENR |= RCC_APB2Periph; and RCC->APB2ENR &= ~RCC_APB2Periph;
reset and clock control register RCC: 0x40000000+0x20000+0x1000, including ten registers, written in the same structure type , APB2ENR is the APB2 peripheral clock enable register, RCC_APB2Periph is the function parameter, the two perform the |= operation, that is, select a register, and finally enable or disable it.

2. GPIO_EXTILineConfig (uint8_t GPIO_PortSource, uint8_t GPIO_PinSource), used to select GPIO pins as external interrupt lines.
First , the library is defined like this:

/**
 * @brief  Selects the GPIO pin used as EXTI Line.
 * @param  GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.
 *   This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).
 * @param  GPIO_PinSource: specifies the EXTI line to be configured.
 *   This parameter can be GPIO_PinSourcex where x can be (0..15).
 * @retval None
  */
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
  uint32_t tmp = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
  assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));

  tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
  AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
  AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}

Then, analyze the internal composition of GPIO_EXTILineConfig(...,...):
①assert_param(...) is the same as the function mentioned above; ②Lines
16 and 17:

  • The result on line 16 is: 0x0F. (Take GPIO_PinSource8 as an example)

  • In line 17, the value of ~temp is 0xF0, the value of GPIO_PinSource >> 0x02 is 0x02 (take GPIO_PinSource8 as an example), and the value of AFIO is 0x40000000+0x10000+0x0000==0x40010000. ~7 bits are cleared.

  • AFIO_EXTICR is an external interrupt configuration register, there are 4 in total, each register corresponds to 4 EXTIs, covering EXTI0~EXTI15 respectively.

③ In line 18, set the register again to select the port.
3. The void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) function is used for NVIC priority grouping.
First , the library function is defined like this:

/**
  * @brief  Configures the priority grouping: pre-emption priority and subpriority.
  * @param  NVIC_PriorityGroup: specifies the priority grouping bits length. 
  *   This parameter can be one of the following values:
  *     @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
  *                                4 bits for subpriority
  *     @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
  *                                3 bits for subpriority
  *     @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
  *                                2 bits for subpriority
  *     @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
  *                                1 bits for subpriority
  *     @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
  *                                0 bits for subpriority
  * @retval None
  */
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;
}

Secondly , the Chinese description of the firmware library manual:
write picture description here
Finally , analyze the internal composition of NVIC_PriorityGroupConfig(…):
①SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
The value of SCB is 0xE000E000+0x0D00, and the value of AIRCR_VECTKEY_MASK is 0x05FA0000.
②Preemption priority and sub-priority:
From "CM3 Authoritative Guide": "Each external interrupt has a corresponding priority register, and each register occupies 8 bits, but CM3 allows in the most "thick line" case, only use The highest 3 bits. Four adjacent priority registers are combined into a 32-bit register. As mentioned above, according to the setting of the priority group, the priority can be divided into two high and low bit segments, namely the preemption priority and the subordinate one. Priority.
The preemption priority determines the preemption behavior: when the system is responding to an exception L, if an exception H with a higher preemption priority comes, H can preempt L. The subpriority handles "housekeeping": when the preemption priority When more than one exception of the same level is pending, the exception with the highest sub-priority is responded first."

V. Summary

This paper mainly sorts out and analyzes the process of STM32 external interrupt. The test chip is STM32F103C8T6, and the test effect is that the button triggers the LED switch. At the same time, I also learned a lot about the use and writing methods of library functions, such as compiling and testing in the form of assert_param function; using the structure method to define a unique structure type, and setting each parameter through the instantiation method. initialization.
At the same time, when writing code, it is best to use #define to define each variable, such as GPIO_PinSource8. It is not directly referenced to facilitate subsequent code porting. Only modify the .h file to quickly implement the function.
Finally, I understand the problem of interrupt priority, and further about the problem of preemption priority and sub-priority, we still need to carefully read the CM3 authoritative guide.

[references]

[1] "CM3 Authoritative Guide"
[2] "STM32 Firmware Library User Manual"
[3] "Full Analysis of stm32 External Interrupt Library Function Implementation" http://www.eeworld.com.cn/mcu/article_2016100930243.html
[4 ] "STM32 Library Development Practical Guide". Liu Huoliang. Yang Sen
[5] "The use of stm32 external interrupts (including examples)" http://blog.csdn.net/dldw8816/article/details/50910911

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325602565&siteId=291194637