摘要
随着人们对健康和健身的日益关注,智能健身设备成为了现代生活中的重要组成部分。传统的健身设备在功能和使用体验上已难以满足现代人对于健康管理的多元化需求。在此背景下,基于STM32的智能健身设备设计应运而生。STM32作为高性能、低功耗的微控制器,具有强大的数据处理能力和灵活的扩展性,为智能健身设备的创新设计提供了强有力的技术支持。通过集成多种生理参数监测功能,如心率、体温和血氧等,智能健身设备能够为用户提供更为全面、精确的健康数据,从而帮助用户实现个性化的健身管理和健康评估。
本文主要聚焦于基于STM32的智能健身设备设计,通过深入研究和开发,实现了对使用者心率、体温和血氧等关键生理参数的实时监测。本文详细介绍了设备的硬件设计、软件编程以及功能实现过程,基于Proteus软件进行了系统的模拟测试。通过详细的电路搭建和程序烧录,成功地模拟了设备的实际工作环境。
此外,本文还探讨了智能健身设备在未来健康管理领域的发展潜力和应用前景,为相关领域的研究和产品开发提供了有益的参考和借鉴。
关键词:STM32;单片机;血氧检测;心率检测
目录
摘 要 I
Abstract II
1 绪论 3
1.1 研究背景和意义 3
1.2 国内外研究现状 4
1.2.1 国内研究现状 4
1.2.2 国外研究现状 4
1.3 人体体温、血氧、心率的概述 5
2 总体方案设计 7
2.1 功能需求分析 7
2.2 本文主要研究任务 7
2.3 系统结构设计 8
3.1 系统设计原则 9
3.2 主控芯片选型 9
3.4 STM32F103概述及最小系统 11
3.4.1 STM32F103概述 11
3.4.2 STM32F103最小系统 12
3.5 传感器选型及电路设计 14
3.5.1 体温传感器 14
3.5.2 血氧心率传感器 15
3.5.3 蓝牙模块 16
4.1 开发环境介绍 17
4.2 主函数设计 17
4.3 血氧心率检测子程序 17
4.4 蓝牙通信子程序 18
4.5 按键模块子程序 19
5.1 Proteus介绍 21
5.2 仿真结果 22
总结与展望 23
参考文献 24
致 谢 25
说明书

Proteus仿真

部分单片机程序
#include "stm32f10x.h"
#include "OLED_I2C.h"
#include "ds1302.h"
#include "ds18b20.h"
#include "usart1.h"
#include "usart2.h"
#include "led.h"
#include "delay.h"
#include "max30102_read.h"
#include "myiic.h"
#include "key.h"
#include "iic.h"
#include "stdio.h"
#include "string.h"
#include "adxl345.h"
#include "timer.h"
#include "stmflash.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FLASH_SAVE_ADDR STM32_FLASH_BASE + 0X400 //设置FLASH 保存地址(必须为偶数)
unsigned char setn=0;
unsigned char p_r=0; //平年/润年 =0表示平年,=1表示润年
float adx,ady,adz;
float acc;
float acc2,acc3;
u8 flag=0;
u16 bushu=0;
long mileage=0;//里程
u16 mileage_bushu = 0;//里程的步数
u16 bu_long = 40;//步长
long warnMileage=1000;//提醒里程
short temperature=0;
u16 xinlvMin=60,xinlvMax=120;//心率下限上限
u16 spo2Min=80;//血氧下限
u16 tempMax=373;//温度上限
u16 tempMin=150;//温度下限
char display[16];
u8 shanshuo=0;
u8 beepFlag=0x00; //蜂鸣器报警标志
u8 page = 0; //页面切换变量
/*定时时间*/
long timeCountRecord=0;
long shi=1,fen=0,miao=0;
bool startFlag=0; //开始定时标志
bool timingReminder=0; //定时时间提醒标志
bool mileageReminder=0; //里程提醒标志
u8 sendFlag = 1;
bool sendSetValueFlag=0;
int32_t hrAvg;//心率
int32_t spo2Avg;//血氧浓度
void DisplayTime(void)//显示时间函数
{
unsigned char i=0,x=0;
u16 nian_temp;
if(setn==0)DS1302_DateRead(&SysDate);//读时间
nian_temp=2000+SysDate.year;
if((nian_temp%400==0)||((nian_temp%100!=0)&&(nian_temp%4==0))) //判断是否为闰年
p_r=1;
else
p_r=0;
if(setn<8 && page==0)
{
OLED_ShowChar((x++)*8,0,'2',2,setn+1-1);
OLED_ShowChar((x++)*8,0,'0',2,setn+1-1);
OLED_ShowChar((x++)*8,0,SysDate.year/10+'0',2,setn+1-1);
OLED_ShowChar((x++)*8,0,SysDate.year%10+'0',2,setn+1-1);
OLED_ShowChar((x++)*8,0,'-',2,0);
OLED_ShowChar((x++)*8,0,SysDate.mon/10+'0',2,setn+1-2);
OLED_ShowChar((x++)*8,0,SysDate.mon%10+'0',2,setn+1-2);
OLED_ShowChar((x++)*8,0,'-',2,0);
OLED_ShowChar((x++)*8,0,SysDate.day/10+'0',2,setn+1-3);
OLED_ShowChar((x++)*8,0,SysDate.day%10+'0',2,setn+1-3);
OLED_ShowCN(i*16+88,0,0,setn+1-4);//测试显示中文:周
switch(SysDate.week)
{
case 1:
OLED_ShowCN(i*16+104,0,1,setn+1-4);//测试显示中文:一
break;
case 2:
OLED_ShowCN(i*16+104,0,2,setn+1-4);//测试显示中文:二
break;
case 3:
OLED_ShowCN(i*16+104,0,3,setn+1-4);//测试显示中文:三
break;
case 4:
OLED_ShowCN(i*16+104,0,4,setn+1-4);//测试显示中文:四
break;
case 5:
OLED_ShowCN(i*16+104,0,i+5,setn+1-4);//测试显示中文:五
break;
case 6:
OLED_ShowCN(i*16+104,0,6,setn+1-4);//测试显示中文:六
break;
case 7:
OLED_ShowCN(i*16+104,0,7,setn+1-4);//测试显示中文:日
break;
}
x=0;
OLED_ShowChar((x++)*8,2,SysDate.hour/10+'0',2,setn+1-5);
OLED_ShowChar((x++)*8,2,SysDate.hour%10+'0',2,setn+1-5);
OLED_ShowChar((x++)*8,2,':',2,0);
OLED_ShowChar((x++)*8,2,SysDate.min/10+'0',2,setn+1-6);
OLED_ShowChar((x++)*8,2,SysDate.min%10+'0',2,setn+1-6);
OLED_ShowChar((x++)*8,2,':',2,0);
OLED_ShowChar((x++)*8,2,SysDate.sec/10+'0',2,setn+1-7);
OLED_ShowChar((x++)*8,2,SysDate.sec%10+'0',2,setn+1-7);
}
}
void GetHeartRateSpO2(void)
{
unsigned char x=0;
ReadHeartRateSpO2(); //读取心率血氧
if(page==0)
{
//显示相关的信息
if(((hrAvg!=0)&&(hrAvg>=xinlvMax||hrAvg<=xinlvMin))&&shanshuo==1)
{
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
}
else
{
//显示相关的信息
OLED_ShowChar((x++)*8,6,hrAvg%1000/100+'0',2,0);
OLED_ShowChar((x++)*8,6,hrAvg%100/10+'0',2,0);
OLED_ShowChar((x++)*8,6,hrAvg%10+'0',2,0);
}
x=6;
if(((spo2Avg!=0)&&(spo2Avg<=spo2Min))&&shanshuo==1)
{
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
}
else
{
//显示相关的信息
OLED_ShowChar((x++)*8,6,spo2Avg%1000/100+'0',2,0);
OLED_ShowChar((x++)*8,6,spo2Avg%100/10+'0',2,0);
OLED_ShowChar((x++)*8,6,spo2Avg%10+'0',2,0);
}
}
}
void DisplayTemperature(void)//显示温度函数
{
unsigned char x=10;//显示的第几个字符
temperature=DS18B20_Get_Temp();
if(page==0)
{
if((temperature<=tempMin||temperature>=tempMax)&&shanshuo==1)
{
OLED_ShowChar((x++)*8,2,' ',2,0);
OLED_ShowChar((x++)*8,2,' ',2,0);
OLED_ShowChar((x++)*8,2,' ',2,0);
OLED_ShowChar((x++)*8,2,' ',2,0);
}
else
{
OLED_ShowChar((x++)*8,2,temperature/100+'0',2,0);
OLED_ShowChar((x++)*8,2,temperature%100/10+'0',2,0);
OLED_ShowChar((x++)*8,2,'.',2,0);
OLED_ShowChar((x++)*8,2,temperature%10+'0',2,0);
}
}
}
void Store_Data(void) //存储数据
{
u16 DATA_BUF[10];
DATA_BUF[0] = bushu;
DATA_BUF[1] = mileage_bushu;
DATA_BUF[2] = bu_long;
DATA_BUF[3] = xinlvMin;
DATA_BUF[4] = xinlvMax;
DATA_BUF[5] = spo2Min;
DATA_BUF[6] = tempMin;
DATA_BUF[7] = tempMax;
DATA_BUF[8] = (warnMileage>>16)&0xFFFF;
DATA_BUF[9] = (warnMileage>>0)&0xFFFF;
STMFLASH_Write(FLASH_SAVE_ADDR + 0x20,(u16*)DATA_BUF,10); //写入数据
DelayMs(50);
}
void Read_Data(void) //读出数据
{
u16 DATA_BUF[10];
STMFLASH_Read(FLASH_SAVE_ADDR + 0x20,(u16*)DATA_BUF,10); //读取数据
bushu = DATA_BUF[0];
mileage_bushu = DATA_BUF[1];
bu_long = DATA_BUF[2];
xinlvMin = DATA_BUF[3];
xinlvMax = DATA_BUF[4];
spo2Min = DATA_BUF[5];
tempMin = DATA_BUF[6];
tempMax = DATA_BUF[7];
warnMileage = DATA_BUF[8]<<16|DATA_BUF[9];
}
void CheckNewMcu(void) // 检查是否是新的单片机,是的话清空存储区,否则保留
{
u8 comper_str[6];
STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)comper_str,5);
comper_str[5] = '\0';
if(strstr((char *)comper_str,"FDYDZ") == NULL) //新的单片机
{
STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)"FDYDZ",5); //写入“FDYDZ”,方便下次校验
DelayMs(50);
Store_Data();//存储下初始数据
}
Read_Data(); //开机读取下相关数据(步数里程卡路里步长)
if(xinlvMax>300||xinlvMax<0)
{
xinlvMin=60;xinlvMax=120;//心率下限上限
spo2Min=80;//血氧下限
tempMax=373;//温度上限
tempMin=150;//温度下限
bushu = 0;
mileage_bushu = 0;
bu_long = 40;
warnMileage=1000;
}
}
void GetSteps(void)//获取步数函数
{
static u16 temp=0;
float tempMileage=0.0;
u16 x=11;
adxl345_read_average(&adx,&ady,&adz,10);//获取数据
acc=ady;
acc3=adx;
if(acc>0)//在正轴
{
if(acc/10>=10)//加速度值,在正轴值是否大于10,则视为一个周期完成,步数加1
{
flag=0;
if(bushu<60000)bushu++; //步数加1
if(mileage_bushu<60000)mileage_bushu++; //步数加1
if(temp!=bushu)//当步数发生变化才去存储步数
{
temp=bushu;
Store_Data(); //存储步数
}
}
}
if(page==0)
{
if(bushu>9999)
{
OLED_ShowChar((x++)*8,6,bushu/10000+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%10000/1000+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%1000/100+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%100/10+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%10+'0',2,0);
}
else if(bushu>999)
{
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,bushu%10000/1000+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%1000/100+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%100/10+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%10+'0',2,0);
}
else if(bushu>99)
{
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,bushu%1000/100+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%100/10+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%10+'0',2,0);
}
else if(bushu>9)
{
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,bushu%100/10+'0',2,0);
OLED_ShowChar((x++)*8,6,bushu%10+'0',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
}
else
{
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,bushu%10+'0',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
OLED_ShowChar((x++)*8,6,' ',2,0);
}
}
else
{
mileage = (mileage_bushu * bu_long)/100;//计算里程
tempMileage = (float)mileage/1000;//转换为千米
sprintf(display,"%6.3fkm ",tempMileage);
OLED_ShowStr(48,2,(u8 *)display,2);
}
}
void DisplaySetValue(void) //显示设置值
{
if(page==0)
{
if(setn==8||setn==9)
{
OLED_ShowChar(48,4,xinlvMin/100+'0',2,setn+1-8);//显示
OLED_ShowChar(56,4,xinlvMin%100/10+'0',2,setn+1-8);//显示
OLED_ShowChar(64,4,xinlvMin%10+'0',2,setn+1-8);//显示
OLED_ShowChar(48,6,xinlvMax/100+'0',2,setn+1-9);//显示
OLED_ShowChar(56,6,xinlvMax%100/10+'0',2,setn+1-9);//显示
OLED_ShowChar(64,6,xinlvMax%10+'0',2,setn+1-9);//显示
}
if(setn==10)
{
OLED_ShowChar(48,4,spo2Min/100+'0',2,setn+1-10);//显示
OLED_ShowChar(56,4,spo2Min%100/10+'0',2,setn+1-10);//显示
OLED_ShowChar(64,4,spo2Min%10+'0',2,setn+1-10);//显示
}
if(setn==11||setn==12)
{
OLED_ShowChar(48,4,tempMin/100+'0',2,setn+1-11);//显示
OLED_ShowChar(56,4,tempMin%100/10+'0',2,setn+1-11);//显示
OLED_ShowChar(64,4,'.',2,setn+1-11);
OLED_ShowChar(72,4,tempMin%10+'0',2,setn+1-11);//显示
OLED_ShowChar(48,6,tempMax/100+'0',2,setn+1-12);//显示
OLED_ShowChar(56,6,tempMax%100/10+'0',2,setn+1-12);//显示
OLED_ShowChar(64,6,'.',2,setn+1-12);
OLED_ShowChar(72,6,tempMax%10+'0',2,setn+1-12);//显示
}
}
else
{
if(setn==4)
{
sprintf((char *)display,"%dcm ",bu_long);
OLED_ShowStr(48, 4, (u8*)display, 2);//显示经度
}
if(setn==5)
{
sprintf(display,"%6.3fkm ",(float)warnMileage/1000);
OLED_ShowStr(35,4,(u8 *)display,2);
}
}
}
文件列表
