简介:基于51单片机的胎压监测系统仿真资源,面向毕业设计、课程设计及单片机入门进阶人群,完整实现轮胎压力与温度的采集、阈值设定、实时报警与LCD显示。压缩包共82个文件,约30MB,主要包含C语言源码、Proteus仿真工程、原理图与流程图、器件清单、常用芯片手册、使用说明和答辩参考文档,结构清晰便于查阅。系统流程图清晰呈现从阈值设定、数据采集、数据处理到报警显示的完整链路,便于理解中断与报警机制。目前站内已有218人学习或浏览,具备较高参考价值。整套方案以51单片机为核心,配合压力/温度传感器、按键与报警模块,程序覆盖初始化、数据采集、A/D转换、显示刷新和报警控制等模块;同时提供传感器数据手册、烧录方法、常见问题解决合集及器件焊接注意事项,可帮助读者在实物制作前通过仿真验证逻辑,减少调试时间,快速完成课设或毕设方案。
1. 用51单片机做胎压监测仿真:先定量程,再谈报警
仿真里做胎压监测,最大的价值不是省掉一块电路板,而是能把“压力-电压-ADC码值-显示值”这条链路的误差摊开来看。在真实轮胎上没法随手把气压从220kPa调到180kPa触发低压报警,但在Proteus里改一个传感器模型或电位器,边界测试随时能做。很多人把ADC读回来的原始码值直接乘一个系数当成胎压,温度和压力混在一起,报警阈值怎么调都对不上;这套方案的难点其实在量程映射和温度补偿,不在51单片机本身。
这套仿真方案以AT89C51作为主控,压力侧用模拟电压输出型传感器配合ADC0832采集,温度侧用DS18B20走单总线,显示与报警用LCD1602加蜂鸣器,全程在Proteus仿真51单片机的环境中验证。适合正在做51单片机课程设计的学生,也适合想评估胎压监测软件逻辑、又不想先搭真实无气台架的工程师。读完之后能回答三个问题:8位ADC下每1kPa对应多少码值、温度变化时要不要修正压力、连续几次越限才触发报警。
2. 胎压监测的传感器选型与量程映射:先定压力单位,再写51单片机代码
2.1 乘用车胎压与胎温的参数边界
乘用车冷态标准胎压通常落在220~250kPa之间,也就是车门铭牌上常见的2.2~2.5bar。胎压监测不只是低压报警,过充同样危险,所以高压阈值一般取320kPa;当表压低于180kPa时就属于明显亏气,长时间行驶会加剧胎肩磨损和爆胎风险。温度侧的工作区间可以按-40℃~85℃来设计,超过85℃通常伴随长时间高负荷行驶,必须优先于压力变化触发警示。
仿真里第一件要分清的事是表压和绝对压力。胎压表显示的是表压(相对当地大气压),而多数模拟输出压力芯片返回的是绝对压力,固件换算的最后一步要减去环境气压101.325kPa,否则整个量程偏大约一个大气压,低压报警永远触发不了。下表是这套系统的关键参数基准,后面所有阈值宏都是从这张表来的。
| 参数项 | 正常范围 | 报警阈值 | 说明 |
|---|---|---|---|
| 冷态标准胎压 | 220~250kPa | 低于180kPa报警 | 表压 |
| 高压报警 | 250~320kPa | 高于320kPa报警 | 防止过充 |
| 工作温度 | -40~85℃ | 高于85℃报警 | 模拟传感器极限范围 |
| 采样周期 | 100ms | 连续3次越限确认 | 防止单次干扰误报 |
2.2 压力与温度传感器选型对照
Proteus建模并不等于真实量产选型,但仿真模型的选择会直接决定代码结构和调试方式。Proteus库里有MPX4115这类模拟输出压力芯片,也有BMP180、DS18B20之类的数字传感器模型,常见选型对照如下。
| 方案 | 传感器 | 输出形式 | Proteus可用模型 | 适用场景 |
|---|---|---|---|---|
| 模拟电压型 | MPX4115 | 0.2~4.8V | 有现成模型 | 学习ADC采集链路 |
| 数字I2C型 | BMP180 | I2C总线 | 模型时序完整 | 练I2C协议,引脚占用多 |
| 单总线型 | DS18B20 | 1-Wire | 模型成熟 | 温度采样首选 |
| 电阻分压型 | NTC热敏电阻 | 电阻分压 | 电阻+ADC | 成本低,标定麻烦 |
我一般会把压力侧放在模拟通道上,温度侧放在单总线上。原因是51单片机本身没有内置ADC,外扩一颗双通道8位的ADC0832后,正好让压力通道独立占用一个输入口,另一个通道留给电池电压监测或后续标定口。DS18B20的优点不只是引脚少,它在Proteus仿真中对1-Wire时序的还原度不错,可以直接验证复位、读ROM、温度转换三个环节,而NTC方案还需要自己处理查表曲线。
这里有一个必须事先知道的坑:MPX4115的量程是绝对压力15~115kPa,对应输出电压0.2~4.8V,拿来测250kPa表压的轮胎,绝对压力接近350kPa,早就超量程饱和了。真实TPMS使用的传感器通常是量程0~450kPa甚至更大的桥式应变片或专用压力芯片,Proteus里没有现成的这种模型。所以仿真图里的MPX4115更适合当作“电压可变的压力源演示单元”,不能把显示值直接当成真实胎压。更干净的做法是用直流电压源直接模拟一个0.5~4.5V的比例输出传感器,这样固件里的换算公式才和真实产品一致。
2.3 电压到胎压的线性映射与ADC码值表
把传感器按工业变送器常见的比例输出方式处理:0.5V对应0kPa,4.5V对应满量程450kPa,换算代码里只需要两个常数。这是这类51单片机代码里最值得写注释的地方,也最容易写错。
#define SENSOR_ZERO_V 0.5f #define SENSOR_SPAN_V 4.0f #define SENSOR_FS_KPA 450.0f #define ADC_VREF 5.0f float voltage_to_press(unsigned char code) { float v = code * ADC_VREF / 255.0f; // 码值转电压 return (v - SENSOR_ZERO_V) / (SENSOR_SPAN_V / SENSOR_FS_KPA); }SENSOR_SPAN_V / SENSOR_FS_KPA计算出来是每kPa约0.00889V,反过来每1码对应大约1.76kPa。8位ADC在5V参考电压下LSB约19.6mV,这个分辨率接近两个码值对应3.5kPa,作为预警用途够用,但做精确胎压检测则需要10位以上ADC。代码里用float是为了让映射公式可读,后续如果移植到没有FPU的增强型51,可以把整段改成定点运算,以kPa*100为整数单位。
| 输入电压 | 理论表压 | ADC码值 | 显示预期 |
|---|---|---|---|
| 0.5V | 0kPa | 26 | 0~2kPa |
| 1.5V | 112kPa | 77 | 110~114kPa |
| 2.1V | 180kPa | 107 | 178~182kPa |
| 2.72V | 250kPa | 139 | 248~252kPa |
| 3.34V | 320kPa | 170 | 318~322kPa |
| 4.5V | 450kPa | 230 | 448~452kPa |
这张表里的2.1V和3.34V正好对应低压报警与高压报警阈值,后面第5章的验收就是按这张表逐档给电压做的。低压报警阈值180kPa对应码值107,高压320kPa对应码值170,记住这两个数,调试时就不需要用电压表逐次换算。
3. Proteus仿真51单片机的硬件电路:最小系统与采集通道接线
3.1 最小系统与引脚规划
AT89C51配12MHz晶振是Proteus里最常见的组合,复位电路用10μF电解电容加10kΩ电阻即可。引脚规划要在一开始固定下来,尽量避开P3.0、P3.1这两个串口引脚,保留给调试打印。
| 功能模块 | 引脚连接 | 说明 |
|---|---|---|
| LCD1602数据 | P0.0~P0.7 | 8位数据模式,调试更直观 |
| LCD1602控制 | RS=P2.0,RW=P2.1,EN=P2.2 | 典型三线控制 |
| ADC0832 | CS=P1.0,CLK=P1.1,DO/DI=P1.2 | CH0接传感器输出 |
| DS18B20 | DQ=P3.7 | 需要4.7kΩ上拉 |
| 蜂鸣器 | P3.5 | 经S8050三极管驱动 |
| 报警LED | P2.6=高压,P2.7=低压 | 高电平点亮 |
P0口在真实板卡上必须接上拉排阻,因为在51体系里P0是开漏输出。但在Proteus仿真里,P0作为输出口直接连LCD数据线通常也能跑,这种差异是仿真发散的主要来源之一——仿真跑通了,焊板子却不显示,原因往往就是漏了P0上拉。建议从一开始就放一个RESPACK-8排阻在图纸上,既不影响仿真,也保留了移植到硬件的正确性。
3.2 采集通道与报警驱动电路
ADC0832的CH0引脚接压力传感器的电压输出端,传感器地必须和单片机共地,否则ADC读数会整体偏移。CH1可以悬空,也可以接一个电位器中点用来模拟第二路信号,方便演示时切换压力/备用通道。DS18B20的DQ引脚要加4.7kΩ上拉电阻到VCC,这是单总线正常工作的前提,少掉这颗电阻,读回来的温度通常是-55℃或85℃的默认值。
蜂鸣器不能直接接在P3.5引脚上,51单片机IO口的灌电流能力只有约20mA,普通有源蜂鸣器工作电流在30mA左右,长久驱动会损伤端口。常见做法是用P3.5控制S8050三极管的基极,蜂鸣器接在集电极和VCC之间,发射极接地。仿真里即使直接接蜂鸣器能响,也建议按真实电路画三极管驱动,避免以后照图焊板时烧IO。
3.3 在Proteus里搭模型的顺序
搭建顺序会影响排查难度,我习惯按“最小系统→采集通道→显示报警”三步走。
- 新建Proteus工程,放置AT89C51、CRYSTAL、两个30pF电容、10μF电解电容和10kΩ电阻,构成最小系统。
- 双击AT89C51,把Clock Frequency改为12MHz。这一步经常被忽略,导致后面DS18B20时序在仿真里慢半拍。
- 放置ADC0832,按引脚规划连接CS、CLK、DO/DI,CH0接一个直流电压源。先用电压源,不急着放压力传感器。
- 放置DS18B20和LM016L。Proteus里的1602液晶模型名常见是LM016L,直接搜这个名字。
- 用Keil编译工程生成hex文件,双击单片机加载Program File。
- 启动仿真,先调电压源观察ADC码值变化,确认采集链路通了再接温度报警。
3.4 传感器模型和可调直流源怎么选
压力传感器在Proteus里的处理方式建议分两层。第一层验证软件用可调直流源,电压从0.5V调到4.5V,固件里看到的应该是0到450kPa连续变化;第二层演示完整项目时再把直流源替换成MPX4115,或者在传感器输出端加电位器模拟漂移。这样做的好处是,一旦显示值不对,先排除软件换算,再怀疑传感器模型,问题被自然分隔开。
4. 固件实现:双通道采集、温度补偿与迟滞报警
4.1 ADC0832通道读取与压力换算
ADC0832是8位逐次逼近型ADC,通信接口是类SPI的三线时序。前三个时钟上升沿发送配置位,分别是起始位、单端/差分选择位、通道选择位,之后连续8个下降沿从DO引脚读回数据。下面是完整的读取函数,注意DO/DI复用同一个IO时,51的准双向口不需要切换方向就可以实现读引脚。
#include <reg52.h> #include <intrins.h> sbit ADC_CS = P1^0; sbit ADC_CLK = P1^1; sbit ADC_DIO = P1^2; unsigned char adc0832_read(unsigned char ch) { unsigned char i, dat = 0; ADC_CS = 0; ADC_CLK = 0; ADC_DIO = 1; // START位 ADC_CLK = 1; _nop_(); ADC_CLK = 0; ADC_DIO = 1; // SGL/DIFF=1,单端输入 ADC_CLK = 1; _nop_(); ADC_CLK = 0; ADC_DIO = ch; // 0选CH0,1选CH1 ADC_CLK = 1; _nop_(); ADC_CLK = 0; for (i = 0; i < 8; i++) // 连续8个时钟下降沿读数据 { ADC_CLK = 1; ADC_CLK = 0; dat <<= 1; dat |= ADC_DIO; } ADC_CS = 1; return dat; }函数的参数ch取0或1,对应ADC0832的CH0与CH1两个输入通道。配置位必须在第三个上升沿到来前稳定在引脚上,_nop_()在这里起延时作用,12MHz下约1μs,时序余量足够。读回来的8位数据是正比于输入电压的码值,0x00对应0V,0xFF对应接近5V。用这个函数驱动CH1接电位器时,同样可以读到另一路模拟量,不需要新写驱动。
4.2 DS18B20温度读取与温度补偿
DS18B20的温度读取严格依赖1-Wire时序,复位、写字节、读字节三个原语必须写牢。下面的实现返回的温度值放大10倍,单位是0.1℃,避免在主循环里做浮点数除法。
sbit DQ = P3^7; unsigned char ds18b20_reset(void) { unsigned char presence; DQ = 0; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); DQ = 1; // 释放总线,等待存在脉冲 _nop_(); _nop_(); _nop_(); _nop_(); presence = DQ; // 0表示器件应答 return presence; } void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { DQ = 0; _nop_(); _nop_(); _nop_(); _nop_(); DQ = dat & 0x01; // 低位先写 _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); DQ = 1; dat >>= 1; } } unsigned char ds18b20_read_byte(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { dat >>= 1; DQ = 0; _nop_(); _nop_(); _nop_(); _nop_(); DQ = 1; _nop_(); _nop_(); _nop_(); _nop_(); if (DQ) dat |= 0x80; // 低位先读 } return dat; } int ds18b20_read_temp(void) { unsigned char tl, th; int t; ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳过ROM匹配 ds18b20_write_byte(0x44); // 启动温度转换 while (DQ == 0); // 等待转换完成 ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // 读取暂存器 tl = ds18b20_read_byte(); th = ds18b20_read_byte(); t = (th << 8) | tl; // 12位带符号数据 return t * 10 / 16; // 0.0625℃/LSB,返回值单位为0.1℃ }温度转换命令0x44发出去后,12位精度需要最长750ms的转换时间,函数里的while (DQ == 0)会一直等到底层完成。这个问题到主循环里会变成阻塞,所以温度读取应该放到一个2秒周期的慢任务里,不能放在100ms的压力采样循环中。压力采样对实时性敏感,温度本身是缓变量,两者分开调度最合适。
温度补偿的意义在于把当前温度下测到的压力折算回25℃基准状态。胎温升高时胎压自然升高是物理现象,不能把它当作故障误报,只有折算回冷态阈值才有比较意义,用代码表示为:
float press_kpa; // 传感器计算出的当前表压 float temp; // DS18B20读数,单位℃ float press_corr; // 温度修正后的压力 press_corr = press_kpa - 0.8f * (temp - 25.0f);0.8kPa/℃这个系数是经验值,实际取决于轮胎刚度和胎内气体体积,真实产品要通过台架标定得到。仿真项目里把它写成宏常量,后面接入实验室自己的标定数据时,只需要替换一个数字。忽略这一步的典型表现是:温度从25℃升到60℃,压力读数抬升28kPa,原本250kPa的正常压力被误判为高压报警。
4.3 阈值报警与连续确认防抖
报警逻辑使用连续3次越限确认机制,配合100ms采样周期,相当于300ms确认时间。单次ADC抖动不会触发报警,又能保留对压力骤降的响应速度。
#define LOW_PRESS 180.0f #define HIGH_PRESS 320.0f #define HIGH_TEMP 85.0f #define ALARM_CONFIRM 3 unsigned char low_cnt = 0, high_cnt = 0, temp_cnt = 0; bit alarm_low = 0, alarm_high = 0, alarm_temp = 0; void check_alarm(float p, float t) { if (p < LOW_PRESS) { if (++low_cnt >= ALARM_CONFIRM) alarm_low = 1; } else low_cnt = 0; if (p > HIGH_PRESS) { if (++high_cnt >= ALARM_CONFIRM) alarm_high = 1; } else high_cnt = 0; if (t > HIGH_TEMP) { if (++temp_cnt >= ALARM_CONFIRM) alarm_temp = 1; } else temp_cnt = 0; }三种报警的标志位一旦置位就保持,直到人为复位或故障消除后按恢复条件清除。计数器的清零时机要放在条件不满足的else分支里,这是仿真里最容易漏的地方——只累加不归零,过几分钟后一次干扰就可能直接触发报警。阈值参数全部用宏定义放在文件头部,低压180、高压320、温度85三个值随时可调。
4.4 主循环调度与LCD1602刷新
主循环里不要写大段延时,用定时器T0产生100ms中断标志,主循环只做采样、换算、报警判断和显示刷新。
bit flag_100ms = 0; unsigned char adc_val; float voltage, press_kpa, temp, press_corr; void timer0_isr(void) interrupt 1 { TH0 = 0xDC; // 12MHz,50ms定时初值 TL0 = 0x00; flag_100ms = 1; } void lcd_disp_press(float p) { char buf[16]; sprintf(buf, "P:%04.0fkPa", p); lcd_write_string(0, 0, buf); } void main(void) { lcd_init(); timer0_init(); while (1) { if (flag_100ms) { flag_100ms = 0; adc_val = adc0832_read(0); // 读取压力通道 voltage = adc_val * 5.0f / 255.0f; press_kpa = (voltage - 0.5f) / (4.0f / 450.0f); temp = ds18b20_read_temp() / 10.0f; press_corr = press_kpa - 0.8f * (temp - 25.0f); check_alarm(press_corr, temp); lcd_disp_press(press_corr); if (alarm_low) P2_7 = 1; if (alarm_high) P2_6 = 1; if (alarm_temp) buzzer_on(); } } }LCD显示压力保留到个位即可,16字符宽度显示“P: 247kPa”不会溢出。实际调试中如果发现1602花屏,先检查复位延时是否足够,再检查LCD初始化里的功能设置命令,仿真和真机在这个环节行为差异不大。
5. 用固定电压表反推压力,把仿真调试变成一张验收表
5.1 用直流电压源逐档验收压力链路
断开CH0上的传感器支路,把电压源的输出端直接接到CH0与GND,按第2章那张表逐档给电压。每一档电压对应的ADC码值和液晶显示都应落在预期区间,偏差超过±2%时先查显示函数,再查ADC时钟时序。
| 输入电压 | 理论胎压 | ADC码值 | 报警预期 |
|---|---|---|---|
| 0.5V | 0kPa | 26 | 低压报警 |
| 1.5V | 112kPa | 77 | 低压报警 |
| 2.1V | 180kPa | 107 | 阈值临界 |
| 2.72V | 250kPa | 139 | 正常 |
| 3.34V | 320kPa | 170 | 高压报警 |
| 4.5V | 450kPa | 230 | 高压报警 |
这张表把ADC函数、压力换算公式和LCD显示驱动三个模块一次验完。验证低压报警时给1.5V而不是2.1V,避免卡在180kPa边界产生误判;高压报警用3.34V以上电压,观察P2.6引脚跳高。这一步做完,固件侧的换算链路基本可信,后面换传感器模型只是替换信号源。
5.2 用读ROM命令定位DS18B20时序
Proteus里DS18B20最常见的故障是显示-55℃或85℃。这两个值是芯片上电后的默认温度,说明转换和读取根本没发生过。遇到这种情况,先写一个最小诊断程序,把读ROM命令0x33的结果通过串口虚拟终端打印出来。
unsigned char rom[8]; unsigned char i; if (ds18b20_reset() == 0) // 0表示总线上有器件 { ds18b20_write_byte(0x33); // 读ROM命令 for (i = 0; i < 8; i++) rom[i] = ds18b20_read_byte(); // 通过串口输出rom[0]~rom[7] }8个字节全部是0xFF,基本可以断定是P3.7缺少4.7kΩ上拉。复位函数返回值不为0,则检查复位脉冲拉低时间是否达到480μs,12MHz下至少写8个_nop_()。仿真环境不会因为元件虚焊出问题,所以一旦读不出ROM,问题几乎都集中在时序延时和上拉电阻这两个点上。
5.3 报警恢复带迟滞,避免边界抖动
仿真里最隐蔽的问题是ADC噪声在阈值边缘抖动,表现为报警灯闪烁。只靠连续3次确认还不够,恢复阈值要离开报警阈值一段距离,形成迟滞带。
#define LOW_ALARM_VAL 180.0f #define LOW_RECOVER_VAL 200.0f低压报警在180kPa触发,但压力回升到200kPa才清除报警;高压报警在320kPa触发,回落到300kPa才解除。20kPa的迟滞带让报警状态有明显锁存感,和真实TPMS表现一致。最后再加一个限幅习惯:压力换算结果强制限制在0~500kPa范围,传感器断路、信号线反接产生的异常数据就不会污染报警状态机。这两个习惯加进固件之后,胎压监测仿真才算真正到了可以演示、可以验收的状态。
本文还有配套的精品资源,点击获取