5.3 对射式红外传感器计次
对射式红外传感器,指示灯灭,代表现在输出高电平,拿开挡光片,输出指示灯亮。
在灭到亮之间会产生一个下降沿,这个下降沿触发单片机引脚的外部中断,然后执行数字加1的中断程序
代码讲解:
GPIO文件函数说明(这里主要学习AFIO的数据选择)
//锁定引脚配置,防止随意更改
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//配置事件输出功能
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
//配置引脚重映射(第一个参数:选择重映射方式,第二个参数是新的状态)
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
//配置AFIO的数据选择器选择中断引脚
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//跟以太网有关的外设
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
EXTI函数说明:
void EXTI_DeInit(void); //配置清除,恢复上电默认状态
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //初始化EXTI,根据结构体里的参数配置EXIT外设
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); //把参数传递的结构体变量赋一个默认值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line); //软件触发外部中断
//主程序查看和清除标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line); //获取指定的标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line); //清除指定的标志位
//中断函数查看和清除标志位
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); //获取中断标志位是否被置1
void EXTI_ClearITPendingBit(uint32_t EXTI_Line); //清除中断标志位
配置NVIC:在,misc文件:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //用以中断分组,参数是中断分组的方式
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); //根据结构体里面指定的参数初始化
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); //设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState); //系统低功耗配置
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource); //
主函数:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1, 1, "Count:");
while (1)
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);
}
}
中断函数配置过程:
//step1:配置RCC,把我们这里涉及的外设的时钟都打开(其中EXTI和NVIC两个外设是一直开着的)
//step2:配置GPIO,选择我们的端口为输入模式
//step3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
//step4:配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式(中断响应或事件响应)
//step5:配置NVIC,给我们这个中断选择一个合适的优先级
//最后通过NVIC,外部信号就能进入CPU了
中断函数:(.c)
#include "stm32f10x.h" // Device header
uint16_t CountSensor_Count;
void CountSensor_Init(void)
{
//step1:配置RCC,把我们这里涉及的外设的时钟都打开(其中EXTI和NVIC两个外设是一直开着的)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//step2:配置GPIO,选择我们的端口为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//step3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
//step4:配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式(中断响应或事件响应)
//当前模式是:将EXIT的第14个线路配置为中断模式,下降沿触发,然后开启中断,将电平信号通过EXIT通向下一级NVIC
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
//step5:配置NVIC,给我们这个中断选择一个合适的优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //此分组方式整个芯片只能用一种,必须得确保每个模块选择的都是同一个(或者把其放在主函数的最开始,这样模块就不用再分组了)
NVIC_InitTypeDef NVIC_InitStucture;
NVIC_InitStucture.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStucture.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStucture.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStucture);
//最后通过NVIC,外部信号就能进入CPU了
}
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetFlagStatus(EXTI_Line14) ==SET)
{
CountSensor_Count ++;
EXTI_ClearITPendingBit(EXTI_Line14); //每次启动了中断,记得将中断清除,不然就困死在中断程序里了
}
}
.h文件
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H
void CountSensor_Init(void);
uint16_t CountSensor_Get(void);
#endif
5.4 旋转编码器计次
主程序:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num;
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1, 1, "Num:");
while (1)
{
Num += Encoder_Get();
OLED_ShowSignedNum(1, 5, Num, 5);
}
}
Encoder文件:(.c)
#include "stm32f10x.h" // Device header
int16_t Encoder_Count;
void Encoder_Init(void)
{
//step1:配置RCC,把我们这里涉及的外设的时钟都打开(其中EXTI和NVIC两个外设是一直开着的)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//step2:配置GPIO,选择我们的端口为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
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(GPIOB, &GPIO_InitStructure);
//step3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
//step4:配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式(中断响应或事件响应)
//当前模式是:将EXIT的第0和1个线路配置为中断模式,下降沿触发,然后开启中断,将电平信号通过EXIT通向下一级NVIC
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);
//step5:配置NVIC,给我们这个中断选择一个合适的优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //此分组方式整个芯片只能用一种,必须得确保每个模块选择的都是同一个(或者把其放在主函数的最开始,这样模块就不用再分组了)
NVIC_InitTypeDef NVIC_InitStucture;
NVIC_InitStucture.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStucture.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStucture.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStucture);
NVIC_InitStucture.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStucture.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStucture.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStucture);
//最后通过NVIC,外部信号就能进入CPU了
}
//返回count的变化值
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
//编写中断函数,在Start文件夹下的startup_stm32f10x_md.s文件下寻找
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET) //检查中断标注位
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //反转
{
Encoder_Count --;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET) //检查中断标注位
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //正转
{
Encoder_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
注意:
1.最好不要在主程序和中断程序里操作可能产生冲突二点硬件,可以现在中断里操作变量或标志位,当中断返回时,再对整个变量进行显示操作;
2.如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count --;
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count ++;
}
}
EXTI_ClearITPendingBit(EXTI_Line1);c
}
}