编写外部中断EXTI代码:以【旋转编码器计次】为例
![](https://i-blog.csdnimg.cn/blog_migrate/351db1451b7aa351fcc64862ff053aa2.png)
配置外部中断时从左至右参考该图流程
1、完成驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
2、在Encoder.c中初始化函数Encoder_Init
void Encoder_Init(void){/*第一步:配置RCC,把涉及到的外设时钟打开*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//打开GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//打开AFIO的时钟(AFIO也是APB2的外设)/*还有EXTI和NVIC两个外设的时钟需要打开,但因为这两个外设的时钟一直都是打开的,所以不需要配置开启*//*第二步:配置GPIO,选择端口各种模式*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空、上拉、下拉输入都可以(此处选择上拉输入)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);//初始化GPIOB/*第三步:配置AFIO,选择使用的中断引脚*//*ST公司并未分配专门的库函数文件给AFIO,AFIO的库函数存放在GPIO中*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//配置AFIO的数据选择器,选中需要的中断引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);//配置AFIO的数据选择器,选中需要的中断引脚/*第四步:配置EXTI,选择中断配置各种模式*/EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//指定要配置的中断线EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//指定外部中断线的模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//指定触发信号的有效边沿EXTI_InitStructure.EXTI_LineCmd = ENABLE;//指定选择的中断线的新状态EXTI_Init(&EXTI_InitStructure);//初始化EXTI/*第五步:配置NVIC,选择中断配置各种模式*//*NVIC是内核外设,库函数被ST公司分配到杂项mics.h文件中*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//在配置中断之前先指定NVIC中断分组//有两个中断通道,对其分别设置优先级NVIC_InitTypeDef NVIC_InitStructure;//定义结构体变量(通道可以重复使用)//通道1NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//指定中断通道来开启或关闭//IRQn_Type的定义在stm32f10x.h文件中//选择使用芯片的中断通道列表(STM32F10X_MD)NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定中断通道是使能还是失能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//指定所选通道的抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定所选通道的响应优先级NVIC_Init(&NVIC_InitStructure);//初始化NVIC//通道2NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//指定中断通道来开启或关闭//IRQn_Type的定义在stm32f10x.h文件中//选择使用芯片的中断通道列表(STM32F10X_MD)NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定中断通道是使能还是失能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//指定所选通道的抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//指定所选通道的响应优先级(较通道1响应优先级低)NVIC_Init(&NVIC_InitStructure);//初始化NVIC}
3、在Encoder.c中编写中断服务程序函数EXTI0_IRQHandler、EXTI1_IRQHandler
在STM32中,中断函数的名字都是固定的,每个中断通道都对应一个中断函数(名字参考启动文件startup_stm32f10x_md.s)
文件中以IRQHandler结尾的字符串就是中断函数的名字
中断函数都是无参无返回值的
void EXTI0_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line0) == SET)//中断标志位判断,确定由哪个中断源触发//看看EXTI0的中断标志位是不是1{if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){CountSensor_Count --;}EXTI_ClearITPendingBit(EXTI_Line0);//中断程序结束后,清除中断标志位}}void EXTI1_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line1) == SET)//中断标志位判断,确定由哪个中断源触发看看EXTI1的中断标志位是不是1{if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){CountSensor_Count ++;}EXTI_ClearITPendingBit(EXTI_Line1);//中断程序结束后,清除中断标志位}}//如果用的是9~5或15~10的多个中断通道,只能用一个中断服务程序函数,那么把多个if并列放在一个函数里就行
4、在Encoder.c中编写旋转变量返回函数Encoder_Get
//旋转编码器需要正反转,先定义一个带符号的全局变量int16_t Encoder_Count;int16_t Encoder_Get(void){int16_t Temp;//设置一个局部变量来表示Count的变化程度Temp = Encoder_Count;Encoder_Count = 0;return Temp;}
5、在Encoder.h中声明初始化函数Encoder_Init和返回端口值获取函数Encoder_Get
void Encoder_Init(void);uint16_t Encoder_Get(void);//中断函数不用声明,因为中断函数不需要调用,会自动执行
6、在主程序main.c中#include "Encoder.h"
#include "Encoder.h"
7、在主循环之前先初始化外部中断
![](https://i-blog.csdnimg.cn/blog_migrate/f72c5833291a17d7ba12b0ae4bd7915d.png)
8、在主循环中编写程序主体
int16_t Num;
int main(void){Encoder_Init();OLED_Init();OLED_ShowString(1,1,"Num:");while(1){Num += Encoder_Get();OLED_ShowSignedNum(1,5,Num,5);}}
实现功能:OLED显示屏默认显示Num:+00000,向左转数字减小,向右转数字增加