简介:基于STM32单片机的智能手环项目是一套完整的毕业设计资料,涵盖硬件原理图、软件源码和详细设计文档,适合电子信息、自动化、计算机等相关专业学生用于毕业设计、课程设计或项目练手。压缩包内共104个文件,其中c和h源码负责心率测量、计步显示、血压提醒等功能逻辑与底层驱动,bmp和png图片用于开机广告、菜单界面等屏幕显示,pdf和docx文档介绍设计方案与使用方法,db数据库可存储运行数据;整套资料大小约17.74MB,目录结构清晰,便于对照学习。项目代码经过测试运行稳定,支持二次开发或直接作为毕设课设成果。已有809人学习下载,无论你是要完成毕业设计,还是想入手嵌入式开发,这套资料都能提供从原理到实现的全流程参考,帮助你快速上手项目。
1. STM32智能手环毕业设计:拿到资料包先别急着烧录
收到“基于STM32单片机的智能手环 源码+原理图+详细文档+配套全部资料(毕业设计).zip”这个压缩包,很多人的第一反应是解压、打开 Keil 工程、编译下载。但 STM32 智能手环这类项目,真正值钱的是“整条链路”而不是“能跑起来”。答辩老师最常问的是某一个引脚为什么这样接、计步阈值怎么定、I2C 总线上挂了三个设备为什么没冲突、掉电后 RTC 还走不走。这些答案都在原理图和源码里,需要你顺着硬件、驱动、应用、文档一层层拆开看。所以第一步不是烧录,而是把压缩包当学习样本,按硬件到软件的顺序自己过一遍。
2. 硬件原理图:STM32最小系统到手环外设的接线
2.1 先读 STM32F103C8T6 最小系统原理图,别在电源和晶振上翻车
看一张手环原理图,我一般按电源、时钟、复位、启动模式、下载调试五步走。STM32F103C8T6 是 48 脚芯片,常见于智能手环、鱼缸控制器这类小项目,外设不多但 IO 够用。电源部分通常用 AMS1117-3.3 把 5V 降到 3.3V,或者用锂电池供电时加一颗 TP4056 充电和升压/降压电路。注意 VDD 和 VDDA 上必须有 100nF 加 1μF 的去耦电容,缺了这两颗电容,ADC 采集电池电压时会跳得厉害。
晶振这里是最容易出问题的地方。8MHz 主晶振要并联一个 1MΩ 电阻,两个引脚各接一个 15pF 到 22pF 的负载电容到地,电容值需要和晶振规格书匹配,不是随便装一颗就能稳定起振。STM32F103C8T6 如果外接 HSE,就必须把 OSC_IN/OSC_OUT 固定到 PD0/PD1 上;如果是内部 HSI,那么 PD0/PD1 可以复用为普通 IO。做手环一般还会加 32.768kHz 晶振给 RTC,只能接 PC14/PC15,同时这两个脚不能再配别的外设。
BOOT0 引脚必须通过 10kΩ 下拉到 GND,很多手环原理图会把 BOOT0 用一个 0Ω 电阻接地,方便下载时改跳线。启动模式要保证上电从主 Flash 运行,否则程序烧进去不跑。复位电路在 NRST 上接 10kΩ 上拉和 100nF 电容,ST-Link 连接时也建议把 NRST 引到调试接口,这样用 ST-Link Utility 擦除芯片时更稳定。
调试接口建议用一个 4Pin 或者 5Pin 排针引出 SWDIO、SWCLK、GND、3.3V,不需要带复位脚也可以。使用 ST-Link 的 SWD 模式只需要两根线,比 JTAG 省脚。原理图核对到这里,可以顺手检查 VREF+ 是否接 VDD,F103C8T6 内部参考电压固定为 3.3V,不需要额外电路。最小系统确认没问题后,再去看传感器和显示部分。
2.2 手环外设选型与模块接口:I2C、SPI、UART 怎么分配
智能手环的常见功能包括计步、心率、时间显示和蓝牙上报。基于这些功能,外设一般选择 MPU6050 加速度计、MAX30102 心率传感器、SSD1306 OLED 屏和 HC-08 蓝牙透传模块。它们的接口如下表:
| 功能模块 | 常见型号 | 接口 | STM32F103C8T6 引脚建议 |
|---|---|---|---|
| 加速度/陀螺仪 | MPU6050 | I2C | PB8: SCL,PB9: SDA |
| OLED 显示屏 | SSD1306 0.96寸 | I2C | PB8: SCL,PB9: SDA |
| 心率传感器 | MAX30102 | I2C | PB8: SCL,PB9: SDA,PB5: INT |
| 蓝牙透传 | HC-08 / ATK-HC05 | UART | PA9: TX,PA10: RX |
| 按键 | 轻触按键 x3 | GPIO | PA0: 返回,PA1: 确认,PA2: 加一 |
三个 I2C 设备可以挂在同一条总线上,但必须保证从机地址不同。MPU6050 默认地址是 0x68,AD0 接高电平时变成 0x69;SSD1306 的 7 位地址一般是 0x3C,这个地址硬件固定,MAX30102 的默认地址是 0x57,三者刚好错开。每个设备在 PCB 布局上都要加去耦电容,I2C 的 SCL 和 SDA 还要分别接 4.7kΩ 上拉电阻到 3.3V,上拉电阻不加上拉不起来,通信会时好时坏。
OLED 选 I2C 接口的版本,比 SPI 版本少两根控制线,布板更省位置。SSD1306 的复位引脚 RES 可以接 MCU 的 PB4,也可以直接接 RC 复位电路;为了稳定,我一般用 MCU 控制 RES。按键接口要加上 10kΩ 下拉电阻,PA0 本身支持 WakeUp,待机模式下可以当唤醒源。心率传感器 MAX30102 的 INT 引脚最好接外部中断,这样没有检测到脉搏时主控不用一直读 FIFO。
为了后面写代码方便,把整张原理图的引脚映射整理成头文件。这样原理图评审时也能对着引脚看,少走回头路。下面是一段示意:
#define PIN_SCL GPIO_PIN_8 #define PIN_SDA GPIO_PIN_9 #define I2C_GPIO GPIOB #define HR_INT_PIN GPIO_PIN_5 #define HR_INT_PORT GPIOB #define BT_TX_PIN GPIO_PIN_9 #define BT_RX_PIN GPIO_PIN_10 #define BT_UART USART1这段定义把 I2C、心率中断、蓝牙串口的引脚和串口号固定下来。项目中所有驱动文件只引用这些宏,不改裸引脚号。这样原理图和代码一一对应,后面调试时查错也方便。如果中途换引脚,只需改这一个头文件。
2.3 用嘉立创EDA或Altium Designer检查原理图:ERC、页码和BOM核对
原理图绘制完成不是终点,电气规则检查必须跑一遍。在嘉立创EDA中打开“设计规则检查”,会看到悬空引脚、单端网络、重复页码等告警。比如有同学在画图时把多页原理图每页都设成 page number 1,生成网表后就会出现“有 2 张或以上原理图页面,page number 都设成了 1”的错误,导出到 PCB 时网络连接全乱。遇到这种问题,需要重新设置每一页的图纸编号,保证唯一。
Altium Designer 里可以执行 Project -> Compile,在 Messages 面板按错误级别筛选。最常见的 ERC 告警是“Unconnected Pin”和“Net has only one pin”,前者说明某根线没连上,后者说明网络标签漏了,比如 SDA 引脚旁写了 SDA,但传感器端的标签写成了 SDA0,两个网络实际没连通。这种问题在原理图上不会红报,但焊接完所有传感器都读不到。
核对完电气规则后,生成 BOM 表。逐项检查封装和供货:0805 电容、0603 电阻、LQFP48 的 STM32 芯片、OLED 排针方向。嘉立创EDA可以直接联动立创商城看库存;Altium 则需要去具体供应商核对。尤其注意晶振的负载电容,指定 20pF 就不要换成 22pF,否则频率偏差可能让串口乱码。BOM 对了再送去打样,焊接回来才能顺利进入软件阶段。
3. 源码工程:从STM32CubeMX初始化到手环驱动框架
3.1 用 STM32CubeMX 配置时钟、I2C、UART,生成完整工程
源码包里可能自带 Keil 工程,但我建议用 STM32CubeMX 重新生成一遍,因为初始化代码复用度更高,也方便查参数。选择芯片型号 STM32F103C8Tx,RCC 打开 HSE,使用外部晶振;Debug 选择 Serial Wire,也就是 SWD 模式。时钟树里把 HSE 8MHz 经 PLL 倍频到 72MHz,APB1 总线设置为 36MHz,APB2 设置为 72MHz。
I2C1 配置为 Fast Mode,速率 400kHz,地址 7 位模式。注意 I2C 时钟源来自 APB1,如果 APB1 是 36MHz,I2C 外设时钟 36MHz,400kHz 的分频系数是 90。STM32CubeMX 会自动计算,但你要确认它没有报错。USART1 配置为异步模式,波特率 115200,数据位 8,停止位 1,无校验,开启发送和接收中断。GPIO 配置 PA0-PA2 为输入,下拉模式。PB5 配置为外部中断,上升沿触发使能。
配置完成后生成 MDK-ARM 工程,在 Keil 里编译。这里有一个很常见的坑:如果电脑里同时装了 Keil C51 和 Keil MDK,打开工程时可能加载了错误的编译套装,报“not a valid MDK project”或者找到不到头文件。这时需要在 Project -> Manage -> Project Items 里检查 Toolchain 是不是 ARM Compiler 5/6,同时 Global 里确认 STM32F1xx_DFP 支持包已安装。
3.2 手环核心驱动:I2C 读 MPU6050 和写 OLED 的最小代码
在 CubeMX 生成的main.c基础上,把驱动单独放到 BSP 目录。MPU6050 初始化最关键是退出睡眠模式并配置采样率,下面是精简版:
#include "mpu6050.h" #define MPU6050_ADDR 0x68 #define PWR_MGMT_1 0x6B #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define ACCEL_CONFIG 0x1C uint8_t mpu6050_init(I2C_HandleTypeDef *hi2c) { uint8_t tmp = 0x00; // 写 0 到 PWR_MGMT_1,退出 sleep,并选择内部时钟源 tmp = 0x00; HAL_I2C_Mem_Write(hi2c, MPU6050_ADDR << 1, PWR_MGMT_1, I2C_MEMADD_SIZE_8BIT, &tmp, 1, 100); // 采样率分频:输出频率 = 1kHz / (SMPLRT_DIV + 1) tmp = 0x07; HAL_I2C_Mem_Write(hi2c, MPU6050_ADDR << 1, SMPLRT_DIV, I2C_MEMADD_SIZE_8BIT, &tmp, 1, 100); // 加速度量程 ±2g,写入 0x00 tmp = 0x00; HAL_I2C_Mem_Write(hi2c, MPU6050_ADDR << 1, ACCEL_CONFIG, I2C_MEMADD_SIZE_8BIT, &tmp, 1, 100); return 0; }所有 HAL 函数都带一个hi2c指针,用来指定 I2C1 还是 I2C2。MPU6050 的地址在数据手册上是 7 位值 0x68,HAL_I2C_Mem_Write要求传入 8 位地址,所以要左移一位变成 0xD0。100 是超时毫秒数,在调试时如果返回 HAL_TIMEOUT,说明上拉电阻或接线有问题,或者设备地址写错。
OLED 驱动更简单。SSD1306 通过控制字节区分命令和数据,0x00 表示后一个字节是命令,0x40 表示后一个字节是数据。发送函数如下:
static void ssd1306_write(I2C_HandleTypeDef *hi2c, uint8_t type, uint8_t data) { uint8_t buf[2]; buf[0] = type; // 0x00: 命令, 0x40: 数据 buf[1] = data; HAL_I2C_Master_Transmit(hi2c, 0x3C << 1, buf, 2, 50); }这里 I2C 从机地址是 0x3C,发送两字节。控制字节的高位 Co 为 0 表示后面连续数据,D/C# 位在最低位:命令是 0x00,数据是 0x40。把 OLED 的初始化命令序列封装成数组循环调用,再填充显存,屏幕就能亮了。
3.3 源码工程怎么组织:BSP、App 和文档目录分离
源码工程如果只有main.c,后期维护会非常痛苦。常见的做法是按 BSP(板级支持包)、App(应用逻辑)和 Doc 三层组织。目录结构如下:
Project/ ├─ Core/ # CubeMX 生成的 main.c、中断、系统时钟 │ ├─ Inc/ │ └─ Src/ ├─ Drivers/ # ST 官方 HAL 库与 CMSIS 内核文件 ├─ BSP/ # 板级驱动:oled.c、mpu6050.c、max30102.c ├─ App/ # 应用逻辑:menu.c、step_counter.c、power.c └─ Doc/ # 芯片手册、调试记录、通信协议说明BSP 层每个文件对应一个硬件外设,对外只提供初始化函数和读写函数。App 层不直接操作 HAL,只调用 BSP 提供的接口,例如step_counter_update()。这样如果换了一颗加速度传感器,只需要改 BSP 里的mpu6050.c,App 层计步逻辑完全不用动。
源码里的头文件也要对应清晰:mpu6050.h提供MPU_Init和MPU_Read_Accel;oled.h提供OLED_ShowString;menu.h提供状态机轮询。手环项目的源码包如果目录乱,产物也难以核对。我会在Doc下放一份README.md,写清楚编译工具链、烧录步骤和每个驱动文件的职责,这份 README 对毕业设计文档编写也有直接帮助。
4. 手环功能实现:计步、心率、OLED显示与功耗控制
4.1 计步算法:从加速度数据到步数怎么算
计步在 STM32 上不需要很复杂的神经网络,一个阈值加消抖就能达到可演示效果。MPU6050 读出三轴原始加速度,先转成单位 g,再计算合量平方根。正常走路时加速度合量有周期性高峰,峰值超过阈值就认为可能是一步。为了避免抖动,要加上“最小步间隔”过滤。参考实现如下:
#define STEP_THRESHOLD 1.3f #define MIN_STEP_GAP 300u uint32_t step_count = 0; static uint32_t last_step_ms = 0; void step_detect(float x, float y, float z, uint32_t now_ms) { static float prev_norm = 1.0f; float norm = sqrtf(x * x + y * y + z * z); if (norm > STEP_THRESHOLD && norm > prev_norm && (now_ms - last_step_ms) > MIN_STEP_GAP) { step_count++; last_step_ms = now_ms; } prev_norm = norm; }STEP_THRESHOLD 选 1.3g 是因为正常走路垂直加速度峰值基本在 1.2g 到 1.5g。如果放在手上摆动幅度大,可以提高到 1.5g;放在口袋里可以降到 1.2g。MIN_STEP_GAP 设为 300ms,对应最快每秒 3 步,超过这个频率就忽略掉,防止抖动和误触产生多个峰值。采样率保持 25Hz 足够,过高的采样率只会增加计算量和功耗。
4.2 心率采集:MAX30102 的 INT 中断与 FIFO 读取
心率模块 MAX30102 内部带一个 FIFO,可以缓存采样数据。一般设置采样率为 100Hz,LED 电流根据肤色和佩戴松紧调节。代码里最关键的是中断回调。手指放在传感器上时,INT 引脚会拉低或拉高,进入下面回调:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == HR_INT_PIN) { hr_data_ready = 1; } }主循环检测到hr_data_ready为 1 后,通过 I2C 读取 MAX30102 的 FIFO 寄存器,把红光和红外数据取出。因为 I2C 总线同时挂了 MPU6050 和 OLED,主循环里读取时要注意不能长时间占用总线,否则 OLED 刷新会卡顿。可以把心率采集放在定时器中断里触发,用标志位通知主循环批量读取。
实际测试中,MAX30102 对佩戴位置非常敏感,手指压紧或松动都会让数据失真。因此不能只看瞬时值,需要做 10 个点的滑动平均,再把波峰间隔换算成心率。这个算法放在 App 层,与驱动分离。文档里要写明:波峰检测的阈值是动态的,通常取最近 1 秒数据最大值的 80%,而不是固定值。
4.3 OLED 显示与按键菜单:用状态机管理多个页面
手环界面一般包含时间、计步、心率、菜单四项。用状态机而不是多个 if 嵌套来维护,代码清晰且好扩展。状态定义如下:
typedef enum { PAGE_MAIN, PAGE_STEP, PAGE_HEART_RATE, PAGE_SET_TIME } page_index_t;按键按下时切换page_index,并调用对应的显示函数。长按和短按可以用同一个按键扫描函数区分,PA0 短按返回上一级,长按进入菜单。每次按键操作后点亮 OLED,屏幕超时 10 秒自动熄灭。显示驱动要记得,SSD1306 是显存型芯片,写显存后要调用HAL_I2C_Mem_Write刷新,否则屏幕内容不会变化。刷新频率控制在 20Hz 足够,不要不断重绘。
4.4 低功耗处理:STOP 模式、RTC 唤醒和电池电压测量
手环是电池供电,整机电流最好控制在 30mA 以下。MCU 大部分时间应处于 STOP 模式,用 RTC 闹钟周期性唤醒,比如每 1 秒醒来一次刷新时间,或者每 10 秒醒来一次采集传感器。进入 STOP 模式前要把 OLED 关掉、LED 灭掉,并把 I2C 总线释放。F103 的 STOP 模式代码:
HAL_SuspendTick(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); HAL_ResumeTick(); SystemClock_Config();唤醒后第一件事必须重新调用SystemClock_Config(),因为 STOP 模式会把 PLL 关掉,如果不重新配置,72MHz 主频会变回内部 8MHz,串口波特率会乱。电池电压通过 PA1 接一个分压电阻到 3.3V,用 ADC1 采集,换算公式:
uint16_t adc = HAL_ADC_GetValue(&hadc1); float voltage = adc * 3.3f / 4095.0f; float battery_v = voltage * (100.0f + 51.0f) / 51.0f;这里假设分压电阻是 100kΩ 和 51kΩ,根据实际原理图调整系数。电池电压低于 3.5V 时要提示充电,并在 OLED 上显示低电量图标。
5. 配套文档:原理图说明、源码注释与论文组织
5.1 一个健壮的毕设资料包应该包含哪些文档
压缩包名称里强调“详细文档+配套全部资料”,这说明毕业设计对资料的完整性要求很高。一个合格的手环资料包至少包含以下内容:
| 分类 | 文件名称 | 作用 |
|---|---|---|
| 硬件资料 | 原理图 PDF、PCB、BOM 表 | 评审、打样、焊接 |
| 软件资料 | Keil 工程源码、HEX 烧录文件 | 复现和现场烧录 |
| 说明文档 | README、接线说明、编译说明 | 让别人快速上手 |
| 设计文档 | 开题报告、中期报告、毕业论文 | 满足学校要求 |
| 测试文档 | 功能测试记录、调试截图、演示视频 | 证明系统可用 |
很多同学只在压缩包里放源码和原理图,忽略了 BOM 和测试记录。实际上,BOM 表在打样返工时特别重要,测试记录则展示你从问题到解决的过程,答辩老师非常喜欢看这一类内容。
5.2 源码注释和文档对应的关键参数
详细文档不是把代码抄一遍,而是把“为什么这么设计”解释清楚。比如 I2C 速率为什么是 400kHz 而不是 100kHz,因为手环需要快速刷新 OLED,但 MPU6050 对 400kHz 也完全支持。MPU6050 采样率为什么设成 125Hz,因为计步算法只用 25Hz,降采样后可以省电。MAX30102 的 LED 电流为什么设成 6.4mA,因为太大会穿透手指导致信号饱和。这些参数说明要写进“软件设计”部分,和源码里的宏定义一一对应。
建议在Doc目录下放一个parameter_settings.md,把所有可调参数列成表格,包含默认值、调整范围、对功耗和精度的影响。例如:
| 参数名 | 默认值 | 调整范围 | 说明 |
|---|---|---|---|
| STEP_THRESHOLD | 1.3f | 1.1f - 1.6f | 峰值超过该值才计一步 |
| SAMPLE_RATE | 25Hz | 10Hz - 50Hz | 计步采样率 |
| OLED_REFRESH_MS | 50ms | 20ms - 100ms | 屏幕刷新周期,越低越耗电 |
5.3 毕业论文各章节怎样引用源码和原理图
毕业论文写“硬件电路设计”时,不要贴整张原理图,而是拆成电源模块、STM32 最小系统、传感器模块、蓝牙模块四部分,每部分用一张局部原理图或者引脚说明表格。写“软件设计”时,把系统结构画成模块框图,描述 BSP/App 分层关系,然后针对计步和心率各写一个伪代码逻辑。测试章节则放实际测量数据:待机电流、通信距离、计步误差。文档里所有图编号要和资料包里的原理图页对应,老师按图索骥时能快速定位到原始文件。
6. 验证与进阶:让手环从“能跑”到“能答辩”
6.1 拿到板子后的六步硬件联调
第一步,万用表测电源对地阻抗,避免短路。第二步,上电后用示波器或逻辑分析仪看 8MHz 晶振波形,起振不正常直接查两个 20pF 电容和 1MΩ 电阻。第三步,用 ST-Link 连接,打开 ST-Link Utility,如果提示 “Error: No STM32 Target Found”,先查 SWDIO、SWCLK 是否接反、目标板是否供电、BOOT0 是否被拉高。第四步,烧录一个最简单的 LED 闪烁程序,确认内核能跑。第五步,用 I2C 扫描程序确认总线上三个设备的地址。第六步,逐个测试按键、OLED、蓝牙,每项通过后再组合测试。
扫描 I2C 设备地址的小函数可以写在一个独立工程里,也可以临时放在主循环里:
for (uint8_t addr = 1; addr < 127; addr++) { if (HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 1, 10) == HAL_OK) { printf("Addr: 0x%02X\r\n", addr); } }这个循环会把总线上所有有响应的地址打印到串口,和原理图上的地址一一对照。MPU6050 显示 0x68,SSD1306 显示 0x3C,MAX30102 显示 0x57,这样硬件连接基本可信。串口如果打印乱码,检查串口助手的波特率是不是 115200,以及 STM32 虚拟串口驱动是否安装。
6.2 进阶改造:加入 FreeRTOS 和手机蓝牙
如果学有余力,可以在现有源码中引入 FreeRTOS,用三个任务分别管理传感器采集、OLED 显示和按键处理。I2C 总线是共享资源,每个设备访问时要加互斥锁,否则 OLED 刷新和心率读取会互相打断。蓝牙模块已经在硬件预留了 UART1,可以让手环定时把步数和心率打包成一行 JSON 数据发送到手机蓝牙调试助手,用手机 APP 或微信小程序显示。
最值得做的低功耗优化是把 MAX30102 和 OLED 的电源单独用 MOS 管控制,待机时彻底断电,这样整机待机电流能从 20mA 降到 1mA 以下。调试功耗时,用万用表串联在电池正极,分别测运行、睡眠、关机三挡电流,记录到测试文档里。频率、电流、显示刷新率这几个数据证书齐全了,答辩时无论是被问硬件还是软件,都能拿出实际测量结果来印证。
本文还有配套的精品资源,点击获取