一.实现的功能
本次设计的盲人监护系统,旨在为盲人的外出提供保护。主要功能如下:
超声波测距模块检测前方障碍物,当前方有障碍物时,语音模块报警提示“前方有障碍物,请绕道”,盲人在听到这条语音后就知道前面有障碍物,需要绕道而行,起到一个保护作用。
MPU6050进行姿态检测,检测到老人摔倒时,语音提示,“我已摔倒,请扶我起来”,用于提示路人,该盲人需要帮助。GPS实时检测盲人位置,并通过蓝牙发送到手机上。
功能演示视频如下:
盲人检测系统功能演示1
盲人监护系统功能演示2
二.硬件方案
1.单片机
本次使用的单片机是正点原子的精英版,板载芯片是STM32F103ZET6。
STM32F103ZET6是意法半导体(STMicroelectronics)推出的一款32位ARM Cortex-M3内核的微控制器。它具有多种外设和接口,适合于各种应用领域,包括工业控制、嵌入式系统、通信等。
STM32F103ZET6的主要特性包括:
- 32位ARM Cortex-M3内核,最高工作频率为72MHz
- 512KB闪存和64KB RAM
- 多种外设模块,如通用串行接口、SPI、I2C、CAN、定时器等
- ADC和DAC转换器,用于模拟信号采集和输出
- 多个通用输入输出引脚(GPIO)
- 多种时钟源选择和时钟管理功能
- 支持多种电源管理模式,包括低功耗模式
- 多个中断向量和中断优先级控制
STM32F103ZET6作为STM32系列微控制器的一员,具有丰富的生态系统和软件支持,包括ST官方提供的开发工具和周边库,以及广泛的第三方开发支持。它是一款功能强大的微控制器,可以满足各种复杂应用的需求。
2.超声波测距模块
超声波模块使用HCSR04,HC-SR04是一款常见的超声波测距模块,用于测量物体到传感器的距离。它包含一个超声波发射器和接收器,通过发射超声波脉冲并测量脉冲的回波时间来计算距离。
HC-SR04超声波测距模块的主要特点包括:
- 工作电压:一般为5V直流电源
- 探测范围:2cm - 400cm
- 高测量精度
- 传感器位置指示(通常为发射器和接收器)
- 方便的电气接口,如VCC、Trig、Echo和GND
- 简单易用,适合于DIY项目和嵌入式系统
使用HC-SR04模块时,一般先向Trig引脚发送一个至少10微秒的高电平触发信号,然后模块会发射一系列超声波脉冲。当声波遇到物体并被反射回来时,接收器会接收到回波信号,并输出一个高电平脉冲,其宽度代表声波往返的时间。根据声波传播速度和时间,可以计算出物体到传感器的距离。
HC-SR04广泛应用于测距、距离控制和避障等项目中。在使用HC-SR04模块时,需要结合相应的控制器或开发板进行编程和数据处理
3.语音播报模块
语音播报模块使用JR6001,它是一款常见的语音播报模块,通常用于电子设备或嵌入式系统中,可以实现文字转语音的功能。
JR6001语音播报模块的主要特点包括:
- 高质量的语音合成技术,能够产生自然、流畅的语音输出
- 提供多种控制方式,如串口控制、IO口触发等
- 可以通过外部音箱或耳机进行音频输出
使用JR6001模块时,通常需要提供要播放语音文件,并通过相应的软件进行语音合成和播放。可以通过串口或IO口发送控制指令或触发信号来实现语音播报的功能。
JR6001语音播报模块广泛应用于各种领域,如智能家居、安防系统、导航设备、机器人等。通过语音播报,可以为用户提供更直观、人性化的交互体验。
4.MPU6050
MPU6050是一款常见的六轴惯性测量单元(Inertial Measurement Unit,IMU),由意法半导体(STMicroelectronics)生产。它集成了三轴加速度计和三轴陀螺仪,用于测量和跟踪物体的加速度和旋转角速度。
MPU6050的主要特点包括:
- 高灵敏度的三轴加速度计,可测量静态加速度和动态加速度
- 高精度的三轴陀螺仪,可测量物体的旋转角速度
- 模数转换器(ADC),用于将模拟测量值转换为数字信号
- 数字接口(如I2C),用于与其他设备进行通信和数据传输
MPU6050通常用于嵌入式系统、机器人、飞行器和虚拟现实等应用中,提供姿态测量、运动跟踪和姿态控制的功能。通过读取加速度计和陀螺仪的测量值,并进行数据处理和算法计算,可以获得物体的角度、倾斜、方向等运动信息。
在使用MPU6050时,需要通过相应的接口(如I2C)连接到主控制器,并编写代码来控制和读取测量值。此外,可能还需要进行校准和滤波等处理,以获得更准确和稳定的测量结果。
5.GPS模块
ATGM336H是一款常见的全球定位系统(Global Positioning System,GPS)模块,由安信可(Antenova)公司生产。它基于GPS技术,可以提供准确的位置、速度和时间信息。
ATGM336H的主要特点包括:
- 支持GPS、北斗(BeiDou)、伽利略(Galileo)和格洛纳斯(GLONASS)等多个卫星系统,以提供更广泛的定位覆盖
- 高灵敏度和可靠性,能够在复杂的环境条件下实现稳定的定位性能
- 小型封装和低功耗设计,适合嵌入式应用和便携设备
- 提供UART等串口接口,方便与主控制器进行通信和数据交换
- 具有内建的导航和定位算法,可实现高精度定位和导航功能
使用ATGM336H模块时,需要将其连接到主控制器(如微控制器或单片机)并通过串口进行通信。主控制器可以发送指令给模块,以配置工作模式、请求定位信息或获取导航数据。模块将接收卫星信号,并利用内部的定位算法计算出准确的位置、速度和时间信息,然后通过串口返回给主控制器。
ATGM336H模块广泛应用于车载导航、物流追踪、航空航海、智能交通和无人机等领域。其中,定位和导航功能对于航空航海、车辆追踪和导航系统非常重要。
6.蓝牙模块
蓝牙模块HC08 (或称为BLE_HC08) 是一款基于蓝牙低功耗(Bluetooth Low Energy, BLE)技术的模块。HC08蓝牙模块通常用于连接和通信。
使用HC08蓝牙模块时,你可以通过主控制器与模块进行串口通信,通过发送指令或数据来控制模块的功能。可以配置模块的蓝牙相关参数,如名称、服务和特性等。模块可作为中心设备或周边设备,与其他蓝牙设备进行数据传输、控制和通信。
HC08蓝牙模块广泛应用于物联网、智能家居、健康与医疗设备、传感器网络和可穿戴设备等领域。通过蓝牙连接,可以实现设备之间的无线通信和控制,方便设备之间的数据交换和协同工作。
三.软件方案
接线方式:
/*
接线方式: 蓝牙--->串口2
语音模块--->串口3
GPS模块--->串口1
超声波模块: TRIG--->PA8 Echo--->PA0
MPU6050 SCL-->PB3 MPU6050 SDA--->PB4
*/
1.超声波测距
该模块的工作时序图如下:
超声波模块HCSR04利用超声波的传播速度和返回时间的差值来进行距离测量。它由发送模块和接收模块组成,工作过程如下:
1. 发送超声波脉冲:发送模块向外发出一个超声波脉冲信号,该脉冲信号以一定频率发射,一般为40kHz。
2. 接收超声波信号:接收模块开始等待接收反射回来的超声波信号。
3. 接收到反射信号:超声波脉冲遇到物体后,会被物体反射回来,接收模块接收到这个反射信号。
4. 计算距离:根据发送和接收的时间差,可以计算出物体与传感器的距离。
测距原理如下:
1. 发送模块发出超声波脉冲后,开始计时。
2. 接收模块接收到反射信号后,停止计时。
3. 通过测量发送和接收的时间差,可以得到超声波从传感器发射到物体反射回来所经过的时间。
4. 根据声速传播的公式 v = d / t ,其中 v 为声速,d 为物体到传感器的距离,t 为时间差,可以求得物体与传感器的距离。
使用stm32的PWM输出功能以及捕获功能,就可以顺利地得到距离数值。
2.语音播报
语音播报模块JR6001是使用串口控制的,有很多控制指令,比如:播放下一曲,播放上一曲,控制音量,播放指定曲目等等。使用前要使用语音合成软件,将文本转换为语音,且必须为MP3格式的文件(可以使用格式转换工具将WAV格式转换为MP3格式),我用的合成及转换工具如下:
得到MP3文件后,存放在根目录下面,命名好。直接使用A7指令就可以播报指定的文件:
相应代码如下:
3.MPU6050测倾角
该模块有八个引脚,各引脚功能如下:
模块使用IIC协议,需要了解的引脚有:SCL和SDA是连接MCU的IIC接口,MCU通过这个IIC接口来控制MPU6050,另外还有一个IIC接口:XCL和XDA,这个接口可用来连外部从设备比如磁力计,这样就可以组成一个九轴传感器。AD0是从IIC接口(接MCU)的地址控制引脚,该引脚控制的是IIC地址的最低位。如果接GND,则MPU6050的IIC地址是:0X68,如果接VDD,则是0X69。需要注意的是:这里的地址0x68和0x69是不包含用于数据传输的最低位的,因此并不是八位数据,如0x68表示的是1101000,0x69表示的则是1101001,通常最低位用于表示IIC主机的读取数据/写数据模式。
详细的驱动代码直接使用厂家提供的即可!!!(当测量的俯仰角和翻滚角过大时,判断为盲人摔倒)
4.GPS获取位置信息
ATGM336H定位模块支持GPS系统,BDS(北斗)系统,GLONASS(俄罗斯)系统,伽利略卫星导航系统(欧盟)。这个模块要拿到室外才能接收到信号,且初次初始化或者隔太久时间没有启用会导致获取定位信息的时间很长。
测试最好是带电脑到户外空旷地进行,若是把天线放在阳台外面的话,有一定几率定位失败,这个受楼距,遮挡物等因素影响。空旷地首次定位一般是一分钟以内。板载LED 保持一定的频率闪烁证明定位成功了,我们用串口来看下数据,波特率默认是9600.
其数据格式如下:
串口接收数据倒是不难,重点难在数据解析。这里就不展开讲,直接放在代码里面了!!
5.蓝牙模块
蓝牙模块其实就是一个无线串口,直接当串口用就行,没什么难度的!
6.总程序
总的主程序如下:
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "mpu6050.h"
#include "usmart.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "timer.h"
#include "math.h"
#include "beep.h"
/*
接线方式: 蓝牙--->串口2
语音模块--->串口3
GPS模块--->串口1
超声波模块: TRIG--->PA8 Echo--->PA0
MPU6050 SCL-->PB3 MPU6050 SDA--->PB4
*/
extern u8 TIM2CH1_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH1_CAPTURE_VAL; //输入捕获值
//声明
void errorLog(int num);
void parseGpsBuffer(void);
void printGpsBuffer(void);
//串口1发送1个字符
//c:要发送的字符
void usart1_send_char(u8 c)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART1,c);
}
void usart3_send(u8 *c)
{
u8 i=0;
while(*(c+i)!='\0')
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART3,*(c+i));
i++;
}
}
int main(void)
{
u8 t=0;
u32 distance=0;
float pitch,roll,yaw; //欧拉角
short aacx,aacy,aacz; //加速度传感器原始数据
short gyrox,gyroy,gyroz; //陀螺仪原始数据
short temp; //中间量
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(9600); //串口初始化为9600
USART2_init(9600); //串口2初始化函数
USART3_init(9600);
clrStruct();
delay_init(); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //初始化按键
BEEP_Init(); //初始化蜂鸣器端口
TIM1_PWM_Init(899,0); //不分频。PWM频率=72000/(899+1)=80Khz
TIM2_Cap_Init(0XFFFF,72-1); //以1Mhz的频率计数
LCD_Init(); //初始化LCD
MPU_Init(); //初始化MPU6050
POINT_COLOR=RED; //设置字体为红色
while(mpu_dmp_init())
{
LCD_ShowString(30,130,200,16,16,"MPU6050 Error");
delay_ms(200);
LCD_Fill(30,130,239,130+16,WHITE);
delay_ms(200);
}
LCD_ShowString(30,160,200,16,16,"distance: CM");
LCD_ShowString(30,200,200,16,16,"MPU6050 OK");
LCD_ShowString(30,220,200,16,16,"Pitch: . C");
LCD_ShowString(30,240,200,16,16," Roll: . C");
LCD_ShowString(30,260,200,16,16," Yaw : . C");
while(1)
{
parseGpsBuffer();
printGpsBuffer();
// PAout(8)=!PAout(8);
TIM_SetCompare1(TIM1,720); //在超声波模块的控制端TRIG产生一个10us的高电平
if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
{
temp=TIM2CH1_CAPTURE_STA&0X3F;
temp*=65536; //溢出时间总和
temp+=TIM2CH1_CAPTURE_VAL; //得到总的高电平时间 单位 us
distance=((temp/10000.0)*340)/2;
TIM2CH1_CAPTURE_STA=0; //开启下一次捕获
}
LCD_ShowNum(30+80,160,distance,3,16); //显示温度
if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0)
{
MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺仪数据
if((t%10)==0)
{
temp=pitch*10;
if(temp<0)
{
LCD_ShowChar(30+48,220,'-',16,0); //显示负号
temp=-temp; //转为正数
}else LCD_ShowChar(30+48,220,' ',16,0); //去掉负号
LCD_ShowNum(30+48+8,220,temp/10,3,16); //显示整数部分
LCD_ShowNum(30+48+40,220,temp%10,1,16); //显示小数部分
temp=roll*10;
if(temp<0)
{
LCD_ShowChar(30+48,240,'-',16,0); //显示负号
temp=-temp; //转为正数
}else LCD_ShowChar(30+48,240,' ',16,0); //去掉负号
LCD_ShowNum(30+48+8,240,temp/10,3,16); //显示整数部分
LCD_ShowNum(30+48+40,240,temp%10,1,16); //显示小数部分
temp=yaw*10;
if(temp<0)
{
LCD_ShowChar(30+48,260,'-',16,0); //显示负号
temp=-temp; //转为正数
}else LCD_ShowChar(30+48,260,' ',16,0); //去掉负号
LCD_ShowNum(30+48+8,260,temp/10,3,16); //显示整数部分
LCD_ShowNum(30+48+40,260,temp%10,1,16); //显示小数部分
t=0;
if(abs(pitch)>45 || abs(roll)>45)
{
LED1=!LED1; //判断为摔倒
usart3_send("A7:00001");
}
if(distance<15)
{
usart3_send("A7:00002"); //判断为有障碍物
BEEP=1; //蜂鸣器响
}
else
{
BEEP=0; //蜂鸣器不响
}
LED0=!LED0;//LED闪烁
}
}
t++;
}
}
void errorLog(int num)
{
while (1)
{
printf("ERROR%d\r\n",num);
}
}
void parseGpsBuffer()
{
char *subString;
char *subStringNext;
char i = 0;
if (Save_Data.isGetData)
{
Save_Data.isGetData = false;
printf("**************\r\n");
printf(Save_Data.GPS_Buffer);
for (i = 0 ; i <= 6 ; i++)
{
if (i == 0)
{
if ((subString = strstr(Save_Data.GPS_Buffer, ",")) == NULL)
errorLog(1); //解析错误
}
else
{
subString++;
if ((subStringNext = strstr(subString, ",")) != NULL)
{
char usefullBuffer[2];
switch(i)
{
case 1:memcpy(Save_Data.UTCTime, subString, subStringNext - subString);break; //获取UTC时间
case 2:memcpy(usefullBuffer, subString, subStringNext - subString);break; //获取UTC时间
case 3:memcpy(Save_Data.latitude, subString, subStringNext - subString);break; //获取纬度信息
case 4:memcpy(Save_Data.N_S, subString, subStringNext - subString);break; //获取N/S
case 5:memcpy(Save_Data.longitude, subString, subStringNext - subString);break; //获取经度信息
case 6:memcpy(Save_Data.E_W, subString, subStringNext - subString);break; //获取E/W
default:break;
}
subString = subStringNext;
Save_Data.isParseData = true;
if(usefullBuffer[0] == 'A')
Save_Data.isUsefull = true;
else if(usefullBuffer[0] == 'V')
Save_Data.isUsefull = false;
}
else
{
errorLog(2); //解析错误
}
}
}
}
}
void printGpsBuffer()
{
if (Save_Data.isParseData)
{
Save_Data.isParseData = false;
LCD_ShowString(30,20,200,16,16,Save_Data.UTCTime);
if(Save_Data.isUsefull)
{
Save_Data.isUsefull = false;
LCD_ShowString(30,40,200,16,16,Save_Data.latitude);
usart2_send_data((u8 *)Save_Data.latitude, 2);
LCD_ShowString(30,60,200,16,16,Save_Data.N_S);
usart2_send_data((u8 *)Save_Data.N_S, 1);
LCD_ShowString(30,80,200,16,16,Save_Data.longitude);
usart2_send_data((u8 *)Save_Data.longitude, 3);
LCD_ShowString(30,100,200,16,16,Save_Data.E_W);
usart2_send_data((u8 *)Save_Data.E_W, 1);
LCD_ShowString(30,120,200,16,16,"GPS DATA is usefull!");
}
else
{
LCD_ShowString(30,120,200,16,16,"GPS DATA is not usefull!");
}
}
}
四.实物图