
简介基于51单片机的报警器项目资料适合开设单片机课程设计、电子实训或毕业设计的师生使用重点解决模拟量如水深、浓度、压力等采集显示与超限报警问题。整套项目压缩包内含原理图、流程图、物料清单、仿真图及源代码共42个文件原理图与PDF工程便于阅读电路连接Proteus仿真DSN可直接运行C语言源码和Hex固件用于烧录调试Xlsx物料清单方便按表采购BMP流程图辅助理解程序逻辑。主控采用51单片机借助ADC0832芯片采集外部模拟信号LCD1602液晶实时显示当前测量值与自定义上下限阈值当数值越限时触发报警程序模块化设计显示单位只需修改显示数组即可快速适应不同场景。已有347人学习下载资料完整、包体仅725KB能够帮助开发者快速完成从硬件设计到软件调通的整个课程项目尤其适合需要综合运用AD转换、液晶显示与中断控制等知识点的入门学习者也可作为电子竞赛或毕业设计的参考资料。1. 基于51单片机的报警器一个从采集到报警的完整小闭环很多课设和竞赛里选的“基于51单片机的报警器”本身不是复杂系统传感器把温度或液位变成模拟电压ADC0832 负责把电压转成 8 位数字量51 单片机拿它与设定好的上下限比较超限就点亮指示灯并驱动蜂鸣器LCD1602 在屏幕上同时显示实时值和两个阈值。真正卡人的是题目要求一次性交付原理图、流程图、物料清单、仿真图、源代码五样东西少一样都交不齐。这类项目的价值在于它是典型的仪表类小系统模拟前端、逐次逼近型 ADC、字符液晶、按键状态机和报警输出全都有且每样都能在 Proteus 里跑通。适合还没完整做过一块板子的学生也适合需要快速评估这种“采集-比较-显示-输出”架构是否合理的工程师。把硬件管脚、软件时序和报警状态机三者对应起来读就不需要背代码。2. 原理图与物料清单先定 ADC0832、LCD1602 和上下限的电路接法拿到这类题目别急着打开 Keil先把原理图拆成四块最小系统、ADC0832 采集、LCD1602 显示、阈值设定与报警输出。代码里每个引脚都要跟着原理图走画错一根线后面流程图和源代码全得返工。下面这套接法是我在课设和样机里最常用的一组全部是 5V 逻辑和 STC89C52RC 直接兼容。2.1 最小系统怎么搭一个 51 内核、一块 ADC、一块液晶常见做法是以 STC89C52RC 或 AT89S52 作主控DIP40 封装工作电压 5V。最小系统里三样不能省晶振、复位、P0 口上拉。晶振用 11.0592MHz 还是 12MHz影响的是串口波特率如果只做 LCD 显示和 ADC 读取12MHz 更容易估算延时要加串口调试就选 11.0592MHz。P0 口是开漏结构必须接 10kΩ 上拉电阻很多仿真正常但实物乱码就是漏了这组电阻。ADC0832 是 8 位逐次逼近型 ADC电源 2.7~5.5V和 51 单片机 5V 系统直接兼容。它在单端模式下一共两个模拟输入 CH0/CH1分辨率按 5V 算约 19.6mV做报警器阈值判断足够。LCD1602 用 8 位总线接 P0控制脚 RS、RW、E 分别接 P2.0、P2.1、P2.2背光通过限流电阻接 5V。这里有个省 IO 的做法把 RW 直接接地只写不读省掉读忙判断代价是每次写指令必须给足延时初始化时尤其要慢。模块信号51 单片机引脚说明ADC0832CSP1.0低电平启动一次转换ADC0832CLKP1.1时钟输入ADC0832DI/DOP1.2共用一根线先当输出写配置再置 1 当输入读数ADC0832CH0信号输入接传感器分压或电位器LCD1602D0-D7P0.0-P0.78 位数据总线必须上拉LCD1602RS/RW/EP2.0/P2.1/P2.2RW 接地时只需操作 RS 和 E报警输出BEEPP2.7经 S8050 驱动有源蜂鸣器按键FUNC/UP/DOWNP3.2/P3.3/P3.4功能切换、加、减上下限怎么给我见过两种画法。一种是两个电位器分压分别代表上限和下限让 ADC0832 分时读三路电压但 ADC0832 单端只有两个输入三路要加 CD4051 模拟开关或第二片 ADC0832另一种是按键数字设定阈值保存在 RAM 或 AT24C02 里。答辩更认可第二种能直接在 LCD1602 上显示“上限 2.00V、下限 1.00V”调起来也准。下面代码按按键方案写如果你手里的参考图是电位器方案只是把阈值来源从按键改成读 ADC 的另外两路主逻辑不变。2.2 物料清单照着买不会少料的 13 项标题里要交的物料清单不该只有芯片。最常被漏掉的是排阻、电位器和蜂鸣器驱动三极管。下面是按 5V 供电、按键设阈值、蜂鸣器报警整理的清单改成继电器输出时把三极管那行换成 5V 继电器即可。模块器件型号/规格数量作用主控单片机STC89C52RC DIP401可替换为 AT89S52时钟晶振11.0592MHz 或 12MHz1串口调试优先选 11.0592MHz时钟瓷片电容22pF2晶振负载电容复位电解电容10uF/16V1上电复位复位电阻10kΩ1配合复位电容上拉排阻10kΩ 九脚1P0 口上拉LCD 数据线稳定采集ADCADC0832 DIP818 位双通道逐次逼近 ADC信号源电位器10kΩ 多圈1模拟传感器电压显示液晶LCD1602 16 脚1显示实时值和上下限对比度电位器10kΩ1接 LCD 的 VO 脚按键轻触开关6×6mm3功能、加、减报警蜂鸣器有源 5V1通电即响不用方波驱动驱动三极管S80501IO 驱动蜂鸣器基极串 1kΩ底板洞洞板/PCB单面即可1焊接用注意有源蜂鸣器内部带振荡源5V 一加就响若手头只有无源蜂鸣器代码里要用定时器输出 2~4kHz 方波不能直接用 P2.7 电平驱动。2.3 源代码起点先把 ADC0832 的 8 位读数写对软件上第一个要攻克的是模拟时序不是复杂算法。我把 CS 和 CLK 用普通 IO 模拟DI/DO 共用 P1.2代码里用一个符号AD_IO表示这条总线。#include reg52.h #include intrins.h sbit CS P1^0; sbit CLK P1^1; sbit AD_IO P1^2; // 分时复用写配置时做 DI读数据时做 DO unsigned char read_adc0832(unsigned char ch) { unsigned char i; unsigned char dat_msb 0, dat_lsb 0; CLK 0; CS 0; // 片选拉低启动一次转换 AD_IO 1; // 起始位 CLK 1; _nop_(); CLK 0; AD_IO 1; // SGL1单端模式 CLK 1; _nop_(); CLK 0; AD_IO ch 0x01; // ODD传 0 选 CH0传 1 选 CH1 CLK 1; _nop_(); CLK 0; AD_IO 1; // 释放总线准备读 DO for (i 0; i 8; i) { CLK 1; _nop_(); CLK 0; _nop_(); dat_msb 1; if (AD_IO) dat_msb | 0x01; } for (i 0; i 8; i) { CLK 1; _nop_(); CLK 0; _nop_(); dat_lsb 1; if (AD_IO) dat_lsb | 0x80; } CS 1; if (dat_msb dat_lsb) return dat_msb; return dat_msb; // 两次结果不一致时优先相信 MSB 这次 }这段代码的关键在第二个循环ADC0832 在第一个 8 位周期按 MSB-first 输出第二个 8 位周期按 LSB-first 输出方向相反所以移位方式也相反。两次结果一致才认为是有效转换。ch传 0 读 CH0传 1 读 CH1传其他值没有意义。51 的 P1 口是准双向口写 1 后自动变输入方向所以读数据前必须先AD_IO 1。如果原理图把 DI 和 DO 分开接了这一句要改成直接读 DO 对应引脚。提示有些 Proteus 版本的 ADC0832 模型对时钟个数敏感读数固定在 0x00 或 0xFF 时先把 CH0 电压调到 2.5V期望读数接近 0x80再回头查时钟是否多发或少发一个边沿。3. 流程图与主程序上下限报警状态机怎么写才不误报上下限比较看起来是两个 if实际上直接比较会碰到临界抖动、按键误触、重启误报一堆问题。这一章把“流程图怎么画”和“主程序怎么写”放在一起说因为两张图本质是同一个状态机。3.1 先想清楚上下限比较不是两个 if 那么简单最低级的写法是if (val 上限) 报警; else 关闭。问题在于信号在临界点附近抖动时蜂鸣器会反复通断听起来像噪音。解决方法是给判断加“时间维度”连续 3 次越限才报警连续 3 次回到正常范围才撤销报警。这样抗抖的是时间不是幅度。再进一步可以加回差进入上限报警后要降到上限以下 20mV 才解除进入下限报警后要升回下限以上 20mV 才解除。回差常用量程的 3%~5%对 8 位 ADC 来说就是 8~13 个码值。标题里说的流程图画出来应该是“正常 / 超上限 / 超下限”三个状态之间的转移而不是一堆 if 嵌套。3.2 流程图落到状态表正常、超上限、超下限把流程图转成状态表写代码时就不容易漏分支。主循环每 10ms 执行一次判决下面表格里的“连续 3 次”约等于 30ms 去抖。当前状态条件连续 3 次采样动作下一状态正常采集值 上限启动蜂鸣器LCD 显示 HIGH上限报警正常采集值 下限启动蜂鸣器LCD 显示 LOW下限报警上限报警采集值 ≤ 上限关闭蜂鸣器正常下限报警采集值 ≥ 下限关闭蜂鸣器正常注意退出条件写的是“回到阈值内”而不是“回到阈值减回差”。如果希望带上幅度回差在比较表达式里减一个常量即可状态表结构不变。3.3 主程序初始化、采样滤波、显示、判决的循环LCD1602 用 8 位总线接到 P0初始化时序必须完整否则仿真能过、实物白屏。下面是按 HD44780 控制器标准时序写的最小初始化函数lcd_write_byte第二个参数 0 表示写指令、1 表示写数据。#include reg52.h #include intrins.h sbit RS P2^0; sbit RW P2^1; sbit E P2^2; sbit BEEP P2^7; int high_limit 200; // 上限阈值0~255 int low_limit 60; // 下限阈值0~255 bit target; // 0 调上限1 调下限 void delay_ms(unsigned int n) { unsigned int i, j; for (i 0; i n; i) for (j 0; j 120; j); } void lcd_write_byte(unsigned char dat, bit is_data) { RS is_data; RW 0; // 只写不读 P0 dat; E 1; _nop_(); _nop_(); E 0; // 下降沿锁存数据 delay_ms(2); } void lcd_init(void) { delay_ms(15); lcd_write_byte(0x38, 0); // 8 位、2 行、5x8 点阵 lcd_write_byte(0x0C, 0); // 显示开、光标关 lcd_write_byte(0x06, 0); // 写入后光标右移 lcd_write_byte(0x01, 0); // 清屏 delay_ms(2); } unsigned char adc_filter(void) { unsigned char i, j, t; unsigned char buf[5]; for (i 0; i 5; i) buf[i] read_adc0832(0); for (i 0; i 4; i) for (j i 1; j 5; j) if (buf[j] buf[i]) { t buf[j]; buf[j] buf[i]; buf[i] t; } return (unsigned char)((buf[1] buf[2] buf[3]) / 3); } void alarm_judge(unsigned int val, int hi, int lo) { static unsigned char state 0; // 0 正常、1 上限报警、2 下限报警 static unsigned char cnt 0; switch (state) { case 0: if (val hi) { if (cnt 3) { state 1; cnt 0; BEEP 1; } } else if (val lo) { if (cnt 3) { state 2; cnt 0; BEEP 1; } } else { cnt 0; } break; case 1: if (val hi) { if (cnt 3) { state 0; cnt 0; BEEP 0; } } break; case 2: if (val lo) { if (cnt 3) { state 0; cnt 0; BEEP 0; } } break; } } void main(void) { unsigned char adc_val; lcd_init(); BEEP 0; while (1) { key_scan(); // 按键设置阈值 adc_val adc_filter(); // 采 5 次去掉最大最小取平均 lcd_show_value(adc_val, high_limit, low_limit); alarm_judge(adc_val, high_limit, low_limit); delay_ms(10); // 主循环周期约 10ms } }adc_filter是中位值平均滤波先对 5 次采样排序去掉最大和最小剩下 3 个取平均。这样能同时抑制随机毛刺和偶发尖峰。alarm_judge里的cnt就是 3.2 状态表中的“连续 3 次”每次采样周期约 10ms从越限到报警约 30~50ms人耳听不出延迟但按键抖动的毛刺已经滤掉了。lcd_show_value只留了一个接口在 LCD1602 第一行显示A:xxx H:xxx L:xxx。用sprintf拼字符串最省事Keil C51 里包含stdio.h把缓冲区定义成 16 字节的数组即可。3.4 按键设置上下限消抖、最小间隔与下限保护按键扫描放在主循环里和报警判决共用 10ms 周期。功能键切换当前调节对象加/减键每按一下步进 5 个 AD 码。关键限制是下限最大只能到上限 - 10避免把下限设置得比上限还高导致系统永远误报。void key_scan(void) { if (KEY_FUNC 0) { delay_ms(10); if (KEY_FUNC 0) { target !target; // 切换调节上限/下限 while (KEY_FUNC 0); // 等待释放 } } if (target 0) { if (KEY_UP 0) { delay_ms(10); if (KEY_UP 0) { high_limit 5; if (high_limit 255) high_limit 255; while (KEY_UP 0); } } if (KEY_DOWN 0) { delay_ms(10); if (KEY_DOWN 0) { high_limit - 5; if (high_limit low_limit 10) high_limit low_limit 10; while (KEY_DOWN 0); } } } else { if (KEY_UP 0) { delay_ms(10); if (KEY_UP 0) { low_limit 5; if (low_limit high_limit - 10) low_limit high_limit - 10; while (KEY_UP 0); } } if (KEY_DOWN 0) { delay_ms(10); if (KEY_DOWN 0) { low_limit - 5; if (low_limit 0) low_limit 0; while (KEY_DOWN 0); } } } }步长 5 是针对 8 位 ADC 的一个折中255 个码值分 51 步调完每步约 98mV手感和精度都合适。这个版本是“按一下动一下”要长按加速的话把while (KEY_UP 0);改成按下 500ms 后每 50ms 自动加 1 即可状态机部分不受影响。4. Proteus 仿真与调试LCD1602 不显示、ADC 读数不对时先查哪里仿真图和原理图的唯一区别是仿真必须让每一个引脚都能被模型识别。这一章直接给在 ISIS 里还原这套电路的顺序以及最常见的三个现象怎么排查。4.1 在 ISIS 里搭仿真图的顺序第一步放置 AT89C52、ADC0832、LCD1602 模型。ISIS 元件库里 LCD1602 通常叫 LM016LADC0832 有现成模型电位器用 POT-HG。第二步先把最小系统连完再连 LCD1602 和 ADC0832网络标签能少拉很多线。第三步在 AT89C52 属性里加载 Keil 生成的 HEX 文件Keil 里必须在 Output 选项卡勾选 “Create HEX File”。第四步给 CH0 接一个 10kΩ 电位器模拟传感器旋到 2.5V 时 LCD 应显示约 0x80。第五步分别把电压调到上限以上、下限以下验证蜂鸣器和 LCD 提示。Proteus 里 AT89C52 默认晶振是 12MHz这和你代码里的延时是配套的。LCD 不亮先查 P0 口有没有接上拉电阻再查 VO 脚电位器是否调到 1V 左右这两个原因占了仿真故障的一大半。4.2 仿真常见现象与排查顺序现象优先检查根因LCD 显示两排黑块VO 脚电位器电压对比度不对调到 1V 附近LCD 有背光但无字符P0 上拉电阻、初始化延时8 位总线悬空或初始化时序太短字符乱码D0-D7 接线、E 信号数据线接错位或 E 下降沿没锁存ADC 一直读 0x00CH0 是否悬空、CS 是否拉低输入没接信号或片选没启动ADC 一直读 0xFFDI/DO 共线、释放总线读数据前没把 AD_IO 置 1按键按一次变两次消抖延时不足while 等待释放逻辑写错蜂鸣器临界抖动状态机退出条件没有做连续 3 次确认排查时我一般先用固定电压测试把 CH0 接 2.5V读回来约 0x80 说明 ADC 链路通再把阈值设成 0x90 和 0x70按键加上去看 LCD 显示是否同步变化。这样能把问题快速隔离在采集、显示、判决三段中的某一段。4.3 用串口把调试变量发出来LCD 上显示的内容有限调试阶段我习惯同时把原始 ADC 值通过串口发到虚拟终端。用 11.0592MHz 晶振时定时器 1 做波特率发生器9600bps 的初值是 0xFD。void uart_init(void) { TMOD 0x20; // 定时器 1方式 2自动重装 TH1 0xFD; // 11.0592MHz 下约 9600bps TL1 0xFD; SCON 0x50; // 串口方式 1允许接收 TR1 1; } void uart_send_byte(unsigned char d) { SBUF d; while (TI 0); TI 0; }主循环里每 100ms 调一次uart_send_byte(adc_val)串口助手按 HEX 接收就能看到 0x00~0xFF 的实时变化。Proteus 里放一个 VIRTUAL TERMINAL把 RXD 接到单片机的 P3.1 即可。这个方法的优势是能看到滤波前后的差异没有滤波时数据在 ±3 个码内跳滤波后基本稳定在同一个值。5. 收尾四件事阈值保存、量程显示、输出扩展和阈值合法性报警器能在 Proteus 里跑通三种工况正常、超上限、超下限之后距离“可以验收”还差四个收尾细节。第一件阈值掉电保存。按键设定的上下限存在 RAM 里一断电就归零。加一片 AT24C02把high_limit和low_limit两个字节写到地址 0x00、0x01上电先读再进主循环。I2C 通信用 P2 口两个引脚软件模拟读回后要做合法性检查防止首次上电读到 0xFF。第二件量程显示。ADC 读回来的 0~255 在 LCD 上显示原始码值答辩时还要解释“255 对应几伏”。把 0~255 映射到 0~5000mV显示成电压更直观。unsigned int mv (unsigned long)adc_val * 5000UL / 255UL; lcd_show_mv(mv); // 显示 00.00V ~ 05.00V(unsigned long)强制转换很关键C51 里unsigned int最大 65535直接乘 5000 会溢出。第三件输出扩展。蜂鸣器只能提醒人控制现场设备要加继电器。用 P2.7 驱动光耦光耦再驱动 5V 继电器触点接报警灯或电磁阀继电器线圈两端要并一个续流二极管方向是负极接正极否则关断瞬间的反向电动势会打坏三极管。第四件阈值合法性检查。按键代码里已经限制了“下限不能超过上限减 10”但这个检查要在上电读取 AT24C02 之后再做一次防止外部掉电瞬间写入脏数据。if (low_limit 10 high_limit) { low_limit high_limit - 10; }这行放在上电初始化和按键设置两个入口各执行一次10 个 AD 码约等于 196mV保证上下限之间有肉眼可分辨的报警区间。经过这四个收尾报警器就不再是“能响就完事”的课设板而是一个阈值不掉、显示能读、输出可用、参数自洽的可靠小仪表。本文还有配套的精品资源点击获取