基于STM32的电子血压计毕设:外设配置与示波法算法解析

发布时间:2026/9/12 9:58:58
基于STM32的电子血压计毕设:外设配置与示波法算法解析 简介面向毕业设计、课程作业与嵌入式入门学习者压缩包提供了一个以STM32微控制器为核心的电子血压计完整设计方案涵盖压力传感器信号采集、脉冲波形分析、血压计算、结果显示与串口交互等关键环节。包内共161个文件以C/H源码、Keil工程文件uvproj、编译生成的hex/axf及调试辅助文件为主并包含系统初始化、外设驱动与底层配置代码整体压缩后约2.21MB便于快速导入工程进行二次开发。已有243人浏览学习。资料不仅包含硬件驱动与中断、定时器、USART等基础功能的实现还涉及ADC模拟量采集、数据处理算法与界面显示逻辑并可结合FSMC接口外扩LCD屏有助于理解从底层寄存器到应用层完整的工作流程。对于希望借助真实案例掌握STM32开发环境、调试技巧及电子测量系统设计的读者是一份结构清晰、即下即用的实践参考。1. 从文件清单到功能链路基于STM32的电子血压计毕设到底要动哪些外设拿到毕业设计用的这个压缩包建议先把文件名列表抄下来读一遍system_stm32f10x.c、stm32f10x_usart.c、stm32f10x_exti.c、stm32f10x_gpio.c、stm32f10x_fsmc.c、stm32f10x_pwr.c、stm32f10x_bkp.c、stm32f10x_tim.c、stm32f10x_rtc.c、stm32f10x_adc.c。这套命名来自 STM32F10x 标准外设库把它们和“电子血压计”放在一起等于直接给出整机外设方案ADC 抓压力传感器信号TIM 负责定时采样和 PWM 放气控制FSMC 驱动 LCDEXTI 收按键BKP 配合 RTC 在掉电后留存校准数据和时间戳。整个系统要解决的问题是“袖带气压变化→传感器电压变化→脉搏波提取→收缩压/舒张压计算”。这篇文章直接把这套毕设涉及的硬件链路、工程搭建和算法参数整理成能复现的方式新手能照着建工程老手可以直接把关键代码抽走改进。2. 信号链与选型压力传感器、ADC、定时器和FSMC在电子血压计里的角色2.1 模拟前端把袖带气压变成 STM32 能读的电压电子血压计的信号链路最前端是压力传感器。袖带里的气压变化通过导气管传递到传感器膜片传感器输出随气压变化的电压信号。压缩包里没有列出完整原理图文件但源码引用的外设已经说明硬件前端走的是最常见的“压力传感器运放ADC”结构。我一般会参考 MPX5050 这类带内部集成的传感器方案它在 0~375 mmHg 量程内输出大约 0.2V~4.7V而 STM32F103 的 ADC 输入范围是 0~3.3V所以需要在传感器和 MCU 之间加分压电阻或运放重新调理否则超过 VDDA 的引脚采样值会一直饱和在 4095压力再往上抬也测不出变化。对应到源码层stm32f10x_adc.c 和 stm32f10x_gpio.c 的职责就是初始化这条链路GPIO 配成模拟输入模式ADC1 用规则通道采样。血压脉搏波的主要能量集中在 0.5~5 Hz但为了保证包络完整采样率通常设在 100~200 Hz。这里最容易踩坑的是参考电压如果板子的 VDDA 不是精确 3.3V而是 3.0V 附近浮动换算系数就不能直接用 3.3/4095否则测 100 mmHg 时误差可能会到 8% 以上。这也是后文用 BKP 保存标定系数的直接原因。2.2 定时器、放气阀与采样节奏的关系袖带放气速度是示波法准确度的关键因素。放太快一次心跳在压力轴上只占很少几个采样点后续做幅度包络时峰值会被削平放太慢测量时间超过 60 秒用户舒适度下降且静脉充血加重。常见做法是把放气速度控制在 2~5 mmHg/s用 TIM 输出 PWM 驱动比例阀或通过定时器中断控制电磁阀开关占空比。stm32f10x_tim.c 在这个项目里承担两个职责一个作为 ADC 等间隔采样时基另一个输出 PWM 控制放气阀开度。以 STM32F103 主频 72MHz 为例采样率 100Hz 的定时器配置可以这样理解预分频设为 71计数时钟变成 1MHz自动重载值设为 9999于是每 10000 个计数产生一次更新中断。这样每次进中断的间隔就是 10ms固定住采样节奏算法侧不需要再用HAL_Delay做软等待。下面是配套的外设分配表也是答辩时常被追问的功能模块划分。外设库文件在血压计中的用途关键参数ADC1stm32f10x_adc.c采集袖带压力电压12bit 右对齐采样周期 55.5CyclesTIM2stm32f10x_tim.c100Hz 采样时基、放气阀 PWM预分频 71重载值 9999FSMCstm32f10x_fsmc.c驱动并口 TFT LCDBank116bit 数据宽度USART1stm32f10x_usart.c上位机显示脉搏波形、打印测量结果115200 8-N-1EXTIstm32f10x_exti.c测量开始/停止按键PB0 下降沿触发BKPRTCstm32f10x_bkp.c / rtc保存标定系数和测量时间LSI 时钟VBAT 供电PWRstm32f10x_pwr.c使能备份域访问、低功耗备份访问控制为什么用这套组合而不是一颗专用血压计 SoC因为 STM32F103 在这个场景下性能完全够用FSMC 能带动大尺寸并口屏ADC 自带 DMA 不占 CPU 时间毕业生可以把传感器技术、嵌入式外设、数字滤波这些课程知识点串起来讲。对于毕设或者课程作业外设选型越能解释得通评阅老师越觉得整体设计是完整的。2.3 标准外设库与 HAL 库的取舍这套源码用 stm32f10x_xxx 命名说明是基于标准外设库 V3.5 写的。标准库函数名带有明显特征GPIO_Init、TIM_TimeBaseInit、USART_SendData一眼能看出操作哪个外设数据类型和寄存器对应关系也更直接。毕设选标准库的好处是网上可以找到大量同名库函数例程调试时用仿真器追踪寄存器状态很直观。坏处是 ST 早已停止维护后续换用 STM32F4/H7 时基本要从头改。如果你准备在答辩后继续做产品化建议先用 STM32CubeMX 重新建一个 HAL 库工程把外设初始化部分换掉算法和主流程尽量保持不动。判断依据很简单工程目录里有stm32f10x_conf.h、stm32f10x_it.c代码里大量调用xxx_Init就是标准库工程反之以HAL_xxx开头或有.ioc文件就是 HAL 库。两者不混用强行把 HAL 库的HAL_ADC_Start_DMA塞进标准库工程编译会报一堆重定义错误。提示Keil5 打开工程找不到芯片型号先去 Pack Installer 里安装 STM32F1xx 的设备支持包再检查魔术棒 C/C 页面里的 Include Paths 是否覆盖到 Libraries 目录否则编译会报很多undefined identifier头一批都是找不到stm32f10x.h。3. 搭建可编译的 STM32 工程时钟树、ADC 采集与串口调试3.1 从压缩包到可编译工程的三步解压之后不要着急双击打开工程文件先按下面顺序做可以减少很多来源不明的问题。把整个“Graduation Design”文件夹放到无空格无中文的路径下例如 D:\project\bp_design。Keil 对中文路径和空格的处理比较旧某些编译器版本会出现头文件路径拼接异常。查看有没有.uvprojx或.uvproj工程文件。如果没有打开 Keil5 手动新建一个标准库工程再手工把 User、Libraries 目录添加进 Include Paths。很多课程作业分享包只给了源码和库文件工程文件被删掉重传所以这一步是常态。打开工程前先确认器件支持包Pack Installer 里搜索 STM32F1xx 并安装Keil::STM32F1xx_DFP。如果这台电脑之前只装过 C51 相关的包它并不会顺带提供 STM32 芯片支持导致 Device 下拉列表里找不到 STM32F103RB。工程打开后在 Options for Target 的 Device 页确认型号是 STM32F103RBT6 还是 C8T6。Flash 和 RAM 容量不一样RBT6 是 128KB FlashC8T6 是 64KB Flash。如果工程按 RBT6 编译实际烧到 C8T6 上越界部分会被写入失败或运行到空区域。遇到“下载成功但运行不符合预期”先查这个。3.2 系统时钟配置8MHz 晶振倍频到 72MHz标准库启动文件调用SystemInit()后main 里一般还会显式配一次时钟保证在调试器复位多次后状态一致。常见配置是把 HSE 8MHz 晶振通过 PLL 倍频到 72MHzAPB1 分频到 36MHz。代码如下void SystemClock_Config(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); while (RCC_WaitForHSEStartUp() ! SUCCESS); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // 8MHz * 9 72MHz RCC_PLLCmd(ENABLE); while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); RCC_PCLK2Config(RCC_HCLK_Div1); }这里有几个参数要解释。RCC_PLLMul_9的前提是外部晶振 8MHz如果你板子上焊的是 12MHz 晶振倍频系数要改成 6否则主频变成 108MHz串口波特率、定时器定时全部偏掉。APB1 预分频为 2 时挂在 APB1 上的 TIM2/TIM3/TIM4 内部时钟会自动乘以 2变回 72MHz。很多人在配置定时器时拿 36MHz 去算预分频导致实际采样率变成 200Hz 而不是 100Hz后面滤波算法的参数全对不上。3.3 ADC 连续采集与 DMA 搬运压力采样不能靠主循环里一次一次ADC_GetConversionValue因为放气阶段要同时处理按键、LCD 刷新和放气阀控制轮询会让采样时间点漂移。更稳妥的做法是 ADC 开连续转换模式用 DMA 循环搬运到内存缓冲区。参考配置如下#define ADC_BUF_LEN 64 uint16_t adc_buf[ADC_BUF_LEN]; void ADC1_DMA_Init(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; // PA1 接运放输出 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)adc_buf; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize ADC_BUF_LEN; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel1, DMA_InitStructure); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode ENABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); DMA_Cmd(DMA1_Channel1, ENABLE); }说几个关键点。DMA_PeripheralBaseAddr指向 ADC 的数据寄存器数据宽度是半字对应 12 位 ADC 结果DMA_Mode_Circular让 DMA 不断填充缓冲区不需要每次传输完都重新配置一次。DMA_BufferSize设为 64意味着每攒满 64 个采样点才回到起点主循环只需要比较最新写入序号就能知道有没有新数据。ADC 采样周期ADC_SampleTime_55Cycles5在 12MHz ADC 时钟下约是 4.6us压力信号频率太低这个速度绰绰有余。真正要留意的是校准函数STM32F1 的 ADC 上电后必须先做一次校准否则转换结果的线性度会变差表现为压力波形在零点附近有台阶。3.4 用串口重定向 printf 打印采样值调试阶段最直接的手段是把原始 ADC 值从串口打出来。标准库方式是通过fputc重定向 printf代码如下int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); return ch; }USART_SendData只把数据写入发送数据寄存器不会等待数据真正移位输出所以必须紧跟一个TXE标志位轮询。如果去掉这个 while在连续打印时数据会大量丢失。主循环里可以每 100ms 打印一次adc_buf[0]或者 DMA 写到的最后一位用来观察采集链路通没通。这个步骤建议在接传感器之前就做先用手压住袖带观察串口输出是否随压力变化。如果数值前后抖动超过 20 个 LSB先检查电源纹波和传感器地线不要在噪声环境里直接标定。4. 示波法血压算法定时器采样、滑动平均滤波与特征点定位4.1 压力换算和示波法原理ADC 读到的原始值并不是压力值需要按传感器量程做一次直线映射。如果传感器满量程 300mmHg 对应 3.3V换算函数写成如下形式float adc_to_mmhg(uint16_t adc) { float v (float)adc * 3.3f / 4095.0f; // 还原成电压 float pressure v / 3.3f * 300.0f; // 映射到毫米汞柱 return pressure; }这里的量程和零点不能拍脑袋。实际做毕设时一般先用数字压力表或水银血压计校准在 0 压力下记录 ADC 零点再在 150 或 200 mmHg 气压下记录 ADC 满点代入直线方程求出斜率和截距。校准系数存进 BKP具体做法放到最后一章。如果不校零零点漂移直接变成血压误差这是电子血压计最容易被挑出的问题。示波法的核心过程是放气过程中袖带压力上会叠加微小的脉搏振荡波。记录每个心跳周期的振荡幅度 Amp[i]以及当前袖带压力 P[i]把幅度对压力作图得到包络。包络峰值点对应的袖带压力就是平均动脉压 MAP。收缩压和舒张压在峰值两侧通常收缩压位于上升段幅度约为峰值 50% 的位置舒张压位于下降段幅度约为峰值 80% 的位置。不同厂商的算法会有差别这个比例后面要结合实测调整。4.2 定时器中断里的滑动平均滤波ADC 用 DMA 连续采集后算法侧并不需要对 64 个点逐个做处理而是在定时器中断里取最新一次平滑值往下传。这里给出一个 5 点滑动平均的实现正好适合放在 100Hz 的中断回调里#define FILTER_LEN 5 uint16_t filter_buf[FILTER_LEN]; uint8_t filter_idx 0; uint16_t Smooth_ADC(uint16_t new_data) { uint32_t sum 0; filter_buf[filter_idx] new_data; filter_idx (filter_idx 1) % FILTER_LEN; for (int i 0; i FILTER_LEN; i) { sum filter_buf[i]; } return (uint16_t)(sum / FILTER_LEN); }窗口长度取 5在 100Hz 采样率下对应约 50ms 延迟对 1Hz 附近的脉搏波几乎没有相位影响同时能把 20Hz 以上的传感器毛刺压掉一部分。窗口再加长到 11 或 15波形确实更光滑但脉搏波峰会被削平后面找包络峰值时反而会整体偏低不建议在只有一阶滑动平均的情况下继续加长。如果噪声仍然明显更合适的是先做一次移动平均再叠加一个高通滤波把袖带静压和脉搏波分离开。4.3 特征点定位与放气速度参数拿到平滑后的压力序列后算法要做三件事判断当前处于充气、放气还是稳定阶段在每个放气阶段中找到脉搏波的峰谷在幅度包络上定位 MAP、收缩压和舒张压。一个常见的简化方案是每隔一个采样点记录(pressure, amplitude)然后在放气结束后统一寻找包络最大值而不是在中断里做复杂排序。这样做实时性差一点但代码逻辑清晰遇到异常数据可以直接丢掉重测。下面这张表整理了示波法算法里最常用的一组经验参数毕设按这个范围起步会比较稳判断项经验参数说明采样间隔10ms100Hz保证一个心跳周期有足够多点放气速度2~5 mmHg/s过快会漏掉包络峰过慢体验差MAP 位置振荡幅度最大值对应平均动脉压收缩压位置上升段幅度峰值约 50%部分算法取 46%~57%舒张压位置下降段幅度峰值约 80%部分算法取 74%~82%脉搏波下限频率0.5Hz低于此频率视为杯带泄漏真实产品会把比例做成自适应根据脉搏波幅度动态调整阈值并在检测到不规则脉搏时提示“测量失败”。毕设方案里查表取固定比例完全能讲得过去但如果能加一个 10 次测量的滑动校准答辩时的技术亮点会明显上一个台阶。另外要特别提醒峰值检索必须限制在放气阶段不能在充气阶段就开始找。充气时袖带压力快速上升基线抖动会形成类似振荡幅度的假峰干扰 MAP 定位。5. 校准系数掉电保存与串口验证BKP、RTC 和整机调参5.1 用 BKP 寄存器保存标定值压力传感器标定得到的斜率和偏移量不应该再写进 Flash。STM32F1 的 Flash 写入按扇区擦除数据量小也要消耗几百 ms而且频繁擦写会影响寿命。BKP 寄存器在 VBAT 供电下可以持续保持数据写起来不需要擦除操作所以项目里出现 stm32f10x_pwr.c 和 stm32f10x_bkp.c 的原因就在这里。写入代码可以封装成下面这样void Save_Calib(float scale, float offset) { uint16_t s (uint16_t)(scale * 1000.0f); uint16_t o (uint16_t)(offset * 1000.0f); PWR_BackupAccessCmd(ENABLE); // 打开备份域写保护 BKP_WriteBackupRegister(BKP_DR1, s); BKP_WriteBackupRegister(BKP_DR2, o); PWR_BackupAccessCmd(DISABLE); // 写完立即关保护 }浮点数先放大 1000 倍再转成整数是因为 BKP 寄存器是 16 位直接放浮点区间会溢出。读取时用BKP_ReadBackupRegister拿回整数再除以 1000 恢复。这里最常踩的坑是漏掉PWR_BackupAccessCmd(ENABLE)没有打开写保护时BKP_WriteBackupRegister不会报错但写入会被总线拒绝读回来始终是 0xFFFF。调试时如果发现校准数据上电丢失先检查这一行。5.2 用 RTC 记录测量时间stm32f10x_rtc.c 在这个项目中一般负责给测量记录盖时间戳。使用 LSI 低速时钟配置预分频后在RTC_GetCounter()会每秒钟自增一次。调试时最快确认 RTC 是否在走的方法是打开 Keil 的 Watch 窗口每过一秒看一次计数器变化。如果不变化问题基本出在备份域没有被正确使能或者 RCC 设置里没有打开RCC_RTCCLKConfig。另外要注意RTC 一旦启用读取时最好在进入待机模式前把时间保存到 SRAM 变量否则低功耗唤醒后重新初始化 RTC 会把它清零。5.3 用串口和上位机验证整条链路算法模块写完后不要急着拿真人手臂测。先把袖带接到一个标准气囊上在定时器中断里把当前压力值和包络幅值经过 UART 打印出来。上位机端用 SerialPlot 或匿名上位机画实时曲线会非常直观地看到脉搏波包络先升后降。如果包络在上升段就开始抖动优先检查袖带是否绑松、放气阀是否均匀如果峰值位置对应的压力明显偏离参照血压计先把 5.1 的标定系数重新跑一遍。整个过程记录下来可以直接作为论文里的实验数据页。再补一个改进方向把 UART 接到蓝牙透传模块 HC-05就能改成手机 App 显示接 ESP8266 透传到局域网就变成简单的远程监测终端。这里只提一条经验主循环里不要用while等串口发送完成数据量上来后系统响应会被拖垮改成用USART_ITConfig使能发送完成中断在中断里搬下一段数据整体会顺滑很多。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询