基于STM32的智能手环

前言

这是本人大四上学期实习时做的一个项目,希望对各位有所帮助。

物料清单

STM32F411CEU6型号的单片机
HP6心率血压模块
MPU6050六轴传感器模块
SHT20温湿度传感器
OLED显示屏
直流电机
五向按键

实现的功能

  1. 显示当前日期与时间
  2. 温湿度测量
  3. 心率测量
  4. 血压测量
  5. 步数统计
  6. 闹钟设置
  7. 显示当前的闹钟
  8. 通过按键选择不同的功能

成果展示

菜单选项
通过五向按键的左右两侧切换不同的功能页面,按中间按键确认进入,再按中间按键退出
(注:右上那盏灯是在测量完心率后忘了调用关闭PWM灯的函数)
在这里插入图片描述
按确认按键进入后
图1:温湿度测量。
图2:心率测量前的提示。
图3:心率测量结果–约5s出结果
图4:血压测量结果–约1min出结果
图5:闹钟设置–通过上下选择时分秒,左右设置数值,中确认并退出
图6:闹钟响应–动图且电机转动。
图7:显示当前设置的闹钟–五向按键往下拨
图8:显示步数、卡路里、距离
在这里插入图片描述
在这里插入图片描述

主要代码

main.c

#include "includes.h"
//时间显示:0  温湿度:1  心率测量:2 血压测量:3 计步数:4 设置闹钟:5
//key_ok值:left减1,right加1;up归0

int main(void)
{
	//初始化
	LED_init();
	Breath_Light_Init();
	Key_init();
	Motor_Init();
	exti0_init();
	tim5_init();
	OLED_Init();
	Adc_Init();
	Sht20_Init();
	My_Rtc_Init();
	Hp6_Pin_Init();
	Mpu6050_Init();   
	WatchInfo_init();
	Rtc_Wakeup_Init();
	
	kaiji_gif();   //开机动画
	OLED_Clear(0);
	while(1)
	{
	//在tim5的中断服务函数中改变,key_ok值:left减1,right加1;up归0,down:key_ok = 6
		switch (key_ok) 
		{
			case 0: show_tim();break;     //显示当前时间
			
			case 1: show_TH();break;      //显示当前温湿度
			
			case 2:	show_rate();break;	  //心率测量
			
			case 3:	show_press();break;   //血压测量
			
			case 4: show_step();break;     //步数计数
			
			case 5: show_setclock();break; //设置闹钟
			
			case 6: show_curclock();break; //显示当前闹钟
		}
		end_work();   //收尾工作
	}
}

show.c–各个功能模块的实现

#include "show.h"

int key_ok = 0;
int key_enter = 0;  //确认按钮

//开机动画
void kaiji_gif(void)
{
	Show_Pic(0,0,(u8*)gImage_20);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_21);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_22);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_23);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_24);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_25);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_26);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_27);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_28);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_29);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_30);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_31);
	Delay_ms(100);
	Show_Pic(0,0,(u8*)gImage_32);
	Delay_ms(1000);
}

//显示当前时间
void show_tim(void)
{
	u8 buf[50] = {0};
	TIME_DATA	*p;
	//OLED_Clear(0);
	while(key_ok==0)
	{
		Show_Chinese(0, 20, 4,tim);
		p = RTC_getDateAndTime();
		sprintf((char*)buf,"20%02d-%02d-%02d-%d",p->year,p->month,p->day,p->week);
		Show_String(2, 0, buf);
		
		sprintf((char*)buf,"%02d:%02d:%02d",p->hour,p->minute,p->second);
		Show_String(4, 0, buf);
	}
}
//测量温湿度
void show_TH(void)
{
	float Tem,Hum;
	u8 buf[50] = {0},clear_flag = 1;
	
	Show_Pic(0, 0, (u8*)RT_Picture);
	Show_Chinese(2, 49, 5,rt);
	Delay_ms(800);
	while(key_enter)
	{
		if(clear_flag==1)  //清屏一次
		{
			OLED_Clear(0);
			clear_flag = 0;
		}
		Show_Chinese(0, 20, 5,cur_TH);
		Tem = Sht20_Mesaure(TEMPEATURE);
		Hum = Sht20_Mesaure(HUMIDITY);
		sprintf((char*)buf,"Tem:%.2fC",Tem);
		Show_String(2, 0, buf);
		sprintf((char*)buf,"Hum:%.2f%%",Hum);
		Show_String(4, 0, buf);
		Delay_ms(500);
	}
}

//测量心率
u8 rate_flag = 0;
void show_rate(void)
{
	u8 data[24] = {0},buf[50] = {0},clear_flag = 1;
	Show_Pic(0, 0, (u8*)HR_Picture);
	Show_Chinese(2, 65, 4,Hr);
	Delay_ms(800);
	while(key_enter)
	{
		rate_flag = 1;
		//清屏两次
		if(clear_flag==1)
		{
			OLED_Clear(0);
			Hr_Open();     //开启心率测量
			Show_Pic(0, 0, (u8*)HR_Tip);
			Delay_ms(5000);
			clear_flag = 2;
		}
		if(clear_flag==2)
		{
			OLED_Clear(0);
			clear_flag = 0;
		}
		Get_Hr();  //得到测量数据
		Get_Result(data); //得到测量结果
		PWN_Led();
		sprintf((char*)buf,"heart rate:%d ",data[7]);
		Show_String(2, 0, buf);
		Delay_ms(1250);
	}
}
//测量血压
u8 Bm_flag = 0;
void show_press(void)
{
	u8 data[24] = {0},buf[50] = {0},clear_flag = 1;
	
	Show_Pic(0, 0, (u8*)BM_Picture);
	Show_Chinese(2, 65, 4,Bm);
	Delay_ms(800);
	while(key_enter)
	{
		Bm_flag = 1;
		//清两次屏幕
		if(clear_flag==1)
		{
			OLED_Clear(0);
			Bm_Open();  //开启血压测量
			Show_Pic(0, 0, (u8*)BM_Tip);
			Delay_ms(2560);
			clear_flag = 2;
		}
		if(clear_flag==2)
		{
			OLED_Clear(0);
			clear_flag = 0;
		}
		
		Get_Bm();  //得到测量数据
		Get_Result(data); //得到测量结果
		
		if(data[7]== 0)
		{
			//printf("正在测量中!\r\n");
			PWN_Led();  //PWM灯
			Show_String(2, 10, (u8*)"Measuring...");
		}
		else if(data[7] == 1)
		{
			sprintf((char*)buf,"Hight:%d       ",data[10]);
			Show_String(2, 0, buf);
			sprintf((char*)buf,"Low:%d ",data[11]);
			Show_String(4, 0, buf);
			Bm_Close();   //关闭测量
			TIM_SetCompare2(TIM3,0); // 关灯
		}
		else if(data[7]==2)
		{
			Show_String(2, 0, (u8*)"Measure error!");
			Delay_ms(1500);
			key_enter = 0;   // 退出测量
			Bm_Close();   //关闭测量
			TIM_SetCompare2(TIM3,0); // 关灯
		}
		Delay_ms(1280);
	}
}

//步数计数
void show_step(void)
{
	u8 err,clear_flag = 1;
	u8 uiBuf[40];
	static sportsInfo_t userSportsInfo;
	static timStamp_t timStamp;
	static u8 tempSecond;						 //保存秒钟暂态量
	///timStamp_t *rtcTime; 						 //获取年月日时分秒
	TIME_DATA *rtcTime; 							  //获取年月日时分秒
	accValue_t accValue;
	Show_Pic(0, 0, (u8*)Step_Pic);
	Show_Chinese(2, 65, 4,step);
	Delay_ms(800);
	while(key_enter)
	{
		if(clear_flag==1)
		{
			OLED_Clear(0);
			clear_flag = 0;
		}
		Mcu_Read_Mpu6050_XYZ(&accValue);			  //得到加速度传感器数据
		rtcTime = RTC_getDateAndTime(); 					  //获取当前RTC的值
		if(tempSecond != timStamp.second)					 //秒更新
		{
			tempSecond = timStamp.second;
			timStamp.twentyMsCount = 0 ;//20ms计数变量清零
		}
		else												  //秒不更新,1秒等于50*20ms
		{
			timStamp.twentyMsCount ++;//20ms计数变量++
		}
		timStamp.hour	 = rtcTime->hour;
		timStamp.minute = rtcTime->minute;
		timStamp.second = rtcTime->second;
		//将三轴数据转换为以g为单位的数据

		accValue.accX = ((float)(int)accValue.accX/16384) *10;
		accValue.accY = ((float)(int)accValue.accY/16384) *10;
		accValue.accZ = ((float)(int)accValue.accZ/16384) *10; 
		userSportsInfo = *onSensorChanged(&accValue,&timStamp,WatchInfo_getUserInfo(&err)); //调用计数算法
		sprintf((char*)uiBuf,"Step:%05d ",userSportsInfo.stepCount); // 显示步数
		Show_String(0,0,uiBuf); 
		sprintf((char*)uiBuf,"kal:%.1f KAL",userSportsInfo.calories); // 显示卡路里
		Show_String(2,2,uiBuf); 	
		sprintf((char*)uiBuf,"dis:%.1f M",userSportsInfo.distance); // 显示里程
		Show_String(4,0,uiBuf); 			

		Delay_ms(20);  //以50Hz的频率去读取三轴加速度的XYZ轴加速度值
	}
}

//设置闹钟
u8 setclk_flag = 0;
void show_setclock(void)
{
	u8 clear_flag = 1;
	OLED_Clear(0);
	Show_Pic(0, 0, (u8*)Clock_Pic);  //闹钟图片
	Show_Chinese(2, 65, 4,clock);    //文字:闹钟设置
	Delay_ms(800);
	while(key_enter)
	{
		setclk_flag = 1;
		if(clear_flag==1)  //进入后清一次屏
		{
			OLED_Clear(0);
			Show_String(0, 0, (u8*)"week:0");
			Show_String(2,0,(u8*)"hour:0");
			Show_String(4,0,(u8*)"minute:0");
			clear_flag = 0;
		}
		alarm_control();  //设置闹钟		
	}
}

//显示当前闹钟
void show_curclock(void)
{
	u8 buf[50];
	OLED_Clear(0);
	
	Show_Chinese(0, 20, 4,cur_clk);//文字:当前闹钟
	sprintf((char*)buf,"%d-%02d-%02d",alarm.week,alarm.hour,alarm.minute);
	Show_String(2, 20, buf);
	
	Delay_ms(1000);
}

//收尾工作
void end_work(void)
{ 
	//进入后且退出时,开启闹钟
	if(setclk_flag==1&&key_enter==0) 
	{
		setclk_flag = 0;  //清标志位
		Rtc_Alarm(alarm.week,alarm.hour, alarm.minute,0);//设置闹钟
		OLED_Clear(0);
		Show_String(2,0,(u8*)"set success!");//提示设置成功
		Delay_ms(1500);
		key_enter = 0;// 防止手抖,多次进入
	}
	//进入后且退出时,关闭测量
	if(rate_flag==1&&key_enter==0) 
	{
		rate_flag = 0;
		Hr_Close();   //关闭测量
		TIM_SetCompare2(TIM3,0); // 关灯
	}
	
	if(Bm_flag==1&&key_enter==0) 
	{
		Bm_flag = 0;
		Bm_Close();   //关闭测量
		TIM_SetCompare2(TIM3,0);
	}
	key_enter = 0;
	OLED_Clear(0); //清屏
}

必知宏定义

#define LEFT  (ADC_value >= 4085)//3.3v
#define RIGHT (ADC_value > 2035 && ADC_value < 2050)//4096/2 
#define UP 	  (ADC_value > 1020 && ADC_value < 1030)//4096/4
#define DOWN  (ADC_value > 1360 && ADC_value < 1370)//4096/3

定时器5的中断服务函数
通过不同的key_ok值切换不能的功能模块

extern int key_ok;
//定时器5的中断服务函数
void TIM5_IRQHandler(void)
{
	//判断标志位,未按下确认键时进入
	if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET && key_enter==0)
	{
		if(LEFT)
		{
			Delay_ms(10);
			if(LEFT)
			{
				while(ADC_value>5);
				key_ok--;
				if(key_ok < 0) key_ok = 5;
			}
		}
		else if(RIGHT)
		{
			Delay_ms(10);
			if(RIGHT)
			{
				while(ADC_value>5);
				key_ok++;
				if(key_ok > 5) 
				{
					key_ok = 0;
				}
			}
		}
		else if(UP)
		{
			Delay_ms(10);
			if(UP)
			while(ADC_value>5);
			key_ok = 0;
		}
		else if(DOWN)
		{
			Delay_ms(10);
			if(DOWN)
			while(ADC_value>5);
			key_ok =  6;
		}
	}
	//清空标志位
	TIM_ClearITPendingBit(TIM5, TIM_IT_Update);
}

外部中断0服务函数–50ms1次
使用key_enter全局变量进入/退出功能模块

//外部中断0服务函数,改变key_enter值
void EXTI0_IRQHandler(void)
{
	//判断是否有中断请求
	if(EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		key_enter = !key_enter;  //一开始为0
		
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
	
}

版权声明:本文为CSDN博主「⁽⁽ଘ晴空万里ଓ⁾⁾」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/NICHUN12345/article/details/120924585

生成海报
点赞 0

⁽⁽ଘ晴空万里ଓ⁾⁾

我还没有学会写个人说明!

暂无评论

相关推荐

RT-Thread Studio移植LAN8720A驱动

RTT网络协议栈驱动移植(霸天虎) 1、新建工程 ​ 工程路径不含中文路径名,工程名用纯英文不含任何符号。 2、用CubeMx配置板子外设 2.1、配置时钟 ​ 按照自己板子配置相应时钟。

【STM32Cube笔记】12-配置外部中断

【STM32Cube笔记】系列文章目录 1-基于STM32的VSCode入门级教程前言 2-STM32Cube安装教程 3-STM32CubeIDE汉化 4-STM32Cube配置时钟设置 5-跑马灯引脚配置 6-Cortex-M7内核基本配

stm32cubemx+HAL+串口接收中断

stm32cubemxHAL串口接收中断 在cubemx配置完串口和global interrupt后需要在keil中添加如下代码。 第一步:在main函数中添加接收中断标志位开启函数 HAL_UART_Receive_IT