基于STM32单片机智能手表GSM短信上报GPS定位温湿度检测记步设计
- 1、项目简介
- 1.1 系统功能
- 1.2 演示视频
- 2、部分电路设计
- 2.1 STM32单片机核心板电路设计
- 2.2 UBLOX模块 NEO-6M GPS定位电路设计
- 2.3 ADXL345倾角传感器模块电路设计
- 2.4 SIM900系列短信模块电路
- 2.5 LCD1602液晶显示模块电路设计
- 3、单片机代码展示
- 3.1 系统初始化
- 3.2 LCD1602显示程序
- 3.3 GPS程序
- 3.4 adxl345程序
1、项目简介
- 系统构成:
- 本设计由STM32单片机+LCD1602液晶显示电路+GPS模块+GSM模块+按键电路+LED灯电路+ADXL345记步电路+DHT11温湿度检测电路+电源电路组成。
1.1 系统功能
- 功能介绍:
- 1、液晶1602显示时钟,带日期,且可以显示经纬度信息,两个信息通过一个按键切换显示。
- 2、通过GPS定位,获取当前的经纬度信息(可以通过浏览器查看具体位置)。
- 3、通过电位器可以调节液晶屏的清晰度。
- 4、具有手电筒功能,通过另外一个按键,按下,灯亮,再次按下,灯灭。
- 5、通过第3个按键来发送经纬度信息给指定手机,按下后,开始发短信。
- 6、短信处理过程有指示灯显示。
- 7、接受短信的手机号码可以主动获取经纬度信息,发送"GET"给设备手机号,设备会回复信息给发送者。
- 8、通过屏幕显示温湿度数据、走过的步数
1.2 演示视频
2、部分电路设计
2.1 STM32单片机核心板电路设计
- 基于 ARM Cortex-M3内核的STM32F1系列单片机属于主流STM32单片机,其中增强型STM32F103子系列单片机的CPU 主频高达72MHz,片内Flash容量高达1MB,芯片引脚数量多达144个,有 QFN、LQFP、CSP、BGA 等多种芯片封装形式,并具有多种片内外设、USB接口和CAN 接口。根据STM32F103单片机片内Flash容量的不同,ST 公司将其分为小容量(16-32KB)、中等容量(64-128KB)、大容量(256KB-1MB)3种。
- 电源电路:为单片机提供稳定的工作电压,通常采用3.3V电源供电。电源电路的设计要保证单片机在不同工作条件下都能获得稳定的电压输出,以确保单片机的正常工作。
- 晶振电路:提供单片机工作所需的时钟信号。晶振电路通过晶振和电容组成,为单片机提供稳定的工作脉冲,确保单片机的定时和同步需求。
- 复位电路:实现单片机的复位功能,类似于电脑的重启。复位电路通过电容和电阻的配合,实现单片机在上电启动时的自动复位,以及通过手动按键实现复位功能,保证单片机在程序跑飞或异常情况下能够重新开始执行程序。
STM32单片机是一种功能强大、易于使用、灵活且可靠的32位微控制器,基于ARM Cortex™-M内核。其主要功能特点包括:
- 高性能和低功耗:STM32系列单片机提供多种内核选择,如Cortex-M0、Cortex-M3、Cortex-M4等,满足不同应用场景对性能的需求,同时保持低功耗特性。
- 多种通信和外设接口:STM32单片机具备广泛的通信和外设接口,如I2C、SPI、USART、USB等,便于开发者实现各种复杂功能。
- 易于开发和调试:STM32单片机提供了丰富的软件和硬件工具,如HAL库、CubeMX等,帮助开发者快速创建和调试嵌入式系统。
- 高集成度和设计灵活性:STM32系列单片机全系列产品共用大部分引脚、软件和外设,优异的兼容性为开发人员带来最大的设计灵活性
单片机最小系统原理图如下图所示:
实物图:
2.2 UBLOX模块 NEO-6M GPS定位电路设计
- 本系统使用的GPS模块均具有高灵敏度、低功耗、小型化、其极高追踪灵敏度大大扩大了其定位的 覆盖面, 在普通GPS 接收模块不能定位的地方, 如狭窄都市天空下、密集的丛林环境, 本系统模块都能高精度定位。模块的高灵敏度、小静态漂移、低功耗及轻巧的体积,适用于车载、手持设备如 PDA,车辆监控、手机、摄像机及其他移动定位系统的应用,是GPS产品应用的最佳选择。
- GPS模块有50个通道的u-blox引擎,1百多万个有效相关器,热启动和辅助启动首次定位时间小于1秒。-162dBm的SuperSense捕获和跟踪灵敏度,具备KickStart功能,型号微弱时可实现加速启动。支持AssistNow Online和AssistNow Offline等A-GPS服务。GPS、GALILEO、SBAS(WAAS、EGNOS、MSAS、GAGAN)混合引擎。
其具体电路原理图如下图所示:
实物图:
2.3 ADXL345倾角传感器模块电路设计
- 本设计选择倾角传感器ADXL345模块实时检测相关的状态信息。ADXL345是一款小而薄的超低功耗3轴加速度计,分辨率高(13位),测量范围达± 16g。
- 数字输出数据为16位二进制补码格式,可通过SPI(3线或4线)或I2C数字接口访问。ADXL345非常适合移动设备应用。它可以在倾斜检测应用中测量静态重力加速度,还可以测量运动或冲击导致的动态加速度。其高分辨率(3.9mg/LSB),能够测量不到1.0°的倾斜角度变化。
- 该器件提供多种特殊检测功能。活动和非活动检测功能通过比较任意轴上的加速度与用户设置的阈值来检测有无运动发生。敲击检测功能可以检测任意方向的单振和双振动作。自由落体检测功能可以检测器件是否正在掉落。这些功能可以独立映射到两个中断输出引脚中的一个。
电路原理图:
- 实物图:
2.4 SIM900系列短信模块电路
- GSM模块,是将GSM射频芯片、基带处理芯片、存储器、功放器件等集成在一块线路板上,具有独立的操作系统、GSM射频处理、基带处理并提供标准接口的功能模块。GSM模块具有发送SMS短信,语音通话,GPRS数据传输等基于GSM网络进行通信的所有基本功能。简单来讲,GSM模块加上键盘、显示屏和电池,就是一部手机。
- 工作频段:GSM900/1800MHz。硬件设计在官方设计上加以优化,采用高效开关电源供电,SIM卡采用主流的卡座,质量更坚固。SIM900系列模块在外围硬件的设计上参考了SIM900系列 模块的硬件设计指南,对板子的电源、保护和抗干扰上做足了功夫。电源采用开关电源模块供电,电源利用效率高。
其具体电路原理图如下图所示:
- 实物图:
2.5 LCD1602液晶显示模块电路设计
- LCD显示器分为字段显示和字符显示两种。其中字段显示与LED显示相似,只要送对应的信号到相应的管脚就能显示。字符显示是根据需要显示基本字符。本设计采用的是字符型显示。系统中采用LCD1602作为显示器件输出信息。与传统的LED数码管显示器件相比,液晶显示模块具有体积小、功耗低、显示内容丰富等优点,而且不需要外加驱动电路。
其具体电路原理图如下图所示:
实物图:
3、单片机代码展示
3.1 系统初始化
void HardWare_Init(void)
{
delay_init(); //延时函数初始化
uart_init(115200); //串口初始化为115200
uart2_init(9600) ;
TIM3_Int_Init(499,7199);//50ms
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init();
Lcd_GPIO_init(); //初始化lcd 接口
Lcd_Init(); //初始化函数
delay_ms(200);
Init_ADXL345();
}
3.2 LCD1602显示程序
/*--------------------------------------------------
函数说明:写命令到液晶
---------------------------------------------------*/
void LCD_write_cmd(unsigned char cmd)
{
CLR_RS();
LCD_Write_half_byte(cmd >> 4);
LCD_Write_half_byte(cmd);
delay (10000);
}
/*--------------------------------------------------
函数说明:写数据到液晶
---------------------------------------------------*/
void LCD_write_data(unsigned char w_data)
{
SET_RS();
LCD_Write_half_byte(w_data >> 4);
LCD_Write_half_byte(w_data);
delay (10000);
}
/*--------------------------------------------------
函数说明:写4bit到液晶
--------------------------------------------------*/
void LCD_Write_half_byte(unsigned char half_byte)
{
if (half_byte&0x01)
GPIO_SetBits(GPIOB,GPIO_Pin_8);
else
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
if (half_byte&0x02)
GPIO_SetBits(GPIOB,GPIO_Pin_5);
else
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
if (half_byte&0x04)
GPIO_SetBits(GPIOB,GPIO_Pin_6);
else
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
if (half_byte&0x08)
GPIO_SetBits(GPIOB,GPIO_Pin_7);
else
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
SET_EN();
delay(2000);
CLR_EN();
delay(2000);
}
/*----------------------------------------------------
LCD_set_xy : 设置LCD显示的起始位置
输入参数:x、y : 显示字符串的位置,X:1-16,Y:1-2
-----------------------------------------------------*/
void LCD_set_xy( unsigned char x, unsigned char y )
{
unsigned char address;
if (y==1)
{
address=0x80-1+x;
}
else
{
address=0xc0-1+x;
}
LCD_write_cmd(address);
}
/*---------------------------------------------------
LCD_write_string : 英文字符串显示函数
输入参数:*s :英文字符串指针;
X、Y : 显示字符串的位置
---------------------------------------------------*/
void LCD_write_string(unsigned char X,unsigned char Y,unsigned char *s)
{
LCD_set_xy( X, Y );
while (*s)
{
LCD_write_data(*s);
s++;
}
}
void show_str(unsigned char *s)
{ while(*s != '\0')
{
LCD_write_data(*s);
s++;
}
}
void Lcd_Display_22Date(unsigned char line,unsigned char list,unsigned int Date)//用于DS1302显示
{
// unsigned char i;
unsigned char ta[]={0,0,'\0'}; //????????
switch(line){
case 1: LCD_write_cmd(0x80+list);break; //???
case 2: LCD_write_cmd(0xC0+list);break; //???
default : break;
}
ta[0]=Date/16+48;
ta[1]=Date%16+48;
show_str(ta);
}
void Lcd_Display_2Date(unsigned char line,unsigned char list,unsigned int Date)
{
unsigned char i;
unsigned char ta[]={0,0,'\0'}; //????????
switch(line){
case 1: LCD_write_cmd(0x80+list);break; //???
case 2: LCD_write_cmd(0xC0+list);break; //???
default : break;
}
for(i=2;i>0;i--)
{ //??????????????ta
ta[i-1]=Date%10+48;
Date=Date/10;
}
show_str(ta);
}
void Lcd_Display_3Date(unsigned char line,unsigned char list,unsigned int Date)
{
unsigned char i;
unsigned char ta[]={0,0,0,'\0'}; //????????
switch(line){
case 1: LCD_write_cmd(0x80+list);break; //???
case 2: LCD_write_cmd(0xC0+list);break; //???
default : break;
}
for(i=3;i>0;i--)
{ //??????????????ta
ta[i-1]=Date%10+48;
Date=Date/10;
}
show_str(ta);
}
3.3 GPS程序
void dealGps(void)
{
seco_Beijing=(gps_infor_time[4]-0x30)*10+(gps_infor_time[5]-0x30); //提取时间 秒
minu_Beijing=(gps_infor_time[2]-0x30)*10+(gps_infor_time[3]-0x30);//提取时间 分
hour_Beijing=((gps_infor_time[0]-0x30)*10+(gps_infor_time[1]-0x30))+8;//提取时间 小时
days_Beijing=(gps_infor_date[0]-0x30)*10+(gps_infor_date[1]-0x30);//提取时间 天
mont_Beijing=(gps_infor_date[2]-0x30)*10+(gps_infor_date[3]-0x30);//提取时间 月
year_Beijing=(gps_infor_date[4]-0x30)*10+(gps_infor_date[5]-0x30);//提取时间 年
if(hour_Beijing>=24) //北京时间转换
{
hour_Beijing=hour_Beijing%24;
days_Beijing=days_Beijing+1;
if(year_Beijing%4==0)
{
if(days_Beijing>=monthrun_table[mont_Beijing])//天数超过该月份
{
days_Beijing=1;
mont_Beijing++;
if(mont_Beijing>=12)
{
mont_Beijing=1;
year_Beijing++;
}
}
}
if(year_Beijing%4!=0)
{
if(days_Beijing>=monthmon_table[mont_Beijing])//是否夸年
{
days_Beijing=1;
mont_Beijing++;
if(mont_Beijing>=12)
{
mont_Beijing=1;
year_Beijing++;
}
}
}
}
sprintf(dis0,"20%02d-%02d-%02d ",(u16)year_Beijing,(u16)mont_Beijing,(u16)days_Beijing);//打印日期
sprintf(dis1,"%02d:%02d:%02d ",(u16)hour_Beijing,(u16)minu_Beijing,(u16)seco_Beijing);//打印时间
Mid_Du=(gps_infor_weijing[0]-0x30)*10000000+(gps_infor_weijing[1]-0x30)*1000000; //处理经度扩大10000000
Mid_Fen=(gps_infor_weijing[2]-0x30)*10000000+(gps_infor_weijing[3]-0x30)*1000000+
(gps_infor_weijing[4]-0x30)*100000+(gps_infor_weijing[5]-0x30)*10000+
(gps_infor_weijing[6]-0x30)*1000+(gps_infor_weijing[7]-0x30)*100;
Mid_Fen=Mid_Fen/60; //分秒换算为小数位
Mid_Vale=Mid_Du+Mid_Fen; //最终为度格式000.00000000 非度分秒格式
Lin0_No[0]='N';
Lin0_No[1]=':';
Lin0_No[2]='0';
Lin0_No[3]=Mid_Vale/10000000+0x30; //转化为字符
Lin0_No[4]=(Mid_Vale/1000000)%10+0x30;
Lin0_No[5]='.';
Lin0_No[6]=(Mid_Vale/100000)%10+0x30;
Lin0_No[7]=(Mid_Vale/10000)%10+0x30;
Lin0_No[8]=(Mid_Vale/1000)%10+0x30;
Lin0_No[9]=(Mid_Vale/100)%10+0x30;
Lin0_No[10]=(Mid_Vale/10)%10+0x30;
Lin0_No[11]=Mid_Vale%10+0x30;
Mid_Du=(gps_infor_weijing[8]-0x30)*100000000+(gps_infor_weijing[9]-0x30)*10000000+(gps_infor_weijing[10]-0x30)*1000000; //处理经度扩大10000000
Mid_Fen=(gps_infor_weijing[11]-0x30)*10000000+(gps_infor_weijing[12]-0x30)*1000000+
(gps_infor_weijing[13]-0x30)*100000+(gps_infor_weijing[14]-0x30)*10000+
(gps_infor_weijing[15]-0x30)*1000+(gps_infor_weijing[16]-0x30)*100;
Mid_Fen=Mid_Fen/60; //分秒换算为小数位
Mid_Vale=Mid_Du+Mid_Fen; //最终为度格式000.00000000 非度分秒格式
Lin1_Ea[0]='E';
Lin1_Ea[1]=':';
Lin1_Ea[2]=Mid_Vale/100000000+0x30; //转化为字符
Lin1_Ea[3]=(Mid_Vale/10000000)%10+0x30;
Lin1_Ea[4]=(Mid_Vale/1000000)%10+0x30;
Lin1_Ea[5]='.';
Lin1_Ea[6]=(Mid_Vale/100000)%10+0x30;
Lin1_Ea[7]=(Mid_Vale/10000)%10+0x30;
Lin1_Ea[8]=(Mid_Vale/1000)%10+0x30;
Lin1_Ea[9]=(Mid_Vale/100)%10+0x30;
Lin1_Ea[10]=(Mid_Vale/10)%10+0x30;
Lin1_Ea[11]=Mid_Vale%10+0x30;
}
3.4 adxl345程序
/**************************************
停止信号
**************************************/
void ADXL345_Stop(void)
{
SCL_OUT();
SDA_OUT();
CLE_SDA();//SDA = 0; //拉低数据线
SET_SCL();//SCL = 1; //拉高时钟线
delay_us(2);//Delay5us(); //延时
SET_SDA();//SDA = 1; //产生上升沿
delay_us(2);//Delay5us(); //延时
CLE_SCL();
}
/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void ADXL345_SendACK(uchar ack)
{
SCL_OUT();
SDA_OUT();
if(ack==0)//SDA = ack; //写应答信号
{
CLE_SDA();
}
else
{
SET_SDA();
}
SET_SCL();//SCL = 1; //拉高时钟线
delay_us(2);//Delay5us(); //延时
CLE_SCL();//SCL = 0; //拉低时钟线
delay_us(5);//Delay5us(); //延时
}
/**************************************
接收应答信号
**************************************/
uchar ADXL345_RecvACK(void)
{
SDA_INT();
SCL_OUT();
SET_SCL();//SCL = 1; //拉高时钟线
delay_us(2);// Delay5us(); //延时
SET_SCL();
if(SDA_VAL()== Bit_SET) //CY = SDA; //读应答信号
{
err = 1;
}
else
{
err = 0;
}
CLE_SCL() ;//SCL = 0; //拉低时钟线
delay_us(5);// Delay5us(); //延时
SDA_OUT();
return err;
}
/**************************************
向IIC总线发送一个字节数据
**************************************/
void ADXL345_SendByte(unsigned char dat)
{
unsigned char i;
SCL_OUT();
SDA_OUT();
for (i=0; i<8; i++) //8位计数器
{
delay_us(5); //延时
if(dat&0x80) //SDA = CY; //送数据口
{SET_SDA();}
else
{CLE_SDA();}
delay_us(5); //延时
SET_SCL();//SCL = 1; //拉高时钟线
delay_us(5); //延时
CLE_SCL();//SCL = 0; //拉低时钟线
dat <<= 1; //移出数据的最高位
}
ADXL345_RecvACK();
}
/**************************************
从IIC总线接收一个字节数据
**************************************/
unsigned char ADXL345_RecvByte(void)
{
unsigned char i;
unsigned char Mid;
unsigned char dat = 0;
SDA_INT();
SCL_OUT();
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
delay_us(5); //延时
SET_SCL();
if(SDA_VAL()== Bit_SET) //CY = SDA; //读应答信号
{
Mid = 1;
}
else
{
Mid = 0;
}
// Mid=SDA_VAL();
if(Mid) dat++;
delay_us(5);
CLE_SCL();//SCL = 0; //拉低时钟线
}
return dat;
}
//******单字节写入*******************************************
void Single_Write_ADXL345(uchar REG_Address,uchar REG_data)
{
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf22页
ADXL345_SendByte(REG_data); //内部寄存器数据,请参考中文pdf22页
ADXL345_Stop(); //发送停止信号
}
//********单字节读取*****************************************
uchar Single_Read_ADXL345(uchar REG_Address)
{ uchar REG_data;
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(REG_Address); //发送存储单元地址,从0开始
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=ADXL345_RecvByte(); //读出寄存器数据
ADXL345_SendACK(1);
ADXL345_Stop(); //停止信号
return REG_data;
}
//*********************************************************
//
//连续读出ADXL345内部加速度数据,地址范围0x32~0x37
//
//*********************************************************
void Multiple_Read_ADXL345(void)
{ uchar i;
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(0x32); //发送存储单元地址,从0x32开始
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress+1); //发送设备地址+读信号
for (i=0; i<6; i++) //连续读取6个地址数据,存储中BUF
{
BUF[i] = ADXL345_RecvByte(); //BUF[0]存储0x32地址中的数据
if (i == 5)
{
ADXL345_SendACK(1); //最后一个数据需要回NOACK
}
else
{
ADXL345_SendACK(0); //回应ACK
}
}
ADXL345_Stop(); //停止信号
delay_ms(5);
}
//*****************************************************************
//初始化ADXL345,根据需要请参考pdf进行修改************************
void Init_ADXL345(void)
{
Single_Write_ADXL345(0x31,0x0B); //测量范围,正负16g,13位模式
Single_Write_ADXL345(0x2C,0x08); //速率设定为12.5 参考pdf13页
Single_Write_ADXL345(0x2D,0x08); //选择电源模式 参考pdf24页
Single_Write_ADXL345(0x2E,0x80); //使能 DATA_READY 中断
Single_Write_ADXL345(0x1E,0x00); //X 偏移量 根据测试传感器的状态写入pdf29页
Single_Write_ADXL345(0x1F,0x00); //Y 偏移量 根据测试传感器的状态写入pdf29页
Single_Write_ADXL345(0x20,0x05); //Z 偏移量 根据测试传感器的状态写入pdf29页
}