毕业设计 stm32智能运动计步器 - 物联网 嵌入式 单片机

269 篇文章 42 订阅
269 篇文章 9 订阅


0 前言

🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

🚩 基于stm32的智能运动计步系统

🥇学长这里给一个题目综合评分(每项满分5分)

  • 难度系数:2分
  • 工作量:3分
  • 创新点:4分

1 简介

基于STM32的智能运动计步系统,OLED液晶显示当前时间,心率,血氧,卡路里,运动时间,步数,最大步数;通过DS1302显示当前时间,并且具有掉电行走功能;MAX30102模块检测当前心率和血氧;ADXL3轴传感器采集当前行走步数,并且具有掉电保存功能;第一个按键进行设置,第二个按键加,第三个按键减;第四个一键清除当前行走步数;可以设置当前实时时间和设置最大运动步数。
当前步数大于等于最大运动步数时主板pa2脚会发出一个脉冲,从板接收到后通过gsm模块发送短信给目标手机提示运动量达到

2 主要器件

  • STM32单片机
  • MAX30102心率血氧
  • DS1302时钟模块
  • GSM模块
  • ADXL345加速度传感器
  • OLED液晶显示
  • 按键设置

3 实现效果

在这里插入图片描述

按键设置
在这里插入图片描述

4 设计原理

4.1 MAX30102模块

MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。MAX30102采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指、耳垂和手腕等处。标准的2C兼容的通信接口可以将采集到的数值传输给Arduino、KL25Z等单片机进行心率和血氧计算。此外,该芯片还可通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。正因为其优异的性能,该芯片被大量应用在了三星 Galaxy S7 手机。与前代产品 MAX30100 相比 (MAX30100 目前已经停产淘汰 ) , MAX30102 集成了玻璃盖可以有效排除外界和内部光干扰,拥有最优可靠的性能。
传统的脉搏测量方法主要有三种:

  • 一是从心电信号中提取;
  • 二是从测量血压时压力传感器测到的波动来计算脉率;
  • 三是光电容积法。

前两种方法提取信号都会限制病人的活动,如果长时间使用会增加病人生理和心理上的不舒适感。而光电容积法脉搏测量作为监护测量中最普遍的方法之一,其具有方法简单、佩戴方便、可靠性高等特点。 光电容积法的基本原理是利用人体组织在血管搏动时造成透光率不同来进行脉搏和血 氧饱和度测量的。其使用的传感器由光源和光电变换器两部分组成,通过绑带或夹子固定 在病人的手指、手腕或耳垂上。光源一般采用对动脉血中氧合血红蛋白( HbO2 )和血红蛋 白( Hb )有选择性的特定波长的发光二极管(一般选用 660nm 附近的红光和 900nm 附近的 红外光)。当光束透过人体外周血管,由于动脉搏动充血容积变化导致这束光的透光率发 生改变,此时由光电变换器接收经人体组织反射的光线,转变为电信号并将其放大和输 出。由于脉搏是随心脏的搏动而周期性变化的信号,动脉血管容积也周期性变化,因此光 电变换器的电信号变化周期就是脉搏率。同时根据血氧饱和度的定义,其表示为:
在这里插入图片描述
MAX30102 本身集成了完整的发光 LED 及驱动部分,光感应和 AD 转换部分,环境光干 扰消除及数字滤波部分,只将数字接口留给用户,极大地减轻了用户的设计负担。用户只 需要使用单片机通过硬件 I2C或者模拟I2C接口来读取 MAX30102 本身的FIFO ,就可以得到转换后的光强度数值,通过编写相应算法就可以得到心率值和血氧饱和度。
在这里插入图片描述
主要原理图
在这里插入图片描述

4.2 GSM模块

简介

GSM模块是将GSM射频芯片,基带处理芯片,存储器,放大器设备等集成在基板上的功能模块,以提供独立的OS,GSM射频处理,基带处理和标准接口。
GSM模块具有发送SMS短信,语音通话,GPRS数据传输等基于GSM网络进行通信的所有基本功能。简单来讲,GSM模块加上键盘、显示屏和电池,就是一部手机。

gsm模块工作原理
1、GSM 是一个蜂窝网络,也就是说移动电话要连接到它能搜索到的最近的蜂窝单元区域。GSM网络运行在多个不同的无线电频率上。

2、GSM网络一共有4种不同的蜂窝单元尺寸:巨蜂窝,微蜂窝,微微蜂窝和伞蜂窝。覆盖面积因不同的环境而不同。巨蜂窝可以被看作那种基站天线安装在天线杆或者建筑物顶上那种。微蜂窝则是那些天线高度低于平均建筑高度的那些,一般用于市区内。微微蜂窝则是那种很小的蜂窝只覆盖几十米的范围,主要用于室内。伞蜂窝则是用于覆盖更小的蜂窝网的盲区,填补蜂窝之间的信号空白区域。

3、蜂窝半径范围根据天线高度、增益和传播条件可以从百米以上至数十公里。实际使用的最长距离GSM规范支持到35公里。还有个扩展蜂窝的概念,蜂窝半径可以增加一倍甚至更多。

GSM同样支持室内覆盖,通过功率分配器可以把室外天线的功率分配到室内天线分布系统上。这是一种典型的配置方案,用于满足室内高密度通话要求,在购物中心和机场十分常见。然而这并不是必须的,因为室内覆盖也可以通过无线信号穿越建筑物来实现,只是这样可以提高信号质量减少干扰和回声。

4.3 ADXL345加速度传感器

ADXL345 是 ADI 公司推出的基于 iMEMS 技术的 3 轴、数字输出加速度传感器。该加速度传感器的特点有:

  • 分辨率高。最高 13 位分辨率。

  • 量程可变。具有+/-2g, +/-4g, +/-8g, +/-16g 可变的测量范围。

  • 灵敏度高。最高达 3.9mg/LSB,能测量不到 1.0°的倾斜角度变化。

  • 功耗低。 40~145uA 的超低功耗,待机模式只有 0.1uA。

  • 尺寸小。整个 IC 尺寸只有 3mm5mm1mm, LGA 封装。

在这里插入图片描述

ADXL 支持标准的 I2C 或 SPI 数字接口,自带 32 级 FIFO 存储,并且内部有多种运动状态检测和灵活的中断方式等特性。

ADXL345引脚介绍

在这里插入图片描述
引脚功能简介:
在这里插入图片描述

5 部分核心代码

/*
*        ADXL345 三轴加速度传感器DEMO
*        powered by 零知实验室-[url=http://www.lingzhilab.com]www.lingzhilab.com[/url]
*/
#include "ADXL345.h"
 
// 默认的I2C地址为0x53
 
ADXL345 accel;
 
int16_t ax, ay, az;//原始值
float nx,ny,nz;//重力加速度
 
#define LED_PIN LED_BUILTIN
bool blinkState = false;
 
//检测单击、双击动作参数设置
void checkActivitySetup()
{
        accel.setTapAxisXEnabled(false);//设置X轴方向敲击检测
        accel.setTapAxisYEnabled(false);//y轴方向检测
        accel.setTapAxisZEnabled(true);
         
        accel.setTapThreshold(40);//界限设置:40*0.0625 = 2.5g
        accel.setTapDuration(32);//持续时间:32*0.625ms = 20 ms
        accel.setDoubleTapLatency(80);//延迟:80*1.25ms = 100ms
        accel.setDoubleTapWindow(240);//窗口:240*1.25ms = 300ms
       
}
 
void checkActivity()
{
        Activites act = accel.readActivites();
 
  if (act.isDoubleTap)
  {
    Serial.println("Double Tap Detected");
  } else if (act.isTap)
  {
    Serial.println("Tap Detected");
  }else
  {
         Serial.println("no activity");
  }
}
 
void setup() {
     
    Serial.begin(9600);
 
    // 初始化ADXL345
    Serial.println("Initializing I2C devices...");
    accel.initialize();
 
    // 检测是否连接ADXL345
    Serial.println("Testing device connections...");
        if(accel.testConnection()){
                Serial.println("ADXL345 connection successful");
        }else{
                Serial.println("ADXL345 connection failed");
        }
         
         // Set measurement range
  // +/-  2G: ADXL345_RANGE_2G
  // +/-  4G: ADXL345_RANGE_4G
  // +/-  8G: ADXL345_RANGE_8G
  // +/- 16G: ADXL345_RANGE_16G
  accel.setRange(ADXL345_RANGE_16G);
         
        checkActivitySetup();//开启单击、双击动作检测
     
    // 用LED指示数据读取
    pinMode(LED_PIN, OUTPUT);
}
 
void loop() {
#if 0        
        // 获取原始的X,Y,Z三轴的数据
    accel.getAcceleration(&ax, &ay, &az);
 
    // 显示 x/y/z 原始数值
    Serial.print("accel:\t");
    Serial.print(ax); Serial.print("\t");
    Serial.print(ay); Serial.print("\t");
    Serial.println(az);
         
#endif
#if 1
        //获取重力转换后的加速度数值
        accel.readNormalize(&nx,&ny,&nz);
         
        //使用低通滤波进行处理,以去除尖峰的不准确结果
        float fx, fy, fz;
        //系数范围 0.1~0.9
//        accel.lowPassFilter(nx,ny,nz,&fx,&fy,&fz,0.5);
         
        Serial.print("gravity:\t");
    Serial.print(nx); Serial.print("\t");
    Serial.print(ny); Serial.print("\t");
    Serial.println(nz);
//        
//        Serial.print("filter:\t");
//    Serial.print(fx); Serial.print("\t");
//    Serial.print(fy); Serial.print("\t");
//    Serial.println(fz);
#endif
#if 1
        checkActivity();//单双击动作检测
#endif
 
    // 闪烁LED
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
         
        delay(300);
}
/** \file max30102.cpp ******************************************************
*
* Project: MAXREFDES117#
* Filename: max30102.cpp
* Description: This module is an embedded controller driver for the MAX30102
*
* Revision History:
*\n 1-18-2016 Rev 01.00 GL Initial release.
*\n
*/

#include "max30102.h"
#include "myiic.h"

#define max30102_WR_address 0xAE
bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
/**
* \brief        Write a value to a MAX30102 register
* \par          Details
*               This function writes a value to a MAX30102 register
*
* \param[in]    uch_addr    - register address
* \param[in]    uch_data    - register data
*
* \retval       true on success
*/
{
    /*1步:发起I2C总线启动信号 */
    i2c_Start();

    /*2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(max30102_WR_address | I2C_WR);	/* 此处是写指令 */

    /*3步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    /*4步:发送字节地址 */
    i2c_SendByte(uch_addr);
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    /*5步:开始写入数据 */
    i2c_SendByte(uch_data);

    /*6步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return true;	/* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return false;
}

bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
/**
* \brief        Read a MAX30102 register
* \par          Details
*               This function reads a MAX30102 register
*
* \param[in]    uch_addr    - register address
* \param[out]   puch_data    - pointer that stores the register data
*
* \retval       true on success
*/
{
    /*1步:发起I2C总线启动信号 */
    i2c_Start();

    /*2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(max30102_WR_address | I2C_WR);	/* 此处是写指令 */

    /*3步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    /*4步:发送字节地址, */
    i2c_SendByte((uint8_t)uch_addr);
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }


    /*6步:重新启动I2C总线。下面开始读取数据 */
    i2c_Start();

    /*7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(max30102_WR_address | I2C_RD);	/* 此处是读指令 */

    /*8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    /*9步:读取数据 */
    {
        *puch_data = i2c_ReadByte();	/*1个字节 */

        i2c_NAck();	/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
    }
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return true;	/* 执行成功 返回data值 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return false;
}

bool maxim_max30102_init(void)
/**
* \brief        Initialize the MAX30102
* \par          Details
*               This function initializes the MAX30102
*
* \param        None
*
* \retval       true on success
*/
{
    if(!maxim_max30102_write_reg(REG_INTR_ENABLE_1, 0xc0)) // INTR setting
        return false;
    if(!maxim_max30102_write_reg(REG_INTR_ENABLE_2, 0x00))
        return false;
    if(!maxim_max30102_write_reg(REG_FIFO_WR_PTR, 0x00)) //FIFO_WR_PTR[4:0]
        return false;
    if(!maxim_max30102_write_reg(REG_OVF_COUNTER, 0x00)) //OVF_COUNTER[4:0]
        return false;
    if(!maxim_max30102_write_reg(REG_FIFO_RD_PTR, 0x00)) //FIFO_RD_PTR[4:0]
        return false;
    if(!maxim_max30102_write_reg(REG_FIFO_CONFIG, 0x6f)) //sample avg = 8, fifo rollover=false, fifo almost full = 17
        return false;
    if(!maxim_max30102_write_reg(REG_MODE_CONFIG, 0x03))  //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
        return false;
    if(!maxim_max30102_write_reg(REG_SPO2_CONFIG, 0x2F)) // SPO2_ADC range = 4096nA, SPO2 sample rate (400 Hz), LED pulseWidth (411uS)
        return false;

    if(!maxim_max30102_write_reg(REG_LED1_PA, 0x17))  //Choose value for ~ 4.5mA for LED1
        return false;
    if(!maxim_max30102_write_reg(REG_LED2_PA, 0x17))  // Choose value for ~ 4.5mA for LED2
        return false;
    if(!maxim_max30102_write_reg(REG_PILOT_PA, 0x7f))  // Choose value for ~ 25mA for Pilot LED
        return false;
    return true;
}

bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)

/**
* \brief        Read a set of samples from the MAX30102 FIFO register
* \par          Details
*               This function reads a set of samples from the MAX30102 FIFO register
*
* \param[out]   *pun_red_led   - pointer that stores the red LED reading data
* \param[out]   *pun_ir_led    - pointer that stores the IR LED reading data
*
* \retval       true on success
*/
{
    uint32_t un_temp;
    uint8_t uch_temp;
    *pun_ir_led = 0;
    *pun_red_led = 0;
    maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
    maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);



    /*1步:发起I2C总线启动信号 */
    i2c_Start();

    /*2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(max30102_WR_address | I2C_WR);	/* 此处是写指令 */

    /*3步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        printf("read fifo failed");
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    /*4步:发送字节地址, */
    i2c_SendByte((uint8_t)REG_FIFO_DATA);
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }


    /*6步:重新启动I2C总线。下面开始读取数据 */
    i2c_Start();

    /*7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(max30102_WR_address | I2C_RD);	/* 此处是读指令 */

    /*8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;	/* EEPROM器件无应答 */
    }

    un_temp = i2c_ReadByte();
    i2c_Ack();
    un_temp <<= 16;
    *pun_red_led += un_temp;
    un_temp = i2c_ReadByte();
    i2c_Ack();
    un_temp <<= 8;
    *pun_red_led += un_temp;
    un_temp = i2c_ReadByte();
    i2c_Ack();
    *pun_red_led += un_temp;

    un_temp = i2c_ReadByte();
    i2c_Ack();
    un_temp <<= 16;
    *pun_ir_led += un_temp;
    un_temp = i2c_ReadByte();
    i2c_Ack();
    un_temp <<= 8;
    *pun_ir_led += un_temp;
    un_temp = i2c_ReadByte();
    i2c_Ack();
    *pun_ir_led += un_temp;
    *pun_red_led &= 0x03FFFF; //Mask MSB [23:18]
    *pun_ir_led &= 0x03FFFF; //Mask MSB [23:18]

    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return true;
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return false;
}

bool maxim_max30102_reset()
/**
* \brief        Reset the MAX30102
* \par          Details
*               This function resets the MAX30102
*
* \param        None
*
* \retval       true on success
*/
{
    if(!maxim_max30102_write_reg(REG_MODE_CONFIG, 0x40))
        return false;
    else
        return true;
}



6 最后

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值