基于STM32单片机的步数测量系统(免费开源)

基于STM32单片机的步数测量系统

1.1设计要求

设计一款基于STM32的步数测量系统,要求数据可以传输到云端和OLED屏幕显示以及手机APP显示。

1.2设计所需器材

STM32F407 MPU6050,ESP8266,OLED,一部安卓手机

1.3 设计原理

(1)人们的对于生命健康问题越来越重视,身体锻炼越来越在中国大地形成风潮。人们最喜爱乐见的运动方式是跑步和散步。计步器携带方便,准确度高,能够很好地对运动量进行量化。所以,以手环形式的计步器以及以微信运动QQ运动等运动APP在近些年大量出现。鉴于人们对运动量化的诉求,专门的计步器很有研究意义。
本设计的目的是基于MPU6050陀螺仪与ESP8266以及STM32单片机设计一款高精度的带互联网功能的便携计步器。本设计的的主要工作集中在了MPU6050陀螺仪数据的读取和处理、ESP8266WIFI模块的数据通信传输以及上位机端“步数测量系统”APP的安卓端开发。本设计,利用了MPU6050DMP库的计步算法实现了对步数的超高精度的测量,设计了OLED人机交互界面,可以实时显示步数,目标运动量,运动时间,消耗卡路里。最后,专门设计了一款“步数测量系统”的安卓端APP,用户可以方便的通过APP进行查看相关运动数据。

[关键字]:STM32;MPU6050;计步;ESP8266;高精度
本电子计步器需要对MPU6050陀螺仪的数据处理,所以需要一款性能还不错的微处理器做为主芯片。需要人机交互的OLED屏幕,按键切换用户界面,以及ESP8266—01S WIFI模块数据传输到云端,进而手机APP显示。
基于STM32单片机的步数测量系统(免费开源)_第1张图片
(2)MPU6050简介
MPU6050 是 InvenSense 公司推出的全球首款整合性 6 轴运动处理组件,相较于多组件方 案,免除了组合陀螺仪与加速器时之轴间差的问题,减少了安装空间。 MPU6050 内部整合了 3 轴陀螺仪和 3 轴加速度传感器,并且含有一个IIC接口,可用 于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor)硬件 加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。

基于STM32单片机的步数测量系统(免费开源)_第2张图片
(3)计步原理
人体在走动的过程中,一定伴随的手臂的摇摆,手臂的摇摆一定会引起加速度的变化,加速度的变化会引起MPU6050传感器数据的变化,通过对MPU6050加速度数据寄存器,偏向角数据寄存器数据的读取,进一步数据滤波和处理之后就可以得到步数。

基于STM32单片机的步数测量系统(免费开源)_第3张图片
(4) ESP8266WIFI模块
由于本次设计不仅仅需要将数据显示在OLED显示屏上面,还需要将数据显示到云端和APP上面,所以需要将数据利用WiFi模块进行数据无线传输。由于ESP8266WiFi模块,其尺寸小,传输温度,价格低,所以是本次设计的最佳选择。本次实验使用ESP82660的01S版本。ESP8266与单片机之间是串口传输数据,其RX连接单片机的PB10(USART3的TX复用口),其TX连接单片机的PB11(USART3的RX复用口)。具体接线如下图所示。

基于STM32单片机的步数测量系统(免费开源)_第4张图片

1.4核心代码

(1)软件设计
本设计的软件主要涉及到OLED的初始化,MPU6050的初始化,ESP8266WIFI模块的初始化以及系统初始化。

基于STM32单片机的步数测量系统(免费开源)_第5张图片
整个系统在在完成各个模块的初始化后,在while(1)循环里面分为两个页面,第一个页面(页面一)负责提示一些设计信息并完成数据的WiFi传输,第二个页面(页面二)负责步数测量,消耗卡路里,运动时间,目标运动步数等信息和这些信息的测量结果。页面一与页面二通过KEY0按键进行切换。具体页面如图13所示。
(2)IIC时序设计
IIC模拟时序设计
由于在本次设计中OLED液晶显示屏和MPU6050加速度陀螺仪传感器都是IIC接口协议进行数据通信的,所以我们需要在单片机读取的时候要设计IIC读取时序。在STM32本身的硬件系统中,有已经设置好的硬件IIC接口,但是在使用中发现其IIC时序并不是很稳定,所以我们需要利用IO模拟IIC时序。
IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。

基于STM32单片机的步数测量系统(免费开源)_第6张图片
(3)ESP8266无线传输
本次设计中,我们不仅仅需要在下位机端进行显示,而且需要在上位机端供用户查看,所以需要将数据进行无线传输。在前几章中我们已经阐述了无线传输方案,也对无线传输模块有个简单的介绍。下面我们来仔细阐述一下WiFi模块的传输原理和实际的传输操作。
本次设计的方案是使用ESP8266的AT指令连接巴发云,巴发云进行再推送消息在手机端APP上。
ESP8266共有三种工作模式,分别为softAP模式,station模式,softAP+ station模式。我们本次设计使用两种模式同时兼容的第三种模式。
基于STM32单片机的步数测量系统(免费开源)_第7张图片
基于STM32单片机的步数测量系统(免费开源)_第8张图片
基于STM32单片机的步数测量系统(免费开源)_第9张图片
**
备注:巴发云简介
因为物联网设备的特殊性,为了保证数据在低带宽、不可靠的网络中传输有效的数据,故云协议的设计原则是精简,不添加可有可无的功能,尽力保持云平台轻量级的特性,因此,结合当下设计趋势,构思了一套新的平台产品交互语言。巴发云是一个为轻量型物联网开发的云平台,支持发布订阅模式,提供遗嘱机制,支持TCP长链接,并且实现全平台兼容。云平台支持TCP心跳长链接,MQTT3.1和MQTT3.1.1协议,支持HTTP API接入,目前还支持天猫精灵,小爱同学,微信小程序控制。

基于STM32单片机的步数测量系统(免费开源)_第10张图片

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "oled.h"
#include "oled_myiic.h"
#include "myiic.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h" 
#include "mpu6050.h" 
#include 
#include "esp8266.h"
#include "timer.h"
typedef enum {no=0,yes=!no}bool;//定义一个bool类型
extern bool key_flag;
bool  key_flag=no;

float weight=60,weight_index=0.8214;//假设体重为60kG,运动指数
void OLED1_showchinese(void);//第一页显示函数
void OLED_2showchinese(void);//第二页显示
int main(void)
{   
	  u8 key_number;//获取按键值
	 
	  unsigned long	walk_count ,walk_time;//步行步数,步行时间。
    delay_init(168);		  
	  LED_Init();		
    usart3_init(115200);		//初始化串口1波特率为115200

    IIC_Init();         //MPU6050的IIC初始化
	  IIC_Init1();       //OLED的IIC初始化
    OLED_Init();       //OLED初始化     
		MPU_Init();					//初始化MPU6050
    mpu_dmp_init();        //初始化6050的DMP库
	OLED_init_showchinese();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	TIM3_Int_Init(60000-1,8400-1);	//定时器时钟84M,分频系数8400,所以84M/8400=10Khz的计数频率,计数400000次为40000ms     
	ESP8266_Set_ok(USART3,"AT+RST");
	delay_ms(1000);	
	ESP8266_Set_ok(USART3,"AT+CWMODE=3");
	delay_ms(1000);
	ESP8266_Set_ok(USART3,"AT+CWJAP=\""Ssid"\",\""PassWord"\"");
	delay_ms(1000);
	connect_bemfa();//连接巴发云
	while(1)
	{
		
		key_number=KEY_Scan(1);//读取按键返回值
    if(key_number==1)//切换页面函数
		{
			key_flag=!key_flag;
		  OLED_CLS();
		}

		
		if(key_flag==no)
			
		   {   OLED1_showchinese();//第一页显示函数
				   //connect_fasong(char_step,char_sporttime,char_caloreis,"50"	);//传输
				   LED2=!LED2;
			 }
		else
		{  
			
			   OLED_2showchinese();
			   dmp_get_pedometer_step_count(&walk_count); 
		     dmp_get_pedometer_walk_time(&walk_time);
			   sprintf(char_step,"%lu",walk_count );//unsigend long 型变为无符号整型
			   sprintf(char_caloreis,"%.2f",walk_count*30*weight*weight_index/100000);//计算消耗的能量
			   OLED_P8x16Str(56,0,char_step );//显示计步步数
			   OLED_P8x16Str(48,2,char_caloreis );//显示消耗能量
			   sprintf(char_sporttime,"%lu",walk_time/1000 );
			   OLED_P8x16Str(70,4,char_sporttime);//显示运动时间
			   OLED_P8x16Str(84,6,"50"); //显示目标步
				 delay_ms(10);
			if(jishi==2)
				connect_fasong(char_step,char_sporttime,char_caloreis,"50"	);//传输
			
		 }
             
			
         
	                     
	}
}



ESP8266.c

#include 	 	  
#include "esp8266.h"
#include "string.h"	
#include "sys.h"
unsigned char Receive_shuju[150];
unsigned char Okword=0;
char char_step[4],char_caloreis[6],char_sporttime[4];
void usart3_init(u32 bound)
{  
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
 
	USART_DeInit(USART3);  //复位串口3
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟
	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOB11和GPIOB10初始化
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10
	
	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11复用为USART3
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10复用为USART3	  
	
	USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
	USART_Init(USART3, &USART_InitStructure); //初始化串口3
	
  USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断  

	USART_Cmd(USART3, ENABLE);                    //使能串口 
	
 
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x02 ;//抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	
	

}
      




//发送字节
void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)
{
	USART_SendData(USARTx,Data);
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}	

/********************************************************************
名称:WIFI模块设置函数
作用: 启动模块,以便可以实现无线接入和控制,并等待OK回复
********************************************************************/
void  ESP8266_Set_ok(USART_TypeDef* USARTx, char *str)
{
	uint16_t i = 0;
	do{
			USART_SendByte(USARTx, *(str + i));
			i++;
	}while(*(str + i) !='\0');
	delay_us(5);
	USART_SendByte( USART3,'\r');
	delay_us(5);
	USART_SendByte( USART3,'\n');

	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
	while(!Okword);
		Okword=0;
}
没有等待OK
void  ESP8266_Set(USART_TypeDef* USARTx, char *str)
{
	uint16_t i = 0;
	do{
			USART_SendByte(USARTx, *(str + i));
			i++;
	}while(*(str + i) !='\0');
	delay_us(5);
	USART_SendByte( USART3,'\r');
	delay_us(5);
	USART_SendByte( USART3,'\n');
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);

}
/********************************************************************
名称:WIFI模块设置函数
作用: 启动模块,没有回车换行
********************************************************************/
void ESP8266_Set_Only(USART_TypeDef* USARTx, char *str)
{
	uint16_t i = 0;
	do{
			USART_SendByte(USARTx, *(str + i));
			i++;
	}while(*(str + i) !='\0');

	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}



void USART3_IRQHandler()
{ 
  
static int im=0;
	 

	//当 RDR 移位寄存器的内容已传输到 USART_DR 寄存器时,该位由硬件置 1
	if (USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)//获取中断标志位,为1
		{
				   Receive_shuju[im] = USART_ReceiveData( USART3 );
			     im++;
			  
			  	USART_ClearITPendingBit(USART3,USART_IT_RXNE);	//清除中断标志位
			if((Receive_shuju[im-2]=='O')&&(Receive_shuju[im-1]=='K'))//接收到OK之后 Okword为1
			{
				im=0;
				Okword=1;
			}
				
		}
		if( im>140)
			im=0;
	}
	
/********************************************************************
名称:心跳函数
作用:发送心跳。如果一分钟不发送心跳,服务器就会认为设备掉线,故建议30s发送一次心跳
在中断1中调用,和发送数据函数,留一个就行,发送数据也相当于心跳
********************************************************************/
void Ping(void)
{        
  ESP8266_Set(USART3,"cmd=0&msg=ping"); //发送心跳
}


void connect_topic(void)
	{
        //发送订阅指令,原格式为:cmd=1&uid=***UID***&topic=***Topic***
        ESP8266_Set_Only(USART3,"cmd=1&uid=");
        ESP8266_Set_Only(USART3,Uid);
        ESP8266_Set_Only(USART3,"&topic=");
        
        //************ 需要修改以下部分 ************
   
        ESP8266_Set_Only(USART3,Topic);
    

        
       delay_us(5);
        USART_SendByte(USART3,'\r');//回车
       delay_us(5);
        USART_SendByte(USART3,'\n');   //换行
}
	
void connect_bemfa(void)
	{
       
        ESP8266_Set(USART3,"AT+CIPMODE=1"); //开启透明传输模式
        delay_ms(1000);
        ESP8266_Set(USART3,"AT+CIPSTART=\"TCP\",\"bemfa.com\",8344");  // 连接服务器和端口
        delay_ms(1000);
        ESP8266_Set(USART3,"AT+CIPSEND"); //进入透传模式,下面发的都会无条件传输
        delay_ms(1000);
        
        connect_topic(); //连接订阅主题
      
      
               

}
	void connect_fasong(char data1[],char data2[],char data3[],char data4[]	)
	{
		    //发送订阅指令,原格式为:cmd=1&uid=***UID***&topic=***Topic***
        ESP8266_Set_Only(USART3,"cmd=2&uid=");
        ESP8266_Set_Only(USART3,Uid);
        ESP8266_Set_Only(USART3,"&topic=");
        ESP8266_Set_Only(USART3,Topic);
		    ESP8266_Set_Only(USART3,"&msg=");
		    ESP8266_Set_Only(USART3,"#");
        ESP8266_Set_Only(USART3,data1);
		    ESP8266_Set_Only(USART3,"#");
		    ESP8266_Set_Only(USART3,data2);
		    ESP8266_Set_Only(USART3,"#");
		    ESP8266_Set_Only(USART3,data3);
		    ESP8266_Set_Only(USART3,"#");
		    ESP8266_Set_Only(USART3,data4);
		    ESP8266_Set_Only(USART3,"#");
        delay_us(5);
        USART_SendByte(USART3,'\r');//回车
        delay_us(5);
        USART_SendByte(USART3,'\n');   //换行
	
		
	}
	

	

	
	

ESP8266.h

#ifndef _ESP8266_H
#define _ESP8266_H
#include "delay.h"
#include "sys.h"
#define Ssid  "mawei"											//WIFI名称,,修改为自己路由器的WIFI名称,好像不支持中文
#define PassWord  "12345678"              //WIFI密码,修改为自己路由器的
#define Uid  "bfcf65510b5a4895a30b449eaaeb4b34" //巴法云UID密钥,控制台获取
#define Topic  "MPU6050"          //巴法云控制台创建,名称自定义,app订阅主题要和这个推送主题一致
extern	unsigned char Receive_shuju[150];
extern	unsigned char Okword;
extern  char char_step[4],char_caloreis[6],char_sporttime[4];//步数,卡路里,运动时间

void usart3_init(u32 bound);
void POST(char data1[] ,char data2[],char data3[],char data4[]);//POST数据函数
void  ESP8266_Set_ok(USART_TypeDef* USARTx, char *str);
void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data);
void ESP8266_Set_Only(USART_TypeDef* USARTx, char *str);
void  ESP8266_Set(USART_TypeDef* USARTx, char *str);
void Ping(void);
void connect_topic(void);
void connect_bemfa(void);
void connect_fasong(char data1[],char data2[],char data3[],char data4[]	);
#endif


1.5结果展示

基于STM32单片机的步数测量系统(免费开源)_第11张图片

基于STM32单片机的步数测量系统(免费开源)_第12张图片

基于STM32单片机的步数测量系统(免费开源)_第13张图片

基于STM32单片机的步数测量系统(免费开源)_第14张图片

#6# 1.6资源链接
链接:https://pan.baidu.com/s/1-gpK4esXYDvpuTZc0HVPeQ
提取码:a520

1.7硬件电路图

基于STM32单片机的步数测量系统(免费开源)_第15张图片

你可能感兴趣的:(Stm32,嵌入式,C语言,单片机,stm32,嵌入式硬件)