项目需求
- 点击遥控器 A 按键,系统进入警戒模式,一旦检测到震动(小偷偷车),则喇叭发出声响报警,吓退小偷。
- 点击遥控器 B 按键,系统退出警戒模式,再怎么摇晃系统都不会报警,否则系统一直发出尖叫,让车主尴尬。
硬件清单
震动传感器 | 继电器 | 高功率喇叭 | 433M无线接受发射模块 | 杜邦线 | 开发板 | ST-Link |
模块:震动传感器
型号介绍
有很多种型号的震动模块,801S、SW-520D、SW-420、SW-18010P 等等,它们之间大同小异。
今天我们的主角是 SW-18010P,模块介绍如下。
- SW-18010P 为密封弹簧型、无方向性震动感应触发开关,任何角度均可触发。
- 接好 VCC 和 GND,电源指示灯将被点亮。
- 当检测到震动时,开关信号指示灯点亮;震动停止,开关信号指示灯熄灭。
- 用螺丝刀转动「灵敏度调节电位器」可以调节灵敏度。可以将电位器旋到最左或最右,然后测试往哪边旋可以提高灵敏度。
工作参数和引脚介绍
震动传感器引脚 | 接STM32板上的引脚 |
SW-18010P | STM32 |
VCC | 3.3或5V |
GND | GND |
DO | 任意 GPIO 口 |
AO | 模拟输出,无效,不接 |
小实验:震动点灯
实验目的:使用中断的方法,振动传感器检测到振动时,LED1 点亮 2 秒,之后熄灭。
硬件清单:振动器、开发板、ST-Link
硬件接线:
震动传感器 | STM32 |
DO | GPIO口:PA4 |
VCC | 3.3V |
GND | GND |
- exti.h文件代码:
#include "exti.h"
#include "stm32f1xx.h"
#include "delay.h"
#include "led.h"
//定义一个标志位
uint8_t vibrate_flag = False;
void exti_init(void){
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//初始化GPIO口
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode =GPIO_MODE_IT_FALLING;
GPIO_Initstruct.Pin = GPIO_PIN_4;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Initstruct);
//在HAL_Init()函数中进行优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
//中断服务函数,
void EXTI4_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
//回调函数(GPIO口只有一个),业务代码:按键;消抖
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
//消抖,震动可忽略不计: delay_ms(20);
if(GPIO_Pin == GPIO_PIN_4){ //判断EXTI0是否是GPIO_PIN0
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4) == GPIO_PIN_RESET){//判断GPIO口是否是低电平
// led1_toggle();
//设置中断标志位
vibrate_flag = Ture;
}
}
}
//让主函数识别中断标志位,定义一个获取中断标志位的函数
uint8_t vibrate_flag_get(void){
uint8_t temp = vibrate_flag; //第一次调用这个函数,返回:True;第二次调用,返回:False
vibrate_flag = False;
return temp;
}
//设置中断标志位的值
void vibrate_flag_set(uint8_t value){
vibrate_flag = value;
}
- exti.h中的代码:
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define Ture 1
#define False 0
void exti_init(void);
uint8_t vibrate_flag_get(void);
void vibrate_flag_set(uint8_t value);
#endif
- main.c中代码:
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
exti_init();
while(1)
{
if(vibrate_flag_get() == Ture){
led1_on();
delay_ms(2000);
led1_off();
vibrate_flag_set(False);
//避免在小灯亮2s时,由于外界继续产生震动小灯一直亮,在亮2s后,清除标志位。这样小灯只亮2s。
}
}
}
模块:继电器
继电器的介绍
定义:继电器,可以被形象地比作一个开关,就像电灯开关一样,按下,电路闭合,电路通,灯亮;再按下,电路断开,电路不通,灯灭。
作用:它能够通过控制小电流来打开或关闭大电流电路,实现电路的控制和信号的传递。不过,与普通的物理开关相比,继电器具有更高的可靠性和灵活性,能够应对复杂的电路控制需求。
继电器的工作原理
继电器的分类
按照两方面分类:电压和路数。
电压分类 | 按路数分类 |
根据控制电路的工作电压来分类,如 5V、12V、24V 等继电器。
| 按路数可分为 1/2/4/8 等路数,就像一板电灯开关上可以有 1/2/4/8 个开关,每个开关可以控制各自的电灯开关。8 路继电器就可以控制 8 个电路的开关。 |
工作参数及引脚介绍
工作参数:
本篇文章使用的继电器引脚介绍:
工作示意图如下:
注意:一般输入的是3.3v电压
- 输入部分:
继电器 | 单片机/电路 | 备注 |
DC+/VCC | 电源正极 | 按继电器要求,接5v、12v、24v或其他电压 |
DC-/GND | 电源负极 | |
IN | IO口 | 可以高或低电平控制继电器吸合 |
- 输出部分
继电器 | 电路 | 备注 |
NO | 需要控制电路,与NC二选一 | 常开接口,继电器吸合前悬空,吸合后与COM短接,电路闭合 |
COM | 公共端 | 继电器公用接口 |
NC | 需要控制的电路,与 NO 二选一 | 常闭接口,继电器吸合前与COM短接,吸合后悬空,电路断开 |
小实验 :继电器定时开闭
实验目的:继电器500ms闭合,再500ms断开,如此循环
硬件:继电器、开发板、ST-Link
硬件接线:
STM32 | 继电器 |
GPIO口:PB7
|
I/O
|
3V3
|
VCC
|
GND
|
GND
|
- alarm.c文件代码:
#include "alarm.h"
#include "stm32f1xx.h" // 外设的驱动函数
//初始化GPIO口
void alarm_init(void){
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO的初始化函数
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initstruct.Pin = GPIO_PIN_7;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
//关闭继电器
alarm_off();
}
//接通继电器的函数
void alarm_on(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
}
//断开继电器的函数
void alarm_off(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
}
//获取继电器的状态
uint8_t alarm_status_get(void){
return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}
- alarm.h文件代码:
#ifndef __ALARM_H__
#define __ALARM_H__
#include "stdint.h"
//宏定义继电器的状态
#define alarm_status_on 0
#define alarm_ststus_off 1
void alarm_init(void);
void alarm_on(void);
void alarm_off(void);
uint8_t alarm_status_gets(void);
#endif
- main.c文件代码:
#include "sys.h"
#include "led.h"
#include "alarm.h"
#include "delay.h"
void led_init(void); /* LED初始化函数声明 */
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
alarm_init(); /* 继电器初始化 */
while(1)
{
alarm_on();
delay_ms(2000);
alarm_off();
delay_ms(2000);
}
}
模块:433M无线收发模块
无线通讯技术的介绍:无线通信技术在现代社会中扮演着至关重要的角色,它让我们能够实现便捷的远程控制、智能家居、自动化以及各种物联网应用。无线通信技术包括 WiFi、蓝牙、NFC、Zigbee、5G 等等。
433M无线收发模块介绍
- 定义:无线收发模块 433MHz(或称RF433射频小模块)采用高频射频技术,并结合了全数字技术和 AVR 单片机,成为一种微型收发器。它能够实现高速数据信号传输,并具备打包、检错和纠错处理的功能。
- 型号介绍
首先我们知道遥控器上有四个按键,分别对应 433M 模块的四个数据位输出脚 D0、D1、D2、D3。按下按键发射信号,对应的引脚就会输出高电平。
M点动 | T锁存 | L自锁 |
瞬态输出,相当于自复位开关的状态收到信息输出高电平,无信号时为低电平。 | 上电时为低,收到信号输出高电平并锁定高,再次收到信号输出低并锁定低。 | 上电时为低,收到信号输出高电平并锁定高,同时将其他输出置低。 |
注意:
这个模块是 M4 点动,「4」表示有4个按键,按一下按键 A,D0 将输出一个高电平。
大家购买的时候要看清楚啦,选择合适的工作模式的 433M 模块哦,它们长的都一样,但是工作模式是不能改的。
温馨小贴士:
- 天线对模块的接收效果影响很大,最好接 1/4 波长的天线,一般采用 50 欧姆单芯导线,433M 的天线的长度约为 17 cm。
- 天线位置对模块接收效果有影响,安装时,天线尽可能伸直,远离屏蔽体、高压及干扰源的地方。
工作参数以及引脚介绍
- 工作参数:
433M 模块工作参数 | 遥控器工作参数 |
|
|
- 433M模块接线参考如下:
433M 模块 | STM32 | 说明 |
GND | G | 接地 |
5V | 5V | 电源正极 |
D0 | 任意一个GPIO口 | 对应遥控器的A |
D1 | 任意一个GPIO口 | 对应遥控器的B |
D2 | 任意一个GPIO口 | 对应遥控器的C |
D3 | 任意一个GPIO口 | 对应遥控器的D |
VT | 任意一个GPIO口 | 输出状态提示,收到有效信号输出高电平 |
小实验:433M电灯实验
实验目的:按下遥控器 A 键,LED1 亮/灭;按下遥控器 B 键,LED2 亮/灭
硬件:433M发送/接收模块,上官二号,ST-Link
硬件接线:
STM32
|
433M
|
PB5
|
D0
|
PA12
|
D1
|
5V
|
5V
|
GND
|
GND
|
- exti.c代码:
#include "exti.h"
#include "stm32f1xx.h"
#include "delay.h"
#include "led.h"
uint8_t buttonA_flag = False;
uint8_t buttonB_flag = False;
void exti_init(void){
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
//初始化GPIO口,PA12接D1,PB5接D0
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode =GPIO_MODE_IT_RISING;
GPIO_Initstruct.Pin = GPIO_PIN_12;
GPIO_Initstruct.Pull = GPIO_PULLDOWN;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Initstruct);
//在HAL_Init()函数中优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
GPIO_Initstruct.Mode =GPIO_MODE_IT_RISING;
GPIO_Initstruct.Pin = GPIO_PIN_6;
GPIO_Initstruct.Pull = GPIO_PULLDOWN;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_Initstruct);
//在HAL_Init()函数中优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
//PA12的中断服务函数,
void EXTI15_10_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
//PB5的中断服务函数,
void EXTI9_5_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
//回调函数,业务代码:按键;消抖
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
//不需要消抖 delay_ms(20);
if(GPIO_Pin == GPIO_PIN_12){ //判断EXTI0是否是GPIO_PIN0
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == GPIO_PIN_SET){//判断GPIO口是否是高电平
// led2_toggle();
buttonB_flag = True;
}
}else if(GPIO_Pin == GPIO_PIN_6){
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6) == 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 buttornA_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 buttornB_flag_set(uint8_t value){
buttonB_flag = value;
}
- exti.h文件代码:
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define True 1
#define False 0
void exti_init(void);
uint8_t buttonA_flag_get(void);
void buttornA_flag_set(uint8_t value);
uint8_t buttonB_flag_get(void);
void buttornB_flag_set(uint8_t value);
#endif
- main.c文件代码:
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
exti_init();
while(1)
{
if(buttonA_flag_get() == True){
//PA12控制LED1
led1_toggle();
}
if(buttonB_flag_get() == True){
//PB5控制LED2
led2_toggle();
}
}
}
模块:高功率喇叭
高功率喇叭一般有继电器控制通断;但是本章采用蜂鸣器来替代高功率喇叭。二者的原理相同。
完结项目
接线参考如下:
433M收发模块 | 报警器(蜂鸣器) | 震动传感器 |
|
|
|
- exti.c文件代码
小结:有三个中断输出,分别是 按键A 按键B(高电平输出)和震动传感器(低电平输出),分别写上三个初始化函数,三个中断服务函数,三个中断标志位,三个获取和设置中断标志位函数。
#include "exti.h"
#include "stm32f1xx.h"
#include "delay.h"
#include "led.h"
uint8_t buttonA_flag = False;
uint8_t buttonB_flag = False;
uint8_t vibrate_flag = False;
void exti_init(void){
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
//初始化GPIO口,B键:PA12接D1,A键:PB6接D0
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode =GPIO_MODE_IT_RISING;
GPIO_Initstruct.Pin = GPIO_PIN_12;
GPIO_Initstruct.Pull = GPIO_PULLDOWN;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Initstruct);
//在HAL_Init()函数中优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
GPIO_Initstruct.Mode =GPIO_MODE_IT_RISING;
GPIO_Initstruct.Pin = GPIO_PIN_6;
GPIO_Initstruct.Pull = GPIO_PULLDOWN;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_Initstruct);
//在HAL_Init()函数中优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
//震动传感器初始化
GPIO_Initstruct.Mode =GPIO_MODE_IT_FALLING;
GPIO_Initstruct.Pin = GPIO_PIN_4;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Initstruct);
//在HAL_Init()函数中进行优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
//按键B:PA12的中断服务函数,
void EXTI15_10_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
//按键A:PB5的中断服务函数,
void EXTI9_5_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
//震动传感器的中断服务函数,
void EXTI4_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
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){ //判断EXTI0是否是GPIO_PIN0
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == GPIO_PIN_SET){//判断GPIO口是否是高电平
// led2_toggle();
buttonB_flag = True;
}
}else if(GPIO_Pin == GPIO_PIN_6){
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6) == GPIO_PIN_SET){
// led1_toggle();
buttonA_flag = True;
}
}
else if(GPIO_Pin == GPIO_PIN_4){ //判断EXTI0是否是GPIO_PIN0
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4) == GPIO_PIN_RESET){//判断GPIO口是否是低电平
// led1_toggle();
//设置中断标志位
vibrate_flag = True;
}
}
}
uint8_t buttonA_flag_get(void){ //按键A的获取中断标志位的函数
uint8_t temp = buttonA_flag;
buttonA_flag = False;
return temp;
}
void buttornA_flag_set(uint8_t value){
buttonA_flag = value;
}
uint8_t buttonB_flag_get(void){ //按键B的获取中断标志位的函数
uint8_t temp = buttonB_flag;
buttonB_flag = False;
return temp;
}
void buttornB_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;
}
- exti.h文件代码
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stdint.h"
#define True 1
#define False 0
void exti_init(void);
uint8_t buttonA_flag_get(void);
void buttornA_flag_set(uint8_t value);
uint8_t buttonB_flag_get(void);
void buttornB_flag_set(uint8_t value);
uint8_t vibrate_flag_get(void);
void vibrate_flag_set(uint8_t value);
#endif
- alarm.c文件代码
小结:报警器的触发函数, 低电平触发(和点亮LED灯函数一样)。但是要写一个获取警报器状态的函数。
#include "alarm.h"
#include "stm32f1xx.h" // 外设的驱动函数
//初始化GPIO口
void alarm_init(void){
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO的初始化函数
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initstruct.Pin = GPIO_PIN_7;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
//关闭继电器
alarm_off();
}
//接通继电器的函数
void alarm_on(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
}
//断开继电器的函数
void alarm_off(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
}
//获取继电器的状态
uint8_t alarm_status_get(void){
return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}
- alarm.h文件代码
#ifndef __ALARM_H__
#define __ALARM_H__
#include "stdint.h"
//宏定义继电器的状态
#define alarm_status_on 0
#define alarm_ststus_off 1
void alarm_init(void);
void alarm_on(void);
void alarm_off(void);
uint8_t alarm_status_get(void);
#endif
- main.c文件代码:
小结: 主函数的流程图如下
注意点:
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "exti.h"
#include "alarm.h"
/*==========接线==========
按键A:D0-PB6,按键B:D1-PA12;VCC-5v
震动传感器:PA4;VCC-3.3v
继电器/蜂鸣器:PB7,VCC-3.3V
*/
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
exti_init();
alarm_init();
uint8_t alart_mode = False;
while(1)
{
if(buttonA_flag_get() == True){
alarm_on();
delay_ms(2000);
alarm_off();
alart_mode = True;
}
if(buttonB_flag_get() == True){
if(alarm_status_get() == alarm_status_on){
//调用这个函数,是当检查到此时报警时,按下按键立马停止报警,避免解除警戒时,先响1s。
alarm_off();
}else{
alarm_on();
delay_ms(1000);
alarm_off();
}
alart_mode = False;
}
if(alart_mode == True){
if(vibrate_flag_get() == True){
//在调用这个函数时,内部有清除中断标志,避免在5s内响时,受到震动触发标位置1,会一直响
alarm_on();
delay_ms(5000);
alarm_off();
vibrate_flag_set(False); //避免当报警时,继续产生震动,使震动标志位置True,一直响,这样只响5s.
}
}else{
vibrate_flag_set(False);
//避免由于按键B按下时,震动传感器接收到震动时,震动标位置True,当按键A按下时,会造成常响。
}
}
}