stm32 外部中断嵌套[操作寄存器+库函数]

时间:2022-09-08 12:24:41
stm32共有19个外部中断:
 
  • 线0~15:对应外部I/O口的输入中断
  • 线16:连接到PVD输出。PVD(Programmable Votage Detector),即可编程电压监测器。作用是监视供电电压,在供电电压下降到给定的阀值以下时,产生一个中断,通知软件做紧急处理。当供电电压又恢复到给定的阀值以上时,也会产生一个中断,通知软件供电恢复。
  • 线17:连接到RTC实时时钟产生闹钟事件。
  • 线18:连接到USB唤醒事件
 
在  stm32 NVIC中断 和  stm32 USART串口通信  中已经介绍过stm32的中断和串口输出使用方法,本文运用外部中断嵌套,通过串口发送相应信息,验证外部中断嵌套。
 
按下PA0(按键按下时为低电平)时,打印出如下信息:
 
EXTI0 IRQHandler enter.
EXTI1 IRQHandler enter.
EXTI2 IRQHandler enter.
EXTI2 IRQHandler return.
EXTI1 IRQHandler return.
EXTI0 IRQHandler return.
 
直接操作寄存器
 
对于外部中断EXTI的控制寄存器,MDK定义了如下的结构体:
typedef struct
{
  vu32 IMR;
  vu32 EMR;
  vu32 RTSR;
  vu32 FTSR;
  vu32 SWIER;
  vu32 PR;
} EXTI_TypeDef;
 
IMR:中断屏蔽寄存器
这个32位的寄存器只有前19位有效。当位x设置为1时,则开启这个线上的中断。
 
EMR:事件屏蔽寄存器
只有前19位有效。当位x设置为1时,则开启这个线上的事件触发。
 
RTSR/FTSR:上升沿/下降沿触发选择寄存器
只有低19位有效,当位x设置为1时,则允许这个线上上升/下降沿触发中断/事件。下降上升沿可以同时设置,则为任意电平触发。
 
SWIER:软件中断事件寄存器 
设置IMR开启某个外部中断后,可以通过向该寄存器对应此外部中断的位x写1,产生一个软件中断,效果通外部中断触发 。
 
PR:挂起寄存器
当在外部中断线上发生了选择的边沿事件,该位被置’1’。在该位中写入’1’可以清除它,也可以通过改变边沿检测的极性清除。外部中断发生时,相应位置被置1,可以用于查询中断。

 
stm32的I/O复用外部中断只有16个,但是引脚却有112(16*7)个之多。为了让每一个I/O口都可以设置为外部中断入口,stm32使用了4个EXTICR寄存器来实现分配。
EXTICR1~4寄存器描述类似,EXTICR1如下:
 
stm32 外部中断嵌套[操作寄存器+库函数]
 
EXTIx[3:0]:EXTIx配置(x = 0 … 3) (EXTI x configuration)  这些位可由软件读写,用于选择EXTIx外部中断的输入源。
0000对应PA引脚   0001 对应 PB引脚  0010对应PC引脚 0011对应PD引脚   
0100对应PE引脚  0101对应PF引脚  0110对应PG引脚 
 
需要注意的是:实际上 AFIO_EXTICR1 寄存器 对应的操作寄存器是 AFIO->EXTICR[0]
 
直接操作寄存器代码:
User/main.c
01 #include
<stm32f10x_lib.h>    
02 #include
"system.h" 
03 #include
"usart.h" 
04 #include
"exti.h"  
05  
06 void Gpio_Init(void);
07  
08 int main(void)
09 {                
10  
11     Rcc_Init(9);             //系统时钟设置
12     Usart1_Init(72,9600);   //设置系统时钟和波特率
13  
14     Gpio_Init();
15  
16     Exti_Init(GPIO_A,0,FTIR);  //设置PA0~3 为下降沿触发,参数GPIO_x 和 FTIR 在system.h中有定义
17     Exti_Init(GPIO_A,1,FTIR);
18     Exti_Init(GPIO_A,2,FTIR);
19       
20     Nvic_Init(2,1,EXTI0_IRQChannel,2);    //设置抢占优先级为2,响应优先级为1,中断分组为2
21     Nvic_Init(1,1,EXTI1_IRQChannel,2);    //设置抢占优先级为1,响应优先级为1,中断分组为2
22     Nvic_Init(0,1,EXTI2_IRQChannel,2);    //设置抢占优先级为0,响应优先级为1,中断分组为2
23  
24     while(1);
25 }
26  
27  
28 void Gpio_Init(void)
29 {
30     RCC->APB2ENR|=1<<2;    //使能PORTA时钟     
31  
32     GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
33     GPIOA->CRL|=0x33334444;
34  
35      
36     //USART1 串口I/O设置
37  
38     GPIOA -> CRH&=0xFFFFF00F;   //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
39     GPIOA -> CRH|=0x000008B0;     
40 }
User/stm23f10x_it.c
01 #include
"stm32f10x_it.h"
02 #include
"stdio.h"
03  
04 void EXTI0_IRQHandler(void)
05 {
06     printf("\r\nEXTI0 IRQHandler enter.\r\n");
07     EXTI->SWIER = 1<<1;     //产生一个EXTI1上的软件中断,让此中断挂起
08     printf("\r\nEXTI0 IRQHandler return.\r\n");
09     EXTI->PR = 1<<0;    //清除中断标志位
10 }
11  
12 void EXTI1_IRQHandler(void)
13 {
14     printf("\r\nEXTI1 IRQHandler enter.\r\n");
15     EXTI->SWIER = 1<<2;     //产生一个EXTI2上的软件中断,让此中断挂起
16     printf("\r\nEXTI1 IRQHandler return.\r\n");
17     EXTI->PR = 1<<1;
18 }
19  
20 void EXTI2_IRQHandler(void)
21 {
22     printf("\r\nEXTI2 IRQHandler enter.\r\n");
23     printf("\r\nEXTI2 IRQHandler return.\r\n");
24     EXTI->PR = 1<<2;
25 }   
Library/src/exti.c
01 #include
<stm32f10x_lib.h>
02 #include
"exti.h"
03  
04 //外部中断配置函数
05 //只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个
06 //参数:GPIOx:0~6,代表GPIOA~G;BITx:需要使能的位;TRIM:触发模式,1,下升沿;2,上降沿;3,任意电平触发
07 //该函数一次只能配置1个IO口,多个IO口,需多次调用
08 //该函数会自动开启对应中断,以及屏蔽线  
09 void Exti_Init(u8 GPIOx,u8 BITx,u8 TRIM)
10 {
11     u8 EXTADDR;
12     u8 EXTOFFSET;
13     EXTADDR=BITx/4; //得到中断寄存器组的编号
14     EXTOFFSET=(BITx%4)*4;
15  
16     RCC->APB2ENR|=0x01;  //使能io复用时钟
17  
18     AFIO->EXTICR[EXTADDR]&=~(0x000F<<EXTOFFSET);//清除原来设置!!!
19     AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;//EXTI.BITx映射到GPIOx.BITx
20      
21     //自动设置
22     EXTI->IMR|=1<<BITx;//  开启line BITx上的中断
23     EXTI->EMR|=1<<BITx;//  开启line BITx上的事件触发 (如果不屏蔽这句,在硬件上是可以的,但是在软件仿真的时候无法进入中断!)
24     if(TRIM&0x01)EXTI->FTSR|=1<<BITx;//line BITx上事件下降沿触发
25     if(TRIM&0x02)EXTI->RTSR|=1<<BITx;//line BITx上事件上升降沿触发
26 }
Library/inc/exti.h
1 #include
<stm32f10x_lib.h>
2  
3 void Exti_Init(u8 GPIOx,u8 BITx,u8 TRIM);
PS: 将Library下的exti.c加入MDK的工程
 
库函数操作
 
库函数操作代码:
main.c
001 #include
"stm32f10x.h"
002 #include
"stdio.h"
003  
004  
005 #define 
PRINTF_ON  1
006  
007 void RCC_Configuration(void);
008 void GPIO_Configuration(void);
009 void USART_Configuration(void);
010 void NVIC_Configuration(void);
011 void EXTI_Configuration(void);
012  
013  
014  
015 int main(void)
016 {
017     RCC_Configuration();
018     GPIO_Configuration();
019     USART_Configuration();
020     NVIC_Configuration();
021     EXTI_Configuration();
022     while(1);
023 }
024  
025  
026 void NVIC_Configuration(void)
027 {
028     NVIC_InitTypeDef NVIC_InitStructure;
029  
030     #ifdef   VECT_TAB_RAM
031         NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);
032     #else
033         NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);
034     #endif
035  
036     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
037  
038     NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
039     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     //优先级数字越大,优先级越小
040     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
041     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
042     NVIC_Init(&NVIC_InitStructure);
043  
044     NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
045     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
046     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
047     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
048     NVIC_Init(&NVIC_InitStructure);
049  
050     NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
051     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
052     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
053     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
054  
055     NVIC_Init(&NVIC_InitStructure);
056 }
057  
058  
059  
060    
061 void GPIO_Configuration(void)
062 {
063     GPIO_InitTypeDef GPIO_InitStructure;                                                                                                                                                                   
064     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
065     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;          
066     GPIO_Init(GPIOA , &GPIO_InitStructure);
067  
068     GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
069     GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
070     GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource2);
071  
072  
073      
074     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
075     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
076     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        
077     GPIO_Init(GPIOA , &GPIO_InitStructure);
078  
079     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
080     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;          
081     GPIO_Init(GPIOA , &GPIO_InitStructure);
082 }
083  
084 void EXTI_Configuration(void)
085 {
086     EXTI_InitTypeDef EXTI_InitStructure;
087  
088     EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2;
089     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
090     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
091     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
092     EXTI_Init(&EXTI_InitStructure);
093  
094  
095 }
096  
097  
098 void RCC_Configuration(void)
099 {
100     /* 定义枚举类型变量 HSEStartUpStatus */
101     ErrorStatus HSEStartUpStatus;
102  
103     /* 复位系统时钟设置*/
104     RCC_DeInit();
105     /* 开启HSE*/
106     RCC_HSEConfig(RCC_HSE_ON);
107     /* 等待HSE起振并稳定*/
108     HSEStartUpStatus = RCC_WaitForHSEStartUp();
109     /* 判断HSE起是否振成功,是则进入if()内部 */
110     if(HSEStartUpStatus == SUCCESS)
111     {
112         /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
113         RCC_HCLKConfig(RCC_SYSCLK_Div1);
114         /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
115         RCC_PCLK2Config(RCC_HCLK_Div1);
116         /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
117         RCC_PCLK1Config(RCC_HCLK_Div2);
118         /* 设置FLASH延时周期数为2 */
119         FLASH_SetLatency(FLASH_Latency_2);
120         /* 使能FLASH预取缓存 */
121         FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
122         /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
123         RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
124         /* 使能PLL */
125         RCC_PLLCmd(ENABLE);
126         /* 等待PLL输出稳定 */
127         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
128         /* 选择SYSCLK时钟源为PLL */
129         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
130         /* 等待PLL成为SYSCLK时钟源 */
131         while(RCC_GetSYSCLKSource() != 0x08);
132     }
133     /* 打开APB2总线上的GPIOA时钟*/
134     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);
135  
136     //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
137          
138 }
139  
140   
141 void USART_Configuration(void)
142 {
143     USART_InitTypeDef USART_InitStructure;
144     USART_ClockInitTypeDef USART_ClockInitStructure;
145  
146     USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
147     USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
148     USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                     
149     USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
150     USART_ClockInit(USART1 , &USART_ClockInitStructure);
151  
152     USART_InitStructure.USART_BaudRate = 9600;
153     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
154     USART_InitStructure.USART_StopBits = USART_StopBits_1;
155     USART_InitStructure.USART_Parity = USART_Parity_No;
156     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
157     USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
158     USART_Init(USART1,&USART_InitStructure);
159  
160     USART_Cmd(USART1,ENABLE);
161 }
162  
163  
164 void TIM_Configuration(void)
165 {
166  
167  
168 }
169  
170 #if 
PRINTF_ON
171  
172 int fputc(int ch,FILE *f)
173 {
174     USART_SendData(USART1,(u8) ch);
175     while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
176     return ch;
177 }
178  
179 #endif
stm32f10x_it.c

01 #include
"stm32f10x_it.h"
02 #include
"stdio.h"
03  
04 void EXTI0_IRQHandler(void)
05 {
06     printf("\r\nEXTI0 IRQHandler enter.\r\n");
07     EXTI_GenerateSWInterrupt(EXTI_Line1);
08     printf("\r\nEXTI0 IRQHandler return.\r\n");
09     EXTI_ClearFlag(EXTI_Line0);
10 }
11  
12 void EXTI1_IRQHandler(void)
13 {
14     printf("\r\nEXTI1 IRQHandler enter.\r\n");
15     EXTI_GenerateSWInterrupt(EXTI_Line2);
16     printf("\r\nEXTI1 IRQHandler return.\r\n");
17     EXTI_ClearFlag(EXTI_Line1);
18 }
19  
20 void EXTI2_IRQHandler(void)
21 {
22     printf("\r\nEXTI2 IRQHandler enter.\r\n");
23     printf("\r\nEXTI2 IRQHandler return.\r\n");
24     EXTI_ClearFlag(EXTI_Line2);
25 }