STM32心率血氧手环(机智云)

目录

一、前言

二、项目简介

1.功能详解

2.主要器件

三、原理图设计

四、PCB硬件设计

[PCB图 ​编辑​编辑](#PCB图 编辑编辑)

五、程序设计

六、实验效果

七、包含内容

项目分享


一、前言

项目成品图片:

哔哩哔哩视频链接:

https://www.bilibili.com/video/BV1wnwCz1Esv/?spm_id_from=333.1387.homepage.video_card.click&vd_source=199aed5297a00e80e1faf7e270afe8d7

(资料分享见文末)

二、项目简介

1.功能详解

基于STM32的心率血氧手环(机智云)系统

功能如下:

  1. 环境采集:自动模式界面实时时间(年月日周时分秒)显示、心率显示、血氧显示、体温显示、步数显示、状态(正常/跌倒)
  2. 显示功能:环境数据显示在OLED屏幕上
  3. 模式切换:通过按键可以切换手动模式和自动模式
  4. 自动模式:自动模式时心率/血氧/体温处于异常范围内或跌倒时蜂鸣器报警
  5. 手动模式:按键进入手动模式,可控制步数清零、LED的开关
  6. 阈值调节:阈值设置界面可设置当前时间,设置心率上下限、血氧下限、体温上下限。
  7. 机智云APP: APP接收心率血氧体温步数和状态(正常/跌倒)信息,可修改所有阈值、可手动清除步数

2.主要器件

  • STM32F103C8T6最小系统板
  • OLED显示屏(4针IIC协议)
  • DS18B20温度传感器
  • MAX30102心率血氧传感器
  • ADXL345加速度传感器
  • ESP8266-01S(WIFI模块)
  • 蜂鸣器
  • LED灯

三、原理图设计

四、PCB硬件设计

PCB图

五、程序设计

cpp 复制代码
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "key.h"
#include "Modules.h"
#include "adcx.h"
#include "flash.h"
#include "usart2.h"
#include "usart3.h"
#include "TIM2.h" 
#include "timer.h"
#include "TIM3.h" 
#include "MyRTC.h" 
#include "max30102_read.h"  
#include "ds18b20.h"		
#include "adxl345.h"		
#include "myiic.h"
#include "iic.h"
#include "gizwits_product.h"         
#include "gizwits_protocol.h"   

/****************异方辰电子工作室******************
					STM32
											
 * 项目			:	STM32心率血氧手环(机智云版)                 
 * 版本			:   V2.0
 * 日期			:   2026.3.11
 * MCU			:	STM32F103C8T6
 * 接口			:	见代码		
 * IP账号		:	异方辰电子/辰哥单片机设计(同BILIBILI|抖音|快手|小红书|CSDN|公众号|视频号等)
 * 作者			:	辰哥 
 * 工作室		:   异方辰电子工作室
 * 授权IP	 	:	辰哥单片机设计、异方辰电子、YFC电子、异方辰系列 
 * 官方网站    	:	www.yfcdz.cn	

**********************BEGIN***********************/

#define KEY_Long1	11
#define KEY_1		1
#define KEY_2		2
#define KEY_3		3
#define KEY_4		4

#define AUTO_MODE     1   // 自动模式
#define MANUAL_MODE   2   // 手动模式
#define SETTINGS_MODE 3   // 设置模式


#define FLASH_START_ADDR	0x0801f000	// 起始地址

// 外部变量声明
  SensorModules sensorData;								
  SensorThresholdValue Sensorthreshold;		
  DriveModules driveData;									

// 全局变量定义
uint8_t mode = 1;	//系统模式  1自动  2手动  3设置
u8 dakai;			//串口3使用的传递变量
u8 Flag_dakai;		//串口3接收标志位
uint8_t is_secondary_menu = 0;  // 0一级菜单,1二级菜单
uint8_t secondary_pos = 1;      // 二级菜单光标位置(1-3对应时/分/秒)
uint8_t secondary_type = 0;     // 二级菜单类型:0=RTC时间,1=定时开启,2=定时关闭
uint8_t step_num = 0;
// 星期文字数组(1=周一,7=周日)
const uint8_t Week_Chinese[7][2] = {0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7}; 
extern unsigned char p[16];
// 自动模式缓存变量(修复未定义错误)
static uint16_t last_RTC_Time[6] = {0, 0, 0, 0, 0, 0}; // 自动模式时间缓存
static uint8_t last_week = 0;                          // 自动模式星期缓存

// 时间设置页面光标缓存(保留原始坐标)
static uint16_t adjust_RTC_Time[6] = {0, 0, 0, 0, 0, 0}; // 时间设置临时缓存(uint16_t无符号)
//static uint8_t adjust_week = 0;                          // 时间设置星期缓存

// 时间设置全局控制变量
static uint8_t time_adjust_flag = 0; // 时间修改标志(1=进入时间设置,0=退出)
static uint8_t count_t = 1;          // 时间设置页面KEY2计数(1-6对应年/月/日/时/分/秒)

// 系统静态变量
uint8_t count_m = 1;  // 手动模式按键计数
uint8_t count_s = 1;	 // 设置模式按键计数
uint8_t last_mode = 0; // 记录上一次的模式
uint8_t auto_page = 1;   



int main(void)
{ 
    // 系统初始化
    SystemInit();
    delay_init(72);  
    ADCx_Init();
    LED_Init();
    BEEP_Init();
    BEEP_OFF;
    USART1_Config();
    USART2_Config(); 
    USART3_Config();
    Key_Init();
    OLED_Init(); 
    OLED_Clear();
    MyRTC_Init(); // 初始化RTC
    DS18B20_Init(); // DS18B20 温度传感器初始化
    IIC_init();		// IIC初始化
    adxl345_init(); // ADXL345模块初始化
    Init_MAX30102();// MAX30102模块初始化
    delay_ms(100);
    static uint8_t last_mode = 0;  
    static uint32_t last_sensor_time = 0; 
    static uint32_t last_display_time = 0; 
    static uint32_t last_save_flash_time = 0; // 上次保存FLASH的时间戳
    const uint32_t SAVE_FLASH_INTERVAL = 60000; // 保存间隔:60000ms = 1分钟
    static uint8_t flash_save_flag = 0; 
    TIM2_Init(72-1,1000-1);  
	  TIM3_Int_Init(1000-1,72-1);
    delay_ms(200);


// 读取FLASH中的阈值和时间
    FLASH_ReadThreshold(); 
    
    // 新增:校验阈值合法性,非法则初始化默认值
    if(FLASH_CheckThresholdValid() == 0)
    {
       FLASH_InitDefaultThreshold(); // 初始化默认阈值
    }

    // 时间数据合法性校验(原有代码保留,补充注释)
    if(adjust_RTC_Time[0] >= 2000 && adjust_RTC_Time[0] <= 2099 && // 年合法
       adjust_RTC_Time[1] >= 1 && adjust_RTC_Time[1] <= 12 &&       // 月合法
       adjust_RTC_Time[2] >= 1 && adjust_RTC_Time[2] <= 31 &&       // 日合法
       adjust_RTC_Time[3] <= 23 && adjust_RTC_Time[4] <= 59 && adjust_RTC_Time[5] <= 59)
    {
        // 将FLASH读取的时间写入RTC工作数组
        MyRTC_Time[0] = adjust_RTC_Time[0]; // 年
        MyRTC_Time[1] = adjust_RTC_Time[1]; // 月
        MyRTC_Time[2] = adjust_RTC_Time[2]; // 日
        MyRTC_Time[3] = adjust_RTC_Time[3]; // 时
        MyRTC_Time[4] = adjust_RTC_Time[4]; // 分
        MyRTC_Time[5] = adjust_RTC_Time[5]; // 秒
        MyRTC_SetTime(); // 生效到RTC硬件
    }
    else
    {
        // 时间非法时,使用默认时间(2025-01-01 00:00:00)
        adjust_RTC_Time[0] = 2026;
        adjust_RTC_Time[1] = 3;
        adjust_RTC_Time[2] = 12;
        adjust_RTC_Time[3] = 12;
        adjust_RTC_Time[4] = 12;
        adjust_RTC_Time[5] = 12;
        MyRTC_Time[0] = 2026;
        MyRTC_Time[1] = 3;
        MyRTC_Time[2] = 11;
        MyRTC_Time[3] = 12;
        MyRTC_Time[4] = 45;
        MyRTC_Time[5] = 0;
        MyRTC_SetTime();
        WriteTimeToFlash(); // 将默认时间写入FLASH
    }
		
		userInit();       // 机智云用户初始化(数据点初始化等)
    gizwitsInit();    // 机智云协议初始化(WiFi配网、数据通信初始化)
	  gizwitsSetMode(WIFI_AIRLINK_MODE); //默认一键配网
	  delay_ms(200);
    printf("Start \n"); 
    gizwitsHandle((dataPoint_t *)&currentDataPoint);
    ScanGizwitsMode();
    delay_ms(200);

    // 主循环
    while (1)
    {	
			
			  gizwitsHandle((dataPoint_t *)&currentDataPoint);			
        uint32_t current_time = delay_get_tick();       
			  if(current_time - last_save_flash_time >= SAVE_FLASH_INTERVAL)
      {
        // 改用"标志位+低优先级执行",避免直接在主循环执行FLASH写入
        flash_save_flag = 1; 
        last_save_flash_time = current_time;
      }
						
        // 传感器扫描(100ms一次)
        if(current_time - last_sensor_time > 100) 
        {
            SensorScan(); 	
            last_sensor_time = current_time;
					   userHandle(); 
        }
				
				  if(flash_save_flag == 1)
    {
        MyRTC_ReadTime(); 
        WriteAllDataToFlash();
        flash_save_flag = 0; // 清除标志位
    }
        // 按键处理(模式切换)
        uint8_t current_key_num = KeyNum; 
        if(current_key_num != 0)
        {
            switch(mode)
            {
                case AUTO_MODE:
                    if(current_key_num == KEY_1)
                    {
                        mode = MANUAL_MODE;
                        count_m = 1;
                        driveData.LED_Flag = 0;
                        driveData.BEEP_Flag = 0;
                        KeyNum = 0;
                    }
                    else if(current_key_num == KEY_Long1)
                    {
                        mode = SETTINGS_MODE;
                        count_s = 1;
                        KeyNum = 0; 
                    }
                    break;
                case MANUAL_MODE:
                    if(current_key_num == KEY_1)
                    {
                        mode = AUTO_MODE;

                         auto_page = 1; // 强制重置自动模式为第一页
                         KeyNum = 0; 

                    }
										
                    break;
                case SETTINGS_MODE:
                    if(current_key_num == KEY_1)
                    {
                        // 若处于时间设置子页面,先退出子页面
                        if(time_adjust_flag == 1)
                        {
                            time_adjust_flag = 0;
                            count_t = 1; // 重置时间页面计数
                            // 保存修改后的时间到RTC
                            MyRTC_Time[0] = adjust_RTC_Time[0]; 
                            MyRTC_Time[1] = adjust_RTC_Time[1]; 
                            MyRTC_Time[2] = adjust_RTC_Time[2]; 
                            MyRTC_Time[3] = adjust_RTC_Time[3]; 
                            MyRTC_Time[4] = adjust_RTC_Time[4]; 
                            MyRTC_Time[5] = adjust_RTC_Time[5]; 
                            MyRTC_SetTime(); 
													  WriteTimeToFlash();
                            // 退出时全屏刷新回到设置模式
                            OLED_Clear(); 
                            OLED_settingsPage1(); 
                            SettingsThresholdDisplay1();
                            OLED_settingsOption(count_s);
                            OLED_Refresh(); 
                        }
                        else
                        {
                            // 保存阈值和时间到FLASH,回到自动模式
                           WriteThresholdToFlash();
                            mode = AUTO_MODE;
                            count_s = 1; // 重置设置模式计数
                            OLED_Clear();
                            OLED_autoPage1();
                        }
                        KeyNum = 0;
                    }
                    break;
            }
        }
        
        // 模式切换清屏
        if(last_mode != mode)
        {
            OLED_Clear();
            last_mode = mode;
            switch(mode)
            {
              case AUTO_MODE: 
            OLED_autoPage1();               // 绘制固定文字
            SensorDataDisplay1();            // 立即绘制数据(心率/血氧/步数)
            OLED_ShowRTC_Time();             // 绘制时间并触发刷新
            break;
                case MANUAL_MODE: OLED_manualPage1(); break;
                case SETTINGS_MODE: OLED_settingsPage1(); break;
            }
            OLED_Refresh();
        }
        
        // 模式逻辑处理
        switch(mode)
        {
            // 自动模式
            case AUTO_MODE:
						{
							 uint8_t curr_auto_page = SetAuto();
							if(curr_auto_page == 1)
                {
                SensorDataDisplay1();	
									
                // 仅时间变化时刷新
                if(OLED_ShowRTC_Time()) OLED_Refresh();
								}
								 else
                {
                    SensorDataDisplay2();	// 显示GPS数据+蓝牙发送
                }
                AutoControl();
                Control_Manager();
                break;
						}  
            // 手动模式
            case MANUAL_MODE:
            {
                static uint8_t manual_page_initialized = 0;
                static uint8_t last_manual_count = 0;
                static uint8_t last_LED_Flag = 0;
                static uint8_t last_BEEP_Flag = 0;
                static uint8_t force_refresh = 0;  
                
                if(last_mode != mode)
                {
                    manual_page_initialized = 0;
                    last_manual_count = 0;
                    last_LED_Flag = driveData.LED_Flag;
                    last_BEEP_Flag = driveData.BEEP_Flag;
                    force_refresh = 1;  
                    count_m = 1;
                    driveData.LED_Flag = 0;
                    driveData.BEEP_Flag = 0;
                    driveData.JSQ_Flag = 0;
                    driveData.Fan_Flag = 0;
                }
                
                uint8_t current_manual_count = SetManual();
                uint8_t need_refresh = 0;
                if(driveData.LED_Flag != last_LED_Flag || driveData.BEEP_Flag != last_BEEP_Flag)
                {
                    need_refresh = 1;
                    last_LED_Flag = driveData.LED_Flag;
                    last_BEEP_Flag = driveData.BEEP_Flag;
                }
                
                if(!manual_page_initialized || current_manual_count != last_manual_count || need_refresh || force_refresh)
                {
                    OLED_manualPage1();         
                    OLED_manualOption(current_manual_count); 
                    ManualSettingsDisplay1();    
                    manual_page_initialized = 1;
                    last_manual_count = current_manual_count;
                    force_refresh = 0;  
                    OLED_Refresh();
                }
                
                if(current_key_num != 0)
                {
                    ManualControl(current_manual_count);
                    OLED_manualPage1();          
                    OLED_manualOption(current_manual_count); 
                    ManualSettingsDisplay1();    
                    OLED_Refresh(); 
                    KeyNum = 0;
                }
                
                OLED_manualPage1();          
                OLED_manualOption(current_manual_count); 
                ManualSettingsDisplay1();    
                Control_Manager();
                break;
            }
                
            // 设置模式
            case SETTINGS_MODE:
            {
                static uint8_t is_threshold_page_inited = 0;
                static uint8_t current_settings_page = 1;
                uint8_t curr_count_s = SetSelection();
                
                // 按键处理
                if(current_key_num != 0)
                {
                    if(time_adjust_flag == 1)
                    {
                        // 时间设置页面:先处理KEY2光标切换
                        uint8_t curr_count_t = SetTimeSelection();                       
                        // 处理KEY3/KEY4修改时间值
                        if(KeyNum == KEY_3 || KeyNum == KEY_4)
                        {
                            Adjust_Time_Value(curr_count_t);
                            KeyNum = 0;
                        }
                    }
                    // 进入时间设置页面(光标在系统时间项,按KEY3)
                    else if (curr_count_s == 1 && current_key_num == KEY_3)
                    {
                        time_adjust_flag = 1;
                        count_t = 1; // 初始化时间页面计数
                        // 读取RTC到临时缓存
                        MyRTC_ReadTime(); 
                        for(uint8_t i=0; i<6; i++) adjust_RTC_Time[i] = MyRTC_Time[i];          
                        // 进入时全屏刷新
                        OLED_ShowTimeAdjustPage(1); 
                        KeyNum = 0;
                    }
                    // 处理阈值修改
                    else if(current_key_num == KEY_3 || current_key_num == KEY_4)
                    {
                        ThresholdSettings(curr_count_s);
            if (curr_count_s >= 1 && curr_count_s <= 4)
            {
                SettingsThresholdDisplay1(); // 第一页阈值显示
            }
            else if (curr_count_s >=5 && curr_count_s <=7)
            {
                SettingsThresholdDisplay2(); // 第二页阈值显示
            }
            OLED_Refresh();
            KeyNum = 0;
                    }
                }
                
                // 显示逻辑
                if(time_adjust_flag == 1)
                {
                    // 时间设置页面:保持光标显示
                    OLED_TimeAdjustOption(count_t);
                }
                else
                {
                    if (curr_count_s >= 1 && curr_count_s <= 4)
                    {
                        if(current_settings_page != 1)
                        {
                            is_threshold_page_inited = 0;
                            current_settings_page = 1;
                        }
                        if (is_threshold_page_inited == 0)
                        {
                            OLED_settingsPage1();
                            is_threshold_page_inited = 1;
                        }
                        OLED_settingsOption(curr_count_s);
                        SettingsThresholdDisplay1();
                    }
                    else if(curr_count_s >=5 && curr_count_s <=7)
                    {
                        if(current_settings_page != 2)
                        {
                            is_threshold_page_inited = 0;
                            current_settings_page = 2;
                        }
                        if (is_threshold_page_inited == 0)
                        {
                            OLED_settingsPage2();
                            is_threshold_page_inited = 1;
                        }
                        OLED_settingsOption(curr_count_s);
                        SettingsThresholdDisplay2();
                    }
                }
                break;
            }
        }
        
        // 兜底刷新(500ms一次,仅非时间修改页面)
        if(current_time - last_display_time > 500)
        {
            if(mode != SETTINGS_MODE || (mode == SETTINGS_MODE && !time_adjust_flag))
            {
                OLED_Refresh();
            }
            last_display_time = current_time;
        }
				userHandle();
    }
}

六、实验效果

七、包含内容

项目分享

相关推荐
沐欣工作室_lvyiyi2 小时前
基于单片机的机房火灾预警系统(论文+源码)
单片机·嵌入式硬件·云平台·火灾预警
天天爱吃肉82182 小时前
【新能源汽车NTC+VCU温度采集全链路解析:原理、试验与测不准根源定位】
功能测试·嵌入式硬件·机器学习·信息可视化·汽车
somi714 小时前
51单片机-02-中断系统
单片机·嵌入式硬件·51单片机·中断
逐步前行16 小时前
STM32_SysTick_寄存器操作
stm32·嵌入式硬件·fpga开发
DLGXY16 小时前
STM32(二十四)——PWR电源控制
stm32·单片机·嵌入式硬件
csg110716 小时前
PIC单片机高阶实战(五):PIC32MX系列的数据存储
单片机·嵌入式硬件·物联网
GreenGoblin17 小时前
12.DSP学习记录之SCI
单片机·dsp开发·电机控制
爱喝纯牛奶的柠檬18 小时前
基于STM32的GPS NMEA解析驱动设计与实现
stm32·单片机·嵌入式硬件
v先v关v住v获v取18 小时前
茶树修剪机结构设计2张cad+设计说明书+三维图
科技·单片机·51单片机