基于AT89C51 上MPU60506轴加速度传感器计步功能实现

~~

本项目 为大学小学期实践项目。参考了大量资料。

分以下部分进行讲解

用户需求

此次项目需求为,通过将mpu60506轴加速传感器电焊到at89c51上,将传感器的获得数据显示到我们LCD1602液晶屏模块上。其中为了实现计步需要设计算法。

模块设计

其中我们需要模块为一下几个

1. MPU6050

2. LCD1602

3. 3. i2c总线传输(at89c51内已集成)

4 计步算法实现

1mpu6050

感测范围编辑
MPU-6000(6050)的角速度全格感测范围为±250、±500、±1000与±2000°/sec (dps),可准确追踪快速与慢速动作,并且,用户可程式控制的加速器全格感测范围为±2g、±4g±8g与±16g。产品传输可透过最高至400kHz的IIC或最高达20MHz的SPI(MPU-6050没有SPI)。MPU-6000可在不同电压下工作,VDD供电电压介为2.5V±5%、3.0V±5%或3.3V±5%,逻辑接口VDDIO供电为1.8V± 5%(MPU6000仅用VDD)。MPU-6000的包装尺寸4x4x0.9mm(QFN),在业界是革命性的尺寸。其他的特征包含内建的温度感测器、包含在运作环境中仅有±1%变动的振荡器。
应用编辑
运动感测游戏 现实增强 电子稳像 (EIS: Electronic Image Stabilization) 光学稳像(OIS: Optical Image Stabilization) 行人导航器 “零触控”手势用户接口 姿势快捷方式 认证 车轮力传感器
市场编辑
智能型手机 平板装置设备 手持型游戏产品 游戏机 3D遥控器 可携式导航设备
特征编辑
以数字输出6轴或9轴的旋转矩阵、四元数(quaternion)、欧拉角格式(Euler Angle forma)的融合演算数据。 具有131 LSBs/°/sec 敏感度与全格感测范围为±250、±500、±1000与±2000°/sec 的3轴角速度感测器(陀螺仪)。 可程式控制,且程式控制范围为±2g、±4g、±8g和±16g的3轴加速器。 移除加速器与陀螺仪轴间敏感度,降低设定给予的影响与感测器的飘移。 数字运动处理(DMP: Digital Motion Processing)引擎可减少复杂的融合演算数据、感测器同步化、姿势感应等的负荷。 运动处理数据库支持Android、Linux与Windows 内建之运作时间偏差与磁力感测器校正演算技术,免除了客户须另外进行校正的需求。 以数位输出的温度传感器 以数位输入的同步引脚(Sync pin)支援视频电子影相稳定技术与GPS 可程式控制的中断(interrupt)支援姿势识别、摇摄、画面放大缩小、滚动、快速下降中断、high-G中断、零动作感应、触击感应、摇动感应功能。 VDD供电电压为2.5V±5%、3.0V±5%、3.3V±5%;VDDIO为1.8V± 5% 陀螺仪运作电流:5mA,陀螺仪待命电流:5µA;加速器运作电流:350µA,加速器省电模式电流: 20µA@10Hz 高达400kHz快速模式的I2C,或最高至20MHz的SPI串行主机接口(serial host interface) 内建频率产生器在所有温度范围(full temperature range)仅有±1%频率变化。 使用者亲自测试 10,000 g 碰撞容忍度 为可携式产品量身订作的最小最薄包装 (4x4x0.9mm QFN) 符合RoHS及环境标准
关于引脚编辑
SCL和SDA是连接MCU的IIC接口,MCU通过这个IIC接口来控制MPU6050,
另外还有一个IIC接口:AXCL和 XDA,这个接口可用来连接外部从设备,比如磁传感 器,这样就可以组成一个九轴传感器。VLOGIC是IO口电压,该引脚最低可以到1.8V,我们 一般直接接VDD即可。AD0是从IIC接口(接MCU)的地址控制引脚,该引脚控制IIC地址 的最低位。如果接GND,则MPU6050的IIC地址是:0X68,如果接VDD,则是0X69,注意: 这里的地址是不包含数据传输的最低位的(最低位用来表示读写)
~~
需要定义寄存器
(此处直接给出运行需要定义的寄存器,若需其他功能还需要添加,寄存器引脚地址参考下列链接
https://wenku.baidu.com/view/8dbd8f9e360cba1aa911da1d.html?rec_flag=default&sxts=1566910136994

https://wenku.baidu.com/view/d33c7b71ac02de80d4d8d15abe23482fb4da02c9.html

MPU-6000 可以使用 SPI 和 I2C 接口,而 MPU-6050 只能使用 I2C,其中 I2C 的地址由 AD0
引脚决定;寄存器共 117 个,挺多的,下面的是精简常用的,根据具体的要求,适当的添加。
#define SMPLRT_DIV 0x19 //采样率分频,典型值:0x07(125Hz) */
#define CONFIG 0x1A // 低通滤波频率,典型值:0x06(5Hz) */
#define GYRO_CONFIG 0x1B // 陀螺仪自检及测量范围,典型值:0x18(不自检,
2000deg/s) */
#define ACCEL_CONFIG 0x1C // 加速计自检、测量范围及高通滤波频率,典型值:0x01(不
自检,2G,5Hz) */
#define ACCEL_XOUT_H 0x3B // 存储最近的 X 轴、Y 轴、Z 轴加速度感应器的测量值 */
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41 // 存储的最近温度传感器的测量值 */
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43 // 存储最近的 X 轴、Y 轴、Z 轴陀螺仪感应器的测量值 */
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B // 电源管理,典型值:0x00(正常启用) */
#define WHO_AM_I 0x75 //IIC 地址寄存器(默认数值 0x68,只读) */

初始化

void InitMPU6050()
{
	Single_WriteI2C(PWR_MGMT_1, 0x00);	//解除休眠状态
	Single_WriteI2C(SMPLRT_DIV, 0x07);
	Single_WriteI2C(CONFIG, 0x06);
	Single_WriteI2C(GYRO_CONFIG, 0x18);
	
  Single_WriteI2C(ACCEL_CONFIG, 0x03); 03 //表示设置检测灵敏度为 16位(65536/2*16g=2048)表示每一G加速度变化量为2048
}

LCD1602

该模块用于显示功能此处直接给出相关函数
其中用到LCD1602详细见单片机原理及接口技术第5章的5.5节(单片机控制LCD1602液晶显示器)

void  InitLcd();														//初始化lcd1602
void  lcd_printf(uchar *s,int temp_data);
void  WriteDataLCM(uchar dataW);									//LCD数据
void  WriteCommandLCM(uchar CMD,uchar Attribc);				//LCD指令
void  DisplayOneChar(uchar X,uchar Y,uchar DData);			//显示一个字符
void  DisplayListChar(uchar X,uchar Y,uchar *DData,L);	/

I2C总线传输

void  I2C_Start();
void  I2C_Stop();
void  I2C_SendACK(bit ack);
bit   I2C_RecvACK();
void  I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void  I2C_ReadPage();
void  I2C_WritePage();
uchar Single_ReadI2C(uchar REG_Address);						//读取I2C数据
void  Single_WriteI2C(uchar REG_Address,uchar REG_data);	//向I2C写入数据

算法实现参考一下链接

https://blog.csdn.net/Dancer__Sky/article/details/81504778
算法分为4步

采样

每50次获取动态阀值(最大与最小值)

线性移位器(排除遐思让获取采样值精确)

判定步数

数据分析—>模型
这里拿到的是ADI公司的测试数据,可以看到不管如何佩戴计步器,总有至少一个轴具有相对较大的周期性加速度变化。那么我们就可以从这里着手,进行数据分析,判断步伐。

算法
1,均值滤波器—滤波

均值滤波器实现均值滤波,其实很简单就是拿到多组x,y,z三轴数据,求平均值。最后的平均值作为输出结果,使输出结果更加平滑。完成初步滤波。

下面是伪代码

#define FILTER_CNT			4
 
typedef struct {
  short x;
  short y;
  short z;
}axis_info_t;
 
typedef struct filter_avg{
	axis_info_t info[FILTER_CNT];
	unsigned char count;
}filter_avg_t;

//读取xyz数据存入均值滤波器,存满进行计算,滤波后样本存入sample,如何读取存满就不多说了。

static void filter_calculate(filter_avg_t *filter, axis_info_t *sample)
{
	unsigned int i;
	short x_sum = 0, y_sum = 0, z_sum = 0;
	for (i = 0; i < FILTER_CNT; i++) {
		x_sum += filter->info[i].x;
		y_sum += filter->info[i].y;
		z_sum += filter->info[i].z;
	}
	sample->x = x_sum / FILTER_CNT;
	sample->y = y_sum / FILTER_CNT;
	sample->z = z_sum / FILTER_CNT;
}

2,动态阈值

动态阈值: 系统持续更新3轴加速度的最大值和最小值,每采样50次更新一次。平均值(Max + Min)/2称为"动态阈值"。

对于动态阈值其实就是我们在采样过程中拿到的每个轴的最大值和最小值的平均值,它是每取50个样本就更新一次,是动态变化的。所以我们只需每50次样本,取出其中的最大最小值就行了。最大最小值,还有一个重要的功能,判断最活跃轴,因为我们最后判断步伐,也是根据哪个轴加速度变化最大认为哪个是活跃轴。

下面是动态阈值的伪代码部分

#define MAX(a,b) ((a) > (b) ? (a) : (b)) 
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define SAMPLE_SIZE   50
 
typedef struct {
	axis_info_t newmax;
	axis_info_t newmin;
	axis_info_t oldmax;
	axis_info_t oldmin;
}peak_value_t;
 
static void peak_value_init(peak_value_t *peak);

//在动态阈值结构体初始化时,一定要将max的值都赋值为最小值,min赋值为最大值,这样才有利于动态更新。

static void peak_update(peak_value_t *peak, axis_info_t *cur_sample)
{
  	static unsigned int sample_size = 0;
    sample_size ++;
	if (sample_size > SAMPLE_SIZE) {
		/*采样达到50个,更新一次*/
		sample_size = 1;
		peak->oldmax = peak->newmax;
		peak->oldmin = peak->newmin;
      	//初始化
      	peak_value_init(peak);
	}
	peak->newmax.x = MAX(peak->newmax.x, cur_sample->x);
	peak->newmax.y = MAX(peak->newmax.y, cur_sample->y);
	peak->newmax.z = MAX(peak->newmax.z, cur_sample->z);
	
	peak->newmin.x = MIN(peak->newmin.x, cur_sample->x);
	peak->newmin.y = MIN(peak->newmin.y, cur_sample->y);
	peak->newmin.z = MIN(peak->newmin.z, cur_sample->z);
	
}

3,动态精度

动态精度:这其实是由一个线性移位寄存器加上一个我们预设定的动态变化值来消除高频噪声。其实它的操作很简单,移位寄存器中有两个样本,一个是new_sample,一个是old_sample.只要我们采集到一个样本,当前样本cur_sample.当cur_sample到来,old_sample无条件更新为new_sample的值,但是new_sample就不是无条件更新了,假设我们设定动态精度DYNAMIC_PRECISION=10,当|cur_sample.x-new_sample.x|,如果X轴变化量大于DYNAMIC_PRECISION,那么new_sample.x就更新为cur_sample.x,否则不变。

下面是部分伪代码

#define ABS(a) (0 - (a)) > 0 ? (-(a)) : (a)
#define DYNAMIC_PRECISION     			30     	 /*动态精度*/

/一个线性移位寄存器,用于过滤高频噪声/

typedef struct slid_reg{
	axis_info_t new_sample;
	axis_info_t old_sample;
}slid_reg_t;
 
static char slid_update(slid_reg_t *slid, axis_info_t *cur_sample)
{
  	char res = 0;
  	if (ABS((cur_sample->x - slid->new_sample.x)) > DYNAMIC_PRECISION) {
		slid->old_sample.x = slid->new_sample.x;
		slid->new_sample.x = cur_sample->x;
		res = 1;
	} else {
		slid->old_sample.x = slid->new_sample.x;
	}
	if (ABS((cur_sample->y - slid->new_sample.y)) > DYNAMIC_PRECISION) {
		slid->old_sample.y = slid->new_sample.y;
		slid->new_sample.y = cur_sample->y;
		res = 1;
	} else {
		slid->old_sample.y = slid->new_sample.y;
	}
	
	if (ABS((cur_sample->z - slid->new_sample.z)) > DYNAMIC_PRECISION) {
		slid->old_sample.z = slid->new_sample.z;
		slid->new_sample.z = cur_sample->z;
		res = 1;
	} else {
		slid->old_sample.z = slid->new_sample.z;
	}
	return res;
}

4,步伐判断

步伐判断:通过资料图,我们可以看到,判断为一步的条件是,先找到最活跃轴,然后最活跃轴的old_sample > 动态阈值,new_sample < 动态阈值。满足上述条件,认为走了一步。在我们运动过程,可以认为连续运动大于5步才认为是走步,这样可以过滤一些不必要的错误步数,同时资料所说的最快大约每秒5步,相当于200ms一步,我是利用每40ms取一次数据,200ms得到5组数据,滤波得到一个样本,这样可能更加准确一点。对于文档所说的时间窗口我就没有去做了,通过对比实验结果,我买的华为手环步数和我这个设备的步数基本一致,相差大约在200步以内。

下面是步伐判断伪代码:

#define MOST_ACTIVE_NULL      			0      	 /*未找到最活跃轴*/
#define MOST_ACTIVE_X					1		 /*最活跃轴X*/	
#define MOST_ACTIVE_Y					2        /*最活跃轴Y*/	
#define MOST_ACTIVE_Z					3        /*最活跃轴Z*/	
 
#define ACTIVE_PRECISION      			60       /*活跃轴最小变化值*/
 
/*判断当前最活跃轴*/
static char is_most_active(peak_value_t *peak)
{
	char res = MOST_ACTIVE_NULL;
	short x_change = ABS((peak->newmax.x - peak->newmin.x));
	short y_change = ABS((peak->newmax.y - peak->newmin.y));
	short z_change = ABS((peak->newmax.z - peak->newmin.z));
	
	if (x_change > y_change && x_change > z_change && x_change >= ACTIVE_PRECISION) {
		res = MOST_ACTIVE_X;
	} else if (y_change > x_change && y_change > z_change && y_change >= ACTIVE_PRECISION) {
		res = MOST_ACTIVE_Y;
	} else if (z_change > x_change && z_change > y_change && z_change >= ACTIVE_PRECISION) {
		res = MOST_ACTIVE_Z;
	}
	return res;
}
 
/*判断是否走步*/
static void detect_step(peak_value_t *peak, slid_reg_t *slid, axis_info_t *cur_sample)
{
	static step_cnt = 0;
	char res = is_most_active(peak);
	switch (res) {
		case MOST_ACTIVE_NULL: {
			//fix
			break;
		}
		case MOST_ACTIVE_X: {
			short threshold_x = (peak->oldmax.x + peak->oldmin.x) / 2;
			if (slid->old_sample.x > threshold_x && slid->new_sample.x < threshold_x) {
				step_cnt ++;
			}
			break;
		}
		case MOST_ACTIVE_Y: {
			short threshold_y = (peak->oldmax.y + peak->oldmin.y) / 2;
			if (slid->old_sample.y > threshold_y && slid->new_sample.y < threshold_y) {
				step_cnt ++;
			}
			break;
		}
		case MOST_ACTIVE_Z: {
			short threshold_z = (peak->oldmax.z + peak->oldmin.z) / 2;
			if (slid->old_sample.z > threshold_z && slid->new_sample.z < threshold_z) {
				step_cntt ++;
			}
			break;
		}
		default: 
			break;
	}
}

调试代码(内含详细注释)

//****************************************
// Update to MPU6050 by shinetop
// MCU: STC89C52
// 2012.3.1
// 功能: 显示加速度计和陀螺仪的10位原始数据,以及实现计步,温度显示
//****************************************
// GY-52 MPU3050 IIC测试程序
// 使用单片机STC89C51 
// 晶振:11.0592M
// 显示:LCD1602
// 编译环境 Keil uVision2
// 参考宏晶网站24c04通信程序
// 时间:2019年9月3日

//****************************************
#include <REG52.H>	
#include <math.h>    //Keil library  
#include <stdio.h>   //Keil library	
#include <INTRINS.H>
typedef unsigned char  uchar;
typedef unsigned short ushort;
typedef unsigned int   uint;
//****************************************
// 定义51单片机端口
//****************************************

sbit    SCL=P2^1;			//IIC时钟引脚定义
sbit    SDA=P2^0;			//IIC数据引脚定义
sbit    LCM_RS=P2^6;		//LCD1602命令端口		
sbit    LCM_RW=P2^5;		//LCD1602命令端口		
sbit    LCM_EN=P2^7;		//LCD1602命令端口 
//****************************************
// 定义MPU6050内部地址
//****************************************
#define DataPort P0		//LCD1602数据端口
#define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define	ACCEL_XOUT_H	0x3B
#define	ACCEL_XOUT_L	0x3C
#define	ACCEL_YOUT_H	0x3D
#define	ACCEL_YOUT_L	0x3E
#define	ACCEL_ZOUT_H	0x3F
#define	ACCEL_ZOUT_L	0x40
#define	TEMP_OUT_H		0x41
#define	TEMP_OUT_L		0x42
#define	GYRO_XOUT_H		0x43
#define	GYRO_XOUT_L		0x44	
#define	GYRO_YOUT_H		0x45
#define	GYRO_YOUT_L		0x46
#define	GYRO_ZOUT_H		0x47
#define	GYRO_ZOUT_L		0x48
#define	PWR_MGMT_1		0x6B	//电源管理,典型值:0x00(正常启用)
#define	WHO_AM_I			0x75	//IIC地址寄存器(默认数值0x68,只读)
#define	SlaveAddress	0xD0	//IIC写入时的地址字节数据,+1为读取

//**************************计步器算法*********************
//*********************8*****均值滤波*****************
#define filter_cnt  4

#define MAX(a,b)((a)>(b)?(a):(b))
#define MIN(a,b)((a)<(b)?(a):(b))
#define SAMPLE_SIZE 50
#define ABS(a)(0-(a))>0?((-a)):(a)
#define DYNAMIC_PRECISION  30  // 动态精度值

//******************判断步伐代码******************8*
#define MOST_ACTIVE_NULL 0
#define MOST_ACTIVE_X    1
#define MOST_ACTIVE_Y    2
#define MOST_ACTIVE_Z    3
#define ACTIVE_PRECISION 60       //活跃轴最小判定值//
typedef struct{
  short x;
  short y;
  short z;
  
}axis_info_t;                     //该结构体用于接收X,Y,Z的3轴灵敏度

//****************88*****1动态阀值

typedef struct{
      axis_info_t newmax;
      axis_info_t newmin;
      axis_info_t oldmax;
      axis_info_t oldmin;
}peak_value_t;    // 更新这一时间 与上一时间 最大最小值

typedef struct slid_reg
{
  axis_info_t new_sample;
  axis_info_t old_sample;
  
}slid_reg_t;

axis_info_t  xdata sample;
axis_info_t  xdata cur_sample;
peak_value_t xdata peak;
slid_reg_t   xdata slid;
//****************************************
//定义类型及变量
//****************************************
uchar dis[4];		
int bushu=0;
int step_cnt=0;
uchar DSY_Buffer[5]={0,0,0,0,0};//显示数字(-511至512)的字符数组
int	dis_data;						//变量
int	Temperature,Temp_h,Temp_l;	//温度及高低位数据
uchar dis_temp[2];
//****************************************
//函数声明
//****************************************
void  delay(unsigned int k);										//延时
//LCD相关函数
void  InitLcd();														//初始化lcd1602
void  lcd_printf(uchar *s,int temp_data);
void  WriteDataLCM(uchar dataW);									//LCD数据
void  WriteCommandLCM(uchar CMD,uchar Attribc);				//LCD指令
void  DisplayOneChar(uchar X,uchar Y,uchar DData);			//显示一个字符
void  DisplayListChar(uchar X,uchar Y,uchar *DData,L);	//显示字符串
//MPU6050操作函数
void  InitMPU6050();													//初始化MPU6050
void  Delay5us();
void  I2C_Start();
void  I2C_Stop();
void  I2C_SendACK(bit ack);
bit   I2C_RecvACK();
void  I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void  I2C_ReadPage();
void  I2C_WritePage();
void  display_ACCEL_x();
void  display_ACCEL_y();
void  display_ACCEL_z();
uchar Single_ReadI2C(uchar REG_Address);						//读取I2C数据
void  Single_WriteI2C(uchar REG_Address,uchar REG_data);	//向I2C写入数据
//****************************************
//整数转字符串
//****************************************
void lcd_printf(uchar *s,int temp_data)
{
	if(temp_data<0)
	{
		temp_data=-temp_data;
		*s='-';
	}
	else *s=' ';
	*++s =temp_data/100+0x30;
	temp_data=temp_data%100;     //取余运算
	*++s =temp_data/10+0x30;
	temp_data=temp_data%10;      //取余运算
	*++s =temp_data+0x30; 	
}
//****************************************
//延时
//****************************************
void delay(unsigned int k)	
{						
	unsigned int i,j;				
	for(i=0;i<k;i++)
	{			
		for(j=0;j<121;j++);
	}						
}
//****************************************
//LCD1602初始化
//****************************************
void InitLcd()				
{			
	WriteCommandLCM(0x38,1);	
	WriteCommandLCM(0x08,1);	
	WriteCommandLCM(0x01,1);	
	WriteCommandLCM(0x06,1);	
	WriteCommandLCM(0x0c,1);
	DisplayOneChar(0,0,'A');
	DisplayOneChar(0,1,'J');
  DisplayOneChar(1,1,'B');
  DisplayOneChar(8,1,'C');
  
}			
//****************************************
//LCD1602写允许
//****************************************
void WaitForEnable(void)	
{					
	DataPort=0xff;		
	LCM_RS=0;LCM_RW=1;_nop_();
	LCM_EN=1;_nop_();_nop_();
	while(DataPort&0x80);	
	LCM_EN=0;				
}					
//****************************************
//LCD1602写入命令
//****************************************
void WriteCommandLCM(uchar CMD,uchar Attribc)
{					
	if(Attribc)WaitForEnable();	
	LCM_RS=0;LCM_RW=0;_nop_();
	DataPort=CMD;_nop_();	
	LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}					
//****************************************
//LCD1602写入数据
//****************************************
void WriteDataLCM(uchar dataW)
{					
	WaitForEnable();		
	LCM_RS=1;LCM_RW=0;_nop_();
	DataPort=dataW;_nop_();	
	LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}		

// 自己定义 LCD写命令与写数据。用数组方式实现显示计步数,按照数组个位分别显示

void check_busy(void)	    //检查忙标志函数
{
	uchar dt;
	do
	{
		dt=0xff;
		LCM_EN=0;
		LCM_RS=0;	
		LCM_RW=1;
        LCM_EN=1;
        dt=DataPort;
    }while(dt&0x80);
    LCM_EN=0;
}
void write_command(uchar com) 	//写命令函数
{
check_busy();
LCM_EN=0;
LCM_RS=0;
LCM_RW=0;
DataPort=com;
LCM_EN=1;
_nop_( );
LCM_EN=0;
  _nop_();_nop_();
//delay2(1);
}

void write_data(uchar dat)		 //写数据函数
{ check_busy();
	LCM_EN=0;
	LCM_RS=1;
	LCM_RW=0;
	DataPort=dat;
	LCM_EN=1;
	_nop_();
	LCM_EN=0;
  _nop_();_nop_();
	//delay2(1);	
}

void string(uchar ad,uchar s)	//输出显示字符串的函数
{
    write_command(ad);
    write_data((0x30+s));
   
}
//****************************************
//LCD1602写入一个字符
//****************************************
void DisplayOneChar(uchar X,uchar Y,uchar DData)
{						
	Y&=1;						
	X&=15;						
	if(Y)X|=0x40;					
	X|=0x80;			
	WriteCommandLCM(X,0);		
	WriteDataLCM(DData);		
}						
//****************************************
//LCD1602显示字符串
//****************************************
void DisplayListChar(uchar X,uchar Y,uchar *DData,L)
{
	uchar ListLength=0; 
	Y&=0x1;                
	X&=0xF;                
	while(L--)             
	{                       
		DisplayOneChar(X,Y,DData[ListLength]);
		ListLength++;  
		X++;                        
	}    
}
//**************************************
//延时5微秒(STC90C52RC@12M)
//不同的工作环境,需要调整此函数
//当改用1T的MCU时,请调整此延时函数
//**************************************
void Delay5us()
{
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
}
//**************************************
//I2C起始信号
//**************************************
void I2C_Start()
{
    SDA = 1;                    //拉高数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 0;                    //产生下降沿
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
}
//**************************************
//I2C停止信号
//**************************************
void I2C_Stop()
{
    SDA = 0;                    //拉低数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 1;                    //产生上升沿
    Delay5us();                 //延时
}
//**************************************
//I2C发送应答信号
//入口参数:ack (0:ACK 1:NAK)
//**************************************
void I2C_SendACK(bit ack)
{
    SDA = ack;                  //写应答信号
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
}
//**************************************
//I2C接收应答信号
//**************************************
bit I2C_RecvACK()
{
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    CY = SDA;                   //读应答信号
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
    return CY;
}
//**************************************
//向I2C总线发送一个字节数据
//**************************************
void I2C_SendByte(uchar dat)
{
    uchar i;
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    I2C_RecvACK();
}
//**************************************
//从I2C总线接收一个字节数据
//**************************************
uchar I2C_RecvByte()
{
    uchar i;
    uchar dat = 0;
    SDA = 1;                    //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        dat |= SDA;             //读数据               
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    return dat;
}
//**************************************
//向I2C设备写入一个字节数据
//**************************************
void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
    I2C_Start();                  //起始信号
    I2C_SendByte(SlaveAddress);   //发送设备地址+写信号
    I2C_SendByte(REG_Address);    //内部寄存器地址,
    I2C_SendByte(REG_data);       //内部寄存器数据,
    I2C_Stop();                   //发送停止信号
}
//**************************************
//从I2C设备读取一个字节数据
//**************************************
uchar Single_ReadI2C(uchar REG_Address)
{
	uchar REG_data;
	I2C_Start();                   //起始信号
	I2C_SendByte(SlaveAddress);    //发送设备地址+写信号
	I2C_SendByte(REG_Address);     //发送存储单元地址,从0开始	
	I2C_Start();                   //起始信号
	I2C_SendByte(SlaveAddress+1);  //发送设备地址+读信号
	REG_data=I2C_RecvByte();       //读出寄存器数据
	I2C_SendACK(1);                //接收应答信号
	I2C_Stop();                    //停止信号
	return REG_data;
}
//**************************************
//初始化MPU6050
//**************************************
void InitMPU6050()
{
	Single_WriteI2C(PWR_MGMT_1, 0x00);	//解除休眠状态
	Single_WriteI2C(SMPLRT_DIV, 0x07);
	Single_WriteI2C(CONFIG, 0x06);
	Single_WriteI2C(GYRO_CONFIG, 0x18);
	//Single_WriteI2C(ACCEL_CONFIG, 0x01);
  Single_WriteI2C(ACCEL_CONFIG, 0x03);
}
//**************************************
//合成数据
//**************************************
int GetData(uchar REG_Address)
{
	char H,L;
	H=Single_ReadI2C(REG_Address);
	L=Single_ReadI2C(REG_Address+1);
	return (H<<8)+L;   //合成数据
}
//**************************************
//在1602上显示10位数据
//**************************************
void Display10BitData(int value,uchar x,uchar y)
{
	value/=64;							//转换为10位数据
	lcd_printf(dis, value);			//转换数据显示
	DisplayListChar(x,y,dis,4);	//启始列,行,显示数组,显示长度
}
//**************************************
//显示温度注意该测量出来的为华氏度 ,需要使用摄氏度转换
//**************************************
void display_temp()
{ 
	Temp_h=Single_ReadI2C(TEMP_OUT_H); //读取温度
	Temp_l=Single_ReadI2C(TEMP_OUT_L); //读取温度
	Temperature=Temp_h<<8|Temp_l;     //合成温度
	Temperature = 35+ ((double) (Temperature + 13200)) / 280; // 计算出温度
  Temperature=(Temperature-32)/1.8;
  dis_temp[0]=Temperature/10;
  dis_temp[1]=Temperature%10;
  string(0xC9,dis_temp[0]);
  string(0xCa,dis_temp[1]);
	//lcd_printf(dis,Temperature);     //转换数据显示
	//DisplayListChar(11,1,dis,4);     //启始列,行,显示数组,显示位数
}
//*********************************************************
//主程序
//*********************************************************
// ************初始化 采样结构体的值,最大变成最小,最小变成最大***********
static void peak_value_init(peak_value_t *peak)
{
      peak->newmax.x=0;
      peak->newmax.y=0;
      peak->newmax.z=0;
     
      peak->newmin.x=2048;
      peak->newmin.y=2048;
      peak->newmin.z=2048;
     
}
static void peak_update(peak_value_t *peak,axis_info_t *cur_sample)
{
       uint sample_size=0;
       sample_size ++;
      if(sample_size>SAMPLE_SIZE)
      {
            sample_size=0;
            peak->oldmax=peak->newmax;
            peak->oldmin=peak->newmin;
            peak_value_init(peak);
      }
      peak->newmax.x=MAX(peak->newmax.x,cur_sample->x);
      peak->newmax.y=MAX(peak->newmax.y,cur_sample->y);
      peak->newmax.z=MAX(peak->newmax.z,cur_sample->z);
      
      peak->newmin.x=MIN(peak->newmin.x,cur_sample->x);
      peak->newmin.y=MIN(peak->newmin.y,cur_sample->y);
      peak->newmin.z=MIN(peak->newmin.y,cur_sample->z);
}
//*********8*****动态精度********

static void slid_update(slid_reg_t *slid,axis_info_t *cur_sample)
{
    //char res=0;
    if(ABS((cur_sample->x-slid->new_sample.x))>DYNAMIC_PRECISION)
    {
        slid->old_sample.x=slid->new_sample.x;
        slid->new_sample.x=cur_sample->x;
        //res=1;
    }
    else
    {
        slid->old_sample.x=slid->new_sample.x;
    }
    //char res=0;
    if(ABS((cur_sample->y-slid->new_sample.y))>DYNAMIC_PRECISION)
    {
        slid->old_sample.y=slid->new_sample.y;
        slid->new_sample.y=cur_sample->y;
        //res=1;
    }
    else
    {
        slid->old_sample.y=slid->new_sample.y;
    }
    //char res=0;
    if(ABS((cur_sample->z-slid->new_sample.z))>DYNAMIC_PRECISION)
    {
        slid->old_sample.z=slid->new_sample.z;
        slid->new_sample.z=cur_sample->z;
        //res=1;
    }
    else
    {
        slid->old_sample.z=slid->new_sample.z;
    }
        //return res;
}

static char is_most_active(peak_value_t *peak)
{
    char res=MOST_ACTIVE_NULL;
    short x_change =ABS((peak->newmax.x-peak->newmin.x));
    short y_change =ABS((peak->newmax.y-peak->newmin.y));
    short z_change =ABS((peak->newmax.z-peak->newmin.z));
    
  
    if(x_change>y_change&&x_change>z_change&&x_change>=ACTIVE_PRECISION)
    {res=MOST_ACTIVE_X;}
    else if(y_change>x_change&&y_change>z_change&&y_change>=ACTIVE_PRECISION)
    { res=MOST_ACTIVE_Y;}
    
    else if(z_change>x_change&&z_change>y_change&&z_change>=ACTIVE_PRECISION)
    {res=MOST_ACTIVE_Z;}
    return res;
}
static int detect_step(peak_value_t *peak,slid_reg_t *slid)
{
      //char flag=0;
        
        char res=is_most_active(peak);
      switch (res)
      {
        case MOST_ACTIVE_NULL:
      {
          break;
      }
      case MOST_ACTIVE_X:
      {
        short threshold_x=(peak->oldmax.x+peak->oldmin.x)/2;
        if(slid->old_sample.x>threshold_x&&slid->new_sample.x<threshold_x)
        {
          //flag=1;
          step_cnt++;
        }
        break;
      }
      case MOST_ACTIVE_Y:
      {
          short threshold_y=(peak->oldmax.y+peak->oldmin.y)/2;
           if(slid->old_sample.y>threshold_y&&slid->new_sample.y<threshold_y)
           {
             //flag=1;
              step_cnt++;
           }
           break;
      }
      case MOST_ACTIVE_Z:
      {
          short threshold_z=(peak->oldmax.z+peak->oldmin.y)/2;
           if(slid->old_sample.z>threshold_z&&slid->new_sample.z<threshold_z)
           {
             //flag=1;
              step_cnt++;
           }
           break;
      }
      default:
        break;
      }
      return step_cnt;
      
}

void main()
{ 
  uint X,Y,Z;
  uint Num;
	delay(500);		//上电延时		
	InitLcd();		//液晶初始化
	InitMPU6050();
  peak_value_init(&peak);
	//初始化MPU6050
	delay(150);
	while(1)
	{
    X=GetData(ACCEL_XOUT_H);
    Y=GetData(ACCEL_YOUT_H);
    Z=GetData(ACCEL_ZOUT_H);
    sample.x=X;
    sample.y=Y;
    sample.z=Z;
  
    peak_update(&peak,&sample);
    slid_update(&slid,&sample);
    
		Display10BitData(GetData(ACCEL_XOUT_H),2,0);	//显示X轴加速度
		Display10BitData(GetData(ACCEL_YOUT_H),7,0);	//显示Y轴加速度
		Display10BitData(GetData(ACCEL_ZOUT_H),12,0);  //显示Z轴加速度
    Num=detect_step(&peak,&slid);
    
    DSY_Buffer[0] = Num/10000;
    DSY_Buffer[1] = Num/1000%10;
    DSY_Buffer[2] = Num/100%10;
    
		DSY_Buffer[3] = Num/10%10;
		DSY_Buffer[4] = Num%10;  
    string(0xC2,DSY_Buffer[0]);	//显示的第2行字符串,,从左边第2个字符开始显示
    string(0xC3,DSY_Buffer[1]);
    string(0xC4,DSY_Buffer[2]);
    string(0xC5,DSY_Buffer[3]);
    string(0xC6,DSY_Buffer[4]);
    
    display_temp();
    
		//Display10BitData(GetData(GYRO_XOUT_H),2,1);	//显示X轴角速度
		//Display10BitData(GetData(GYRO_YOUT_H),7,1);	//显示Y轴角速度
		//Display10BitData(GetData(GYRO_ZOUT_H),12,1);	//显示Z轴角速度
		delay(500);
	}
}```

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值