当前位置:   article > 正文

基于STM32的智能手环

基于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();   //收尾工作
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

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); //清屏
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290

必知宏定义

#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
  • 1
  • 2
  • 3
  • 4

定时器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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

外部中断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);
	}
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最后:
需要代码的可以自行下载,下载链接
下载操作:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/687900
推荐阅读
相关标签
  

闽ICP备14008679号