stm32教程:EXTI外部中断应用
早上好啊大佬们,上一期我们讲了EXTI外部中断的原理以及基础代码的书写,这一期就来尝试一下用它来写一些有实际效能的工程吧。
这一期里,我用两个案例代码来让大家感受一下外部中断的作用和使用价值。
旋转编码器计数
整体思路讲解
这里,我们需要使用到 OLED 和 旋转编码器。
先来说一下旋转编码器 ——
我们使用的是EC11旋转编码器,这是一种增量式旋转编码器,拥有A、B、C三个输出通道,其中A、B两相输出正交信号,相位差为90°,C相输出零脉冲信号,用于标识位置。
当编码器正转时,A相的输出信号超前B相90°;当编码器反转时,A相滞后B相90°。我们在程序中可以根据A、B两相信号输出的先后顺序,来判断旋转编码器是正转还是反转。
用 波形图 显示结果就是:
然后对于我们的代码实现思路就很清晰了,
检测A相的下降沿或上升沿,然后判断B相的电平高低,用来区分正转还是反转。
接线
OLED | STM32 |
---|---|
VCC | 3.3V |
GND | GND |
SCL | PB8 |
SDA | PB9 |
旋转编码器 | STM32 |
---|---|
VCC | 3.3V |
GND | GND |
A | PA0 |
B | PA1 |
代码
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "count.h"
int main()
{
OLED_Init();
External_Init();
OLED_ShowString(0, 0, "cnt:", OLED_8X16);
OLED_Update();
while(1)
{
OLED_ShowNum(32, 0, Count_Get(), 3, OLED_8X16);
OLED_Update();
}
}
count.c
#include "stm32f10x.h" // Device header
int16_t cnt = 0;
void External_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //打开AFIO的时钟
//然后打开 EXTI 和 NVIC 的外设时钟,但是出于它们是一直打开的,所以就不需要了。
GPIO_InitTypeDef GPIO_InitStructure;
/*配置GPIO*/
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(GPIOA, &GPIO_InitStructure);
/*重映射GPIO*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //将外部中断的0号线映射到GPIOB
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); //将外部中断的1号线映射到GPIOB
/*配置EXTI*/
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //检测下降沿
EXTI_Init(&EXTI_InitStructure); //EXTI中断初始化
/*配置NVIC*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//中断分组, 2抢占,2响应
//这个分组函数一个工程只选择一次,多次调用会覆盖之前的配置
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //选择配置NVIC的EXTI0线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //选择配置NVIC的EXTI1线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //指定NVIC线路的响应优先级为2
NVIC_Init(&NVIC_InitStructure);
//这里就是两个中断线的写法,但是这里的中断配置比较随意,因为两种情况不会同时发生
}
int16_t Count_Get(void)
{
return cnt;
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET) //判断返回值是否为 SET
{
// 如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0) //PA0的下降沿触发中断,此时检测另一相PA1的电平,目的是判断旋转方向
{
cnt --; //此方向定义为反转,计数变量自减
}
}
EXTI_ClearITPendingBit(EXTI_Line0); //重置中断判断位,否则会卡死在中断函数中
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断返回值是否为 SET
{
// 如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) //PA1的下降沿触发中断,此时检测另一相PA0的电平,目的是判断旋转方向
{
cnt ++; //此方向定义为反转,计数变量自减
}
}
EXTI_ClearITPendingBit(EXTI_Line1); //重置中断判断位,否则会卡死在中断函数中
}
}
count.h
#ifndef __COUNT_H
#define __COUNT_H
void External_Init(void);
int16_t Count_Get(void);
#endif
红外避障实现
当车辆行驶时,前方遇到障碍物被我们的红外探测到,就需要停下当前的所有行为,进行避障操作。
对于红外部分内容,大家可以看看之前的一篇文章。
stm32教程:红外循迹模块 & 红外避障模块 & 光电门模块-CSDN博客
整体功能讲解
我们这里主要是来演示一下EXTI外部中断,所以这里就简单模拟一下。
首先是红外避障模块,当它没有探测到物品时,车辆正常行驶。
当它探测到物品时,就需要进行紧急避障。
然后,我们用一个LED灯来模拟车辆状态,车辆正常行驶时,LED灯灭。
车辆进行避障时,LED灯亮
接线
红外 | STM32 |
---|---|
VCC | 3.3V |
GND | GND |
OUT | PB7 |
LED我们使用STM32系统板的板载LED
代码
bizhang.c
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //PC13是板载的LED
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_13);
}
void LED_Turn(void)
{
if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13) == 0) //获取输出寄存器的状态,如果当前引脚输出低电平
{
GPIO_SetBits(GPIOC, GPIO_Pin_13); //则设置PC13引脚为高电平,高电平灭
}
else //否则,即当前引脚输出高电平
{
GPIO_ResetBits(GPIOC, GPIO_Pin_13); //则设置PC13引脚为低电平,低电平亮
}
}
void External_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //打开AFIO的时钟
//然后打开 EXTI 和 NVIC 的外设时钟,但是出于它们是一直打开的,所以就不需要了。
GPIO_InitTypeDef GPIO_InitStructure;
/*配置GPIO*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*重映射GPIO*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource7); //将外部中断的7号线映射到GPIOB
/*配置EXTI*/
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //改成双向,上升沿切换为避障,下降沿切换为正常行驶。
EXTI_Init(&EXTI_InitStructure); //EXTI中断初始化
/*配置NVIC*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//中断分组, 2抢占,2响应
//这个分组函数一个工程只选择一次,多次调用会覆盖之前的配置
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //选择配置NVIC的EXTI9_5线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure);
}
void EXTI9_5_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line7) == SET) //判断返回值是否为 SET
{
LED_Turn();
EXTI_ClearITPendingBit(EXTI_Line7); //重置中断判断位,否则会卡死在中断函数中
}
}
bizhang.h
#ifndef __BIZHANG_H
#define __BIZHANG_H
void LED_Init(void);
void LED_Turn(void);
void External_Init(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "bizhang.h"
int main()
{
LED_Init();
External_Init();
while(1)
{
}
}
尾声
OK,那么这一期就到这里了,如果需要这期的代码,可以私信我。
原文地址:https://blog.csdn.net/sikimayi/article/details/144561388
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/527084.html 如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/527084.html 如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!