Bootstrap

基于STM32C8T6的ADXL345加速度传感器计步器

1.ADXL345工作原理

1.1 基本参数

工作电压范围 :2.0 ~ 3.6 V ;

温度范围 :-40℃至+105℃ ;

分辨率 高(13位),测量范围达± 16g ;

数字输出数据为16位二进制 补码格式 ;

1.2  引脚介绍

我购买的ADXL345的引脚有 SCL、SDA、SDO、INT1、INT2、CS、VCC、GND,本计步器没有用到INT1和INT2中断引脚。

1.3 通信方式

ADXL345加速度传感器的通信方式有SPI和IIC两种通信方式。由于我用的是IIC的通信方式,因此在这里就不多介绍SPI了,感兴趣的可以去找数据手册看介绍。

引脚连接图

CS'是片选信号,低电平时是选择SPI作为通信方式,高电平时选择IIC作为通信方式;ALT ADDRESS引脚处于高电平,器件的7位I2C地址是0x1D,随后为R/W位。这转化为 0x3A写入,0x3B读取;通过ALT ADDRESS引脚(SDO)接地, 可以选择备用I2C地址0x53(随后为R/W位)。这转化为0xA6写 入,0xA7读取。ADXL345没有内部上拉或下拉电阻,因此在接线时建议SDA和SCL上拉4.7KΩ-10KΩ的电阻。

IIC支持的通信速率是标准 (100 kHz)和快速(400 kHz)数据传输模式。

1.4 IIC写一个字节、写多个字节、读取一个字节、读多个字节的器件寻址过程。

1.5 官方给的ADXL345最小初始化序列

但是拿到的每一个ADXL345输出的数据与典型值都会有一定的偏差,如果有需求可以在初始化使用偏移寄存器校准;没有校准时,如果将ADXL345平放在桌面,X、Y、Z轴的输出数据可能是X=+2LSB,Y=-20LSB,Z=292LSB(这个数值是我在测试过程中测到的,不代表每一个都这样,仅供参考)。水平放置时,x读数+2,y读数-20, Z读数292;这时,正常值应该是0,0,255,因此需要把偏移量设置为-2, +20, -37。但是,ADXL345默认13位分辨率,16g量程时,1 LSB为1/256 = 3.9mg/LSB。偏移寄存器为15.6mg/LSB,所以偏移寄存器写1个LSB,相当于偏移了4个LSB,故需将差值除4的结果写入对应偏移器即可。

2.ADXL345初始化,写/读字节/从ADXL345读取加速度传感器数据

2.1 初始化

      使用的是软件IIC,配置过程借鉴的江协科技;

      由于本计步器处理数据时一般是相对值,所以对于使用寄存器偏移校准没有强制的要求。因此随机找了网上用的比较多的初始化。

void ADXL345_Init(void)
{
	MyI2C_adxl345_Init();
	
	ADXL345_WriteReg(0x31,0x0B);   //测量范围,正负16g,13位模式
	ADXL345_WriteReg(0x2C,0x08);   //速率设定为12.5 参考pdf13页
	ADXL345_WriteReg(0x2D,0x08);   //选择电源模式   参考pdf24页
	ADXL345_WriteReg(0x2E,0x80);   //使能 DATA_READY 中断
//下列的偏移量可根据具体需求更改
	ADXL345_WriteReg(0x1E,0x00);   //X 偏移量 根据测试传感器的状态写入
	ADXL345_WriteReg(0x1F,0x00);   //Y 偏移量 根据测试传感器的状态写入
	ADXL345_WriteReg(0x20,0x05);   //Z 偏移量 根据测试传感器的状态写入
}

2.2 STM32C8T6向ADXL345写入一个字节

void ADXL345_WriteReg(uint8_t RegAddress,uint8_t Data)
{
	MyI2C_adxl345_Init();
	
	MyI2C_adxl345_Start();						//I2C起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(Data);				//发送要写入寄存器的数据
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_Stop();						//I2C终止
}

2.3 STM32C8T6从ADXL345 读取一个字节

uint8_t ADXL345_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_adxl345_Start();						//I2C起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_adxl345_ReceiveAck();					//接收应答
	
	MyI2C_adxl345_Start();							//I2C重复起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_adxl345_ReceiveAck();					//接收应答
	Data = MyI2C_adxl345_ReceiveByte();			//接收指定寄存器的数据
	MyI2C_adxl345_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_adxl345_Stop();						//I2C终止
	
	return Data;
}

2.4 STM32C8T6从ADXL345的FIFO中读取数据(多个字节读取)

X、Y、Z轴的数据存储在地址为0x32~0x37中,输出数据为二进制补码,DATAx0为最低有 效字节,DATAx1为最高有效字节。

void ADXL345_ReadReg_Get_Data(short *x,short *y,short *z)
{
	int8_t i;
	u8 BUF[6];
	MyI2C_adxl345_Start();						//I2C起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(0x32);			//发送寄存器地址
	MyI2C_adxl345_ReceiveAck();	//接收应答
	
	MyI2C_adxl345_Start();							//I2C重复起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_adxl345_ReceiveAck();					//接收应答
	
	for (i=0;i<6;i++)
	{
		BUF[i] = MyI2C_adxl345_ReceiveByte();
		if(i == 5)
		{
			MyI2C_adxl345_SendAck(1);
		}
		else
			MyI2C_adxl345_SendAck(0);
	}
	
	MyI2C_adxl345_Stop();						//I2C终止	
	
	*x=(short)(((u16)BUF[1]<<8)+BUF[0]); // 合成数据    
	*y=(short)(((u16)BUF[3]<<8)+BUF[2]); 	    
	*z=(short)(((u16)BUF[5]<<8)+BUF[4]); 

	
}

初始化完ADXL345、能写入\读取一个字节和得到加速度传感器数据之后,可根据计步器算法进行数据处理和判断。

3. 基于三轴加速度传感器ADXL345的计步器算法

如下算法介绍中的数据均是ADI公司的,附上我的一些理解(仅供参考)利用 3 轴数字加速度计实现功能全面的计步器设计 | Analog Devices

程序借鉴了一个博主的思路:基于三轴加速度传感器的计步算法_三轴加速度推测步幅-CSDN博客

看完ADI官方的一些关于ADXL345计步器算法的介绍和网上查的一些资料,我对这个实现过程计步器的理解:各获取xyz轴的4个数据滤波作为一个样本,然后实现采集50样本之后,在采集50样本过程找出最大样本和最小样本的值的平均值作为动态阈值并实时更新old_sample,new_sample的值,old_sample始终无条件等于new_sample,而new_sample是否等于新采集的样本取决于ABS(cur_sample-newsaple)的值是否大于动态精度(这个值可根据需求设定),大于就更新new_sample=cur_sample,小于则new_sample不变,最终50个样本的动态阈值和old_sample,new_sample移位寄存器的数据更新好后,找出这50个数据中最活跃的轴,最活跃的轴的判断方法是找出50个样本的某个轴上的最大样本值减去最小样本值得绝对值大于40,则对应的轴就是最活跃的轴,找出该轴后,用new_sample和old_sample的值和动态阈值比较,如果满足最活跃轴的old_sample > 动态阈值,new_sample < 动态阈值,则计算为1步,也就是说找到最活跃轴后,一步的完成过程中加速度的大小是先增后减的。

3.1 加速度和走步的关系

可见一个步伐过程中会出现前进方向上加速度变大然后减小的过程;

显示了与一名跑步者的竖向、前向和侧向加速度相对应的x、y和z轴测量结果的典型图样。无论如何穿戴计步器,总有至少一个轴具有相对较大的周期性加速度变化,因此峰值检测和针对所有三个轴上的加速度的动态阈值决策算法对于检测单位步行或跑步周期至关重要。

3.2  均值滤波器

均值滤波器实现均值滤波,拿到多组x,y,z三轴数据,求平均值。最后的平均值作为一个样本,使输出样本更加平滑。

typedef struct {
  short x;
  short y;
  short z;
}axis_info_t;


typedef struct filter_avg{
	axis_info_t info[FILTER_CNT];
}filter_avg_t;

//读取xyz数据存入均值滤波器,存满进行计算,滤波后样本存入sample
void filter_calculate(filter_avg_t *filter, axis_info_t *sample)
{
	uint8_t i;
	short x_sum = 0, y_sum = 0, z_sum = 0;
	
	for (i = 0; i < FILTER_CNT; i++) {
		ADXL345_ReadReg_Get_Data(&filter->info[i].x, &filter->info[i].y, &filter->info[i].z);
		x_sum += filter->info[i].x;
		y_sum += filter->info[i].y;
		z_sum += filter->info[i].z;
	}
	sample->x = x_sum / FILTER_CNT; //FILTER_CNT为4
	sample->y = y_sum / FILTER_CNT;
	sample->z = z_sum / FILTER_CNT;
}

3.3 动态阈值

动态阈值: 系统持续更新3轴加速度的最大值和最小值,每采样50次更新一次。平均值(Max + Min)/2称为"动态阈值"。接下来的50次采样(50个样本)利用此阈值判断个体是否迈出步伐。由于此阈值每50次采样更新一次,因此它是动态的。这种选择具有自适应性,并且足够快。

typedef struct {
	axis_info_t newmax;
	axis_info_t newmin;
	axis_info_t oldmax;
	axis_info_t oldmin;
}peak_value_t;

//50个样本中找出最大最小值的平均值作为动态阈值
//找出最大值和最小值

void peak_update(peak_value_t *peak, axis_info_t *sample)
{
	int8_t i;
	filter_avg_t filter;
	short min_value = -32768;
	short max_value = 32767;
	peak->newmax.x = min_value; 
	peak->newmax.y = min_value;
	peak->newmax.z = min_value;
	
	peak->newmin.x = max_value;
	peak->newmin.y = max_value;
	peak->newmin.z = max_value;
	for (i = 0; i < SAMPLE_SIZE; i++) {
		filter_calculate(&filter,sample);
		
		peak->newmax.x = MAX(peak->newmax.x, sample->x);
		peak->newmax.y = MAX(peak->newmax.y, sample->y);
		peak->newmax.z = MAX(peak->newmax.z, sample->z);
	
		peak->newmin.x = MIN(peak->newmin.x, sample->x);
		peak->newmin.y = MIN(peak->newmin.y, sample->y);
		peak->newmin.z = MIN(peak->newmin.z, sample->z);
	
	}	
	peak->oldmax.x = peak->newmax.x;
	peak->oldmax.y = peak->newmax.y;
	peak->oldmax.z = peak->newmax.z;
	
	peak->oldmin.x = peak->newmin.x;
	peak->oldmin.y = peak->newmin.y;
	peak->oldmin.z = peak->newmin.z;
	
}

3.4 动态精度

利用一个线性移位寄存器和动态阈值判断个体是否有效地迈出一步。该线性移位寄存器含有2个寄存器:sample_new寄存器和sample_old寄存器。这些寄存器中的数据分别称为sample_new和sample_old。当新采样数据到来时,sample_new无条件移入sample_old寄存器。然而,sample_result是否移入sample_new寄存器取决于下述条件:如果加速度变化大于预定义精度,则最新的采样结果sample_result移入sample_new寄存器,否则sample_new寄存器保持不变。因此,移位寄存器组可以消除高频噪声,从而保证结果更加精确。

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


void slid_update(slid_reg_t *slid, axis_info_t *cur_sample)
{
	filter_avg_t filter;
	filter_calculate(&filter, cur_sample);
	cur_sample->x = cur_sample->x;
	cur_sample->y = cur_sample->y;
	cur_sample->z = cur_sample->z;
	
	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;
	} 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;
	} 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;
	} else {
		slid->old_sample.z = slid->new_sample.z;
	}
}

3.5 步伐判断

步伐迈出的条件定义为:当加速度曲线跨过动态阈值下方时,加速度曲线的斜率为负值。判断条件:先找到最活跃轴,然后最活跃轴的old_sample > 动态阈值,new_sample < 动态阈值。满足上述条件,认为走了一步。

char is_most_active(peak_value_t *peak)
{
	char res;
	short x_change;
	short y_change;
	short z_change;
	axis_info_t cur_sample;
	peak_update(peak,&cur_sample);
	res = MOST_ACTIVE_NULL;
	
	x_change = ABS((peak->newmax.x - peak->newmin.x));
	y_change = ABS((peak->newmax.y - peak->newmin.y));
	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;
}



/*判断是否走步*/
int8_t detect_step(peak_value_t *peak, slid_reg_t *slid, axis_info_t *cur_sample)
{
	static int8_t step_cnt = 0;
	uint32_t step_interval;
	filter_avg_t filter;
	char res;
	res = is_most_active(peak);
	slid_update(slid, cur_sample);
	
	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)
			{
				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_cnt ++;
				}
			break;
		}
		default: 
			break;
	}
	return step_cnt;
}

步伐计数器利用此算法可以很好地工作,但有时显得太敏感。当计步器因为步行或跑步之外的原因而非常迅速或非常缓慢地振动时,步伐计数器也会认为它是步伐。为了找到真正的有节奏的步伐,必须排除这种无效振动。利用"时间窗口"和"计数规则"可以解决这个问题。(这个时间窗口没来得及完成,等后续更新)。

附:

以下附上ADXL345.c的程序和主函数测试部分

#include "stm32f10x.h"                  // Device header
#include "MyI2C_adxl345.h"
#include "delay.h"

#define ADXL345_ADDRESS   0xA6  // ALT ADDRESS引脚接地,0xA6写入,0xA7读取

#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   	 /*动态精度*/

#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      			40       /*活跃轴最小变化值*/



void ADXL345_WriteReg(uint8_t RegAddress,uint8_t Data)
{
	MyI2C_adxl345_Init();
	
	MyI2C_adxl345_Start();						//I2C起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(Data);				//发送要写入寄存器的数据
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_Stop();						//I2C终止
}



uint8_t ADXL345_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_adxl345_Start();						//I2C起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_adxl345_ReceiveAck();					//接收应答
	
	MyI2C_adxl345_Start();							//I2C重复起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_adxl345_ReceiveAck();					//接收应答
	Data = MyI2C_adxl345_ReceiveByte();			//接收指定寄存器的数据
	MyI2C_adxl345_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_adxl345_Stop();						//I2C终止
	
	return Data;
}

void ADXL345_ReadReg_Get_Data(short *x,short *y,short *z)
{
	int8_t i;
	u8 BUF[6];
	MyI2C_adxl345_Start();						//I2C起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_adxl345_ReceiveAck();					//接收应答
	MyI2C_adxl345_SendByte(0x32);			//发送寄存器地址
	MyI2C_adxl345_ReceiveAck();	//接收应答
	
	MyI2C_adxl345_Start();							//I2C重复起始
	MyI2C_adxl345_SendByte(ADXL345_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_adxl345_ReceiveAck();					//接收应答
	
	for (i=0;i<6;i++)
	{
		BUF[i] = MyI2C_adxl345_ReceiveByte();
		if(i == 5)
		{
			MyI2C_adxl345_SendAck(1);
		}
		else
			MyI2C_adxl345_SendAck(0);
	}
	
	MyI2C_adxl345_Stop();						//I2C终止	
	
	*x=(short)(((u16)BUF[1]<<8)+BUF[0]); // 合成数据    
	*y=(short)(((u16)BUF[3]<<8)+BUF[2]); 	    
	*z=(short)(((u16)BUF[5]<<8)+BUF[4]); 

	
}


void ADXL345_Init(void)
{
	MyI2C_adxl345_Init();
	
	ADXL345_WriteReg(0x31,0x0B);   //测量范围,正负4g,13位模式
	ADXL345_WriteReg(0x2C,0x08);   //速率设定为50Hz 参考pdf13页
	ADXL345_WriteReg(0x2D,0x08);   //选择电源模式   参考pdf24页
	ADXL345_WriteReg(0x2E,0x80);   //使能 DATA_READY 中断
	ADXL345_WriteReg(0x1E,0x00);   //X 偏移量 根据测试传感器的状态写入pdf29页
	ADXL345_WriteReg(0x1F,0x00);   //Y 偏移量 根据测试传感器的状态写入pdf29页
	ADXL345_WriteReg(0x20,0x05);   //Z 偏移量 根据测试传感器的状态写入pdf29页
}


typedef struct {
  short x;
  short y;
  short z;
}axis_info_t;


typedef struct filter_avg{
	axis_info_t info[FILTER_CNT];
}filter_avg_t;

//读取xyz数据存入均值滤波器,存满进行计算,滤波后样本存入sample
void filter_calculate(filter_avg_t *filter, axis_info_t *sample)
{
	uint8_t i;
	short x_sum = 0, y_sum = 0, z_sum = 0;
	
	for (i = 0; i < FILTER_CNT; i++) {
		ADXL345_ReadReg_Get_Data(&filter->info[i].x, &filter->info[i].y, &filter->info[i].z);
		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;
}


typedef struct {
	axis_info_t newmax;
	axis_info_t newmin;
	axis_info_t oldmax;
	axis_info_t oldmin;
}peak_value_t;

//50个样本中找出最大最小值作为动态阈值
//找出最大值和最小值

void peak_update(peak_value_t *peak, axis_info_t *sample)
{
	int8_t i;
	filter_avg_t filter;
	short min_value = -32768;
	short max_value = 32767;
	peak->newmax.x = min_value;
	peak->newmax.y = min_value;
	peak->newmax.z = min_value;
	
	peak->newmin.x = max_value;
	peak->newmin.y = max_value;
	peak->newmin.z = max_value;
	for (i = 0; i < SAMPLE_SIZE; i++) {
		filter_calculate(&filter,sample);
		
		peak->newmax.x = MAX(peak->newmax.x, sample->x);
		peak->newmax.y = MAX(peak->newmax.y, sample->y);
		peak->newmax.z = MAX(peak->newmax.z, sample->z);
	
		peak->newmin.x = MIN(peak->newmin.x, sample->x);
		peak->newmin.y = MIN(peak->newmin.y, sample->y);
		peak->newmin.z = MIN(peak->newmin.z, sample->z);
	
	}	
	peak->oldmax.x = peak->newmax.x;
	peak->oldmax.y = peak->newmax.y;
	peak->oldmax.z = peak->newmax.z;
	
	peak->oldmin.x = peak->newmin.x;
	peak->oldmin.y = peak->newmin.y;
	peak->oldmin.z = peak->newmin.z;
	
}


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

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


void slid_update(slid_reg_t *slid, axis_info_t *cur_sample)
{
	filter_avg_t filter;
	filter_calculate(&filter, cur_sample);
	cur_sample->x = cur_sample->x;
	cur_sample->y = cur_sample->y;
	cur_sample->z = cur_sample->z;
	
	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;
	} 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;
	} 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;
	} else {
		slid->old_sample.z = slid->new_sample.z;
	}
}


char is_most_active(peak_value_t *peak)
{
	char res;
	short x_change;
	short y_change;
	short z_change;
	axis_info_t cur_sample;
	peak_update(peak,&cur_sample);
	res = MOST_ACTIVE_NULL;
	
	x_change = ABS((peak->newmax.x - peak->newmin.x));
	y_change = ABS((peak->newmax.y - peak->newmin.y));
	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;
}



/*判断是否走步*/
int8_t detect_step(peak_value_t *peak, slid_reg_t *slid, axis_info_t *cur_sample)
{
	static int8_t step_cnt = 0;
	uint32_t step_interval;
	filter_avg_t filter;
	char res;
	res = is_most_active(peak);
	slid_update(slid, cur_sample);
	
	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_cnt ++;
				}
			break;
		}
		default: 
			break;
	}
	return step_cnt;
}




main.c

#include "stm32f10x.h"
#include "stdio.h"
#include <string.h>
#include "delay.h"
#include "OLED.h"
#include "LED.h"
#include "Serial.h"
#include "ADXL345.h"

volatile int ADXL345_OK = 0;
int8_t Check_ADXL345_Status(void)
{
    if (ADXL345_ReadReg(0x00) == 0xE5)
    {
        ADXL345_OK = 1;
    }
    else
    {
        ADXL345_OK = 0;
    }
	return ADXL345_OK;
}

int main()
{
    peak_value_t peak ;
	axis_info_t cur_sample;
	slid_reg_t slid;

    ADXL345_Init();
	delay_ms(12);
	Check_ADXL345_Status();
    while (1)
    {
        cnt = detect_step(&peak,&slid,&cur_sample);
		OLED_ShowSignedNum(1, 7, cnt, 4);
    }

}

一些测试过程

//测试4个数据滤波后数据
filter_calculate(&filter, &sample);
		
Serial_Printf("sample.x: %d\r\n",sample.x);
Serial_Printf("sample.y: %d\r\n",sample.y);
Serial_Printf("sample.z: %d\r\n",sample.z);

//测试动态阈值的更新		
peak_update(&peak,&sample);
Serial Prtinf("peak.oldmin.x: %d\r\n", peak.oldmin.x);
Serial Prtinf("peak.oldmin.y: %d\r\n", peak.oldmin.y);
Serial Prtinf("peak.oldmin.z: %d\r\n", peak.oldmin.z);

//测试是否更新样本数据
slid_update(&slid, &cur_sample);
Serial_Printf("cur_sample.z: %d\r\n", cur_sample.z);
Serial_Printf("slid.new_sample.z: %d\r\n", slid.new_sample.z);
Serial_Printf("slid.old_sample.z: %d\r\n", slid.old_sample.z);

//测试步数

//测试步数	
res = is_most_active(&peak);
Serial_Printf("res:%d\r\n", res);
cnt = detect_step(&peak,&slid,&cur_sample);
Serial_Printf("cur_sample.z: %d\r\n",cur_sample.z);
Serial_Printf("threshold_z: %d\r\n", (peak.oldmax.z + peak.oldmin.z) / 2);
Serial_Printf("slid->old_sample.z: %d\r\n", slid.old_sample.z);
Serial_Printf("slid->new_sample.z: %d\r\n", slid.new_sample.z);
Serial_Printf("cnt:%d\r\n", cnt);

;