标题:
一个基于STC12单片机的无线运动监测器设计 含源码与视频
[打印本页]
作者:
sanshuige
时间:
2018-6-25 15:53
标题:
一个基于STC12单片机的无线运动监测器设计 含源码与视频
毕设结束了,本来是想搞一个简单的计步器的,这功能加的太多给变成运动监测器了。使用2个STC12C5A60S2,4个HC05,还有NEO-7N,PULSE SENSOR,BMP180,ADXL345以及简单的1602和几个独立按键,实现的功能相当全面,具体如下:
有效检测人体的运动状态信息,记录行动步数,并且能够显示心率,具体定位信息(经纬度,时间,日期,海拔高度,运动速度),温度,气压。大体原理是:
通过蓝牙由一个发送单片机将记录的经过处理的数据(步数信息和心率信息)无线发送,接收单片机一方面通过蓝牙无线接收(发送单片机发送的数据),另一方面通过蓝牙无线接收
GPS
的数据。并且将数据在
LCD1602
上显示,独立按键在接收单片机上负责
LCD1602
显示翻页的功能。
正常本科的毕业设计应该不会用到如此多的功能,选其中几个功能实现一下就可以了,反正就是能混则混。具体的程序,原理图还有视频我会贴出来的。视频我发个百度网盘的链接吧
https://pan.baidu.com/s/1rQXeFTQxPRO0yWu5yOeLZQ [qq]1851215323[/qq]
单片机源程序:
#include <stc12c5a60s2.h>
#include <math.h>
#include <stdio.h>
#include <INTRINS.H>
#define uchar unsigned char
#define uint unsigned int
//**************************************
#define false 0
#define true 1
#define FOSC 11059200L //系统时钟
#define BAUD 115200 //波特率
#define T0MS (65536-FOSC/12/500) //500HZ在12T模式下
//**************************************
#define ADC_POWER 0x80 //ADC功率控制位
#define ADC_FLAG 0x10 //ADC完成标志
#define ADC_START 0x08; //ADC开始控制位
#define ADC_SPEEDLL 0x00 //转换速度控制位SPEED0和SPEED1,共四种状态,对应四种转换速度
#define ADC_SPEEDL 0x20
#define ADC_SPEEDH 0x40
#define ADC_SPEEDHH 0x60
#define ADC_MASK 0x01
//**************************************
sbit SCL=P1^1; //IIC时钟引脚定义
sbit SDA=P1^2; //IIC数据引脚定义
//**************************************
#define SlaveAddress 0xA6 //定义器件在IIC总线中的从地址,根据ALT ADDRESS地址引脚不同修改
//ALT ADDRESS引脚接地时地址为0xA6,接电源时地址为0x3A
typedef unsigned char BYTE;
typedef unsigned short WORD;
#define length 10
uchar buffer[length];
unsigned char ADXL345_FLAG=0;
unsigned char START_FLAG=0;
unsigned char number=0;
unsigned char idata bad_flag[3];
unsigned int idata array0[3]={1,1,1};
unsigned int idata array1[3]={1,1,1};
unsigned int idata array2[3]={0,0,0};
unsigned int idata adresult[3];
unsigned int idata max[3]={0,0,0};
unsigned int idata min[3]={1000,1000,1000};
unsigned int idata dc[3]={500,500,500};
unsigned int idata vpp[3]={30,30,30};
unsigned int idata precision[3]={5,5,5};
unsigned int idata old_fixed[3];
unsigned int idata new_fixed[3];
unsigned int idata STEPS=0;
BYTE BUF[16]; //接收数据缓存区
uchar ge,shi,bai,qian,wan; //显示变量
void delay(unsigned int n);
void Init_ADXL345(void); //初始化ADXL345
void Send_Byte(unsigned char *temp);
void conversion(uint temp_data);
void Single_Write_ADXL345(uchar REG_Address,uchar REG_data); //单个写入数据
uchar Single_Read_ADXL345(uchar REG_Address); //单个读取内部寄存器数据
void Multiple_Read_ADXL345(); //连续的读取内部寄存器数据
//------------------------------------
void Delay5us();
void Delay5ms();
void ADXL345_Start();
void ADXL345_Stop();
void ADXL345_SendACK(bit ack);
bit ADXL345_RecvACK();
void ADXL345_SendByte(BYTE dat);
BYTE ADXL345_RecvByte();
void ADXL345_ReadPage();
void ADXL345_WritePage();
void UART_init(void);
void ADC_init(unsigned char channel);
void T0_init(void);
//------------------------------------
unsigned char PulsePin = 0; // P1.0为传感器输入口
// 这些变量是在中断服务程序执行期间使用的
volatile unsigned int BPM; // 用于保持脉搏
volatile unsigned int Signal; // 保存传入的原始数据
volatile unsigned int IBI = 600; // 保存相邻心跳之间的时间间隔
volatile bit Pulse = false; // 脉搏波高时为真,低时为假
volatile bit QS = false; // 当单片机发现一个心跳时变为真
volatile int rate[10]; //数组,保存最后10个IBI值
volatile unsigned long sampleCounter = 0; // 用于确定脉冲时间
volatile unsigned long lastBeatTime = 0; // 用于发现IBI
volatile int Peak =512; //用于发现脉搏波峰值
volatile int Trough = 512; // 用于发现脉搏波谷
volatile int thresh = 512; // 用于发现心跳的即时时刻
volatile int amp = 100; //用于保持脉冲波形的幅度
volatile bit firstBeat = true; // 用于开始合理的BPM
volatile bit secondBeat = false;
static unsigned char order=0;
unsigned char DisBuff[4]={0};
//------------------------------------
void conversion(uint temp_data)
{
wan=temp_data/10000+0x30 ;
temp_data=temp_data%10000; //取余运算
qian=temp_data/1000+0x30 ;
temp_data=temp_data%1000; //取余运算
bai=temp_data/100+0x30 ;
temp_data=temp_data%100; //取余运算
shi=temp_data/10+0x30 ;
temp_data=temp_data%10; //取余运算
ge=temp_data+0x30;
}
void Delay5us()
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
void Delay5ms()
{
WORD n = 560;
while (n--);
}
void ADXL345_Start() //起始信号
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
void ADXL345_Stop() //停止信号
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
void ADXL345_SendACK(bit ack) //发送应答信号 入口参数:ack(0:ACK 1:NAK)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
bit ADXL345_RecvACK() //接收应答信号
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
void ADXL345_SendByte(BYTE dat) //向IIC总线发送一字节数据
{
BYTE i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
ADXL345_RecvACK();
}
BYTE ADXL345_RecvByte() //从IIC总线接收一字节数据
{
BYTE i;
BYTE dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
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;
}
void Multiple_read_ADXL345(void) //连续读出ADXL345内部加速度数据,地址范围0x32~0x37
{ 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(); //停止信号
Delay5ms();
}
void Init_ADXL345() //初始化ADXL345,根据需要请参考pdf进行修改
{
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页
}
void step_counter(void)
{
static uchar sampling_counter=0;
uchar jtemp;
ADXL345_FLAG=0;
Multiple_read_ADXL345(); //连续读取加速度
//--------------------------采样滤波----------------------//
for(jtemp=0;jtemp<=2;jtemp++) //求平均值,计算最大值和最小值
{
array2[jtemp]=array1[jtemp]; // array0[3]={1,1,1}
array1[jtemp]=array0[jtemp]; // array1[3]={1,1,1}
array0[jtemp]=BUF[2*jtemp]+(BUF[2*jtemp+1]<<8); // array2[3]={0,0,0}
adresult[jtemp]=array0[jtemp]+array1[jtemp]+array2[jtemp];
adresult[jtemp]=adresult[jtemp]/3; //连续三个的平均值
if(adresult[jtemp]>max[jtemp]) //max[3]={0,0,0}
{
max[jtemp]=adresult[jtemp];
}
if(adresult[jtemp]<min[jtemp]) //min[3]={1000,1000,1000}
{
min[jtemp]=adresult[jtemp];
}
}
sampling_counter=sampling_counter+1;
//---------------------------------计算动态门限和动态精度-----------------------//
if(sampling_counter>=50) //每50个采样更新一次
{
sampling_counter=0;
for(jtemp=0;jtemp<=2;jtemp++)
{
vpp[jtemp]=max[jtemp]-min[jtemp]; //vpp[3]={30,30,30}
dc[jtemp] =min[jtemp]+(vpp[jtemp]>>1); //dc[3]={500,500,500} 动态门限
min[jtemp]=1023;
bad_flag[jtemp]=0;
if(vpp[jtemp]>=160)
{
precision[jtemp]=vpp[jtemp]/32; //动态精度 precision[3]={5,5,5}
}
else if((vpp[jtemp]>=50)&& (vpp[jtemp]<160))
{
precision[jtemp]=4;
}
else if((vpp[jtemp]>=15) && (vpp[jtemp]<50))
{
precision[jtemp]=3;
}
else
{
precision[jtemp]=2;
bad_flag[jtemp]=1;
}
}
}
//--------------------------线性移位寄存器-------------------------------------
for(jtemp=0;jtemp<=2;jtemp++) //用动态精度来滤波
{
old_fixed[jtemp]=new_fixed[jtemp];
if(adresult[jtemp]>=new_fixed[jtemp])
{
if((adresult[jtemp]-new_fixed[jtemp])>=precision[jtemp])
{
new_fixed[jtemp]=adresult[jtemp];
}
}
else if(adresult[jtemp]<new_fixed[jtemp])
{
if((new_fixed[jtemp]-adresult[jtemp])>=precision[jtemp])
{
new_fixed[jtemp]=adresult[jtemp];
}
}
}
//------------------------- 动态门限判决----------------------------------
if((vpp[0]>=vpp[1])&&(vpp[0]>=vpp[2])) //x轴加速度变化最大
{
if((old_fixed[0]>=dc[0])&&(new_fixed[0]<dc[0])&&(bad_flag[0]==0)) //判决是否走步
{
STEPS=STEPS+1;
}
}
else if((vpp[1]>=vpp[0])&&(vpp[1]>=vpp[2])) //y轴加速度变化最大
{
if((old_fixed[1]>=dc[1])&&(new_fixed[1]<dc[1])&&(bad_flag[1]==0))
{
STEPS=STEPS+1;
}
}
else if((vpp[2]>=vpp[1])&&(vpp[2]>=vpp[0])) //z轴加速度变化最大
{
if((old_fixed[2]>=dc[2])&&(new_fixed[2]<dc[2])&&(bad_flag[2]==0))
{
STEPS=STEPS+1;
}
}
conversion(STEPS);
buffer[4] =wan;
buffer[5] =qian;
buffer[6] =bai;
buffer[7] =shi;
buffer[8] =ge;
buffer[9] ='\0'; //终止符
}
void delay(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<100;j++);
}
void sys_init()
{
UART_init(); //串口初始化
ADC_init(PulsePin);
T0_init(); // 定时器初始化,设置为每2ms读取一次信号
Init_ADXL345();
}
void main(void)
{
uchar devid;
delay(500); //上电延时
sys_init(); //系统初始化
devid=Single_Read_ADXL345(0X00);//读出的数据为0XE5,表示正确
while(1)
{
step_counter(); //步数计算
buffer[0]='
[/font]
; //判断标志$
if (QS == true)
{ // 当单片机发现心跳时,量化自标记为true
buffer[1] =BPM/100+48;
buffer[2] =BPM%100/10+48;
buffer[3] =BPM%10+48;
QS = false; // 下一次重置量化自标记
}
Send_Byte(buffer); //发送数据
}
}
void UART_init(void) //串口初始化
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x04; //独立波特率发生器时钟为Fosc,即1T
BRT = 0xDC; //设置独立波特率发生器重装值
AUXR |= 0x01; //串口一选择独立波特率发生器为波特率发生器
AUXR |= 0x10; //启用独立波特率发生器
}
void Send_Byte(unsigned char *temp) //发送一串数据
{
unsigned char i=0;
ES=0; //禁止串口中断
while(temp[i] != '\0')
{
SBUF=*(temp+i);
i++;
while(TI==0); //发送数据完毕时,TI会自动置高
TI=0; //发送数据完毕,将TI清零,准备下一次发送
}
ES=1; //清零串口中断发送标志
}
void T0_init(void) //定时器初始化
{
TMOD |= 0x01; //16位定时器
TL0=T0MS;
TH0=T0MS>>8;
TR0=1; //启动定时器0
ET0=1; //启用定时器中断
EA=1; //确保全局中断已启用
}
void ADC_init(unsigned char channel) //ADC初始化
{
P1ASF=ADC_MASK<<channel; //启用PulsePin作为ADC输入
ADC_RES=0; //清除之前的ADC结果
ADC_RESL=0; //清除之前的ADC结果
AUXR1 |= 0x04; //调整ADC结果的格式
ADC_CONTR=channel|ADC_POWER|ADC_SPEEDLL|ADC_START; //开启AFC并开始转换
}
unsigned int analogRead(unsigned char channel)
{
unsigned int result;
ADC_CONTR &=!ADC_FLAG; //清除ADC FLAG
result=ADC_RES;
result=result<<8;
result+=ADC_RESL;
ADC_CONTR|=channel|ADC_POWER|ADC_SPEEDLL|ADC_START;
return result;
}
// Timer 0中断子程序,每2MS中断一次,读取AD值,计算心率值
void Timer0_rountine(void) interrupt 1
{
int N;
unsigned char i;
// 保持最后10个IBI值的runningtotal
unsigned int runningTotal = 0; // 清除runningTotal变量
EA=0; // 禁用中断
TL0=T0MS;
TH0=T0MS>>8; //重新加载16位定时器0
Signal = analogRead(PulsePin); // 读取传感器值
sampleCounter += 2; // 以ms为单位跟踪变量
N = sampleCounter - lastBeatTime; // 监视自上次跳动以来的时间以避免噪音
// 找到脉搏的波峰和波谷
if(Signal < thresh && N > (IBI/5)*3){ // 通过等待最后IBI的3/5以避免严重的噪音
if (Signal < Trough){ // T是低谷
Trough = Signal; // 跟踪脉搏波的最低点
}
}
if(Signal > thresh && Signal > Peak){ // thresh条件有助于避免噪音
Peak = Signal; // P是峰值
} // 跟踪脉搏波的最高点
// 每次出现脉冲信号时都会激增
if (N > 250){ // 避免高频噪音
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) )
{
Pulse = true; // 当我们认为有脉冲时设置脉冲标志
IBI = sampleCounter - lastBeatTime; //以ms为单位测量心跳之间的时间
lastBeatTime = sampleCounter; // 跟踪下一个脉冲的时间
if(secondBeat){ // 如果这是第二次心跳
secondBeat = false; // 清除第二次心跳标志
for(i=0; i<=9; i++){ // 在启动时记录运行总数以获得实际的BPM
rate[i] = IBI;
}
}
if(firstBeat){ // 如果这是我们第一次发现心跳
firstBeat = false; // 清除第一次心跳标志
secondBeat = true; // 设置第二次心跳标志
EA=1; // 再次启用中断
return; // IBI值不可靠,丢弃它
}
for(i=0; i<=8; i++){ // 移动速率数组中的数据
rate[i] = rate[i+1]; // 放弃最早的IBI值
runningTotal += rate[i]; //加载9个最早的ibi值
}
rate[9] = IBI; //将最新的IBI添加到速率数组中
runningTotal += rate[9]; // 将最新的IBI添加到runningtotal
runningTotal /= 10; //求最后10个IBI的平均值
BPM = 60000/runningTotal; // 一分钟内可以发现多少个心跳,这就是BPM
if(BPM>200)BPM=200; //限制BPM最高显示值
if(BPM<30)BPM=30; //限制BPM最低显示值
DisBuff[2] = BPM%10+48;//取个位数
DisBuff[1] = BPM%100/10+48; //取十位数
DisBuff[0] = BPM/100+48; //百位数
if(DisBuff[0]==48)
DisBuff[0]=32;
QS = true; //设置量化自我标志
}
}
if (Signal < thresh && Pulse == true){ // 当数值下降时,心跳结束
Pulse = false; // 重置脉冲标志,以便下次执行
amp = Peak - Trough; // 得到脉搏波的幅度
thresh = amp/2 + Trough; // 将幅度设置为50%
Peak = thresh; // 重置以便下次使用
Trough = thresh;
}
if (N > 2500){ // 如果2.5秒内没有心跳
thresh = 512; // 设置thresh默认值
Peak = 512; // 设置P默认值
Trough = 512; // 设置T默认值
lastBeatTime = sampleCounter; // 使lastBeatTime保持最新状态
firstBeat = true; //设置这些以避免噪声
secondBeat = false; // 当我们得到心跳回来
}
EA=1; // 完成后启用中断
}
#include <stc12c5a60s2.h>
#include <math.h>
#include <stdio.h>
#include <INTRINS.H>
#include <stdlib.h>
//------------------------------------
#define uchar unsigned char
#define uint unsigned int
#define S2RI 0x01 //串口2接收中断请求标志位
#define S2TI 0x02 //串口2发送中断请求标志位
unsigned char flag2,temp2;
unsigned char Buffer[10];
unsigned char RXCount = 0;
unsigned char Display_Buffer[10];
//------------------------------------
#define BMP180_SlaveAddress 0xee //定义器件在IIC总线中的地址
#define OSS 0 // 过采样设置 代码未设置为使用其他oss值
typedef unsigned char BYTE;
typedef unsigned short WORD;
uchar ge,shi,bai,qian,wan,shiwan; //显示变量
int dis_data; //变量
short ac1;
short ac2;
short ac3;
unsigned short ac4;
unsigned short ac5;
unsigned short ac6;
short b1;
short b2;
short mb;
short mc;
short md;
//------------------------------------
//按键 IO设置
sbit KEY1 = P2^3;
sbit KEY2 = P2^2;
sbit KEY3 = P2^1;
sbit KEY4 = P2^0;
//LCD1602 IO设置
#define LCD1602_PORT P0
sbit LCD1602_RS = P2^5; //数据 命令选择
sbit LCD1602_RW = P2^6; //读 写选择
sbit LCD1602_EN = P2^7; //使能信号
//------------------------------------
sbit SCL=P1^4; //IIC时钟引脚定义
sbit SDA=P1^5; //IIC数组引脚定义
//------------------------------------
void gps(void);
void display_receive(void);
extern void LCD1602_delay_ms(unsigned int n);
extern void LCD1602_write_com(unsigned char com);
extern void LCD1602_write_data(unsigned char dat);
extern void LCD1602_write_word(unsigned char *s);
extern void Init_LCD1602();
extern void Delay_ms(unsigned int n);
extern void Uart_Init();
//------------------------------------
unsigned char KEY_NUM = 0;
bit Page;
unsigned char xdata Display_GPGGA_Buffer[68];
unsigned char xdata Display_GPRMC_Buffer[68];
bit Flag_OV = 0;
bit Flag_Calc_GPGGA_OK = 0;
bit Flag_Calc_GPRMC_OK = 0;
unsigned char RX_Buffer[68];
unsigned char RX_Count = 0;
unsigned char Hour = 0,Min_High = 0,Min_Low = 0,Sec_High = 0,Sec_Low = 0;
unsigned char Month = 0,Day = 0,Month_High = 0, Month_Low = 0,Day_Low = 0 ,Day_High = 0, Year_High = 0,Year_Low = 0;
unsigned int Year = 0;
bit Flag_GPS_OK = 0;
unsigned char MaxDay = 0;
extern void Scan_Key();
extern void UTCDate2LocalDate(void);
extern bit IsLeapYear(unsigned int uiYear);
extern unsigned char GetMaxDay(unsigned char Month_Value,unsigned int Year_Value);
extern unsigned char RX_Buffer[68];
extern unsigned char RX_Count;
extern unsigned char Hour,Min_High,Min_Low,Sec_High,Sec_Low;
extern unsigned char Month,Day,Month_High, Month_Low,Day_Low ,Day_High, Year_High,Year_Low;
extern unsigned int Year;
extern unsigned char MaxDay;
extern bit Flag_GPS_OK;
//------------------------------------
void conversion(long temp_data);
void Single_Write(uchar SlaveAddress,uchar REG_Address,uchar REG_data);
uchar Single_Read(uchar REG_Address);
void Multiple_Read(uchar,uchar);
void delay(unsigned int k);
void Delay5us();
void Delay5ms();
void BMP180_Start();
void BMP180_Stop();
void BMP180_SendACK(bit ack);
bit BMP180_RecvACK();
void BMP180_SendByte(BYTE dat);
BYTE BMP180_RecvByte();
void BMP180_ReadPage();
void BMP180_WritePage();
void Init_BMP180(void);
void bmp180Convert(void);
//------------------------------------
void UTCDate2LocalDate(void) //UTC日期和当前日期的转换
{
Day = (Day_High - 0x30) * 10 + (Day_Low-0x30) + 1; //日 加一
Month = (Month_High - 0x30) * 10 + (Month_Low - 0x30);
Year = 2000 + (Year_High - 0x30) * 10 + (Year_Low - 0x30);
MaxDay = GetMaxDay(Month,Year); //获取当月 天数 最大值
if(Day > MaxDay) //溢出
{
Day = 1;
Month += 1;
if(Month > 12)
{
Year+=1;
}
}
Day_High = Day/10 + 0x30; //转换日期值为ASCII
Day_Low = Day%10 + 0x30;
Month_High = Month/10 + 0x30; //转换月份值为ASCII
Month_Low = Month%10 + 0x30;
Year_High = Year%100/10 + 0x30; //转换年份值为ASCII
Year_Low = Year%10 + 0x30;
}
unsigned char GetMaxDay(unsigned char Month_Value,unsigned int Year_Value) //获取当月日期的最大值
{
unsigned char iDays;
switch(Month_Value)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
{
iDays = 31;
}
break;
case 2:
{
//2月份比较特殊,需要根据是不是闰年来判断当月是28天还29天
iDays = IsLeapYear(Year_Value)?29:28;
}
break;
case 4:
case 6:
case 9:
case 11:
{
iDays = 30;
}
break;
default : break;
}
return(iDays);
}
bit IsLeapYear(unsigned int uiYear) //闰年检测
{
return (((uiYear%4)==0)&&((uiYear%100)!=0))||((uiYear%400)==0);
}
void Uart_Init() //串口初始化函数
{
SCON = 0X50; //UART方式1;8位UART
REN = 1; //允许串行口接收数据
PCON = 0x00; //SMOD=0;波特率不加倍
TMOD = 0x20; //T1方式2,用于产生波特率
TH1 = 0xFD; //装初值
TL1 = 0xFD;
TR1 = 1; //启动定时器1
EA = 1; //打开全局中断控制
ES = 1; //打开串行口中断
AUXR &= 0xF7; //波特率不倍速
S2CON = 0x50; //8位数据,可变波特率
AUXR &= 0xFB; //独立波特率发生器时钟为Fosc/12,即12T
BRT = 0xFD; //设定独立波特率发生器重装值
AUXR |= 0x10; //启动独立波特率发生器
IE2 =0x01; //开串口二中断 ES2=1
EA=1;
}
void RECEIVE_DATA(void) interrupt 4 using 3 //串口一中断接收,接收gps数据
{
unsigned char temp = 0;
ES=0;
temp = SBUF;
RI = 0;
if(temp == '
[/font]
)
{
RX_Count = 0;
Flag_GPS_OK = 0;
}
RX_Buffer[RX_Count++] = temp;
if(RX_Count >= 59)
{
RX_Count = 59;
Flag_GPS_OK = 1;
}
ES=1;
}
void UART_2Interrupt(void) interrupt 8 using 1 //串口二中断接收
{
if(S2CON&S2RI)
{
S2CON&=~S2RI;
flag2=1;
temp2=S2BUF;
Buffer[RXCount++] = temp2;
}
if(S2CON&S2TI)
{
S2CON&=~S2TI;
}
}
void Delay_ms(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<123;j++);
}
void LCD1602_delay_ms(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<123;j++);
}
void LCD1602_write_com(unsigned char com) //1602写指令
{
LCD1602_RS = 0;
LCD1602_delay_ms(1);
LCD1602_EN = 1;
LCD1602_PORT = com;
LCD1602_delay_ms(1);
LCD1602_EN = 0;
}
void LCD1602_write_data(unsigned char dat) //1602写数据
{
LCD1602_RS = 1;
LCD1602_delay_ms(1);
LCD1602_PORT = dat;
LCD1602_EN = 1;
LCD1602_delay_ms(1);
LCD1602_EN = 0;
}
void LCD1602_write_word(unsigned char *s) //1602连续写字符
{
while(*s>0)
{
LCD1602_write_data(*s);
s++;
}
}
void Init_LCD1602() //初始化1602
{
LCD1602_EN = 0;
LCD1602_RW = 0; //设置为写状态
LCD1602_write_com(0x38); //显示模式设定 16*2显示 5*7点阵 8位数据口
LCD1602_write_com(0x0c); //开关显示、光标有无设置、光标闪烁设置
LCD1602_write_com(0x06); //写一个字符后指针加一
LCD1602_write_com(0x01); //清屏指令
}
void display_receive() //显示串口二接收到的内容,即从另一个单片机接收到的步数和心率数据
{
if (Buffer[0]=='
[/font]
)
{unsigned char i;
for( i = 0; i < 10 ; i++)
{
Display_Buffer[i] = Buffer[i];
}
LCD1602_write_com(0X01); //清屏
Delay_ms(50);
LCD1602_write_com(0x80); //指针设置
LCD1602_write_word("STEPS:");
LCD1602_write_data(Display_Buffer[4]);
LCD1602_write_data(Display_Buffer[5]);
LCD1602_write_data(Display_Buffer[6]);
LCD1602_write_data(Display_Buffer[7]);
LCD1602_write_data(Display_Buffer[8]);
LCD1602_write_com(0x80+0x40); //设置指针 0xc0
LCD1602_write_word("BPM:");
LCD1602_write_data(Display_Buffer[1]);
LCD1602_write_data(Display_Buffer[2]);
LCD1602_write_data(Display_Buffer[3]);
}
}
void Scan_Key() //扫描按键
{
if( KEY1 == 0 ) //按键1扫描
{
Delay_ms(10); //延时去抖
if( KEY1 == 0 )
{
while(KEY1 == 0); //等待松手
KEY_NUM = 3;
Page = ~Page;
LCD1602_write_com(0X01); //清屏
}
}
}
void Delay5000ms() //@11.0592MHz stc12 延时5秒
{
unsigned char i, j, k;
i = 211;
j = 30;
k = 11;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//------------------------------------
void main() //主程序
{
Init_LCD1602(); //初始化1602
Init_BMP180(); //初始化bmp180
Delay_ms(1000);
LCD1602_write_com(0x80); //显示欢迎界面
LCD1602_write_word("WELCOME");
LCD1602_write_com(0x80+0x40);
LCD1602_write_word("sanshuige");
Delay_ms(10000);
LCD1602_write_com(0X01); //显示温度,气压界面
bmp180Convert();
Delay5000ms();
LCD1602_write_com(0X01);
Uart_Init(); //双串口初始化
while(1)
{
gps(); //处理gps数据
Scan_Key(); //按键扫描
//显示gps界面一,按下key1后显示gps界面二。连续按key1,在2个界面之间来回切换显示
if (KEY2==0)
{
while(1)
{
display_receive(); //如果按下key2,显示步数和心率界面
Delay_ms(2000);
if (KEY1==0) //在步数和心率界面下按下key1,则跳回gps界面
{
break;
}
}
}
}
}
void gps() //处理和显示串口一接收到的gps数据
{
unsigned char i = 0;
if(Flag_GPS_OK == 1 && RX_Buffer[4] == 'G' && RX_Buffer[6] == ',' && RX_Buffer[13] == '.') //确定是否收到"GPGGA"这一帧数据
{
for( i = 0; i < 68 ; i++)
{
Display_GPGGA_Buffer[i] = RX_Buffer[i];
}
Hour = (Display_GPGGA_Buffer[7]-0x30)*10+(Display_GPGGA_Buffer[8]-0x30)+8; //UTC时间转换到北京时间 UTC+8 0x30为ascii转换为数字
if( Hour >= 24) //溢出
{
Hour %= 24; //获取当前Hour
Flag_OV = 1; //日期进位
}
else
{
Flag_OV = 0;
}
Min_High = Display_GPGGA_Buffer[9];
Min_Low = Display_GPGGA_Buffer[10];
Sec_High = Display_GPGGA_Buffer[11];
Sec_Low = Display_GPGGA_Buffer[12];
Flag_Calc_GPGGA_OK = 1;
}
if(Page == 0 && Flag_Calc_GPGGA_OK == 1)
{
Flag_Calc_GPGGA_OK = 0;
LCD1602_write_com(0x80); //设置指针
LCD1602_write_data(Hour/10+0x30);
LCD1602_write_data(Hour%10+0x30);
LCD1602_write_data(':');
LCD1602_write_data(Min_High);
LCD1602_write_data(Min_Low);
LCD1602_write_data(':');
LCD1602_write_data(Sec_High);
LCD1602_write_data(Sec_Low);
LCD1602_write_word(" ");
LCD1602_write_data(Display_GPGGA_Buffer[54]);
LCD1602_write_data(Display_GPGGA_Buffer[55]);
LCD1602_write_data(Display_GPGGA_Buffer[56]);
LCD1602_write_data(Display_GPGGA_Buffer[57]);
LCD1602_write_word("m");
LCD1602_write_com(0x80+0x40); //设置指针
LCD1602_write_data(Display_GPGGA_Buffer[28]); //N 或者 S
LCD1602_write_data(Display_GPGGA_Buffer[17]); //纬度
LCD1602_write_data(Display_GPGGA_Buffer[18]); //纬度
LCD1602_write_data(0xdf); //度
LCD1602_write_data(Display_GPGGA_Buffer[19]); //纬度
LCD1602_write_data(Display_GPGGA_Buffer[20]); //纬度
LCD1602_write_word("'"); //秒
LCD1602_write_data(Display_GPGGA_Buffer[42]); //E 或者 W
LCD1602_write_data(Display_GPGGA_Buffer[30]); //经度
LCD1602_write_data(Display_GPGGA_Buffer[31]);
LCD1602_write_data(Display_GPGGA_Buffer[32]);
LCD1602_write_data(0xdf);
LCD1602_write_data(Display_GPGGA_Buffer[33]);
LCD1602_write_data(Display_GPGGA_Buffer[34]);
LCD1602_write_word("'");
}
if(Flag_GPS_OK == 1 && RX_Buffer[4] == 'M' && RX_Buffer[52] == ',' && RX_Buffer[59] == ',') //确定是否收到"GPRMC"这一帧数据
{
for( i = 0; i < 68 ; i++)
{
Display_GPRMC_Buffer[i] = RX_Buffer[i];
}
Year_High = Display_GPRMC_Buffer[57];
Year_Low = Display_GPRMC_Buffer[58];
Month_High = Display_GPRMC_Buffer[55];
Month_Low = Display_GPRMC_Buffer[56];
Day_High = Display_GPRMC_Buffer[53];
Day_Low = Display_GPRMC_Buffer[54];
if(Flag_OV == 1) //有进位
{
UTCDate2LocalDate(); //UTC日期转换为北京时间
}
Flag_Calc_GPRMC_OK = 1;
}
if(Page == 1 && Flag_Calc_GPRMC_OK == 1)
{
Flag_Calc_GPRMC_OK = 0;
LCD1602_write_com(0x80); //设置指针
LCD1602_write_word("20");
LCD1602_write_data(Year_High);
LCD1602_write_data(Year_Low);
LCD1602_write_data('-');
LCD1602_write_data(Month_High);
LCD1602_write_data(Month_Low);
LCD1602_write_data('-');
LCD1602_write_data(Day_High);
LCD1602_write_data(Day_Low);
LCD1602_write_com(0x80+0x40); //设置指针
LCD1602_write_word("Speed:"); //显示内容
LCD1602_write_data(Display_GPRMC_Buffer[46]);
LCD1602_write_data(Display_GPRMC_Buffer[47]);
LCD1602_write_data(Display_GPRMC_Buffer[48]);
LCD1602_write_data(Display_GPRMC_Buffer[49]);
LCD1602_write_data(Display_GPRMC_Buffer[50]);
LCD1602_write_word("m/s");
}
}
void conversion(long temp_data) //转变函数
{
shiwan=temp_data/100000+0x30 ;
temp_data=temp_data%100000; //取余运算
wan=temp_data/10000+0x30 ;
temp_data=temp_data%10000;
qian=temp_data/1000+0x30 ;
temp_data=temp_data%1000;
bai=temp_data/100+0x30 ;
temp_data=temp_data%100;
shi=temp_data/10+0x30 ;
temp_data=temp_data%10;
ge=temp_data+0x30;
}
void delay(unsigned int k)
{
unsigned int i,j;
for(i=0;i<k;i++)
{
for(j=0;j<121;j++)
{;}
}
}
void Delay5us()
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
void Delay5ms()
{
WORD n = 560;
while (n--);
}
void BMP180_Start() //起始信号
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
void BMP180_Stop() //停止信号
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
void BMP180_SendACK(bit ack) //发送应答信号 入口参数:ack(0:ACK 1:NAK)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
bit BMP180_RecvACK() //接收应答信号
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
void BMP180_SendByte(BYTE dat) //向iic总线发送一个字节数据
{
BYTE i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
BMP180_RecvACK();
}
BYTE BMP180_RecvByte() //从iic总线接收一个字节数据
{
BYTE i;
BYTE dat = 0;
SDA = 1; //使能内部上拉,准备读取数据
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
short Multiple_read(uchar ST_Address) //读出bmp180内部数据 连续两个
{
uchar msb, lsb;
short _data;
BMP180_Start(); //起始信号
BMP180_SendByte(BMP180_SlaveAddress); //发送设备地址+写信号
BMP180_SendByte(ST_Address); //发送存储单元地址
BMP180_Start(); //起始信号
BMP180_SendByte(BMP180_SlaveAddress+1); //发送设备地址+读信号
msb = BMP180_RecvByte(); //BUF[0]存储
BMP180_SendACK(0); //回应ACK
lsb = BMP180_RecvByte();
BMP180_SendACK(1); //最后一个数据需要回NOACK
BMP180_Stop(); //停止信号
Delay5ms();
_data = msb << 8;
_data |= lsb;
return _data;
}
long bmp180ReadTemp(void)
{
BMP180_Start(); //起始信号
BMP180_SendByte(BMP180_SlaveAddress); //发送设备地址+写信号
BMP180_SendByte(0xF4); //写寄存器地址
BMP180_SendByte(0x2E); // 写入温度的寄存器数据
BMP180_Stop(); //发送停止信号
delay(10);
return (long) Multiple_read(0xF6);
}
long bmp180ReadPressure(void)
{
long pressure = 0;
BMP180_Start(); //起始信号
BMP180_SendByte(BMP180_SlaveAddress); //发送设备地址+写信号
BMP180_SendByte(0xF4); // 写寄存器地址
BMP180_SendByte(0x34); // 写入气压的寄存器数据
BMP180_Stop(); //发送停止信号
delay(10);
pressure = Multiple_read(0xF6);
pressure &= 0x0000FFFF;
return pressure;
}
void Init_BMP180() //初始化bmp180
{
ac1 = Multiple_read(0xAA);
ac2 = Multiple_read(0xAC);
ac3 = Multiple_read(0xAE);
ac4 = Multiple_read(0xB0);
ac5 = Multiple_read(0xB2);
ac6 = Multiple_read(0xB4);
b1 = Multiple_read(0xB6);
b2 = Multiple_read(0xB8);
mb = Multiple_read(0xBA);
mc = Multiple_read(0xBC);
md = Multiple_read(0xBE);
}
void bmp180Convert() //读取并在1602上显示温度和气压数据
{
long ut;
long up;
long x1, x2, b5, b6, x3, b3, p;
unsigned long b4, b7;
long temperature;
long pressure;
ut = bmp180ReadTemp();
ut = bmp180ReadTemp(); //读取温度
up = bmp180ReadPressure();
up = bmp180ReadPressure(); //读取大气压
x1 = ((long)ut - ac6) * ac5 >> 15;
x2 = ((long) mc << 11) / (x1 + md);
b5 = x1 + x2;
temperature = (b5 + 8) >> 4;
LCD1602_write_com(0X01); //清屏
Delay_ms(50);
conversion(temperature);
LCD1602_write_word("temp:");
LCD1602_write_data(bai);
LCD1602_write_data(shi);
LCD1602_write_word(".");
LCD1602_write_data(ge);
LCD1602_write_data(0XDF); //温度单位
LCD1602_write_word("C");
b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = ac2 * b6 >> 11;
x3 = x1 + x2;
b3 = (((long)ac1 * 4 + x3) + 2)/4;
x1 = ac3 * b6 >> 13;
x2 = (b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15;
b7 = ((unsigned long) up - b3) * (50000 >> OSS);
if( b7 < 0x80000000)
p = (b7 * 2) / b4 ;
else
p = (b7 / b4) * 2;
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
pressure = p + ((x1 + x2 + 3791) >> 4);
LCD1602_write_com(0x80+0x40); //设置指针
conversion(pressure);
LCD1602_write_word("atm:");
LCD1602_write_data(shiwan);
LCD1602_write_data(wan);
LCD1602_write_data(qian);
LCD1602_write_word(".");
LCD1602_write_data(bai);
LCD1602_write_data(shi);
LCD1602_write_word("Kpa");
}
复制代码
全部资料51hei下载地址:
新建文件夹.rar
(936.23 KB, 下载次数: 47)
2018-6-25 15:30 上传
点击文件名下载附件
下载积分: 黑币 -5
作者:
xsj1877578806
时间:
2018-8-28 18:39
感谢分享很实用
作者:
宇宙浪子521
时间:
2019-11-26 20:24
这个是模块化编程吗
作者:
宇宙浪子521
时间:
2019-11-26 20:25
这个是模块化编程吗?
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1