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如下:
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 |
02 |
#include |
03 |
#include |
04 |
#include |
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 |
02 |
#include |
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 |
02 |
#include |
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 |
2 |
3 |
void Exti_Init(u8 GPIOx,u8 BITx,u8 TRIM);
|
PS: 将Library下的exti.c加入MDK的工程
库函数操作
库函数操作代码:
main.c
001 |
#include |
002 |
#include |
003 |
004 |
005 |
#define |
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 |
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 |
02 |
#include |
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 |
} |