一、项目准备

1. 工程模板

  本篇项目所用模板包含以下模块,声明函数见头文件,模块添加和函数功能详见往期记录。
请添加图片描述

2. 器件接线

  主装置:ST-Link仿真器,STM32系统板,MB102面包板,OLED显示屏(接线详见往期记录)

  除了A15B3B4JLINK的调试端口,其他端口可随意使用。各类器件默认接线方式如下。

器件 端口/电源
3.3/VCC +
GND -
OLED-SCL B8
OLED-SDA B9
Sensor-DO B13

  增加模块:对射式红外传感器(B13端口)

请添加图片描述
  观察传感器,默认输出高电平,开关指示灯点亮;挡光片遮住红外射线时,输出低电平,开关指示灯熄灭

二、外部中断

  在单片机执行程序的过程中,有时会出现特定触发条件,使得CPU暂时停止当前程序的执行,转而去执行处理该事件的程序,事件处理完毕后再返回原程序继续执行

  这个过程被称为中断,触发条件称为中断源,紧急处理的程序称为中断程序,中断的发生需要经过响应处理返回三个阶段。中断源可以来自外部或者内部,中断也可分为硬件中断软件中断

  当有多个中断源申请中断时,会根据中断优先级裁决,给出响应顺序。当中断程序运行时,接收到了更高优先级的中断申请,会再次触发中断,这就是中断嵌套

请添加图片描述

  当外部设备传来突变信号时,如果不及时处理会导致数据覆盖丢失,这时我们希望CPU能执行外部中断,也就是本项目的构建原理:

  1. 开启时钟RCC,保证以下外设正常工作;
  2. 配置通用型输入输出端口GPIO,接收红外传感器的信号;
  3. 配置复用型输入输出端口AFIO,选择中断引脚传入数据;
  4. 配置外部中断事件控制器EXTI,边沿检测信号,发送中断申请;
  5. 配置嵌套中断向量控制器NVIC,分配优先级以管理中断;
  6. 中央处理器CPU执行中断。

请添加图片描述
  其中,AFIO外设充当数据选择器的作用,所以相同的引脚是不能同时触发中断的,例如,GPIOA1GPIOB1GPIOC1的信号都会被导入中断通道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);
	}
}

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐