
简介基于51单片机与Proteus仿真的人体健康检测系统设计面向电子、自动化等专业学生和嵌入式开发入门者可用于课程设计、毕业设计或自学实践。系统以DS18B20采集人体温度通过压力传感器模拟检测心率检测结果实时显示在LCD1602液晶屏上用户可通过按键切换测量温度或心率也可主动结束测量。当体温或心率超过正常标准时系统会自动触发声光告警。压缩包共42个文件容量仅473KB包含C语言源代码、头文件、Keil工程文件、Proteus仿真工程及备份、Hex烧录文件、编译中间文件和相关文档结构清晰便于直接打开仿真和二次开发适合作为单片机课程设计或毕业设计的参考范本。目前已有157人学习下载可作为理解单片机外设接口、传感器数据采集与处理、LCD显示和声光报警控制逻辑的完整参考也适合扩展加入体温历史记录、心率变异性分析等进阶功能。1. 这套人体健康检测系统先想清楚它能演示什么体温和心率是所有健康检测设备里最基础的两个指标。这个项目用 51 单片机跑 Proteus 仿真主控是 AT89C51配合 DS18B20 测温、压力传感器加 ADC0832 采集心率信号再把结果输出到 LCD1602。按键可以切换测量模式体温超过 37.8℃ 或心率不在 50100 范围时蜂鸣器和 LED 会同时报警。对学习单片机的人来说它的价值在于把单总线时序、串行 ADC 采集、1602 并行驱动、按键状态机放在同一个工程里一次串起来。对正在做课程设计或毕业设计的人仿真图和 C51 源代码直接给了二次开发的底子。2. 单片机仿真的健康指标链路主控选型、传感器与信号走向2.1 为什么用 51 而不是 STM32这个系统的采样率要求不高体温刷新一次需要几百毫秒心率采样每 10 毫秒一次也足够AT89C51 在这种场景下比 STM32 更合适。51 的 IO 口是准双向口直接位操作就能模拟 DS18B20 的单总线协议和 ADC0832 的三线串行时序新手能看清每个时钟沿在干什么。Proteus 里 AT89C51 模型稳定Keil C51 编译出来的 HEX 文件拖进仿真芯片就能跑。换成 STC89C52 这类国产 51 内核芯片也只需要改芯片型号Keil 工程不用动这一点对硬件验证很有用。从信号链路上看系统可以分成三条路第一条是 DS18B20 把温度转成 12 位数字量通过单总线送到 P3.7第二条是压力传感器输出模拟电压经 ADC0832 的 CH0 通道转成 8 位数据通过 P3.4P3.6 送给 CPU第三条是 LCD1602 显示并行数据由 P2 口控制 RS、RW、ENP0 口送字符数据。按键和报警则分别占用 P3.0、P3.1 和 P2.3、P2.4。2.2 各模块引脚分配与 Proteus 连线下面这张表是仿真电路里的默认分配如果你拿到的是另一份引脚定义只要同步修改头文件里的sbit即可不用改逻辑模块信号单片机端口说明温度采集DS18B20 DQP3.7单总线数据线需上拉 4.7kΩ心率采集ADC0832 CSP3.5片选低电平有效心率采集ADC0832 CLKP3.6串行时钟心率采集ADC0832 DO/DIP3.4分时复用的数据口LCD1602RS / RW / ENP2.0 / P2.1 / P2.2控制线LCD1602D0D7P0并行数据P0 口接 10kΩ 排阻上拉按键模式切换P3.0温度/心率切换低电平触发按键结束测量P3.1回到待机低电平触发报警蜂鸣器 / LEDP2.3 / P2.4超限时同时动作P0 口上拉这一项很容易漏。Proteus 里 AT89C51 的 P0 是开漏输出不接排阻时 LCD1602 会显示乱码或直接不显示。用排阻元件RESPACK-8就能解决。2.3 DS18B20 测温为什么这么慢又为什么够用DS18B20 出厂默认 12 位测温精度分辨率是 0.0625℃对应转换时间最长 750ms。这个速度放在健康检测场景里没有任何问题人体体温变化本来就慢每秒刷新一次足够了。单总线协议最麻烦的是时序初始化时主机拉低至少 480μs 再释放等待 60μs 后若检测到低电平脉冲说明设备在线写 0 和写 1 的时序宽度不同读时序也要精确卡在 70μs 左右。Keil C51 里这些延时需要用循环实现晶振频率变了延时参数就要跟着变否则读出来可能是 85℃ 这种异常值。2.4 心率检测压力传感器变成血压脉搏的模拟量实际硬件里压力传感器常见是压电薄膜或应变片贴在手指上每一次脉搏会让传感器输出一个脉冲电压经过放大调理后输入 ADC0832。在 Proteus 里没有真实的应变片我用的是一个滑动变阻器和信号源组合把 CH0 电压按脉搏波形调制到 12Hz程序通过 ADC 采样值判断脉冲跳变。ADC0832 是 8 位逐次逼近型输入范围 05V测量精度 19.5mV/LSB辨别人的脉搏包络足够。3. 工程代码怎么拆main 状态机、DS18B20 与 ADC0832 驱动3.1 工程文件结构与主循环状态机下载到的工程里除了主文件外通常还有ds18b20.c、adc0832.c、lcd1602.c、delay.c和对应的.h头文件。这样拆分的好处是每个传感器驱动独立替换硬件时只改对应的.c文件。主循环用一个mode变量记录当前测量模式模式切换放在按键中断或主循环扫描里。// main.c 主状态机 #include REGX51.H #include ds18b20.h #include adc0832.h #include lcd1602.h #include delay.h #define MODE_TEMP 0 #define MODE_HR 1 unsigned char mode MODE_TEMP; // 初始进入体温测量 bit measuring 0; // 测量进行标志用于结束测量 void main(void) { unsigned int temp16, hr; lcd1602_init(); lcd1602_show_string(0, 0, Health Monitor); while(1) { key_scan(); // 扫描按键更新 mode 和 measuring if(measuring) { if(mode MODE_TEMP) { temp16 ds18b20_read_temp(); // 返回 12 位温度原始值 lcd1602_show_temp(temp16); // 转换成实际温度并显示 temp_alarm(temp16); // 超限报警判断 } else { hr heart_rate_get(); // 测心率内部阻塞 5 秒 lcd1602_show_hr(hr); hr_alarm(hr); } } delay_ms(1); // 让出 CPU防止按键扫描过频 } }key_scan()负责检测 P3.0 和 P3.1 的电平变化。按键按下一次模式切换测量过程中再按一次 P3.0 就结束当前测量。这种状态机写法比直接在主循环里if(P3_00)可靠得多因为测量心率时需要阻塞 5 秒如果没有状态机这 5 秒内按键会失效。3.2 DS18B20 驱动读温度原始值DS18B20 驱动里最关键的是读温度子程序它遵循“初始化 → 跳过 ROM → 启动转换 → 等待 750ms → 初始化 → 跳过 ROM → 读暂存器”的流程// ds18b20.c 读取 12 位温度 unsigned int ds18b20_read_temp(void) { unsigned char tl, th; ds18b20_reset(); // 复位单总线 write_byte(0xCC); // 跳过 ROM不用匹配序列号 write_byte(0x44); // 启动温度转换 delay_ms(750); // 等转换完成12 位精度最长为 750ms ds18b20_reset(); write_byte(0xCC); write_byte(0xBE); // 读暂存器先读低字节 tl read_byte(); th read_byte(); return (th 8) | tl; // 低 11 位是有符号温度数据 }write_byte(0xCC)表示所有 DS18B20 都响应仿真里只有一颗传感器所以可以省去搜索 ROM 的步骤。返回值th8 | tl是一个 16 位整数实际温度需要右移 4 位再乘 0.0625。如果温度是负数最高位为 1C51 的int型直接右移会自动填充符号位负数也能正确转换。3.3 ADC0832 驱动双端转换结果比较ADC0832 的 DI 和 DO 是同一个物理引脚在发送起始位和通道选择时作为输入在读取转换结果时作为输出。读取时先在第一段读高 8 位再从第二段读反码两段结果相同才认为有效// adc0832.c 读取指定通道的 8 位 ADC 值 unsigned char adc0832_read(unsigned char channel) { unsigned char i, dat1 0, dat2 0; AD_CS 1; AD_CLK 0; AD_DIO 1; AD_CS 0; // 片选拉低进入转换 AD_CLK 1; AD_CLK 0; // 第一个时钟起始位DIO1 AD_DIO channel ? 1 : 0; // 第二个时钟选择单端或差分 AD_CLK 1; AD_CLK 0; AD_DIO 0; // 第三个时钟选择通道 CH0/CH1 AD_CLK 1; AD_CLK 0; AD_DIO 1; // 释放数据线准备读高 8 位 for(i 0; i 8; i) { AD_CLK 1; AD_CLK 0; dat1 1; if(AD_DIO) dat1 | 0x01; } for(i 0; i 8; i) { dat2 1; if(AD_DIO) dat2 | 0x80; AD_CLK 1; AD_CLK 0; } AD_CS 1; return (dat1 dat2) ? dat1 : 0; }调用时传0读 CH0传1读 CH1。第二个循环读的是反码正常情况下dat1和dat2完全相等如果两个数对不上说明时钟时序有问题返回值直接置 0 可以避免把错误电压当成正常测量值。仿真中如果发现 ADC 读数一直跳变优先检查 CLK 引脚是否被其他模块占用。3.4 心率算法在 5 秒窗口内数波峰心率检测不能只看一次 ADC 结果需要连续采样一段时间统计超过阈值的脉冲个数再换算成每分钟心率。我用的方法是对 ADC 采样值做阈值判决检测到一个上升沿就计一次数// main.c 在心率测量模式下调用的统计函数 #define ADC_THRESHOLD 128 // 阈值由第 5 章的标定步骤确定 #define SAMPLE_WINDOW_MS 5000 // 统计窗口 5 秒 unsigned int heart_rate_get(void) { unsigned int pulse_count 0; unsigned int sample; bit last_level 0; unsigned int elapsed 0; lcd1602_show_string(1, 0, HR Measuring...); while(elapsed SAMPLE_WINDOW_MS) { sample adc0832_read(0); // 读压力传感器电压 if(sample ADC_THRESHOLD last_level 0) { pulse_count; // 低电平转高电平算一次心跳 last_level 1; } if(sample ADC_THRESHOLD) { last_level 0; } delay_ms(10); // 10ms 采样一次 elapsed 10; } return pulse_count * 12; // 5秒次数 × 12 每分钟心率 }这里pulse_count * 12是因为 5 秒内有pulse_count次跳动折算到每分钟就是乘以 12。10ms 采样间隔对应 100Hz 采样率人的心率一般不超过 180bpm也就是 3Hz所以不会漏采。阈值 128 是 05V 中间值实际使用时要根据压力传感器的静态输出电压微调否则手放上去可能一直判定为高电平。4. Proteus 仿真搭建与联调从原理图到 HEX 加载4.1 元件清单和连线顺序在 Proteus 中新建工程后器件列表按下面这些名称放置AT89C51主控DS18B20温度传感器模型ADC0832串行 ADCPOT滑动变阻器模拟压力传感器输出电压LCD1602液晶显示RESPACK-8P0 上拉排阻BUTTON×2模式切换和结束测量BUZZER、LED声光报警连线时先连最小系统AT89C51 的 18、19 脚接 12MHz 晶振两个 30pF 电容接地9 脚通过 10μF 电容和 10kΩ 电阻构成上电复位。然后依次接 LCD1602、DS18B20、ADC0832。所有芯片的电源脚都接 5V地线统一接 GND。4.2 加载 HEX 文件双击 AT89C51在 Program File 项选择 Keil 工程输出目录下的温度采集显示.hex。加载后点运行LCD1602 应该能显示初始化信息。有个细节PROTEUS 时序仿真默认是即时模式如果 DS18B20 转换延时长整个仿真会显得很卡。可以在菜单 Debug 里把仿真时间步长调快或者临时把主程序里 750ms 的delay_ms改成 100ms验证功能后再改回真实值。4.3 心率信号源怎么搭才像真实脉搏实际压力传感器输出的是模拟脉冲我见过很多人在仿真里直接用开关控制电压跳变然后说心率不稳定。这其实是激励源的问题不是代码问题。要在 Proteus 里模拟真实脉搏我一般这样接用VSINE正弦电压源频率设成 1.2Hz幅度 2V偏置 2.5V。把该电压源接到 ADC0832 的 CH0 输入引脚。在 ADC0832 的 DO/DI 引脚挂一个DIGITAL OUTPUT探针。这样 ADC 输入波形就是 1.2Hz 的脉冲包络ADC 采样值会在 0.5V4.5V 之间变化阈值 2.5V 左右能稳定触发波峰计数。代码跑完后显示的心率应在 72bpm 上下偏差不超过 ±2。4.4 联调时最容易踩的四个坑第一个坑是 DS18B20 的 DQ 引脚在 Proteus 中不叫 1叫 DQ连错位置会一直显示 85℃。第二个坑是 LCD1602 的 VL 对比度引脚必须接一个 10kΩ 电位器扭到合适位置才能看到字形。每个新工程我都要重新调一次对比度因为接 5V 时显示全黑接地时常看不到文字。第三个坑是 ADC0832 的 CLK 频率。Proteus 仿真里如果主程序用while循环操作 CLK速度可能太快导致 ADC 响应不过来。可以适当在翻转电平之间加 23 个空指令延时例如_nop_();。我见过很多人不是逻辑错而是时序太快读不到有效数据。第四个坑是仿真时按键盘无效先查按键引脚是否接了下拉电阻。AT89C51 的 P3 口内部有弱上拉按键另一端接地即可如果两个按键接在同一个网络里就会互相干扰。5. 参数标定、按键防抖与仿真验证技巧5.1 报警阈值从哪来代码里的体温和心率阈值不是拍脑袋定的参考常见健康指标范围我按下表设置参数下限上限报警动作体温36.0℃37.8℃超过上限响蜂鸣器并亮 LED心率60bpm100bpm低于下限或高于上限都报警体温 37.3℃ 是健康成年人正常值上限37.8℃ 作为报警阈值是给传感器误差留了余量。心率 60100 是静息心率的常用参考如果是仿真里用 1.2Hz 信号源测出来的 72bpm 正好落在区间里。5.2 按键消抖最好别用纯延时很多例程在主循环里检测按键后用delay_ms(20)消抖在仿真里跑没问题但放进心率测量状态时20ms 的延时会打断采样节拍。我的做法是用状态机和计时器计数只在主循环里查询按键是否被处理过// 按键扫描带标志位处理 void key_scan(void) { if(P3_0 0) // P3.0 按下检查是否已处理 { delay_ms(5); // 短延时避开抖动 if(P3_0 0 key_ok 0) { mode !mode; // 切换温度/心率 measuring 1; key_ok 1; // 防止同一按键重复触发 } } else { key_ok 0; // 按键释放恢复允许下一次触发 } }关键在key_ok这个标志只有按键从未按下状态变成按下状态标志位为 0才能触发一次动作。如果在按住按键的时间里主循环反复扫描P3_0 0一直成立没有标志位就会连续切换模式。5.3 验证方法用秒表对心率用水温对体温调通后先用固定信号源检查算法把 VSINE 的频率设成 1.0Hz应该显示 60bpm设成 1.5Hz 显示 90bpm。如果输出频率不对问题一定在窗口计时或delay_ms(10)的晶振参数上。验证体温的时候把 DS18B20 模型放在一个可控温度源旁边在 Proteus 里直接右键 DS18B20 修改Temperature值为 36.5观察液晶显示值偏差应在 ±0.2℃ 内。5.4 一个可以立刻用上的技巧把心率窗口做成可配置调试时每次都改#define SAMPLE_WINDOW_MS 5000再重新编译太麻烦。我把窗口长度放到idata变量里仿真时通过按键临时切换成 2 秒窗口等算法调稳再改回 5 秒。这样做的另一个好处是把SAMPLE_WINDOW_MS替换成外部变量后后续版本可以做成用户可选的“快速测量”和“标准测量”不需要重构心率算法。本文还有配套的精品资源点击获取