尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于51单片机的脉搏呼吸监测报警系统设计与Proteus仿真

基于51单片机的脉搏呼吸监测报警系统设计与Proteus仿真 简介基于51单片机的脉搏呼吸监测报警设计资料融合仿真电路图、单片机程序与模拟信号处理原理图面向电子工程、自动化等专业的课程设计与毕业设计场景也可作为单片机爱好者学习传感器信号处理的参考案例。系统实现了脉搏和呼吸频率的实时检测通过LCD1602液晶屏显示测量值利用独立按键设置上下限报警阈值超过范围即触发声光报警覆盖了从数据采集、数据处理到显示交互的完整流程。压缩包内共46个文件体积约2.43MB主要包含仿真工程文件、C语言源程序、AD原理图文档、可烧录的hex文件、元件清单、多张流程框图以及PDF版原理图预览结构划分清晰便于按模块查阅和复用。目前已有78人学习下载除了可直接运行的仿真与代码外还提供了呼吸流程图、脉搏流程图、按键流程图等设计文档能帮助使用者快速理解各功能模块的逻辑关系。对于需要完成类似人体生理参数监测项目或希望掌握51单片机外设应用的开发者这套资料具有较高的参考价值可明显缩短系统原型搭建与调试的时间。1. 拆一个 51 核的脉搏呼吸报警项目先搞清楚它到底在测什么医疗电子入门里最常见的选题不是心电就是脉搏加呼吸。这个项目把两者合到了一起做了个带上下限报警的监测仪跑在 51 单片机上用 Proteus 仿真验证附带 AD 原理图。它的典型应用场景是病房或家庭护理传感器给出脉冲信号单片机数脉冲算频率LCD1602 显示实时值超限就驱动蜂鸣器和 LED 报警。为什么这个东西到现在还有参考价值因为它的技术骨架——外部中断计数、定时器闸门、按键设置阈值、LCD 驱动——正好覆盖了从 51 单片机过渡到 ARM 之前必须掌握的裸机外设用法。你要是在做 51 单片机课程设计、protues 仿真入门或者想验证自己写的中断嵌套和按键消抖逻辑这个包是能直接跑起来看效果的。它的完整度在于仿真图、源程序、.hex 文件、AD 原理图和流程图全给齐了不是只给一段残缺代码。2. 系统硬件架构与传感器信号调理别急着写代码先把计数通道定下来2.1 从传感器信号到单片机引脚波形整形的必要性光电式脉搏传感器的原始输出是一个随血流脉动变化的微弱模拟信号幅度从几毫伏到几十毫伏不等还叠加了环境光和工频噪声。如果直接把这个信号送进单片机引脚结果只有一个无法稳定触发中断频率计数全是乱的。处理路径是固定的前置放大 → 滤波 → 比较器整形 → 限幅保护 → 单片机输入引脚。项目里 AD 部分的核心就是这条链路前级用运放搭同相放大把 mV 级信号放大到 V 级再用比较器和一个基准电压比较把正弦样式的波形变成标准方波。方波的上升沿就代表一次脉搏搏动单片机只需要数上升沿的次数。呼吸信号的来源类似。胸带式呼吸传感器或压电薄膜传感器在胸腔扩张收缩时产生形变输出一个缓慢变化的电压。它的频率比脉搏低得多成年静息状态下每分钟 12 到 20 次也就是 0.2 到 0.33 Hz。对单片机的计数器而言这个频率是极其宽松的。真正麻烦的不是频率低而是传感器信号太弱、太缓比较器容易在阈值附近抖动导致一个呼吸周期里出现多个沿。这里有一个容易被初学者忽略的选型点比较器整形之后必须在输出端加一个施密特触发器或 RC 正反馈网络制造滞回区间。没有滞回仿真里看着正常的逻辑实物上一接传感器就可能狂跳中断。2.1.1 单片机引脚资源分配51 单片机这里以 STC89C52 或 AT89S52 为参考有 32 个 IO 引脚但能作为外部中断输入的只有 INT0P3.2和 INT1P3.3。脉搏和呼吸两个信号正好各占一个外部中断引脚用下降沿或上升沿触发。这个选择比用定时器计数引脚更合理。定时器 T0 和 T1 虽然也可以配置成计数器模式但只有 T0 的 P3.4 和 T1 的 P3.5 两个外部计数输入且计数功能一旦启用该定时器就无法再做时间基准。而本项目里需要一个定时器产生 1 秒闸门用来计算频率所以必须把两个定时器都留给时间基准让外部中断承担计数角色。另一个原因在于动态调节。外部中断在每次脉冲沿到来时给软件变量自增主循环在闸门到期后读取变量计算频率这种方式在低频率信号下误差可控。如果反过来用定时器直接计数脉冲频率极低时呼吸 0.3 Hz1 秒只能计数 0 到 1 次分辨率为 1 bpm显示会一卡一卡的。信号接入引脚触发方式中断服务脉搏方波P3.2 / INT0下降沿pulse_count呼吸方波P3.3 / INT1下降沿breath_count蜂鸣器P1.0低电平驱动报警输出报警 LEDP1.1低电平驱动报警指示LCD1602 RSP2.0复用数据口指令/数据选择LCD1602 RWP2.1复用数据口读/写控制LCD1602 ENP2.2复用数据口使能脉冲LCD1602 D4-D7P2.4-P2.7四线模式数据位独立按键P1.2-P1.5高电平有效阈值设置当然IO 分配在 Proteus 仿真和 AD 原理图里可以灵活调整仿真环境里没有传感器接入直接用 DCLOCK 数字时钟源产生脉冲即可。2.2 Proteus 仿真中的传感器模拟方式真实传感器在 Proteus 里没有现成的模型常规做法是用信号发生器或数字脉冲源替代。脉搏信号的频率取 72 次/分钟1.2 Hz呼吸取 18 次/分钟0.3 Hz。在 ISIS 界面左侧工具栏的Generator Mode里选DCLOCK或DIGITAL PULSE接到单片机的 P3.2 和 P3.3 引脚上把频率参数分别设为 1.2 Hz 和 0.3 Hz幅度设为 0 到 5 V。一个重要细节仿真里脉冲源是理想的不存在抖动和毛刺所以你在仿真时看到的频率值会非常稳定。但实物中传感器信号即使整形了也可能有边缘抖动。所以拿到这个项目包验证逻辑功能时用仿真验证信号处理时必须回到 AD 原理图上检查比较器阈值设计和滞回网络两者解决的问题完全不同。3. 频率测量、按键阈值设置与 LCD1602 驱动的核心代码实现3.1 1 秒闸门与外部中断计数主循环、中断和显示三者怎么协作频率测量的本质是固定时间窗口内统计脉冲个数。窗口太长显示刷新慢窗口太短低频信号损失精度。1 秒窗口对脉搏1-3 Hz和呼吸0.2-0.5 Hz都是可接受的折中方案。使用定时器 0 产生 50 ms 中断软件累加 20 次得到 1 秒。外部中断 0 和外部中断 1 分别处理脉搏和呼吸的下降沿中断服务程序里只做变量自增不处理任何显示和判断逻辑保证中断服务程序足够短。这个写法和很多入门教程不一样教程里喜欢在中断里直接判断是否报警看起来省代码但定时器中断里如果有耗时操作会产生计数丢中断的隐患。下面给出一个可以直接运行在 12 MHz 晶振下的代码框架对应项目包里的 main.c 和 lcd1602.c// main.c 核心代码1s 闸门 外部中断计数 #include reg52.h #include lcd1602.h sbit BEEP P1^0; sbit LED_ALARM P1^1; sbit KEY_MODE P1^2; // 模式/设置键 sbit KEY_UP P1^3; // 上限加/调整向上 sbit KEY_DOWN P1^4; // 下限减/调整向下 sbit KEY_CONFIRM P1^5; // 确认键 volatile unsigned int pulse_count 0; volatile unsigned int breath_count 0; volatile unsigned int timer_50ms 0; unsigned char flag_1s 0; unsigned int pulse_rate 0; // 脉搏频率 次/分钟 unsigned int breath_rate 0; // 呼吸频率 次/分钟 // 报警上下限默认值 unsigned int pulse_high 100; unsigned int pulse_low 60; unsigned int breath_high 24; unsigned int breath_low 12; // 定时器0 50ms初始化 void timer0_init(void) { TMOD 0xF0; TMOD | 0x01; // 定时器0模式116位定时 TH0 0x4C; // 50ms 12MHz TL0 0x00; ET0 1; TR0 1; EA 1; } // 外部中断0脉搏下降沿 void int0_isr(void) interrupt 0 { pulse_count; } // 外部中断1呼吸下降沿 void int1_isr(void) interrupt 2 { breath_count; } // 定时器0中断50ms 累加 20 次得 1s void timer0_isr(void) interrupt 1 { TH0 0x4C; TL0 0x00; timer_50ms; if (timer_50ms 20) { timer_50ms 0; flag_1s 1; } }这段代码的关键点在于外部中断服务函数中只做pulse_count和breath_count这两个变量用volatile修饰防止 Keil C51 优化器把它们误优化到寄存器里。中断函数里不能调用 LCD 显示函数因为在 51 单片机里显示函数涉及较长的循环操作会拉长中断响应时间。定时器 0 重装初值的写法直接写在中断函数里没有用自动重装模式因为定时器 0 模式 2 是 8 位计数器最大定时时间不够 50 ms12 MHz 下最长 256 个机器周期即 256 us所以要手动重装 16 位初值。3.2 主循环读计数、算频率、判断报警阈值在flag_1s置位后主循环需要立即读取pulse_count和breath_count并清零。这里有一个数据一致性问题如果在主循环读取计数值时恰好外部中断触发了pulse_count读到的值就是读到一半的旧值问题不大但更好的做法是先关闭外部中断再复制变量再打开。// main.c 主循环部分 void main(void) { unsigned int pulse_temp 0; unsigned int breath_temp 0; lcd1602_init(); timer0_init(); IT0 1; // 外部中断0下降沿触发 IT1 1; // 外部中断1下降沿触发 EX0 1; EX1 1; lcd1602_show_string(0, 0, PR:000 BPM BR:00); lcd1602_show_string(0, 1, H:100 L:060 H:24 L:12); while (1) { if (flag_1s) { flag_1s 0; // 关闭外部中断保证读取一致性 EX0 0; EX1 0; pulse_temp pulse_count; breath_temp breath_count; pulse_count 0; breath_count 0; EX0 1; EX1 1; // 频率换算1s 内的脉冲数即 Hz转每分钟 pulse_rate pulse_temp * 60; breath_rate breath_temp * 60; // 更新 LCD 显示 lcd1602_show_uint8(3, 0, pulse_rate); lcd1602_show_uint8(6, 0, breath_rate); // 报警逻辑 if ((pulse_rate pulse_high) || (pulse_rate pulse_low) || (breath_rate breath_high) || (breath_rate breath_low)) { BEEP 0; LED_ALARM 0; } else { BEEP 1; LED_ALARM 1; } } scan_key(); } }这里有一个很关键的换算逻辑1 秒窗口内计数到的脉冲个数数值上等于多少 Hz。比如 1 秒内数到 30 个脉搏脉冲那就是 30 Hz但这显然不符合人体生理因为静息脉搏 60-100 次/分钟对应 1-1.67 Hz。所以代码里用pulse_temp * 60把每秒脉冲数换算成每分钟这正是次/分钟这个概念的本质。对应到仿真里DCLOCK 设为 1.2 Hz1 秒内计数 1 到 2 个上升沿pulse_temp * 60得到 60 到 120。你会发现仿真读数在 60 和 120 之间跳变原因在于 1 秒闸门和 1.2 Hz 脉冲的相位不完全对齐这是固定窗口计数的固有误差。3.3 独立按键设置阈值的状态机实现按键要支持上限加、上限减、下限加、下限减最简单的做法是四个独立按键分别对应四个方向的调整。虽然这个项目标注了独立按键但没有说明按键数量。从 PCB 引脚复用考虑2 个按键加一个模式键的方案更常见模式键循环选择当前要修改的参数脉搏上限、脉搏下限、呼吸上限、呼吸下限加减键调整数值。按键处理的核心是消抖。仿真里按键是理想的按下即闭合不加消抖也能跑通。但实物如果不在软件里做消抖机械抖动会造成一次按下被识别成多次。// 按键扫描模式键切换调整项加减键修改阈值 void scan_key(void) { static unsigned char set_index 0; // 0P高 1P低 2B高 3B低 static unsigned int key_count 0; static bit last_mode 0; if (KEY_MODE 1) { // 按键按下高电平有效 if (key_count 10) { key_count; } if (key_count 5) { // 持续稳定后生效约 50ms 消抖 set_index (set_index 1) % 4; lcd1602_show_uint8(11, 1, set_index); // 显示当前调整项编号 } } else { key_count 0; } if (KEY_UP 1) { switch (set_index) { case 0: if (pulse_high 200) pulse_high; break; case 1: if (pulse_low 30) pulse_low--; break; case 2: if (breath_high 40) breath_high; break; case 3: if (breath_low 6) breath_low--; break; } lcd1602_show_uint8(3, 1, pulse_high); // 刷新 LCD 对应位置 } if (KEY_DOWN 1) { switch (set_index) { case 0: if (pulse_high pulse_low) pulse_high--; break; case 1: if (pulse_low 30) pulse_low--; break; case 2: if (breath_high breath_low) breath_high--; break; case 3: if (breath_low 6) breath_low--; break; } lcd1602_show_uint8(7, 1, breath_high); // 刷新 LCD 对应位置 } // 延时等待按键释放 while (KEY_UP 1 || KEY_DOWN 1); }注意last_mode变量的存在意义。它用来记录上一次模式键的状态只有检测到从 0 变 1 的上升沿时才认为是一次新的按下连续按住不会连续切换。这是按键处理中容易被忽略的一点很多人在仿真中测试按键正常到实物上发现按一次跳好几个数值原因往往是缺少边沿检测把按住期间持续为 1当成了多次按下。4. 从 Proteus 仿真联调到 AD 原理图转实物参数设置和隐藏的坑4.1 仿真环境里的操作顺序拿到项目包后先用 Proteus 打开仿真.DSN然后双击 51 单片机芯片在 Program File 栏加载 main.hex。晶振频率和代码里的定时器初值必须匹配。如果代码按 12 MHz 写的 50 ms 初值 0x4C00仿真里晶振却设置成 11.0592 MHz定时周期会变成 54 ms 左右显示刷新偏差不大但串口波特率相关的模块会出问题。本项目中只有定时和显示影响是显示刷新略慢逻辑仍然正确。建议的联调步骤先把两个 DCLOCK 的频率都设为 0 Hz确认 LCD 正常显示PR:000 BPM BR:00初始化和显示函数没问题。设脉搏信号 1.2 Hz观察一秒后 PR 值是否在 60-120 之间跳变同时把报警阈值改到 80-90 之间触发一次报警验证蜂鸣器逻辑。设呼吸信号 0.3 Hz观察 BR 值是否在 12-24 区间振荡。连接虚拟示波器Virtual Oscilloscope到 P3.2 引脚确认方波下降沿的时序和 LCD 刷新时序没有明显冲突。一个常见的仿真排错现象是LCD 有背光但无字符。先量单片机 XTAL 引脚是否有脉冲再看 EA 引脚是否接高电平最后检查程序里 LCD 时序的延时是否过短。Proteus 里如果延时时间给得太短虽然逻辑波形正确但液晶模块没有响应。4.2 AD 原理图的模块划分传感器电路要单独留测试点项目包含原理图 Sheet1.SchDoc 和 Free Documents 里的输出文件。原理图模块按功能应该划分为最小系统单片机晶振复位电容、电源5V 稳压、LCD1602 接口、独立按键、声光报警。传感器信号调理部分作为独立电路块输入端接接插件模拟地和数字地之间用 0 欧电阻或磁珠隔离。模块关键器件参数/模型注意事项信号放大LM358 或 LM324同相放大增益 20-50 倍电源去耦电容靠近运放引脚比较整形LM393 或 TLV3501滞回比较Vref 取电源中值反馈电阻计算滞回宽度光电脉搏探头红外 LED 光敏管发射管限流 10-20 mA遮光罩不可省蜂鸣器驱动三极管 S8550 蜂鸣器IO 低电平导通蜂鸣器线圈并联续流二极管晶振电路12 MHz 2x22pF 负载电容晶振频率与代码匹配PCB 布局尽量靠近单片机从 AD 原理图转到 PCB 时信号调理电路的地线处理很重要。脉搏传感器信号的幅度只有毫伏级模拟地和数字地在单片机底部单点汇合避免数字信号回流穿过模拟区。这是仿真中完全体现不出来、但实物必踩的坑。AD 原理图检查时重点看网络标号是否和单片机引脚一一对应特别是 P3.2 和 P3.3在仿真和实物上对应着单片机物理引脚 12 和 13原理图和 PCB 的封装引脚映射不能出错。4.3 Proteus 仿真和实物测试的差异清单仿真里信号源是理想的按键是理想的LCD 时序也是理想化的。实物会引入电源纹波、按键抖动、传感器信号漂移、LCD 对比度调节等问题。项目包里有一个新建文本文档.txt里面通常记录了作者实测时发现的问题和改动记录拿到包先打开看那里比任何教程都更接近实际踩坑点。具体差异参考Proteus 里不需要外接上拉电阻但 51 单片机 P0 口在实物上必须外接 10k 排阻仿真里蜂鸣器可以直连 IO 模拟实物必须加三极管驱动芯片 IO 输出电流不足以直接驱动蜂鸣器传感器在仿真里不存在DCLOCK 输出摆幅 0-5 V实物传感器经过整形后摆幅可能只有 3.3 V如果单片机是 5V 供电需确认阈值电平兼容5. 周期法替代固定窗口计数低频率信号下的精度提升实测技巧固定 1 秒窗口计数在脉搏 1.2 Hz、呼吸 0.3 Hz 时误差是显著的根本原因是窗口和信号周期之间没有对齐。改进方向是周期法不数固定时间内有多少个脉冲而是数一个脉冲周期内有多少个定时器计数值。以脉搏为例在两次脉搏下降沿之间用定时器 0 记录经过的机器周期数。假设晶振 12 MHz一个机器周期 1 us连续两个下降沿之间记录了 830000 个机器周期则脉搏频率是 1/0.83 s 1.2 Hz也就是 72 次/分钟。这个方法在低频下比固定窗口计数精确得多因为测量的直接是信号周期本身不受窗口对齐影响。具体实现思路是INT0 下降沿触发时读取当前定时器值并和上次记录值做差差值的倒数乘以 60 得到频率。如果信号频率升高到几十 Hz周期法对定时器溢出处理和重载时机要求更高需要用 16 位定时器并处理溢出中断记录。测量精度限度和优化手段如下信号频率范围适用方法精度特征优化手段0.2 - 1 Hz周期法周期时长 1-5 s计数分辨率高处理定时器溢出滑动平均1 - 5 Hz周期法或计数法计数法窗口偏差不足一个脉冲多窗口平均丢弃突变值5 Hz 以上计数法优先周期太短定时器读取开销占比高扩展 1s 窗口为 2s呼吸频率 0.3 Hz 意味着一个完整周期约 3.3 秒周期法测量时每 3 秒多才能刷新一次显示响应较慢。所以改进版本常见做法是动态切换呼吸用 2 秒窗口计数脉搏用周期法两者独立运算。这个技巧可以直接在项目包的 main.c 基础上改动不改硬件。最后两段值得特别关注报警阈值设置存在边界条件pulse_high不能设置成低于pulse_lowpulse_low不能低于生理极限值否则会出现永远报警或永不报警的假死状态。在按键处理里加上大小关系约束比在仿真后加一个值域判断要可靠得多。set_index切换时LCD 第二行对应位置的数字要做闪烁提示没有这种视觉反馈用户会以为按键失效。把模式切换的闪烁逻辑写进主循环的刷新分支里改动量不大但体验差异明显。本文还有配套的精品资源点击获取
返回列表