使用外部中断步骤:(以对射式传感器计数为例,1-6为完整中断需要的步骤,其余自设)
1.开启时钟
/**开启GPIO与AFIO外设的时钟,EXIT在外设中是默认开启的,
NVIC是内核外设(相当于皇帝(CPU)身边太监,可以帮忙下达命令,管理中断),也默认开启**/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
2.配置GPIO输入
GPIO_InitTypeDef GPIO_Structure;
GPIO_Structure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Structure.GPIO_Pin =GPIO_Pin_14;
GPIO_Init(GPIOB,&GPIO_Structure);
3.AFIO选择中断引脚
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
4.配置EXTI数据选择器
//配置EXIT寄存器配置,因为只有一个,不需要像GPIO一样选择端口
EXTI_InitTypeDef EXTI_Structure; //结构体命名
EXTI_Structure.EXTI_Line = EXTI_Line14; //对应Pin14
EXTI_Structure.EXTI_LineCmd = ENABLE; //开启中断
EXTI_Structure.EXTI_Mode = EXTI_Mode_Interrupt; //选择中断模式
EXTI_Structure.EXTI_Trigger = EXTI_Trigger_Falling; //触发方式:下降沿
EXTI_Init(&EXTI_Structure);//调用函数,完成配置
5.配置NVIC的寄存器,设置中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//配置优先级分组,2组两位抢占,两位响应,整个芯片只能用一种分组方式,可放主函数最开始
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; //选择通道,该通道包含10-15,包含Pin14
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //设置响应优先级
/** 分组2:2位抢占优先级,2位响应优先级,取值范围都为0-3,即2的2次方
多个中断产生拥挤时,才有作用**/
NVIC_Init(&NVIC_InitStruct);
6.写中断程序(识别到中断触发就跳到该程序)
每个EXIT线路都有属于自己的函数名,可在.s文件查看
void EXTI15_10_IRQHandler(void) //不需要声明,因为不用调用,自动执行
{
if (EXTI_GetITStatus(EXTI_Line14) == SET);//10-15口公用一个函数,判断是否是14口
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
Delay_ms(10);
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)//消抖
{
Delay_ms(10);
Count ++;
}
}
EXTI_ClearITPendingBit(EXTI_Line14); //清楚14口的中断标志位,否则会卡死在中断里
}
}
注:不写消抖程序会出现遮和拿开时都计数,改成下拉输入也可以解决该问题,个人猜测原因:红外对射式传感器正常状态是输入低电平,遮挡时输入高电平,由于上拉输入,在未遮挡完(产生抖动)就被上拉为高电平,此时其实还是低电平,又快速下降,实现一次计数。
7.返回计数值
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
8.mian.c文件调用
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1,1,"Count:");
while(1)
{
OLED_ShowNum(1,7,Count_Get(),5);//获取返回值,在OLED上显示
}
}
9.(改写)用EXTI15_10端口实现旋转编码器计数
.h文件配置
void Encoder_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//配置GPIOA和B
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//配置AFIO通道
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
//配置EXTI
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14|EXTI_Line15;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
/**15到10共用一个,后续需要通过读取中断标志位,识别不同口的中断**/
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
int16_t Encoder_get(void)
{
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count=0;
/**如果不清0,由于有数值在main.c中会无无限加+1,清0后就是+0,直到下次触发变为1**/
return Temp ;
}
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) == SET)// 14口触发中断程序
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)==1)
{
Encoder_Count--;
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
if (EXTI_GetITStatus(EXTI_Line15) == SET) //15口触发中断程序
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1)
{
Encoder_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line15);
}
}
main.c文件
int main(void)
{
Encoder_Init();
OLED_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
Num+= Encoder_get();
OLED_ShowSignedNum(1,5,Num,5);
}
}
另一种更简洁的累加方式
.h文件中修改
int16_t Encoder_get(void)
{
return Encoder_Count;
}
.c文件修改
while(1)
{
Num= Encoder_get();
OLED_ShowSignedNum(1,5,Num,5);
}