5.1 ~ 5.2 EXTI外部中断
视频选集(12/50)[5-2] 对射式红外传感器计次&旋转编码器计次
0. 库函数
0.1 AFIO配置
/**
* @brief 配置AFIO数据选择器,以选择想要作为中断引脚的GPIO端口
* @param GPIO_PortSource: 选择某个GPIO作为中断源
* @param GPIO_PinSource: 指定端口
* @retval None
*/
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
0.2 EXTI配置
/**
* @brief 根据 EXTI_InitStruct 中指定的参数初始化 EXTI 外设。
* @param 指向 EXTI_InitTypeDef 结构体的指针,该结构体包含 EXTI 的配置信息
* @retval None
*/
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
typedef struct
{
uint32_t EXTI_Line; // 指定要配置的中断线
EXTIMode_TypeDef EXTI_Mode; // 指定EXTI模式
EXTITrigger_TypeDef EXTI_Trigger; // 指定触发信号的有效边沿
FunctionalState EXTI_LineCmd; // 指定中断线的新状态
}EXTI_InitTypeDef;
/** @defgroup EXTI_Lines
* @{
*/
#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17 Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS
Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */
typedef enum
{
EXTI_Mode_Interrupt = 0x00,
EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;
typedef enum
{
EXTI_Trigger_Rising = 0x08,
EXTI_Trigger_Falling = 0x0C,
EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;
// 在中断函数中查看和清除标志位
/**
* @brief 检查指定的中断线是否被开启
* @param EXTI_Line: 指定要检查的中断线
* @retval 中断线的状态 (SET or RESET).
*/
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
/**
* @brief 清除 EXTI 的标志位
* @param EXTI_Line: 指定要清除的中断线
* @retval None
*/
void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
// 在主程序中查看和清除标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
本质上,这4个函数都是对状态寄存器的读写。上面的两个函数只能读取于中断有关的标志位,并且对中断是否允许做出了判断;下面的两个函数只是一般的读写标志位,没有额外的处理,能不能触发中断的标志位都能读取
0.3 NVIC配置
/**
* @brief 配置优先级分组:抢占优先级和响应优先级。
* @param NVIC_PriorityGroup: specifies the priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
* 4 bits for subpriority
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
* 0 bits for subpriority
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
/**
* @brief 根据 NVIC_InitStruct 中指定的参数初始化 NVIC 外设。
* @param NVIC_InitStruct: 指向 NVIC_InitTypeDef 结构体的指针,该结构体包含指定 NVIC 外设的配置信息。
* @retval None
*/
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
typedef struct
{
uint8_t NVIC_IRQChannel; // 指定要启用或禁用的 IRQ 信道
uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
specified in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
will be enabled or disabled.
This parameter can be set either to ENABLE or DISABLE */
} NVIC_InitTypeDef;
1. 对射式红外传感器计次
1.1器件
- STM32最小系统板
- ST-LINK V2下载器
- OLED显示屏
- 对射式红外传感器
1.2 实现目标
遮挡模块槽一次,计数一次,累积计数实时显示在OLED屏幕上
1.3 接线图
注.我没有按照图中的IO口来接LED和按键,具体看程序代码

1.4 程序
注.OLED部分我是直接用的资料里的程序,所以没有写笔记
main.c文件如下:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Sensor_Dinf.h"
/*------------------------------主函数------------------------------*/
int main(void)
{
/*-----硬件初始化-----*/
OLED_Init(); // OLED初始化
Sensor_Dinf_Init(); // 对射式红外传感器初始化
/*-----OLED显示-----*/
OLED_ShowString(1, 1, "COUNT:");
while(1){
OLED_ShowNum(1, 8, GetCOUNT(), 3);
}
}
Sensor_Dinf.c文件如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
uint8_t COUNT = 0; // 存储计数
/*------------------------------对射式红外传感器初始化函数------------------------------
@DO A1
*/
void Sensor_Dinf_Init(void)
{
/*-----变量声明-----*/
GPIO_InitTypeDef GPIO_InitStruct; // 定义包含配置参数的结构体变量
EXTI_InitTypeDef EXTI_InitStruc;
NVIC_InitTypeDef NVIC_InitStruct;
/*-----时钟使能-----*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 开启AFIO的时钟
// EXTI和NVIC的时钟是一直开启的,不需要手动使能
/*-----GPIO配置-----*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入:悬空时默认高电平
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化A1端口
/*-----AFIO配置-----*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); // 将PA1引脚连接到EXTI1中断线路
/*-----EXTI配置-----*/
EXTI_InitStruc.EXTI_Line = EXTI_Line1; // 指定EXTI1中断线路
EXTI_InitStruc.EXTI_LineCmd = ENABLE; // 开启中断
EXTI_InitStruc.EXTI_Mode = EXTI_Mode_Interrupt; // 选择中断模式
EXTI_InitStruc.EXTI_Trigger = EXTI_Trigger_Falling; // 选择下降沿触发
EXTI_Init(&EXTI_InitStruc); // 初始化EXTI1
/*-----NVIC配置-----*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 优先级分组:2位抢占,2位响应
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 指定通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 该工程中断只有一个,所以抢占和响应优先级可以随意配置
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
}
/*------------------------------读取计数值函数------------------------------
@Return COUNT
*/
uint8_t GetCOUNT(void)
{
return COUNT;
}
/*------------------------------EXTI1外部中断函数------------------------------
@Brief 光线被遮挡时发生由低到高的跳变,遮挡物拿开时发生由高到低的跳变,进入中断。计次加1
@Pin A1
*/
void EXTI1_IRQHandler(void)
{
// Delay_ms(500); // 中断里面不要加延时。此处仅为测试
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
COUNT ++;
}
EXTI_ClearITPendingBit(EXTI_Line1);// 清除中断标志位
}
Sensor_Dinf.h文件如下:
#ifndef __SENSOR_DINF_H
#define __SENSOR_DINF_H
void Sensor_Dinf_Init(void);
uint8_t GetCOUNT(void);
#endif
2. 旋转编码器计次
2.1 器件
- STM32最小系统板
- ST-LINK V2下载器
- OLED显示屏
- 旋转编码器
2.2 实现目标
- 旋转编码器顺时针旋转时,编码盘每转过一格,计数值+1
- 旋转编码器逆时针旋转时,编码盘每转过一格,计数值-1
- 累计计数值为有符号数,可正可负,其数值实时显示在OLED屏上
2.3 接线图

2.4 程序
main.c文件如下:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Encoder.h"
/*------------------------------主函数------------------------------*/
int main(void)
{
/*-----硬件初始化-----*/
OLED_Init(); // OLED初始化
Encoder_Init(); // 旋转编码器初始化
/*-----OLED显示-----*/
OLED_ShowString(1, 1, "COUNT:");
while(1){
OLED_ShowSignedNum(1, 8, GetCOUNT(), 5);
}
}
Encoder.c文件如下:
#include "stm32f10x.h" // Device header
int16_t COUNT = 0; // 存储旋转编码器的计数值
/*------------------------------旋转编码器初始化函数------------------------------
@A B0
@B B1
*/
void Encoder_Init(void)
{
/*-----变量声明-----*/
GPIO_InitTypeDef GPIO_InitStruct; // 定义包含配置参数的结构体变量
EXTI_InitTypeDef EXTI_InitStruc;
NVIC_InitTypeDef NVIC_InitStruct;
/*-----时钟使能-----*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启GPIOB的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 开启AFIO的时钟
// EXTI和NVIC的时钟是一直开启的,不需要手动使能
/*-----GPIO配置-----*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入:悬空时默认高电平
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化B0、B1端口
/*-----AFIO配置-----*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0); // 将PB0引脚连接到EXTI0中断线路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); // 将PB1引脚连接到EXTI1中断线路
/*-----EXTI配置-----*/
EXTI_InitStruc.EXTI_Line = EXTI_Line0 | EXTI_Line1; // 指定EXTI0、1中断线路
EXTI_InitStruc.EXTI_LineCmd = ENABLE; // 开启中断
EXTI_InitStruc.EXTI_Mode = EXTI_Mode_Interrupt; // 选择中断模式
EXTI_InitStruc.EXTI_Trigger = EXTI_Trigger_Falling; // 选择下降沿触发
EXTI_Init(&EXTI_InitStruc); // 初始化EXTI0、1
/*-----NVIC配置-----*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 优先级分组:2位抢占,2位响应
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; // 指定通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 指定通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
}
/*------------------------------获取计数值函数------------------------------
@Return COUNT
*/
int16_t GetCOUNT(void)
{
return COUNT;
}
/*------------------------------EXTI0外部中断函数------------------------------
@Brief A相下降沿进入该中断,若此时B相为高电平,旋转编码器正转,计数增加
@Pin B0
*/
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) == SET){
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)){
COUNT ++;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
/*------------------------------EXTI1外部中断函数------------------------------
@Brief B相下降沿进入该中断,若此时A相为高电平,旋转编码器反转,计数减少
@Pin B1
*/
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) == SET){
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)){
COUNT --;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
Encoder.h文件如下:
#ifndef __ENCODER_H
#define __ENCODER_H
void Encoder_Init(void);
int16_t GetCOUNT(void);
#endif
本文来自博客园,作者:LI,Yi-han,转载请注明原文链接:https://www.cnblogs.com/LiYi-han/p/19029306

浙公网安备 33010602011771号