【STM32】电动车报警系统
1. 功能需求
按下按键A,常响2秒,打开报警功能,此时当电动车(振动传感器)晃动时,蜂鸣器叫,若想关闭蜂鸣器,按下按键B,则关闭报警功能,蜂鸣器停止叫声。
电动车报警系统
2. 硬件准备
2.1 硬件介绍
433M无线收发模块、蜂鸣器、振动传感器、STM32开发板、绝缘线、STLINK、typeC
2.1.1 433M无线收发模块
无线收发模块 433MHz(或称RF433射频小模块)采用高频射频技术,并结合了全数字技术和 AVR 单片机,成为一种微型收发器。
- M 点动:瞬态输出,相当于自复位开关的状态收到信息输出高电平,无信号时为低电平。
- T 锁存:上电时为低,收到信号输出高电平并锁定高,再次收到信号输出低并锁定低。
- L 自锁:上电时为低,收到信号输出高电平并锁定高,同时将其他输出置低。
2.1.2 振动传感器
2.1.3 蜂鸣器


2.2 引脚连接
| 433M无线收发模块 | GND | GND |
| 5V | 5V | |
| D0 | PB4 | |
| D1 | PA11 |
| 振动传感器 | VCC | 3V3 |
| GND | GND | |
| DO | PA4 | |
| AO | 不接 |
| 蜂鸣器 | GND | GND |
| IO | PB7 | |
| VCC | 3V3 |
3. 分步实现功能
先分别实现振动点灯功能、无线收发信号、蜂鸣器功能,再集成
3.1 振动点灯功能
3.1.1 准备好写过的led.c文件
#include "led.h"
#include "sys.h"//初始化GPIO函数
void led_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // 两个LED对应的引脚gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出gpio_initstruct.Pull = GPIO_PULLUP; // 上拉gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB, &gpio_initstruct);//关闭LEDled1_off();led2_off();
}//点亮LED1的函数
void led1_on(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}//熄灭LED1的函数
void led1_off(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}//翻转LED1状态的函数
void led1_toggle(void)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}//点亮LED2的函数
void led2_on(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 拉低LED2引脚,点亮LED2
}//熄灭LED2的函数
void led2_off(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 拉高LED2引脚,熄灭LED2
}//翻转LED2状态的函数
void led2_toggle(void)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
}
3.1.2 写exti.c文件
第一步:进行初始化,写初始化函数
写任何实验都必要的步骤
- 打开时钟
- 调用GPIO初始化函数
void exti_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_4; // 震动传感器对应的引脚gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发gpio_initstruct.Pull = GPIO_PULLUP; // 上拉HAL_GPIO_Init(GPIOA, &gpio_initstruct);HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 0); // 设置EXTI0中断线的优先级HAL_NVIC_EnableIRQ(EXTI4_IRQn); // 使能中断
}
这里用到了中断,下次再详细讲中断,这次先简单了解一下~
①HAL_NVIC_SetPriority和HAL_NVIC_EnableIRQ都是中断配置函数
HAL_NVIC_SetPriority是中断优先级配置,HAL_NVIC_EnableIRQ是中断使能配置
设置中断分组 → 设置中断优先级 → 使能中断

第二步:此时就剩中断服务函数EXTI4_IRQHandler没写了,为实现所需功能,一般会用到回调函数HAL_GPIO_EXTI_Callback,后面根据需要功能再写回调函数
void EXTI4_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{}
第三步:设立标志位,这一步需要和main.c配合看才能理解
实际上是检测到振动和没有振动的区别:检测到振动,标志位为1,无振动,标志位为0
在main.c中实现主功能,如果检测到振动,即vibrate_flag_get() == 1,那么就点亮LED1两秒然后熄灭,此时要把标志位置为0,vibrate_flag_set(0),否则标志位一直为1就没有区分的效果了
uint8_t vibrate_flag_get(void)
{uint8_t temp = vibrate_flag;vibrate_flag = FALSE;return temp;}void vibrate_flag_set(uint8_t value)
{vibrate_flag = value;
}
第四步:设立标志位后,怎么和振动传感器联系起来——回调函数
当振动传感器感受到振动时,PA4收到低电平,见“2.1.2 振动传感器”
用HAL_GPIO_ReadPin读PA4,如果是低电平0(GPIO_PIN_RESET),则标志位为1
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{//delay_ms(20);if (GPIO_Pin == GPIO_PIN_4){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET)//led1_toggle();vibrate_flag = TRUE;}
}
3.1.3 写main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init(); /* 初始化LED灯 */exti_init(); /* 初始化EXTI */while(1){ //如果检测到震动if(vibrate_flag_get() == TRUE){//那么就点亮LED1两秒,然后熄灭led1_on();delay_ms(2000);led1_off();vibrate_flag_set(FALSE);}}
}
别忘了补充.h文件的函数声明
3.2 无线收发信号功能
如果检测到A键按下,就翻转LED1状态;如果检测到B键按下,就翻转LED2状态
3.2.1 main.c
很容易理解,就是平常的初始化HAL库和一些函数、设置时钟,然后翻转灯的功能实现
可以扩展到电动车报警系统,翻转的把灯改为蜂鸣器就好了!
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init(); /* 初始化LED灯 */exti_init(); /* 初始化EXTI */while(1){ if(buttonA_flag_get() == TRUE) //如果检测到A键按下led1_toggle(); //则翻转LED1状态if(buttonB_flag_get() == TRUE) //如果检测到B键按下led2_toggle(); //则翻转LED2状态}
}
3.2.2 exti.c
两个按键,在3.1.2的exti.c文件中只设置了一个中断,现在copy过来后,再加一个中断就好了
标志位也只设了一个,我们现在再加一个
等于都要3.1.2的双份的
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"uint8_t buttonA_flag = FALSE; // 按键A按下的标志位
uint8_t buttonB_flag = FALSE; // 按键B按下的标志位void exti_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟__HAL_RCC_GPIOB_CLK_ENABLE();//按键B中断配置gpio_initstruct.Pin = GPIO_PIN_12; // 按键B对应的引脚gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉HAL_GPIO_Init(GPIOA, &gpio_initstruct);HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // 设置EXTI15_10中断线的优先级HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); // 使能中断//按键A中断配置gpio_initstruct.Pin = GPIO_PIN_5; // 按键A对应的引脚gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉HAL_GPIO_Init(GPIOB, &gpio_initstruct);HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0); // 设置EXTI9_5中断线的优先级HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); // 使能中断
}void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}void EXTI9_5_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{//delay_ms(20);if (GPIO_Pin == GPIO_PIN_12) //检测到B键按下{if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_SET)//led2_toggle();buttonB_flag = TRUE;}else if (GPIO_Pin == GPIO_PIN_5) //检测到A键按下{if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_SET)//led1_toggle();buttonA_flag = TRUE;}
}uint8_t buttonA_flag_get(void)
{uint8_t temp = buttonA_flag;buttonA_flag = FALSE;return temp;
}void buttonA_flag_set(uint8_t value)
{buttonA_flag = value;
}uint8_t buttonB_flag_get(void)
{uint8_t temp = buttonB_flag;buttonB_flag = FALSE;return temp;
}void buttonB_flag_set(uint8_t value)
{buttonB_flag = value;
}
led.c不变,补充.h文件就可尝试运行了
3.3 蜂鸣器功能
这个是最简单的,上面的会了,这个一看就懂了
beep.c
#include "beep.h"
#include "sys.h"void beep_init(void)
{GPIO_InitTypeDef gpio_initstruct;__HAL_RCC_GPIOB_CLK_ENABLE();gpio_initstruct.Pin=GPIO_PIN_8 ;gpio_initstruct.Mode=GPIO_MODE_OUTPUT_PP;gpio_initstruct.Speed=GPIO_SPEED_FREQ_HIGH;gpio_initstruct.Pull=GPIO_PULLUP;HAL_GPIO_Init(GPIOB,&gpio_initstruct);void beep_off(void);}void beep_on(void)
{HAL_GPIO_WritePin(GPIOB , GPIO_PIN_8, GPIO_PIN_RESET);}
void beep_off(void)
{HAL_GPIO_WritePin(GPIOB , GPIO_PIN_8, GPIO_PIN_SET);}
main.c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "beep.h"int main()
{HAL_Init();stm32_clock_init(RCC_PLL_MUL9);beep_init();beep_off();//while(1)
//{
// beep_on();
// delay_ms(500);
// beep_off();
// delay_ms(500);
//}}
4. 整体功能实现
4.1 思路

4.2 代码
led.c
#include "led.h"
#include "sys.h"//初始化GPIO函数
void led_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // 两个LED对应的引脚gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出gpio_initstruct.Pull = GPIO_PULLUP; // 上拉gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB, &gpio_initstruct);//关闭LEDled1_off();led2_off();
}//点亮LED1的函数
void led1_on(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}//熄灭LED1的函数
void led1_off(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}//翻转LED1状态的函数
void led1_toggle(void)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}//点亮LED2的函数
void led2_on(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 拉低LED2引脚,点亮LED2
}//熄灭LED2的函数
void led2_off(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 拉高LED2引脚,熄灭LED2
}//翻转LED2状态的函数
void led2_toggle(void)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
}
exti.c
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"uint8_t buttonA_flag = FALSE; // 按键A按下的标志位
uint8_t buttonB_flag = FALSE; // 按键B按下的标志位
uint8_t vibrate_flag = FALSE; // 检测到震动标志位void exti_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟__HAL_RCC_GPIOB_CLK_ENABLE();//按键B中断配置gpio_initstruct.Pin = GPIO_PIN_12; // 按键B对应的引脚gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉HAL_GPIO_Init(GPIOA, &gpio_initstruct);HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // 设置EXTI15_10中断线的优先级HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); // 使能中断//按键A中断配置gpio_initstruct.Pin = GPIO_PIN_5; // 按键A对应的引脚gpio_initstruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发gpio_initstruct.Pull = GPIO_PULLDOWN; // 下拉HAL_GPIO_Init(GPIOB, &gpio_initstruct);HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0); // 设置EXTI9_5中断线的优先级HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); // 使能中断//震动传感器中断配置gpio_initstruct.Pin = GPIO_PIN_4; // 震动传感器对应的引脚gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发gpio_initstruct.Pull = GPIO_PULLUP; // 上拉HAL_GPIO_Init(GPIOA, &gpio_initstruct);HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 0); // 设置EXTI0中断线的优先级HAL_NVIC_EnableIRQ(EXTI4_IRQn); // 使能中断
}void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}void EXTI9_5_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}void EXTI4_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{//delay_ms(20);if (GPIO_Pin == GPIO_PIN_12) //检测到B键按下{if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_SET)//led2_toggle();buttonB_flag = TRUE;}else if (GPIO_Pin == GPIO_PIN_5) //检测到A键按下{if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_SET)//led1_toggle();buttonA_flag = TRUE;}else if (GPIO_Pin == GPIO_PIN_4) //检测到震动{if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET)//led1_toggle();vibrate_flag = TRUE;}
}uint8_t buttonA_flag_get(void)
{uint8_t temp = buttonA_flag;buttonA_flag = FALSE;return temp;
}void buttonA_flag_set(uint8_t value)
{buttonA_flag = value;
}uint8_t buttonB_flag_get(void)
{uint8_t temp = buttonB_flag;buttonB_flag = FALSE;return temp;
}void buttonB_flag_set(uint8_t value)
{buttonB_flag = value;
}uint8_t vibrate_flag_get(void)
{uint8_t temp = vibrate_flag;vibrate_flag = FALSE;return temp;}void vibrate_flag_set(uint8_t value)
{vibrate_flag = value;
}
alarm.c
#include "alarm.h"
#include "sys.h"//初始化GPIO函数
void alarm_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_7; // 继电器对应的引脚gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出gpio_initstruct.Pull = GPIO_PULLUP; // 上拉gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB, &gpio_initstruct);//关闭LEDalarm_off();
}//闭合继电器的函数
void alarm_on(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}//松开继电器的函数
void alarm_off(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}//获取继电器状态的函数
uint8_t alarm_status_get(void)
{return (uint8_t)HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
}
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"
#include "alarm.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init(); /* 初始化LED灯 */exti_init(); /* 初始化EXTI */alarm_init(); /* 初始化继电器 */uint8_t alert_mode = FALSE; //警戒模式标志while(1){ //A按键是否按下?if(buttonA_flag_get() == TRUE){alarm_on();delay_ms(2000);alarm_off();alert_mode = TRUE;}//B按键是否按下?if(buttonB_flag_get() == TRUE){if(alarm_status_get() == ALARM_STATUS_ON)alarm_off();else{alarm_on();delay_ms(1000);alarm_off();}alert_mode = FALSE;}//如果处于警戒模式if(alert_mode == TRUE){if(vibrate_flag_get() == TRUE)alarm_on();}elsevibrate_flag_set(FALSE);}
}
————下一篇:详细学习中断————
