Table of contents
1. Overview of STM32 interrupt application
(2) The sequence of interrupt programming:
2. Interrupt priority grouping:
3. Configure the NVIC register and initialize NVIC_InitTypeDef;
4. Write an interrupt service function
2. EXTI—External Interrupt/Event Controller
(3) Initialize structure members
1. Overview of STM32 interrupt application
(1 Introduction
STM32 interrupts are very powerful, and each peripheral can generate interrupts, so it is not appropriate to put the explanation of interrupts in which peripherals. Here, a separate chapter is taken for a summary introduction.
Interrupt: Interrupt the normal running program, run the interrupt service function, and return to the main program after running, which is roughly similar to 51.
External interrupts are reflected at the peripheral level, and system exceptions are reflected at the kernel level. Below, interrupts are exceptions, and exceptions are interrupts.
In the above vector table, the gray marks are embodied at the core level (exceptions), and the rest are at the peripheral level (external interrupts).
Interrupts are controlled by the NVIC peripheral.
NVIC: Nested Vectored Interrupt Controller, which belongs to the core peripheral and manages interrupt-related functions including part of the core and all peripherals on the chip. Two important library files: core_cm3.h (defines all the registers on the core, the NVIC register is here) and misc.h (related functions).
For interrupts, the most important thing is the priority, which is set by NVIC->IPRx (interrupt priority register). Open the manual for kernel registers,
0-80 in this table corresponds to the position 0-59 of the external interrupt in the vector table, each bit has an 8-bit width, and only 4 bits are really effective.
function:
NVIC_SetPriority (IRQn_Type IRQn, uint32_t priority) // core_cm3.h line 1586
Two formal parameters: interrupt number and priority. The interrupt number corresponds to the position number of the manual vector table.
When configuring, first determine whether the interrupt number is greater than 0. If it is less than 0, it is an interrupt of the kernel; if it is greater than 0, it is an external interrupt. If it is an external interrupt, shift the priority to the left by 4 bits. If it is less than 0, configure another register, SCB->SHP.
(2) The sequence of interrupt programming:
1- Enable interrupt request
2- Configure interrupt priority grouping
3- Configure the NVIC register, initialize NVIC_InitTypeDef;
4- Write an interrupt service function
1. Enable interrupt request:
This is related to specific peripherals. Take the serial port as an example, after the data is sent, an interrupt is generated. We go to the specific register to find, there is a bit in the control register 1(USART_CR1) as shown in the figure below, and this position is enough.
2. Interrupt priority grouping:
The priority of the core and peripheral registers depends on the priority group, which is configured by the register SCB->AIRCR:PRIGROUP[10:8].
function:
NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) // misc.c line 96
Priority grouping 1: One bit represents the main priority, and the remaining three bits represent the subpriority.
Judgment priority: group > main priority > sub priority. If they are the same, look at the hardware interrupt number (interrupt vector table).
3. Configure the NVIC register and initialize NVIC_InitTypeDef;
The initialized member is in //misc.h Line 50
typedef struct { uint8_t NVIC_IRQChannel; //Specify the interrupt source uint8_t NVIC_IRQChannelPreemptionPriority; //set preemption priority (primary priority), //Different interrupt priority groups have different preemption priority bits. //If grouping is 1, the primary priority bit is 1, //then the primary priority can only be 0 or 1 uint8_t NVIC_IRQChannelSubPriority; //Set the sub-priority if the previous group is 1, the main priority is 1, //The sub-priority is 3, and 2 to the power of 3 is 8 //(ie 0-78 numbers can be written casually), let him equal to 0. FunctionalState NVIC_IRQChannelCmd; //enable or disable } NVIC_InitTypeDef;
Notice:
Whether it is the preemption priority (main priority) or the response priority (sub-priority), the smaller the priority value, the higher the priority.
A higher priority interrupt can interrupt a lower priority interrupt.
Interrupts with the same preemption priority and the same response priority will be executed according to the fixed priority within the hardware if they occur at the same time.
The grouping of interrupt priorities applies equally to cores and peripherals. When comparing, it is only necessary to group and analyze the four bits of the interrupt priority of the kernel peripherals according to the interrupt priority of the peripherals, that is, artificially separate the preemption priority and the sub-priority.
4. Write an interrupt service function
The name of the interrupt service function must be the same as the name in the interrupt vector table. If the project is very large, put all the interrupts in one file, generally put the interrupt service function in stm32f10x_it.c.
2. EXTI—External Interrupt/Event Controller
(1 Introduction
EXTI manages the controller's 20 interrupt/event lines. Each interrupt/event line corresponds to an edge detector, which can detect the rising edge and falling edge of the input signal. EXTI can realize the individual configuration of each interrupt/event line, which can be individually configured as an interrupt or an event, and the attributes of the trigger event.
External: GPIO in the chip. You can output 0 or 1 to the outside world, and you can also detect whether the input is 0 or 1. When there is a change between 0 and 1, an interrupt or an event can be generated.
(2) EXTI structure diagram
Red is an interrupt, green is an event.
Input line: EXTI 0-15 are connected to GPIO (each GPIO port has 16 pins), when we use EXTI0, it controls the 0th pin of all ports. The specific port is controlled by the EXTIx[3:0] bits of the four registers AFIO_EXTICR1, 2, 3, and 4. (x belongs to 0-15)
Edge detection circuit: Detect rising edge or falling edge, which is controlled by the rising edge trigger selection register (EXTI_RTSR) and the falling edge trigger selection register (EXTI_FTSR).
OR gate: receive the signal from the edge detection circuit and control the software interrupt event register (EXTI_SWIER). After the software interrupt event register returns to 1, it is not controlled by the edge detection circuit.
After passing through the OR gate, it is divided into two paths: when the relevant bits of the interrupt mask register and the request pending register are both set to 1, an interrupt will be generated, handed over to the NVIC, and then handed over to the kernel accordingly. If the relevant bit of the event mask register is 1, this 1 signal will be sent to the pulse generator to generate a pulse (the pulse is a high level). Pulse: The ITM timer starts counting and triggers the acquisition of the ADC. [As a trigger signal]
(3) Initialize structure members
EXTI_InitTypeDef
1-EXTI_Line: used to generate interrupt/event lines (input lines in the block diagram)
2-EXTI_Mode: EXTI mode (interrupt/event) (interrupt mask register/event mask register)
3-EXTI_Trigger: Trigger (rising edge/falling edge/up and down)
4-EXTI_LineCmd: enable or disable (IMR/EMR)
(4) Program design:
Controlled by two buttons, the rising edge (press) of PA0 is triggered, and the falling edge (bounce) of PC13 is triggered. An interrupt is generated and the LED is toggled once.
1- Initialize the GPIO to be connected to EXTI.
1. Observe the members of the peripheral (GPIO) port structure: port, speed, output/input method, we put the commonly used ports and macro definitions (related to hardware) with familiar/well-understood names in bsp_led. Re-define the define macro in the h file. In general, the redefinition of the GPIO port includes the APB2 clock, which GPIOX (ABCD) port, and the pin of the GPIOX.
2. Initialize the GPIO in the bsp_led.c file. For detailed steps, please refer to the steps of initializing the I/O port above: turn on the GPIO clock; configure the peripheral initialization structure; call the peripheral initialization function, and put the configured structure members Write to the register.
GPIO_InitTypeDef GPIO_InitStruct;//Peripheral initialization structure variables EXTI_InitTypeDef EXTI_InitStruct;//Peripheral initialization structure variables NVIC_Config(); /*Step 1: Initialize the GPIO to be connected to EXTI*/ /*1: Turn on the peripheral's clock (RCC register control)*/ RCC_APB2PeriphClockCmd(KEY1_EXTI_GPIO_CLK |KEY2_EXTI_GPIO_CLK,ENABLE); /*2: configure peripheral initialization structure*/ GPIO_InitStruct.GPIO_Pin = KEY1_EXTI_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStruct.GPIO_Pin = KEY2_EXTI_GPIO_PIN; /*3: Call the peripheral (GPIO) initialization function and write the configured structure members to the register*/ GPIO_Init(KEY1_EXTI_GPIO_PORT,&GPIO_InitStruct);//initialize port GPIO_Init(KEY2_EXTI_GPIO_PORT,&GPIO_InitStruct);//initialize port
2- Initialize EXTI for generating interrupts/events
Like the first step, first observe the initialization structure members of the peripheral (EXTI):
typedef struct { uint32_t EXTI_Line; //Which input line (20 total) EXTIMode_TypeDef EXTI_Mode; //the mode of this line (interrupt/event) EXTITrigger_TypeDef EXTI_Trigger; //Trigger selection (rise/fall/simultaneous) FunctionalState EXTI_LineCmd; //Enable }EXTI_InitTypeDef;
Define the variables that need macro definitions:
#define KEY1_EXTI_GPIO_PORTSOURCE GPIO_PortSourceGPIOA//Which port the interrupt input line belongs to #define KEY2_EXTI_GPIO_PORTSOURCE GPIO_PortSourceGPIOC #define KEY1_EXTI_GPIO_PINSOURCE GPIO_PinSource0//Which pin the interrupt input line belongs to #define KEY2_EXTI_GPIO_PINSOURCE GPIO_PinSource13 #define KEY1_EXTI_LINE EXTI_Line0//Select which input line #define KEY2_EXTI_LINE EXTI_Line13
Turn on the AFIO clock according to the function block diagram explained earlier; Determine which port and which pin the interrupt input line is used for (the function is to select the GPIO pin to use as the EXTI line); Configuration initialization structure; Call EXTI initialization function to write The configuration structure members to the register. So far the interrupt signal has been generated.
At this point, it can be found that the methods of these configuration registers are roughly the same.
/*Step 2: Initialize EXTI peripherals*/ /*1: Turn on the peripheral's clock (RCC register control)*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); /*2.Determine which port and which pin the interrupt input line is used for*/ GPIO_EXTILineConfig(KEY1_EXTI_GPIO_PORTSOURCE, KEY1_EXTI_GPIO_PINSOURCE); /*3.Configuration initialization structure*/ EXTI_InitStruct.EXTI_Line = KEY1_EXTI_LINE;//Which input line to choose EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//the mode of this line (interrupt/event) EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//Trigger selection (rise/fall/simultaneous) EXTI_InitStruct.EXTI_LineCmd = ENABLE;//Enable /*4.Call the EXTI initialization function and write the configured structure members to the register*/ EXTI_Init(&EXTI_InitStruct); /*2.Determine which port and which pin the interrupt input line is used for*/ GPIO_EXTILineConfig(KEY2_EXTI_GPIO_PORTSOURCE, KEY2_EXTI_GPIO_PINSOURCE); /*3.Configuration initialization structure*/ EXTI_InitStruct.EXTI_Line = KEY2_EXTI_LINE;//Which input line to choose EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//the mode of this line (interrupt/event) EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//Trigger selection (rise/fall/simultaneous) EXTI_InitStruct.EXTI_LineCmd = ENABLE;//Enable /*4.Call the EXTI initialization function and write the configured structure members to the register*/ EXTI_Init(&EXTI_InitStruct); //So far, the interrupt signal has been generated
3- Initialize the NVIC for handling interrupts
First configure the interrupt priority group, then initialize the NVIC initialization structure, then call the initialization NVIC peripheral function, and write the configured structure members into the register.
static void NVIC_Config(void)//Configure the NVIC interrupt controller static function, which can only be used in this file { NVIC_InitTypeDef NVIC_InitTStruct;//Initialize the variables of the structure /*Step 1: Configure interrupt priority grouping*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /*Step 2: Initialize the initialization structure of the NVIC*/ NVIC_InitTStruct.NVIC_IRQChannel = KEY1_EXTI_IRQN; NVIC_InitTStruct.NVIC_IRQChannelPreemptionPriority = 1;//The previously configured interrupt priority group is 1, and the main priority has only one bit to represent NVIC_InitTStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitTStruct.NVIC_IRQChannelCmd = ENABLE; /*The third step: call the initialization NVIC peripheral function, and write the configured structure members to the register*/ NVIC_Init(&NVIC_InitTStruct); /*Step 2: Initialize the initialization structure of the NVIC*/ NVIC_InitTStruct.NVIC_IRQChannel = KEY2_EXTI_IRQN; NVIC_InitTStruct.NVIC_IRQChannelPreemptionPriority = 1;//The previously configured interrupt priority group is 1, and the main priority has only one bit to represent NVIC_InitTStruct.NVIC_IRQChannelSubPriority = 1;//In this way, the interrupt priority of key1 is greater than that of key2. NVIC_InitTStruct.NVIC_IRQChannelCmd = ENABLE; /*The third step: call the initialization NVIC peripheral function, and write the configured structure members to the register*/ NVIC_Init(&NVIC_InitTStruct); }
4- Write an interrupt service function
First of all, there is a function that detects whether an interrupt is generated. If an interrupt is generated, then...
void KEY1_EXTI_IRQHandler(void) { if(EXTI_GetITStatus(KEY1_EXTI_LINE) != RESET )//If an interrupt occurs { LED1_TOGGLE;//LED1 turns on and off } EXTI_ClearITPendingBit(KEY1_EXTI_LINE);//Clear the associated interrupt flag bit } void KEY2_EXTI_IRQHandler(void) { if(EXTI_GetITStatus(KEY2_EXTI_LINE) != RESET )//If an interrupt occurs { LED2_TOGGLE;//LED1 turns on and off } EXTI_ClearITPendingBit(KEY2_EXTI_LINE);//Clear the associated interrupt flag bit }
5-main function Wait for the arrival of external interrupt, and then go to the interrupt service function to execute the program.
int main(void) { /*When the program comes to the main function, the system clock has been configured to 72M*/ LED_GPIO_Config();//initialization function /*Wait for the arrival of an external interrupt, and then go to the interrupt service function to execute the program*/ EXTI_KEY_Config(); while(1) { } }