实现目标
1. 点击遥控器 A 按键,系统进入警戒模式,一旦检测到震动(小偷偷车),则喇叭发出声响报警
2. 点击遥控器 B 按键,系统退出警戒模式,再怎么摇晃系统都不会报警
硬件介绍
1. 震动传感器
产生震动时,会输出低电平,绿色指示灯亮(开关信号指示灯)
2. 继电器模块
(VCC 3.3V)当左侧IN输入低电平时,右侧的ON 和 COM会导通,左上的绿灯会亮
3. 遥控收发 433M
发送端按下ABCD会在接收端被芯片调制解调,最终解析成不同的信号来识别,接收端的D0, D1, D2, D3 对应A,B,C,D,收到哪个对应的信号,相应的引脚就会给出高电平。
CubeMX配置
1. 惯例配置
2. 由于震动传感器的DO口接到了PA4,所以将PA4配置成中断模式,且由于DO口拉低代表有震动,所以将模式改为下降沿触发
3. 配置其他需要使用到的GPIO口, 433M的D0和D1都是上升沿触发无需修改
4. 在NVIC中打开中断:
5. 惯例设置并生成代码
Keil5 代码实现
打开stm32f1xx_it.c --> EXTI4(9_5)_IRQHandler() --> HAL_GPIO_EXTI_IRQHandler() --> HAL_GPIO_EXTI_Callback( )
HAL_GPIO_EXTI_Callback()就是中断处理程序,将他在main.c中重写:
注意,如果直接在中断服务函数里调用 HAL_Delay 函数,则会造成系统卡死。
原因:程序初始化时默认把滴答定时器的中断优先级设为最低,其它中断源很容易打断它导致卡死,也就是说当Delay函数在进行中的时候,如果又发生了震动触发了外部中断,就会又立刻执行这个中断处理程序,导致喇叭一直在响无法停下。
解决办法:
在 main 函数里使用以下函数提高滴答定时器的中断优先级(提升至0);
HAL_NVIC_SetPriority(SysTick_IRQn,0,0); //必须写在" SystemClock_Config() "后面!!!
并且在CubeMX中将 EXTI4 的中断优先级设置比滴答定时器的中断优先级高,比如 2; 将433M的 D0和D1的抢占优先级设为1
然后重新生成代码,Keil中此时会自动弹出这个对话框问是否要重载,选择是:
此时会将刚刚Cube中做出的修改反映到Keil中,并保留之前在Keil中自己写的代码!
//代码仅展示需要自己写的部分,其他自动生成的部分没有展示!
#define secure_ON 1
#define secure_OFF 0
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) //外部中断的处理函数
{
static int status = secure_OFF; //此处一定要加static,不然每次调用这个函数都要再赋值一遍
//一根中断线上接有多个中断源,判断中断源是否来自PA4
if((GPIO_Pin == GPIO_PIN_4) && (status == secure_ON)){
//如果检测到PA4被拉低,且模式为警戒模式,则继电器打开,喇叭响;没有检测到则不响
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); //继电器导通
HAL_Delay(1000); //延时1秒
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); //继电器断开
}else{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); //继电器断开
}
}
if(GPIO_Pin == GPIO_PIN_6){ //D0,对应按键A,按下进入禁戒模式
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET){
status = secure_ON;
}
}
if(GPIO_Pin == GPIO_PIN_7){ //D1,对应按键B,按下退出禁戒模式
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET){
status = secure_OFF;
}
}
}
实现效果
可见,在按下A键后,系统进入警戒模式,继电器会对震动有反应,当按下B键后,解除警报,继电器对震动无反应。