ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Proteus仿真单片机健康测量系统:心率体温血压全流程实现

Proteus仿真单片机健康测量系统:心率体温血压全流程实现 简介一份基于单片机Proteus仿真的心率、体温、血压人体健康测量系统完整设计资料适合电子、嵌入式相关专业学生及开发者用于课程设计、毕业设计或项目预研。系统包含心率检测压电传感器与集成运放信号调理、体温检测DS18B20单总线温度传感器和血压检测压力传感器配合模数转换器ADC0809并实现超限报警、按键切换测量项及数值语音播报。资源共47个文件涵盖C语言源代码、Proteus工程与仿真备份图、Hex烧录文件、LCD1602/ADC0832/DS18B20等模块驱动及演示视频压缩包大小6.67MB。通过仿真图和工程文件可直观理解硬件连接与程序逻辑源代码便于二次开发和移植。目前已有228人学习下载对需要完整健康监测系统方案的人群具有明确参考价值。1. 用 Proteus 仿真“心率、体温、血压”人体健康测量系统先想清楚测量链做单片机课程设计和毕业设计时看到“protues”这个词基本可以断定这是在说 Proteus 仿真。把心率、体温、血压三个测量项放在一个系统里听起来很完整但很多人拿到题目后第一件事就是打开 Proteus 画图结果画到一半发现传感器模型不知道该选哪个、仿真频繁发散、HEX 文件烧进去没反应。问题不在缺少“仿真图、源代码、讲解视频”而在于测量链没有理顺心率是脉冲计数体温是线性电压换算血压是压力波形的特征值提取三种物理量在仿真里的实现路径完全不同。下面按“原理 → 选型 → 画图 → 写码 → 调参”的顺序把这个系统完整落地。2. 心率、体温、血压的测量原理与 Proteus 器件选型2.1 心率测量用脉冲信号模拟脉搏而不是找“心率传感器模型”真实心率计常用光电容积脉搏波或心电信号经过放大、滤波、整形后变成与心跳同步的脉冲单片机通过外部中断或输入捕获测出脉冲周期。但 Proteus 元件库里没有现成的脉搏血氧传感器模型所以常见做法是用一个 PULSE 或 SINE 信号源产生 13Hz 的波形再经过 LM393 比较器整形为标准方波接到单片机的外部中断引脚。这个替代方案保留了真实系统的核心特征每搏一个脉冲单片机只负责数脉冲和时间。在选型上信号源加比较器的组合比直接接数字方波源更接近真实硬件。SINE 信号源输出正弦波LM393 反相输入端接一个电位器分压作为阈值调节阈值可以改变方波占空比。Proteus 中 LM393 模型很稳定不像普通运放容易“仿真发散”。如果只求跑通也可以直接用DIGITAL PULSE源但那样就没法解释“信号调理”这一环答辩时容易被追问。心率换算的常见做法是定时器 T0 定时 1 秒外部中断 INT0 每收到一个脉冲计数变量加 1每秒末用“脉冲数 × 60”得到每分钟心率。这个算法简单但低心率时误差较大可以在代码里做 4 次平均下面 4.2 节会展开讲。2.2 体温测量LM35 的 10mV/℃ 特性直接换算 ADC 值体温部分推荐用 LM35因为它的输出电压与温度呈线性关系每 1℃ 对应 10mV不需要非线性校正。在 Proteus 中 LM35 的模型非常成熟可以直接从元件库搜索“LM35”放置。它与温湿度检测系统 protues 仿真里常用的 DHT11 不一样DHT11 是数字单总线协议必须写时序LM35 是模拟输出直接接 ADC 通道对新手更友好。假设你的系统使用 AT89C52 外部扩展 ADC0809参考电压为 5VADC 值为 8 位。那么电压换算公式是voltage_mv adc_value * 5000 / 255温度是voltage_mv / 10。需要特别注意的是adc_value * 5000在 8 位单片机中可能超过 16 位整型范围C 语言里必须写成(unsigned long)adc_value * 5000UL / 255否则结果溢出后显示异常。这个细节在仿真中往往表现为“电位器拧到中间温度变化却乱跳”很多人误以为传感器坏了。// ADC0809 采样值转温度LM35 unsigned char adc_value read_adc(0); // 读通道0 unsigned int voltage_mv (unsigned long)adc_value * 5000UL / 255; // 单位mV signed int temp_c (signed int)(voltage_mv / 10); // 每℃ 10mV // 例如 adc_value129 时电压约 2530mV温度约 25℃代码说明read_adc(0)的实现在第 3.3 节给出这里先用它拿到 0255 的 ADC 原始值。voltage_mv是毫伏数temp_c是摄氏度。LM35 在 0℃ 时输出 0V所以不需要校准但显示时最好把temp_c限制在 3542℃范围避免电位器旋到极端时屏幕上出现 100℃这种值。2.3 血压测量Proteus 里没有“血压计模型”用信号源模拟袖带压力包络血压测量是整套系统里原理最特殊的一环。真实电子血压计用充气袖带压迫手臂压力传感器测量袖带压力上叠加的脉搏振荡波再通过示波法找到收缩压和舒张压对应的振荡幅度转折点。Proteus 里显然不会有“手臂”和“袖带”模型所以仿真中一般用两种方案。第一种是“特征值模拟”用两个电位器或两个电压源分别代表收缩压和舒张压对应的电压单片机直接读两个 ADC 通道并显示。这个方案电路简单、仿真稳定但讲过不过去“测量过程”被跳过了。第二种是“包络模拟”用低频三角波信号源作为袖带压力再用高频小信号叠加模拟脉搏波叠加后送入 ADC程序里用滑动窗口找波峰和波谷。第二种更接近真实原理但信号源初始相位、幅值匹配、采样窗口长度都容易调到崩溃。如果你做的是课程设计我一般建议以第一种为主体同时在代码里保留一个“极值检测”函数答辩时说明“真实系统会在极值检测基础上做示波法阈值判断”。这样既保证 Proteus 仿真能跑又能体现你理解血压测量的本质。2.4 显示、报警与处理器选型51 单片机 LCD1602 是“低保真但够用”的组合处理器选择上Proteus 中没有 STC 单片机模型最常见的是用 AT89C52 替代。AT89C52 与 STC89C52 的指令集和引脚兼容Keil C51 编译出的 HEX 文件可以直接加载到 Proteus 的 AT89C52 中运行。不过 AT89C52 没有内置 ADC所以必须外扩 ADC0809 或 ADC0832。如果你不想外扩 ADC也可以选择 ATmega16但很多学校课程设计围绕 51 展开因此这里以 AT89C52 为例。显示器件优先选 LCD1602Proteus 里的搜索关键词是“LM016L”或“LCD1602”。LCD12864 虽然能显示中文但模型稳定性和初始化时序都比 1602 麻烦对于心率、体温、血压这种数值型数据1602 完全够用。蜂鸣器报警建议用 NPN 三极管驱动仿真里直接搜索“BUZZER”即可。下表列出这套系统的选型参考模块真实硬件方案Proteus 仿真方案复杂度心率光电脉搏传感器 信号调理SINE 源 LM393 比较器低体温LM35 模拟输出LM35 直接接 ADC0809低血压气压传感器 袖带两个电位器模拟特征值中主控STC89C52 / AT89C52AT89C52低显示LCD1602LM016L低报警蜂鸣器 三极管BUZZER NPN低选型表的核心逻辑是把真实硬件中不稳定、难仿真的部分换成等效信号源而把单片机侧的采集、处理、显示、报警逻辑原样保留。这也正是“基于单片机的 Proteus 仿真心率体温血压测量系统”这个题目的考察重点。3. Proteus 仿真电路搭建从最小系统到三个测量通道3.1 新建工程与元件查找protues 入门时最容易被拖延的一步打开 Proteus 后新建工程并把默认的图纸名称改好。在元件模式中用关键字搜索下表列出的元件放置到原理图上。很多 proteus 入门教程会直接给你一张完整仿真图但如果你不知道每个元件在库里的名字连画图这一步都过不去。下面是最小系统需要的元件清单元件搜索关键字用途主控单片机AT89C52控制系统逻辑排阻RESPACK-8P0 口上拉晶振CRYSTAL12MHz 时钟复位电容电阻CAP / RES上电复位正弦信号源SINE模拟脉搏波形比较器LM393整形为方波温度传感器LM35体温电压源ADC 芯片ADC0809多通道模拟采样液晶屏LM016L显示测量结果电位器POT-HG模拟血压特征值蜂鸣器BUZZER越限报警AT89C52 的 P0 口是漏极开路结构必须外接上拉排阻否则 LCD 数据线上是高阻态仿真时屏幕不会出字。很多同学在 Proteus 里直接画P0.0~P0.7到 LCD 的 D0~D7却漏掉 RESPACK-8导致运行后 LCD 显示乱码或全亮。P1 到 P3 口内部有弱上拉可以直接驱动按键和 LED不需要额外上拉。3.2 心率通道信号源 LM393 整形成中断输入心率通道的连接方法是SINE 信号源输出接 LM393 的同相输入引脚LM393 反相输入接一个电位器的中间抽头电位器两端分别接 5V 和地。比较器输出接 AT89C52 的 P3.2INT0。在输出脚和 5V 之间接一个 10kΩ 电阻上拉保证高电平稳定。信号源参数设置建议Frequency 填 1.2HzAmplitude 填 1VOffset 填 1.5V。为什么不能直接设为 0 到 5V 的方波因为 LM393 是开集比较器输入端如果电压压摆率过高输出会产生振铃外部中断可能重复触发。使用正弦波加比较器输出是干净方波中断次数才准确。连续后可以先用下面这段代码验证引脚是否通// 定时器T0 50ms中断20次计1秒INT0外部中断计脉搏脉冲 unsigned int pulse_count 0; unsigned char second_ticks 0; unsigned int heart_rate 0; void timer0_isr() __interrupt 1 // T0溢出中断 { TH0 0x4C; // 12MHz晶振50ms重载 TL0 0x00; if (second_ticks 20) { heart_rate pulse_count * 60; // 1秒样本换算bpm pulse_count 0; second_ticks 0; } } void ext0_isr() __interrupt 0 // INT0外部中断 { pulse_count; }代码说明定时器 T0 每 50ms 进一次中断20 次是 1 秒。pulse_count * 60得到一分钟的心率。这里没有加软件去抖因为 LM393 输出已经经过整形Proteus 仿真中信号源是理想方波不需要额外延时。晶振必须是 12MHzTH0/TL0 初值才能这样写如果改成 11.0592MHz需要重新计算重载值。3.3 体温与血压通道ADC0809 的模拟输入分配体温通道把 LM35 的 VOUT 接 ADC0809 的 IN0血压通道把一个 10kΩ 电位器接到 5V 和地之间中间抽头接 IN1。如果需要同时显示收缩压和舒张压可以再加一个电位器接 IN2。ADC0809 的时钟 CLK 需要一个 500kHz 左右的方法Proteus 里可以直接放一个CLOCK信号源频率设为 500kHz比从单片机 ALE 分频更方便。ADC0809 与单片机的连接是新手容易出错的地方。START 和 ALE 引脚必须同时收到高电平启动信号EOC 是转换结束标志要接到单片机的一个 IO 口用于查询。数据输出 D0~D7 接 P1 口通道地址线 ADDA/B/C 接 P0 的低三位。注意AT89C52 的 P0 口需要上拉因此 ADC0809 的数据输出接 P1 更简单不用额外上拉。下面是读取一个通道的 C51 函数这段代码可以直接用在主循环里sbit ALE P0^4; sbit START P0^5; sbit EOC P0^6; sbit ADDA P0^0; sbit ADDB P0^1; sbit ADDC P0^2; // 读取ADC0809指定通道的AD值通道号0~7 unsigned char read_adc(unsigned char channel) { unsigned char adc_val; ADDA channel 0x01; // 通道0选择低地址 ADDB (channel 1) 0x01; ADDC (channel 2) 0x01; ALE 1; // 锁存通道地址 START 1; ALE 0; START 0; // 启动转换 while (EOC 0); // 等待转换结束 adc_val P1; // 读取8位结果 return adc_val; }逻辑说明先通过 ADDA/B/C 选中通道然后给 ALE 和 START 一个高脉冲启动转换。EOC 在转换完成前为低之后变为高程序死等它拉高。数据从 P1 口并行读出。这里没有 ADC0809 的 OE 使能控制因为在 Proteus 中 OE 直接接高电平也行如果你要接单片机只要确保读 P1 前 OE 有效即可。3.4 LCD1602 和蜂鸣器确认每个引脚都有确定电平LCD1602 的 16 个引脚里最容易出问题的是 V0 对比度引脚。Proteus 模型也遵循真实逻辑V0 需要通过电位器接到地或电源通常取中间偏小电压值比如 0.5V 左右。如果 V0 悬空或直接接地屏幕可能只显示方块或者干脆无任何反应。对 Proteus 入门者来说画电路时把 V0 通过一个 10kΩ 电位器接到地就是最稳的接法。蜂鸣器部分使用 BUZZER 无源元件时要区分正负极。Proteus 的 BUZZER 一般是直流蜂鸣器高电平就响。它接在 NPN 三极管集电极发射极接地基极通过 1kΩ 电阻接到单片机引脚。这样单片机输出高电平时三极管导通蜂鸣器响。不要直接把蜂鸣器接在单片机引脚和地之间虽然仿真里可能响但那说明你对驱动能力理解不够。单独验证 LCD 和蜂鸣器的最小代码void main() { lcd_init(); // LCD初始化(4位模式) lcd_str(0, 0, Health Test); // 第1行显示 lcd_str(1, 0, HR:000 T:00C); // 第2行显示 BUZZER 1; // 测试蜂鸣器 while (1); }这段代码不需要接任何传感器只要上电后 LCD 出现两行字符且蜂鸣器响就说明单片机最小系统、LCD、蜂鸣器链路都正常。如果屏幕没字符先用 Proteus 的虚拟示波器观察 E 引脚是否有下降沿脉冲再查 V0 和电源。4. 源代码实现心率、体温、血压数据处理与显示4.1 主程序结构先初始化外设再进主循环完整的系统代码在结构上应该分成初始化、数据采集、数据处理、显示、报警五个部分。main()函数不要写太长的逻辑把所有外设的初始化放在init_all()里。主循环里只做“读数据、刷屏、报警判断”三个动作所有需要精确时间的地方都用定时器中断驱动。void main() { lcd_init(); // LCD初始化 adc_init(); // ADC0809初始化 timer0_init(); // 定时器T0初始化 EA 1; // 开启总中断 EX0 1; // 开启INT0外部中断 ET0 1; // 开启T0中断 while (1) { update_health_data(); // 更新心率、体温、血压数据 display_health(); // 刷新LCD check_alarm(); // 越限报警 } }初始化顺序有讲究先初始化 LCD再初始化定时器最后才开中断。如果先开中断单片机可能在 LCD 初始化过程中被打断导致显示乱码。update_health_data()内部要控制采样频率比如体温每 250ms 采一次用全局变量sample_time做计时。4.2 心率计算1 秒样本与 4 秒平均的取舍第 3.2 节给出的心率中断代码已经在每秒末更新heart_rate但它直接用 1 秒的脉冲数乘以 60在信号源频率为 1.2Hz 时还算准确。真实场景中人体脉搏存在呼吸性波动1 秒样本会导致心率跳变在 ±10bpm 之间。改进方法是保存最近 4 次每秒计数值去掉最大值和最小值再平均。在 Proteus 仿真环境中信号源是理想稳定的所以可以不做平均。但如果你让 SINE 信号源频率在运行时缓慢变化比如用物理旋钮那一段简单的滑动平均会非常有用。可以这样改unsigned int hr_history[4]; unsigned char hr_count 0; void update_heart_rate() { hr_history[hr_count % 4] pulse_count * 60; }这种写法规避了数组索引越界hr_count % 4是环形缓冲区的简化写法。心率计算的值应限制在 30200越界时按异常处理而不是直接显示否则蜂鸣器会误报。4.3 体温计算ADC 值到显示字符串的细节将 ADC0809 的 IN0 数据转换成体温核心公式已经在 2.2 节给出。这里要补充的是显示格式化。LCD1602 接收的是 ASCII 字符所以需要把整数温度拆分。用sprintf最方便但 C51 里sprintf会引入较大代码体积更省空间的做法是手动拆分char temp_str[4]; // 形如 36C void format_temperature(signed int temp_c) { temp_str[0] 0 (temp_c / 10) % 10; // 十位 temp_str[1] 0 temp_c % 10; // 个位 temp_str[2] C; temp_str[3] \0; }这段代码假设温度范围是 3542℃所以百位固定不显示。如果你显示的是 36.5℃这样带小数点的小数需要把电压换算放大十倍比如用temp_x10保存十分之一度然后分别拆出十位、个位、十分位。为了少占用篇幅这里建议显示整数即可答辩时说明“小数精度不是本系统重点”。4.4 血压处理极值记忆法与阈值映射如果血压通道使用电位器产生直流电压那么程序只需要读取两个通道的 ADC 值并映射为 mmHg。映射公式取决于你的设定比如 5V 对应 200mmHg则pressure_mmhg adc_value * 200 / 255。如果要模拟袖带充放气过程可以让一个电位器缓慢变化单片机连续采样并记录最大值和最小值这就退化成了极值检测。#define SAMPLE_NUM 200 unsigned int max_v 0; unsigned int min_v 0xFFFF; unsigned int cur_v; unsigned char i; for (i 0; i SAMPLE_NUM; i) { cur_v read_adc(1) * 200UL / 255; // 直接映射mmHg if (cur_v max_v) max_v cur_v; if (cur_v min_v) min_v cur_v; }采样完成后max_v和min_v分别代表收缩压和舒张压。这个循环必须在主循环中执行不能在中断里做否则会阻塞中断处理。SAMPLE_NUM设成 200在仿真中瞬间完成但要有心理准备如果输入信号是恒定直流最大值和最小值几乎相等显示出来收缩压和舒张压一样那是输入问题不是算法问题。4.5 LCD 显示排版与报警输出LCD1602 只有两行要显示心率、体温、血压建议分两屏。第一屏显示HR和T第二屏显示SYS和DIA用按键切换。显示参数需要设计成表格方便代码对照参数变量名范围单位心率heart_rate30200bpm体温temp_c3542℃收缩压sys_mmhg70200mmHg舒张压dia_mmhg50150mmHg报警逻辑不要在主循环里连续翻转蜂鸣器引脚那样会导致蜂鸣器发声频率被循环次数影响。正确做法是只在check_alarm()里置报警标志然后用定时器中断驱动蜂鸣器。这个设计与真实嵌入式系统处理“非阻塞报警”的思路完全一致。bit alarm_flag 0; void check_alarm() { alarm_flag (temp_c 38) || (temp_c 35) || (heart_rate 100) || (heart_rate 50) || (sys_mmhg 140) || (dia_mmhg 90); } void timer1_isr() __interrupt 3 { static unsigned int beep_cnt 0; if (alarm_flag) { beep_cnt; if (beep_cnt % 200 0) BUZZER ~BUZZER; // 约2.5Hz闪烁 } else { BUZZER 0; } }beep_cnt % 200的频率取决于 T1 的中断间隔。如果 T1 设置为 20ms 中断一次那么 200 次就是 4 秒翻转一次声音太慢一般建议 T1 中断 1ms200 次为 0.2 秒形成约 2.5Hz 的“嘀嘀”声。仿真中你可以直接观察 BUZZER 引脚波形不必真听声音。5. Proteus 仿真调试的 5 个坑与验证方法5.1 仿真发散时先查信号源初始条件和模拟电源去耦Proteus 报DC convergence几乎成了健康测量系统仿真标配。最常见的诱因是 SINE 信号源的 Init 电压与 DC 分析结果冲突。解决办法是先把信号源的Time Delay设为 0Amplitude从 0 开始或者给信号源加一个很小的串联电阻。LM393 和 ADC0809 的 VCC 与 GND 之间各并一个 0.1μF 电容能减少仿真迭代中的电压振荡。5.2 心率验证直接看信号源频率和 INT0 波形把 SINE 信号源频率设置为 1.2Hz期望心率显示 72bpm。如果显示 144 或 36先看 INT0 引脚波形。正常情况每个正弦周期只产生一个上升沿如果 LM393 阈值设得太低可能产生多个触发沿。用 Proteus 左侧的“虚拟示波器”挂到 INT0观察 2 秒内是否有 2.4 个高电平脉冲。频率对不上时不要先改代码先调信号源参数。5.3 体温验证用直流源替换 LM35逐级定位换算问题如果体温显示不对把 LM35 从仿真图上拆掉用一个可调直流电压源接 ADC0809 的 IN0。给定 370mV期望显示 37℃。如果显示 185℃说明换算时把5000写成了250如果显示 37 但 LCD 上温度固定不变说明update_temperature()没被主循环调用。这个做法把“传感器模型”和“代码换算”分开能少走很多弯路。5.4 血压验证用固定电位器百分比验证映射公式将血压电位器抽头电压调到 2.5V如果系统设定 5V 对应 200mmHg那么映射得到200 * 2.5 / 5 100mmHg。旋转电位器观察 LCD 显示是否线性变化。如果显示乱码重点检查read_adc(1)的通道地址选择是不是 ADDA/B/C 接反了。Proteus 的电位器POT-HG可以直接在属性里改位置百分比非常方便做边界测试。5.5 三通道验收对照表下面的输入-输出对照表可以作为答辩前的自测清单也可以配合讲解视频做同步演示。表里每一行设置一个固定输入记录期望值和实测值全部通过后再考虑动画演示。通道输入设置期望显示实测结论心率SINE 1.2Hz72 bpm成功/失败体温LM35 电压 0.37V37℃成功/失败血压电位器 50%2.5V约 100 mmHg成功/失败用这张表验收时建议把每个通道的 Probe 电压也记录下来两组数据能同时对上才算通过。本文还有配套的精品资源点击获取
返回列表