前言
大家好,我是林白柏;
希望你看完之后,能有所收获,不足请指正!
PS:本文提到的模块都使用正点原子的stm32开发板战舰驱动,模块用的某宝现成的模块。
模块介绍(可跳过)
模块由菲涅尔透镜、热释电红外探头、放大电路组成。
菲涅尔透镜用于提高探头灵敏度。
透镜在探头前方产生交替变化的“盲区”和“高灵敏区”,这样当人体走过时,人体发射的红外线就会不断交替经过“盲区”和“高灵敏区”,便于探头产生脉冲信号。
热释电红外探头检测到人体发出的红外线时,发出一个脉冲信号(脉冲宽度固定);有人时,人体产生的红外线会交替出现在“盲区”和“高灵敏区”,就一直有脉冲信号输出(实测人静止不动时好像不会输出)。
在通过放大器是BISS0001将脉冲信号转化为更直观的高低电平输出,有人输出高,无人输出低。并在放大电路上设置电位器,调节输出信号的灵敏度(比如探头产生n个脉冲,模块才输出高电平),调节高电平持续时间。
模块使用
模块接口只有三个脚,地、电源、数据线;分别计为GND、VCC、OUT
探头检测到有人移动时,OUT=1;没人移动时,OUT=0;
因此我们可以把它当成一个独立按键,按下时(有人移动)为1,松开时(没人移动)为0。
我们将IO口初始化为外部中断、跳变沿触发,当产生中断时,在中断服务函数中读取IO状态,IO=1则为上升沿——有人移动,IO=0则为下降沿——无人移动。(见头文件human_body_induction.c部分)
将事件返回给事件回调函数。(见头文件human_body_induction.h部分)
代码:h文件
头文件*(为了一个“按键”封装两个文件确实点过于仪式感了哈哈哈哈)*中定义了两种事件,用于返回给事件回调函数。
typedef enum {
HBI_NOBODY,//无人移动
HBI_PEOPLE_MOVING,//有人移动
}hbi_evt_t;
typedef void ( * hbi_evt_handle_t )( hbi_evt_t * event );//事件回调函数类型
void hbi_init( hbi_evt_handle_t event_handle);//初始化函数声明
代码:c文件
源文件中分为两部分:初始化,中断服务函数。
初始函数,初始化io、中断,并保存回调函数指针。
static hbi_evt_handle_t m_event_handle; //保存用户传进来的回调函数指针
void hbi_init( hbi_evt_handle_t event_handle) {
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//[ 与硬件相关 ]
//使能外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE); //使能PORTG口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
//将IO初始化为下拉输入(上拉下拉好像都行)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOG, &GPIO_InitStructure);
//配置GPIOG.8为中断线EXTI_Line8的中断源(这部分需要去了解stm32中断相关知识)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOG,GPIO_PinSource8);
//EXTI_Line8初始化为跳变沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//初始化中断分组
NVIC_InitStructure.NVIC_IRQChannel = HBI_EXTI_IRQ_CHANNEL;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
//[ 与硬件无关 ]
//保存用户传进来的回调函数指针,中断服务函数中使用
m_event_handle = event_handle;
}
中断服务函数中,根据中断类型返回相应的事件给用户
void EXTI9_5_IRQHandler(void)//stm32中,中断线5-9公用一个中断服务函数
{
if(EXTI_GetITStatus( EXTI_Line8 ) != RESET)
{
hbi_evt_t evt = HBI_NOBODY;
EXTI_ClearFlag( EXTI_Line8 ); //清除中断标志位
EXTI_ClearITPendingBit( EXTI_Line8 ); //清除EXTI线路挂起位
if( GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_8) ) {//上升沿
evt = HBI_PEOPLE_MOVING;
}
else {//下降沿
evt = HBI_NOBODY;
}
m_event_handle( &evt );
}
}
代码使用方法
初始化时,应用程序注册一个回调函数,后续就只需要在回调函数中判断事件并执行相应操作即可
//以下使用演示
static void bi_evt_cb( hbi_evt_t * event ) {//定义事件回调函数
switch ( *event ) {
case HBI_NOBODY : _LOG_DEBUG("[hbi] nobody\n\n"); break;
case HBI_PEOPLE_MOVING : _LOG_DEBUG("[hbi] people moving\n\n");; break;
}
}
int main(void){
hbi_init( hbi_evt_cb );//将回调函数指针传给初始化函数
while(1);
}
补充介绍(可/不可重复触发)
模块还有一个重复触发和不重复触发的概念,由BISS0001实现(具体实现方式查看BISS0001规格书),两者的区别如下图红色方框
不可重复触发,探头产生脉冲时,模块out脚输出高电平,时长Tx;在Tx结束前,探头产生的脉冲都无效。
可重复触发,探头产生脉冲时,模块out脚输出高电平,时长Tx;在Tx结束前,探头产生新的脉冲,则从新的脉冲处开始重新计时Tx。只要Tx结束前一直有脉冲产生,则模块高电平会一直持续下去。
可通过模块上跳线帽进行设置,L(不可重复触发)和H(可重复触发)
代码获取
共用代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/common
驱动代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/component