Embedded study notes - STM32 interrupt control system

foreword

In the previous article, I briefly talked about the function and usage of serial port interrupts based on the problem of serial port acceptance. This article will introduce the interrupt control system of STM32.

The concept of STM32 interrupt

The concept of interruption has been introduced in the previous article. The popular point is that the program normally runs related tasks in while (1), such as task 1 and task 2 in the figure below. When interrupted, the running process of the entire code is to complete the initialization, and then loop the tasks inside while (1). At this time, if there is a large blocking delay in task 1, it is similar to the serial port waiting to receive, and the LED light while dead and waiting for the delay Sometimes these will block the running time of the entire while (1), resulting in a seriously low execution efficiency. This situation cannot meet daily needs, so in actual products, blocking delays and while deadlocks are rarely used. In order to solve this problem, it is necessary to join with interruption at this time.
insert image description here
After the interrupt is added, the running flowchart becomes the situation shown in the figure below. First, the interrupt is a kernel function and does not belong to the on-chip peripherals , but it also needs to be initialized during use; secondly, after initialization, there will be a corresponding Interrupt service function , the above blocking functions can be changed from blocking to non-blocking through various flag bits in the interrupt service function; when the conditions corresponding to the initialization interrupt are met, an interrupt signal will be generated , and the CPU will Stop the current task, turn to the corresponding interrupt service function, complete the task in the interrupt service function first, and then come back to continue the original task after completion. When programming, you only need to judge the corresponding flag bit to solve the above blocking problem.
It should be noted that interrupts can solve the blocking problem, but the interrupt service function itself cannot have delays, loops, or blocking programs. Emergency events are responded to in real time, and emergency events cannot be executed for too long.
insert image description here
insert image description here
In general, an interrupt is an abnormal event for a CPU that is running normally, and the priority of the abnormal event is higher than that of the normal running event. When an exception occurs, the exception must be resolved first before returning to normal operation. The idle interrupt and receive interrupt in the previous article are like this, so how does the STM32 interrupt run and manage it.

interrupt type

First of all, the interrupt control is in the kernel, and the interrupt related content is designed by ARM company. Inside the processor, there will be an interrupt processor called nested vector interrupt controller NVIC, which is specially used to manage the entire STM32 There are two main sources of interruption, one is that the core of the processor fails, which will trigger a system exception, and then enter the interrupt ; the other is that the on-chip peripheral triggers its related interrupt signal and also enters the interrupt , such as The exit receiving interrupt and idle interrupt in the previous article belong to the latter, which are interrupts generated by peripherals.
insert image description here

interrupt control

In the previous configuration, the relevant controls of the NVIC have been used. This part of the control is an official packaged function, which can be called during use. That is to say, for developers, the interrupt control only needs to call the function. Here we borrow the code called from the previous article:
The process of configuring the NVIC interrupt controller:
Step 1: Configure priority grouping (main Function configuration)
Step 2: Synthesize priority
Step 3: Assign priority
Step 4: Enable the corresponding interrupt source (in the corresponding on-chip peripheral initialization function)

//NVIC控制器配置
NVIC_SetPriorityGrouping(7-2);//设置中断的优先组别(111-110-101-100)
u32 pri=NVIC_EncodePriority(7-2,0,0);//设置抢占优先级为0,响应优先级为0
NVIC_SetPriority(USART1_IRQn,pri);   //将对应的优先级设置映射到对应的中断
NVIC_EnableIRQ(USART1_IRQn);         //使能中断源

insert image description here

Common Control Functions

1.中断分组函数,此函数用于对整个工程进行中断分组
void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
位置:core_cm4.h------1435
返回值:空
参数:uint32_t PriorityGroup   分组中需要写入的值
传参:7-占先的位数
分组整个工程都要一样,可以只配置一次,只需要在主函数使用一次即可
2.使能中断源配置函数,此函数用于使能对应的中断源
void NVIC_EnableIRQ(IRQn_Type IRQn)
位置:core_cm4.h------1467
返回值:空
形参:IRQn_Type IRQn很明显此处形参是个结构体,需要使用结构体内规定好的名称 
传入对应的片上外设中断源----放中断源名字  例如:串口:USART1_IRQn
作用:使片上外设的中断能够使用   核心级别中断使能

Regarding the formal parameters of this function: IRQn_Type IRQn is obviously a structure, and you need to use the name specified in the structure. When you need to use it, you can directly jump to it and copy it.
Note that when programming the formal parameters here, you must jump to stm32f4xx.h to find the corresponding ones, and you cannot modify them at will.
insert image description here

3.中断优先级配置函数,此函数用于设置对应中断源的中单优先级
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
位置:core_cm4.h------1550
返回值:空
形参:IRQn_Type IRQn, uint32_t priority
IRQn_Type IRQn:中断源和上一个一样的中断源名称,需要去stm32f4xx.h复制
uint32_t priority:优先级(占先和次级一起的优先级)
4.  优先级合成函数,由于优先级写入时是一起写入的,但是分类时又是分开计算的,为了避免写入出错,官方给了一个合成函数,将分组、抢占优先级和响应优先级传入后此函数会返回需要写入的优先级值。
uint32_t  NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
位置:core_cm4.h------1592
返回值:u32类型的值   优先级uint32_t priority
形参:uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority
uint32_t PriorityGroup:优先级分组
uint32_t PreemptPriority:占先优先级
uint32_t SubPriority:次级优先级

用法:u32 pri= NVIC_EncodePriority(7-2,3,2);
      NVIC_SetPriority(USART1_IRQn, pri);

From the above comments, we can see the role of the NVIC controller:
① Group the interrupt priority
② Receive the request from the interrupt
③ Configure the priority of the interrupt
④ Enable the interrupt source of the peripheral
The specific code is in "core_cm4.h ", check the steps as shown in the figure below, click the "+" sign at the position of the No. 1 box in front of main.c, then select "core_cm4.h", slide down to find the relevant functions, and the corresponding function descriptions are commented above it .
insert image description here

Distinguish between interrupt sources and interrupt signals

Think back carefully to the configuration process in the previous article. Regarding the interrupt configuration, in addition to the related operations of the NVIC controller above, the two control bits of the receive interrupt and the idle interrupt are enabled in the post-accident configuration of the USART. What is the difference between it and the above interrupt source enable.
Interrupt source: an on-chip peripheral USART1 independent interrupt source USART2 is also an independent interrupt source
Interrupt signal: refers to each interrupt source, there will be multiple trigger conditions, such as serial port 1 receive interrupt and idle interrupt.
That is to say, the interrupt source is equivalent to the main switch in the house, and the interrupt signal is equivalent to the switch of the light, the switch of the socket, and the switch of the TV in the room. To turn on the light, not only the main switch must be turned on, but also the corresponding sub-switch must be turned on.
Therefore, in the interrupt configuration process, not only the corresponding interrupt source needs to be configured, but also the specific interrupt signal needs to be enabled.
Knowing how to manage interrupts, the next step is how to manage them.

configuration interrupt

In the related functions of the above management, multiple nouns in interrupts are involved, such as grouping, preemptive priority, secondary priority, preemptive digits, etc. It seems a bit confusing. Next, let’s explain these things. specific role.

priority

First of all, it is necessary to clarify the definition of priority. Whether the exception of the Cortex-M processor can be accepted by the processor and when it is accepted by the processor and perform exception handling is determined by the priority of the exception and the current priority of the processor. of.
Higher-priority exceptions can preempt lower-priority exceptions, which is the case of exception/interrupt nesting, which is the preemptive priority mentioned above.
Some exceptions (for example: Reset, Sys_Tick, etc.) have a fixed priority, and their priority is represented by a negative number, so that their priority will be higher than other exceptions.
insert image description here
Other exceptions have programmable priorities ranging from 0 to 255. According to the above description, the priority is divided into three categories:
**1. Preemptive priority: configured by the programmer, it has the preemptive function, that is, when an interrupt is in progress, another interrupt is also generated, and the interrupt with high preemptive priority It can interrupt the priority operation of interrupts with low preemption priority, and the low priority ones will continue to run after the high priority ones are finished. **Similar to the figure below, the preemptive priority of interrupt 1 is 1, and the secondary priority is 0; the preemptive priority of interrupt 2 is 0, and the secondary priority is 0; at this time, interrupt 1 is already running, but due
to Its priority is 1, which is lower than interrupt 2's 0 priority, so interrupt 1 will be interrupted by interrupt 2, and interrupt 2 will be run first. After interrupt 2 runs, it will return to interrupt 1. After interrupt 1 runs, Go back to while(1) to run.
insert image description here

**2. Secondary priority: Programmers do not have the preemptive function for configuration, that is to say, when an interrupt is in progress, another interrupt is also generated. At this time, because it does not have the preemptive function, it needs to wait for the current interrupt After the operation is completed, it enters the interrupt operation generated later. **As shown in the figure below, the preemptive priority of interrupt 1 is 1, and the secondary priority is 1; the preemptive priority of interrupt 2 is 1, and the secondary priority is 0; when interrupt 1 is running, interrupt 2 is also
generated , although the secondary priority of interrupt 2 is higher than that of interrupt 1 at this time, it does not have a preemptive function, so the CPU will run interrupt 1 before running interrupt 2.
The role of the secondary priority is that when interrupts of different priorities arrive at the same time, the CPU will give priority to the execution of the secondary priority.
insert image description here
3. Natural priority: the system configures itself (no need for us to manage) , this priority is given by the official, the last line of defense.
Priority: The lower the number, the higher the priority

grouping problem

After solving the priority, the next step is to clearly define the grouping problem. This grouping actually determines the number of digits in the priority control register of the preemptive priority mentioned above and the number of digits in the priority control register of the secondary priority . Let’s first look at the description of the grouping in the authoritative guide of M3 and M4:
insert image description here
insert image description here
ARM has reserved a total of 8-bit registers to implement priority control programming when it is involved. In theory, there are 2^8=256 combinations of preemptive priority and secondary priority.
However, because too many interrupt priority groups will affect the performance of the microcontroller, ST company discarded the LSB, which is the fourth bit of the eight-bit register, when actually designing the STM32 chip, and only reserved four bits for priority. Level control programming, so that the priority grouping of STM32 is only 2 to the fourth power, that is, 16 combinations.
insert image description here
Under the ARM system, the priority configuration register consists of 8 bits in total, and this register is used by both the preemptive and the secondary. There are 256
interrupt priorities . There are 16 priority levels. It may still be a little unclear. Let’s look at the table below. Since the fourth bit is discarded, the two priority and secondary priority divide the four bits together, and configure the priority of the interrupt through these four bits. Among them The configuration method is as follows:


The number of packets written Number of bits preempted in the priority register The number of bits of the secondary in the priority register The specific priority number of interrupts in this mode
7(111) 0 bits (range: none) 4 bits (range: 0-15) 16 types
6(110) 1 bit (range: 0-1) 3 digits (range: 0-7) 16 types
5(101) 2 bits (range: 0-3) 2 bits (range: 0-3) 16 types
4(100) 3 digits (range: 0-7) 1 bit (range: 0-1) 16 types
3(011) 4 bits (range: 0-15) 0 bit (range none) 16 types
2(011) 4 bits (range: 0-15) 0 bit (range none) 16 types
1(011) 4 bits (range: 0-15) 0 bit (range none) 16 types
0(011) 4 bits (range: 0-15) 0 bit (range none) 16 types

Let’s give a few examples:
1. When writing 101 or 5 to a packet, the preemptive priority and the secondary priority will respectively occupy two bits of the priority control register, each of which has three types of 0-3, combined There are 16 priority levels in total, among which 00 is the highest priority, 33 bits are the lowest priority, and 0x(0,1,2,3) can interrupt 1x(0,1,2,3), 2x(0, 1, 2, 3) and 3x (0, 1, 2, 3) interrupts run first, and so on.
insert image description here
2. Assuming that in this system, we don’t need to preempt the priority, at this time, we can set the preemptive position to zero, that is, write 7 (111) to the packet to let the priority control register have exclusive use of four bits. In this case, the entire system Interruption and preemption will not occur between interrupts. When multiple interrupts arrive at the same time, the interrupt with the highest priority will be executed first. After the execution is completed, it will be transferred to the next interrupt with relatively high priority. The 16 groups of interrupts at this time do not have the preemptive function, the highest priority is 0 and the lowest priority is 15.
insert image description here
3. Suppose we need various interrupt nesting in the whole system, then we can not assign priority to the secondary priority The number of bits in the level control register allows the preemptive priority to enjoy four bits exclusively, that is, write 3 (011); in this way, there can be (0-15) 16 kinds of interrupt nesting, among which the highest priority can interrupt any interrupt , the highest priority is 0, and the lowest priority is 15.
insert image description here
In other cases, just follow the analogy above, let’s do two more exercises here,
1. Preemptive write 3, secondary write 2, what can be the priority grouping? 5
2. Preemptive write 4 Secondary write 1 Priority grouping can be? 4
3. Preemptive write 5 Secondary write 2 What can be the priority group? //This writing method cannot be selected. It should
be noted that a system can only have one interrupt group, so the function of setting the group only needs to be Just call it once during initialization, and the specific writing method is:
write value=7-preceding digits

NVIC_SetPriorityGrouping(7-2);//设置中断的优先组别(111-110-101-100)
//此时写入的分组为5对应的分组,占先占两位取值范围0-3,次级占两位取值范围0-3

In the above priority setting function, there is only one parameter for the final interrupt priority, that is to say, all grouping, preemptive values, and secondary values ​​need to be integrated and calculated into a numerical value to be written. In order to reduce the trouble of calculation, Also in order to reduce the error rate, the official has given a priority calculation function, which can calculate the corresponding priority value only by passing in the corresponding group, preemptive number and secondary number.

u32 pri= NVIC_EncodePriority(7-2,3,2);//传入参数进行计算
      NVIC_SetPriority(USART1_IRQn, pri);//写入对应的优先级

interrupt enable

After figuring out the interrupt priority and interrupt grouping, the last step of the entire interrupt operation is the interrupt enable. The interrupt enable here refers to the enable of the interrupt source. The official has provided programmers with The name of the interrupt source of each peripheral device only needs to be queried in stm32f4xx.h when using it. The specific query steps have been introduced above. If you want to see it, turn it up.
Note: Here only the main switch of the peripheral interrupt is turned on, and the specific interrupt signal configuration needs to be clocked in when the corresponding on-chip peripheral is set.

interrupt service function

After the above interrupt configuration, the interrupt can already be generated, so how does it work? This requires the use of the interrupt service function. The interrupt service function is the code entry for the CPU to process abnormal events after the interrupt is generated. Specifically executed The content is the program code in the interrupt service function; the same as the previous interrupt source enablement, the official also provides the interrupt service function name corresponding to the on-chip peripheral, and can be copied directly in startup_stm32f40_41xxx.s when using it.
insert image description here
The interrupt service function is also divided into two categories, one is the kernel interrupt, and the other is the peripheral interrupt, which is consistent with the classification of interrupts.
Note:
1. The interrupt service function must be copied, and you cannot write it for granted. If you write it wrong, you will not report an error, and it will be difficult to check the error;
2. The interrupt service function does not need to be declared, called, and has no formal parameters and return values;
3. The specific use only needs to trigger the interrupt signal to enter the interrupt service function in order to be able to respond in real time
;
4. There should be no large delays and loops in the interrupt service function, try not to use printf, and try to be as short as possible;

格式:
    void 中断服务函数名(void)
    {
    
    
     //判断是哪一个中断信号
        {
    
    
           //清除标志位------防止CPU重复进入中断
           //紧急事件程序
		}

		//判断为另一个中断信号
		{
    
    
	 	 	 //清除标志位
	   		//紧急事件
		}
}

Take a chestnut:
the execution process of the interrupt:
take the serial port 1 as an example, receive the interrupt, receive the data and enter the interrupt,
idle interrupt, and enter the interrupt after the data reception is completed

void  串口1中断服务函数(void{
    
    
    //判断是否为接收中断
    {
    
    

	}
	//判断是否为空闲中断
	{
    
    
	  //清除标志位
	}
} 

Summarize

Well, the introduction about interruption is recorded here, if there are any deficiencies in the text, please point out. The focus of this article is to describe in detail the configuration process of the interrupt and the operation process of the interrupt.

Guess you like

Origin blog.csdn.net/qq_41954556/article/details/129568570