ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

基于51单片机的四路胎压监测报警系统设计与实现

基于51单片机的四路胎压监测报警系统设计与实现 简介面向单片机课程设计、毕业设计与电子入门实践的51单片机汽车胎压监测报警系统资料包围绕轮胎压力实时采集、阈值判断与声光报警展开帮助读者搭建从传感器信号处理、51单片机程序控制到显示更新和报警联动的完整方案。标题信息显示包内以程序源码、仿真工程、电路原理图和元件清单为主可用于对照理解系统组成、接口设计与仿真验证流程文件总数暂未标注类型明细上游未提供压缩包约9.87MB。现有319人学习适合需要快速获取胎压监测参考实现、梳理硬件连接与程序逻辑的读者。资料覆盖初始化配置、数据采集滤波、压力计算、报警判断及显示刷新等关键环节也便于在仿真环境中提前排查问题、优化硬件配置与代码作为课程设计或项目原型的起步材料较为实用。1. 四个轮子的压力同时盯这套 51 单片机胎压监测报警系统解决的是什么问题四个轮胎的压力值同时挂在一块 1602 屏上任意一路掉到 1.8 bar 以下或者窜到 3.2 bar 以上蜂鸣器就开始叫对应那一路的 LED 闪起来。这套基于 STC89C52 的汽车胎压监测报警系统本质上就是把「采样 → 换算 → 比较 → 报警」这条链路在一个 8 位单片机上完整跑通压缩包里把程序、Proteus 仿真、原理图和元件清单一次性给全了属于拿到手能编译、能仿真、能照着焊的那一类资源。它适合三类人课程设计要交活的、想练手 51 外设驱动ADC0832、LCD1602、独立按键、蜂鸣器的初学者以及要给老设备加个简易胎压巡检的工程开发人员。它不追求 CAN 总线和无线组网胜在链路完整——每一行代码都能对上原理图上的一个网络标号这对刚开始啃单片机的人来说比一个功能花哨但看不透的工程有价值得多。2. 系统架构与元件清单从压力传感器到四路轮询的完整链路2.1 硬件分层主控、采样、人机、报警四块怎么分工把原理图摊开整个系统其实就四块分工特别清楚。第一块是主控最小系统STC89C52RC 加晶体振荡电路、上电复位电路和电源去耦。这块没任何花样DIP40 的芯片、两个 22pF 瓷片电容配一个晶振、10uF 电解加 10k 电阻做复位再加一个复位按键齐活。第二块是压力采样链。四路模拟输出的压力传感器各自经一级 RC 低通进多路开关再进 ADC0832 转成数字量最后从 P3 口的几个引脚进单片机。这条链是整个系统里唯一需要认真对待的模拟通路后面精度崩不崩八成看这一段。第三块是人机接口LCD1602 负责显示四个轮子的实时压力值和当前阈值四个独立按键分别对应「设置」「加」「减」「切轮位」。第四块是报警输出一个有源或无源蜂鸣器加四路 LED每路 LED 对应一个轮胎。哪路异常哪路亮。Proteus 仿真里真实的压力传感器是用四个电位器替代的——仿真模型库里没有 MPX 系列的 spice 模型用可调电阻分压模拟 05V 的传感器输出调一下旋钮就相当于给轮胎放气或打气调试起来反而比实物方便。原理图上留的是标准三针接口VCC / GND / OUT实物上换成真传感器直接插就行这也是这套图纸能同时兼顾仿真和实物的关键设计。2.2 元件清单逐项过每个器件在电路里干什么活压缩包里的元件清单我按电路功能重新梳理了一遍把关键项和选型理由放在下面器件型号/参数数量在电路里的作用选型注意主控STC89C52RCDIP401全部逻辑运算与外设调度兼容 AT89C52Keil 里可直接选 Atmel 器件晶振11.0592MHz1系统时钟要跑串口就选它12MHz 除不尽波特率复位电容10uF / 16V 电解1上电复位延时配 10k 下拉电阻模数转换ADC083218 位串行 A/D只有 2 个单端通道四路要扩展多路开关CD4051 / CD40521四路传感器切换4051 是 8 选 14052 是双 4 选 1压力传感器模拟电压输出型4感受气压并输出电压量程要覆盖 0250kPa液晶LCD16021显示四路压力与状态带背光版更好调对比度电位器10k1调 LCD 的 V0少了它大概率白屏独立按键6×6 轻触4设置阈值、切换轮位一端接地靠内部上拉蜂鸣器5V 有源/无源1声音报警无源的要方波驱动驱动三极管S8550PNP1放大蜂鸣器电流单片机 IO 直接驱动带不动指示 LED5mm 红/绿4分轮位指示串 1k 限流电阻上拉排阻10k9 脚1P0 口上拉51 的 P0 是开漏不加驱动不了滤波电容100uF 0.1uF各若干电源去耦蜂鸣器电源脚必须加有两点值得单独说一下。晶振为什么用 11.0592MHz 而不是 12MHz12MHz 看着整数好看但 51 的定时器算波特率时除不尽一旦后面要加串口打印调试信息就会翻车11.0592 是 9600 波特率在 51 上的整数解即便当前这套程序不用串口留着也是给自己留退路。P0 口的排阻容易被忽略。很多人焊完发现 LCD 有背光但一个字符都不显示折腾半天代码最后发现是 P0 没上拉——51 的 P0 口内部没有上拉电阻输出不了高电平数据线一直是低LCD 收到的全是 0x00。2.3 信号链与供电为什么参考电压决定了一半的精度ADC0832 是 8 位分辨率参考电压直接取单片机 VCC5V 满量程对应 255 个码值一个 LSB 就是 5000mV / 255 ≈ 19.6mV。按 45mV/kPa 的传感器灵敏度折算一个码值大约对应 0.44kPa也就是 0.0044 bar。这个分辨率用来判断「低于 1.8 bar 就报警」绰绰有余显示到小数点后一位完全没有问题。真正的问题不在分辨率在稳定性。蜂鸣器吸合瞬间电流能到 30mA 以上如果它和 ADC 共用同一路 5V 走线VCC 会被拉低几十毫伏换算下来所有通道的读数一起往下掉 23 个码值。这个现象在仿真里看不出来因为 Proteus 的电源是理想的只有焊出实物才会暴露。我一般的处理是三件事蜂鸣器电源脚并一个 100uF 电解加一个 0.1uF 瓷片走线从电源入口单独拉一路模拟地和数字地在电源入口处单点汇合别在板子上到处乱连每路传感器输出串一个 1k 电阻加一个 0.1uF 电容到地做成截止频率约 1.6kHz 的低通把开关电源和电机带来的高频噪声挡在 ADC 外面。还有一件事容易漏多路开关切换通道之后ADC 内部的采样电容需要建立时间。CD4051 的导通电阻在 5V 供电下大概一两百欧配上后面的采样电容稳定下来要几十微秒。代码里切完地址立刻启动转换读回来的值会带着上一路的残留四路读数看起来「互相串」。仿真里因为模型理想不明显实物上就是灾难。提示这套图纸的传感器接口是通用模拟电压输入量程系数写在代码的宏里。换上不同型号的传感器只需要改SENSOR_K和SENSOR_B两个宏不用动采样流程。3. 环境搭起来Keil C51 编译、Proteus 联调、实物上电的三段式跑通流程3.1 Keil C51 工程配置与一次成功编译压缩包里的程序是标准 C51 工程用 Keil uVision4 或 uVision5 加 C51 编译器打开都能编。打开之后先做三件事缺一件就编不出能跑的 hex。第一确认器件选的是 Atmel 的 AT89C52STC89C52 跟它指令兼容Keil 的器件库里没有 STC。第二在 Target Options 的 Output 页把「Create HEX File」勾上否则编译只生成 obj 不生成 hexProteus 那边就会一直加载不到新程序。第三Target 页的 Xtal 填 11.0592跟实际晶振对上不然所有基于delay_ms()的时序都会偏。工程目录整理之后大概是这么个结构TPMS_51/ ├── Project/ │ └── TPMS.uvproj # Keil 工程文件 ├── User/ │ ├── main.c # 主循环与定时调度 │ ├── adc0832.c # 采样时序与压力换算 │ ├── lcd1602.c # 显示驱动 │ └── key.c # 按键扫描 ├── Inc/ │ └── tpms.h # 阈值宏、结构体、引脚定义 ├── Sim/ │ └── TPMS.pdsprj # Proteus 仿真工程 ├── Doc/ │ ├── 原理图.pdf │ └── 元件清单.xlsx └── Output/ └── TPMS.hex # 编译产物仿真和烧录都用它编译完看 Build Output 窗口必须是0 Error(s), 0 Warning(s)。带 Warning 也能生成 hex但那种工程往往埋着隐式类型转换的雷比如把unsigned int的压力值塞进unsigned char变量超过 255 直接截断屏幕上的数会莫名其妙归零。3.2 Proteus 仿真加载hex、时钟频率与虚拟终端仿真工程打开后双击那颗单片机弹出属性框里有两个值必须改Program File 指向Output/TPMS.hexClock Frequency 填11.0592M。Program File 这一项最容易出错。Keil 每次编译生成的 hex 会覆盖旧文件但 Proteus 如果已经加载过一次双击进去看到的路径是对的实际跑的却可能是内存里的旧副本。稳妥做法是每次重新编译后在 Proteus 里把 Program File 那一栏重新选一遍或者直接关掉仿真工程重开。Clock Frequency 这一项更隐蔽。Proteus 默认是 12MHz跟 11.0592 差 8%。LCD1602 的初始化时序对时间敏感差这 8% 有时能跑有时不能跑表现出来就是「昨天还好好的今天一打开就白屏」属于典型的玄学问题。跑起来之后四个电位器就是四个轮胎。往上拧增加电压相当于打气往下拧相当于放气。把某一路拧到底对应的 LED 应该亮、蜂鸣器应该响、屏幕上那一格应该出现异常提示。这一步能全过说明逻辑没问题可以进实物阶段了。3.3 实物焊接与首次上电检查顺序实物阶段最忌讳一次焊完再上电。我的习惯是分四步每步都有一个可验证的输出。第一步只焊最小系统单片机座、晶振、两个 22pF、复位电路、电源座和去耦电容。烧一个最简单的闪灯程序进去P1.0 接个 LED 加 1k 电阻看到灯一闪一闪说明供电、时钟、复位、下载四件事全对了。这一步不过后面全是白费。第二步焊 LCD1602 和对比度电位器。先不接数据线只接电源和背光确认背光亮。再接上数据线和控制线烧一个只显示固定字符串的程序一边显示一边拧那个 10k 电位器直到字符最清晰。很多人卡在「背光亮但没字」九成是这个电位器拧在极端位置上。第三步焊 ADC0832 和传感器接口。这时候先不接传感器把每个通道的输入脚用杜邦线接到 GND 和 VCC看读数是不是稳定在 0 和满量程附近。这一步是在验证采样链路本身把传感器排除在外。第四步才焊蜂鸣器和 LED。蜂鸣器一定要接三极管单片机 IO 的灌电流能力有限直推要么声音小得像蚊子要么把 IO 口烧了。S8550 是 PNP做高边驱动时 IO 输出低电平导通这个极性搞反了蜂鸣器永远不响。4. 核心代码拆解ADC0832 采样、压力换算、阈值状态机四段主逻辑4.1 定时调度与主循环骨架整套程序不用 RTOS也不需要。一个定时器中断做时基主循环按标志位干活对 51 这种资源来说是最省的做法。#include reg52.h unsigned int g_tick 0; /* 1ms 时基计数 */ unsigned char g_scan_flag 0; /* 200ms 采样标志 */ void Timer0_Init(void) { TMOD 0xF0; TMOD | 0x01; /* 定时器0工作方式116位 */ TH0 (65536 - 922) / 256; /* 11.0592MHz/12 921.6kHz1ms 约 922 个计数 */ TL0 (65536 - 922) % 256; ET0 1; EA 1; TR0 1; } void Timer0_ISR(void) interrupt 1 { TH0 (65536 - 922) / 256; TL0 (65536 - 922) % 256; g_tick; if (g_tick % 200 0) { g_scan_flag 1; /* 每 200ms 置一次采样请求 */ } } void main(void) { System_Init(); /* IO 方向、LCD 初始化、阈值赋默认值 */ Timer0_Init(); while (1) { Key_Scan(); /* 按键随时响应不放在中断里 */ if (g_scan_flag) { g_scan_flag 0; Wheel_Sample_All(); /* 四路采样 换算 判断 */ Alarm_Update(); /* 蜂鸣器与 LED 输出 */ LCD_Refresh(); /* 刷屏 */ } } }为什么是 200ms轮胎压力是个慢变量机械系统的响应时间在秒级采样频率再高也只是把噪声一起采进来。200ms 一轮人眼看着数字是连续变化的又给了每路传感器足够的建立时间和软件平均的样本量。按键为什么不放中断按键要处理的是「短按」「长按」「连按」这些语义放在中断里会让中断服务函数变得又长又难维护而且按键抖动会在中断里被放大成多次触发。放主循环里靠状态机消抖逻辑清晰得多。4.2 ADC0832 读时序与压力换算ADC0832 是串行接口DI 和 DO 共用一根线靠分时切换方向。这段时序写错了读出来就是一堆随机数。sbit ADC_CS P3^5; sbit ADC_CLK P3^6; sbit ADC_DIO P3^7; /* ch: 0 或 1对应 ADC0832 的两个单端通道 */ static unsigned char Adc0832_Read(unsigned char ch) { unsigned char i, dat 0; ADC_CS 0; /* 片选拉低一次转换开始 */ ADC_CLK 0; ADC_DIO 1; /* 第1个时钟起始位 */ ADC_CLK 1; ADC_CLK 0; ADC_DIO 1; /* 第2个时钟SGL1单端输入模式 */ ADC_CLK 1; ADC_CLK 0; ADC_DIO ch; /* 第3个时钟ODD 位选择通道 */ ADC_CLK 1; ADC_CLK 0; ADC_DIO 1; /* 释放数据线交给 ADC 驱动 */ for (i 0; i 8; i) { /* 高位在前读 8 位 */ ADC_CLK 1; ADC_CLK 0; dat (dat 1) | (unsigned char)ADC_DIO; } ADC_CS 1; /* 片选拉高本次转换结束 */ return dat; }这段代码最容易翻车的点是ADC_DIO 1;这一句。51 的 IO 口做输入之前必须先写 1准双向口的特性否则内部的下拉会把线拉死在低电平读回来永远是 0。CS 拉高之后也要记得给 DIO 写 1否则下一轮起始位发不出去。另一个点是时钟速度。实物上 ADC0832 最快能跑到 400kHz51 用软件翻转 IO 差不多就是几百 kHz刚好卡在边缘。如果读出来的值不稳定在两个ADC_CLK 1; ADC_CLK 0;之间加一两个空操作_nop_();把时钟压到 200kHz 以下稳定性立刻上来。原始码值要变成压力值中间隔着一个线性换算/* 传感器手册给的线性关系Vout Vcc * (K * P B)P 单位 kPa 这里把 K 折算成 mV/kPaB 折算成 mV全部用整数算避开浮点 */ #define VREF_MV 5000L /* ADC 参考电压直接取 VCC */ #define ADC_FULL 255L /* 8 位满量程 */ #define SENSOR_K 45L /* 45 mV/kPa */ #define SENSOR_B (-475L) /* 零点偏移 -475 mV */ unsigned int Adc_To_Press10(unsigned char adc) { long vout_mv; long p10; vout_mv (long)adc * VREF_MV / ADC_FULL; /* ADC 码值 → 毫伏 */ p10 (vout_mv - SENSOR_B) * 10L / SENSOR_K; /* 反解压力放大 10 倍 */ if (p10 0) p10 0; if (p10 9999) p10 9999; return (unsigned int)p10; /* 单位 0.1 kPa */ }参数怎么改SENSOR_K和SENSOR_B完全来自器件手册的传输函数。上面这组系数对应的是 15115kPa 量程的气压传感器用在实验室演示没问题真要装车测胎压必须换成量程覆盖 0250kPa 的型号然后把手册里的斜率和截距重新折算进去。这一步不做屏幕上的数字永远是六十几 kPa怎么调都上不去。p10用long而不是int是有原因的vout_mv - SENSOR_B最大能到 5000 475 5475乘 10 就是 54750超过 16 位有符号整数的 32767用int直接溢出成负数。采样还要做平均。单次采样的抖动在 ±2 个 LSB 左右不处理的话屏幕末位一直在跳看着像坏了#define SAMPLE_NUM 8 static unsigned int Wheel_ReadRaw(unsigned char idx) { unsigned char i; unsigned long sum 0; for (i 0; i SAMPLE_NUM; i) { sum Adc0832_Read(Channel_Map[idx]); /* 通道映射表兼容 4051 扩展 */ } return (unsigned int)(sum / SAMPLE_NUM); /* 8 次算术平均 */ }8 次平均能把随机噪声压到原来的 1/2.8 左右代价是每路多花几百微秒。四路加起来不到 5ms相对于 200ms 的采样周期完全可以接受。要更狠一点可以改成去掉最大最小值再平均抗脉冲干扰更好但代码量翻倍这个场景没必要。4.3 阈值判断、消抖与报警状态机判断逻辑本身很简单难的是让它别乱叫。车过个减速带压力瞬间波动十几个 kPa如果直接用瞬时值比较蜂鸣器会跟着路面节奏响。#define ST_OK 0 #define ST_LOW 1 #define ST_HIGH 2 #define ST_SENSOR_ERR 3 typedef struct { unsigned int press; /* 当前压力单位 0.1kPa */ unsigned int hi; /* 上限 */ unsigned int lo; /* 下限 */ unsigned char raw_st; /* 本次瞬时判断结果 */ unsigned char st; /* 消抖后的稳定状态 */ unsigned char cnt; /* 连续一致计数 */ } Wheel_t; Wheel_t g_wheel[4]; static unsigned char Wheel_RawJudge(Wheel_t *w) { if (w-press 50 || w-press 9000) return ST_SENSOR_ERR; /* 断线或短路 */ if (w-press w-lo) return ST_LOW; if (w-press w-hi) return ST_HIGH; return ST_OK; } void Wheel_Judge_All(void) { unsigned char i, now; for (i 0; i 4; i) { now Wheel_RawJudge(g_wheel[i]); if (now g_wheel[i].raw_st) { if (g_wheel[i].cnt 250) g_wheel[i].cnt; } else { g_wheel[i].cnt 0; g_wheel[i].raw_st now; } /* 连续 3 轮约 600ms结论一致才认滤掉过坎瞬间的毛刺 */ if (g_wheel[i].cnt 3) { g_wheel[i].st now; } } }600ms 这个数不是拍脑袋来的。轮胎压力变化的物理时间常数在秒级而路面冲击引起的压力尖峰通常在 100300ms 内衰减完。3 轮确认刚好卡在两者中间真的漏气一定撑得过 600ms假的尖峰一定撑不过。报警输出单独抽成一个函数把「判断」和「执行」解耦后面要加分级报警或者换报警方式只动这一个函数void Alarm_Update(void) { unsigned char i, any_alarm 0; static unsigned char beep_cnt 0; for (i 0; i 4; i) { if (g_wheel[i].st ! ST_OK) any_alarm 1; } if (any_alarm) { /* 无源蜂鸣器靠方波驱动这里每 1ms 翻转一次 ≈ 500Hz */ if (beep_cnt 1) { beep_cnt 0; BEEP !BEEP; } } else { BEEP 0; beep_cnt 0; } for (i 0; i 4; i) { LED[i] (g_wheel[i].st ! ST_OK) ? 0 : 1; /* 低电平点亮 */ } }这段里BEEP !BEEP放在主循环里其实不够准主循环一圈的时间不固定实际频率会飘。严谨做法是把翻转动作挪到 1ms 定时器中断里主循环只负责置一个beep_enable标志。仿真里听不出差别实物上声音会明显更干净。4.4 LCD1602 显示与按键设置显示部分没什么技术含量关键是排版。四个轮子分两行每行两个左边前轮右边后轮中间留个空格。/* press 单位 0.1kPa1 bar 100kPa 1000 个计数 所以 bar 的整数位是 press/1000小数位是 press/100%10 */ void LCD_ShowPress(unsigned char pos, unsigned int press) { LCD_ShowChar(pos 0, 0 press / 1000 % 10); LCD_ShowChar(pos 1, .); LCD_ShowChar(pos 2, 0 press / 100 % 10); LCD_ShowChar(pos 3, B); }单位换算这里有个小陷阱0.1kPa 的计数单位下1 bar 正好是 1000 个计数所以取整数位是除以 1000取第一位小数是除以 100 再模 10。搞混了显示出来就是「2.25B」这种一看就不对的数。按键用状态机消抖不用延时/* K1 设置 K2 加 K3 减 K4 切换轮位低电平有效 */ void Key_Scan(void) { static unsigned char last 0xFF, cnt 0; unsigned char cur (K1 0) | (K2 1) | (K3 2) | (K4 3); if (cur last) { if (cnt 10) cnt; } else { cnt 0; last cur; } if (cnt 3) { /* 连续 3 次读到同一状态才认 */ if (!(cur 0x01)) Key_SetMode(); if (!(cur 0x02)) Key_Inc(); if (!(cur 0x04)) Key_Dec(); if (!(cur 0x08)) Key_NextWheel(); } }cnt 3这个写法保证了「一次按下只触发一次」。cnt 会继续涨到 10 3这个条件之后就再不成立直到松开按键把 cnt 清零。用 3就会变成按住不放疯狂连加设个阈值一路飙到底。5. 避坑与排查从仿真到实物最容易翻车的五条记录5.1 编译与仿真阶段的三条现象一Keil 编译 0 错误Proteus 里跑起来 LCD 只有第一行黑块程序像没跑。原因是 hex 没更新。Proteus 的 Program File 指向的路径是对的但它在启动仿真时已经把这个 hex 读进内存了Keil 重新编译覆盖本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表