STM32入门开发实战(六)——射式红外传感器计次操作详解
目录
一、项目准备
1. 工程模板
本篇项目所用模板包含以下模块,声明函数见头文件,模块添加和函数功能详见往期记录。
2. 器件接线
主装置:ST-Link
仿真器,STM32
系统板,MB102
面包板,OLED
显示屏(接线详见往期记录)
除了A15
,B3
,B4
是JLINK
的调试端口,其他端口可随意使用。各类器件默认接线方式如下。
器件 | 端口/电源 |
---|---|
3.3/VCC | + |
GND | – |
OLED-SCL | B8 |
OLED-SDA | B9 |
Sensor-DO | B13 |
增加模块:对射式红外传感器(B13
端口)
观察传感器,默认输出高电平,开关指示灯点亮;挡光片遮住红外射线时,输出低电平,开关指示灯熄灭。
二、外部中断
在单片机执行程序的过程中,有时会出现特定触发条件,使得CPU
暂时停止当前程序的执行,转而去执行处理该事件的程序,事件处理完毕后再返回原程序继续执行。
这个过程被称为中断,触发条件称为中断源,紧急处理的程序称为中断程序,中断的发生需要经过响应、处理和返回三个阶段。中断源可以来自外部或者内部,中断也可分为硬件中断和软件中断。
当有多个中断源申请中断时,会根据中断优先级裁决,给出响应顺序。当中断程序运行时,接收到了更高优先级的中断申请,会再次触发中断,这就是中断嵌套。
当外部设备传来突变信号时,如果不及时处理会导致数据覆盖丢失,这时我们希望CPU
能执行外部中断,也就是本项目的构建原理:
- 开启时钟
RCC
,保证以下外设正常工作; - 配置通用型输入输出端口
GPIO
,接收红外传感器的信号; - 配置复用型输入输出端口
AFIO
,选择中断引脚传入数据; - 配置外部中断事件控制器
EXTI
,边沿检测信号,发送中断申请; - 配置嵌套中断向量控制器
NVIC
,分配优先级以管理中断; - 中央处理器
CPU
执行中断。
其中,AFIO
外设充当数据选择器的作用,所以相同的引脚是不能同时触发中断的,例如,GPIOA1
、GPIOB1
和GPIOC1
的信号都会被导入中断通道1
。
而AFIO
外设的中断优先级由优先级寄存器的4
位(0~15)
决定,抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,两者均相同则按中断号排队。
分组 | 位数 | 抢占优先级取值 | 响应优先级取值 |
---|---|---|---|
0 | 0 | 0 | 0~15 |
1 | 1 | 0~1 | 0~7 |
2 | 2 | 0~3 | 0~3 |
3 | 3 | 0~7 | 0~1 |
4 | 4 | 0~15 | 0 |
三、计数传感器模块
装置接入电脑后,由于GPIO
外设配置为上拉输入模式,传感器会默认向B13
端口输出高电平,该信号经AFIO
外设由13
通道向内传递,由于EXTI
外设配置为下降沿触发,所以仅当电平下降时触发中断。
当挡光片遮住红外射线时,输出转为低电平,中断请求发送至NVIC
内核外设(由于只设置了一个中断,优先级可在范围内随意设定),状态寄存器的标志位置1
。
主程序暂停,执行中断程序EXTI15_10_IRQHandler(void)
,计数增加后,待B13
端口输入电平恢复正常,挡光片已移开,再重置标志位,主程序继续执行CountSensor_Get(void)
,并更新计数。
当然,与按键抖动同理,挡光速度不够快也会产生电平抖动,导致一次挡光产生多个下降沿。
此外,该中断程序调用的函数在模块内部运行,无需在头文件向外部声明。
CountSensor.c
#include "stm32f10x.h"
uint16_t CountSensor_Count; // 次数
// 计数传感器初始化
void CountSensor_Init(void)
{
// 开启外设时钟:外设EXTI、NVIC除外,内核外设不受RCC控制
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 外设GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 外设AFIO
// 配置结构体参数
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式:默认高电平
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 最大速度50MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; // 13号引脚
// 初始化PB13端口
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置中断线路:13号
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource13);
// 配置结构体参数
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line13; // 13号中断线路
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 中断线状态:使能
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
// 初始化外部中断事件控制器
EXTI_Init(&EXTI_InitStructure);
// 配置中断优先级:分组2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 配置结构体参数
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; // 中断请求通道:10~15号
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 中断请求通道状态:使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级:1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级:1
// 初始化嵌套中断向量控制器:10~15号中断请求通道
NVIC_Init(&NVIC_InitStructure);
}
// 中断请求通道控制
void EXTI15_10_IRQHandler(void)
{
// 检查中断线请求状态寄存器的标志位
if(EXTI_GetITStatus(EXTI_Line13) == SET) // 14号中断线:中断源触发
{
CountSensor_Count ++; // 计次
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13)==0); // 等待输入电平恢复
EXTI_ClearITPendingBit(EXTI_Line13); // 重置标志位
}
}
// 获取统计次数
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
CountSensor.h
#ifndef __COUNTSENSOR_H
#define __COUNTSENSOR_H
void CountSensor_Init(void);
uint16_t CountSensor_Get(void);
#endif
四、遮光计数
#include "stm32f10x.h" // 器件模块
#include "OLED.h" // OLED模块
#include "CountSensor.h" // 计数传感器模块
int main(void)
{
// 初始化
OLED_Init();
CountSensor_Init();
// 显示计数
OLED_ShowString(1, 1, "Count:");
while(1)
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);
}
}
作者:dandellion_