我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

基于AT89C52与ADC0832的一氧化碳检测报警器设计

基于AT89C52与ADC0832的一氧化碳检测报警器设计 简介面向电子、自动化专业学生与嵌入式入门开发者的课程设计与毕业设计参考资料围绕AT89C52单片机的一氧化碳检测仪展开帮助读者理解气体检测类环境监测系统的实现思路。资源为单个PDF文档压缩包约1.16MB内容涵盖硬件与软件两部分硬件部分讲解MCU选型与最小系统、CITY 7E/F传感器数据采集、AD0832模数转换、数码管显示、电源及三极管驱动的单音频报警电路软件部分给出软件设计结构、模数转换模块程序及报警上限值可编程设置的实现方法并涉及信号补偿等降低测量误差的处理手段。文档含主电路图、实物图、程序附录与参考文献目录清晰便于按模块对照传感器采集、A/D转换、阈值判断与报警联动的完整链路。目前已有218人学习下载适合需要搭建单片机环境监测方案或撰写相关论文的读者参考借鉴。1. 厨房里那台 CO 报警器最先失灵的往往不是传感器一氧化碳无色无味和血红蛋白结合后让人缺氧轻度头晕恶心重度昏迷甚至死亡。所以公共场所和室内装 CO 检测仪不是“多个传感器”那么简单。这套基于 AT89C52 单片机的方案用 CITY 7E/F 一氧化碳传感器把浓度变成电流经运放和 ADC0832 转成数字量再由数码管显示 ppm超限时 P1.0 驱动三极管让蜂鸣器响。它适合做单片机课程设计、毕业设计或者需要低成本复现气体检测的人。反直觉的是现场最先出问题的通常不是传感器寿命而是 A/D 参考电压漂了、模拟地和数字地混在一起零点先跑偏。把最小系统、采样时序和标定流程拆开看比换传感器有用。2. AT89C52 最小系统与 7E/F 传感器信号调理晶振、复位、放大链2.1 12MHz 晶振与上电复位最小系统先看时序和电平AT89C52 是 8 位 CMOS 单片机片内 8KB Flash、256B RAM、32 个 IO、3 个 16 位定时器、6 个中断源兼容 MCS-51 指令。这个项目里它要干三件事按 ADC0832 时序读通道、做 ppm 换算和滤波、刷新数码管并判断报警。晶振选 12MHz一个机器周期 1μs定时器初值好算软件延时也准。晶振两端接 22pF 到 30pF 电容到地尽量靠近 XTAL1、XTAL2 引脚走线短。复位用 10μF 电解电容加 10kΩ 电阻做上电复位再并一个按键做手动复位。RST 高电平要保持两个机器周期以上AT89C52 才能可靠复位。EA/VPP 接高电平用内部 Flash。有些同学把晶振和复位画上就上电结果数码管乱亮。常见原因是复位电容太大或太小或者晶振电容不匹配。12MHz 晶振配 30pF 一般能起振但布局差时可能停振。用示波器看 XTAL2 有正弦波或者用程序翻转 IO 测频率比盯着电路猜快。2.2 7E/F 传感器输出特性与运放放大倍数计算原文写的是 CITY 公司的 7E/F 传感器也出现 CO/CF-1000 探头参数。这类电化学 CO 传感器输出电流不是电压。典型参数量程 01000ppm输出 100±20nA/ppm分辨率 0.05ppmT90 小于 50 秒负载建议 10Ω。它输出的是 nA 级电流不能直接接 ADC0832否则输入阻抗一压信号就没了。常见做法是用运放搭跨阻放大器把电流转换成电压Vout I × Rf。参数典型值对电路的影响量程01000 ppm决定 Rf 上限避免运放饱和输出灵敏度100±20 nA/ppm个体差异大必须标定分辨率0.05 ppm理论值实际受 ADC 位数限制T90 响应时间50 s报警不能只看瞬时值要加滤波推荐负载10 Ω运放输入阻抗要远大于它跨阻增益选 Rf47kΩ100nA/ppm × 47kΩ 4.7mV/ppm1000ppm 对应 4.7V正好落在 ADC0832 的 05V 输入范围内。如果 Rf 用 100kΩ1000ppm 会到 10V运放直接饱和。运放要选低偏置电流、低失调电压的型号比如 OP07 或 TLC27L 系列偏置电流最好小于 1nA否则会吃掉传感器电流。// 传感器输出电流到电压的换算宏方便改 Rf 后重算 #define SENS_NA_PER_PPM 100.0 // 典型灵敏度单位 nA/ppm #define RF_OHM 47000.0 // 跨阻反馈电阻单位 Ω #define ADC_VREF_MV 5000.0 // ADC0832 参考电压 5V #define ADC_BITS 256.0 // 8 位分辨率 // 每 ppm 对应电压 100nA * 47k 4.7mV #define MV_PER_PPM (SENS_NA_PER_PPM * RF_OHM / 1000000.0) // ADC 每 LSB 电压 5000mV / 256 19.53mV #define MV_PER_LSB (ADC_VREF_MV / ADC_BITS) // 理论浓度分辨率 19.53mV / 4.7mV 4.15ppm/LSB #define PPM_PER_LSB (MV_PER_LSB / MV_PER_PPM)这段宏不是给单片机跑的是用来验算量程和分辨率。Rf 改大灵敏度高但满量程容易超Rf 改小量程够但低浓度分辨率差。ADC0832 是 8 位1 LSB 约 19.53mV换算到浓度约 4.15ppm所以显示到 0.1ppm 只是数码管位数真实分辨率到不了。要测低浓度要么换 12 位 ADC要么加可编程增益放大器分段放大。2.3 模拟电源、数字电源和地线处理传感器和运放对电源纹波敏感。AT89C52 和数码管开关时会在电源上产生尖峰如果直接共用一路 5VADC 读数会跳。常见做法是 9V 电池或适配器经 7805 稳到 5V模拟部分再经 LC 滤波或磁珠隔离ADC0832 的 Vcc/REF 端加 0.1μF 和 10μF 去耦。地线用单点接地模拟地、数字地在电源入口处汇合不要在大电流回流路径上串。传感器信号线尽量短必要时加保护环因为 nA 级电流很容易被漏电流干扰。PCB 上运放输入走线不要和数码管段线平行否则显示刷新时读数会周期性抖动。这些不是玄学是实际调试里最常见的零点漂移来源。3. ADC0832 双通道 A/D 与 AT89C52 采样程序控制字、时序和 ppm 换算3.1 ADC0832 的引脚与 4 线接法ADC0832 是 8 位双通道逐次逼近型 A/D5V 供电时输入 05V工作频率 250kHz转换时间 32μs功耗约 15mWTTL/CMOS 兼容。它有 8 个引脚CS、CH0、CH1、GND、DI、DO、CLK、Vcc/REF。正常接法是 4 根线到单片机CS 片选、CLK 时钟、DI 数据输入、DO 数据输出。因为 DI 和 DO 不会同时有效很多人把 DI 和 DO 并到一根 IO 上省一根线。并线时要注意发控制字时该 IO 是输出读数据时要先写 1 释放成输入。51 单片机 P2 口写 1 就是输入所以能做到但时序上要留一个空档。ADC0832 引脚连接说明CSP2.0低电平使能转换期间保持低CLKP2.1时钟输入空闲低DIP2.2控制字输入并线时兼作 DODOP2.3转换数据输出MSB 在前CH0传感器放大输出本设计用单端通道 0Vcc/REF5V 参考加去耦电容3.2 通道控制字与读时序ADC0832 的控制字是 8 位起始位必须为 1SGL/DIF 位为 1 表示单端输入0 表示差分ODD/SIGN 位在单端时选通道0 选 CH01 选 CH1MSBF 位为 1 表示先输出 MSB。后面 4 位不用。所以单端 CH0、MSB first 的控制字是 1101 0000即 0xD0CH1 是 1111 0000即 0xF0。发完控制字后芯片会返回一个空位再输出 8 位数据。代码里读 8 次每次时钟先高后低在时钟高电平期间读 DO。#include reg52.h #include intrins.h sbit ADC_CS P2^0; sbit ADC_CLK P2^1; sbit ADC_DI P2^2; sbit ADC_DO P2^3; unsigned char adc0832_read(unsigned char ch) { unsigned char i, dat 0; unsigned char ctrl; ctrl (ch 0) ? 0xD0 : 0xF0; // CH0: 1101 0000, CH1: 1111 0000 ADC_CS 0; // 片选拉低开始转换 ADC_CLK 0; // 时钟空闲低 for (i 0; i 8; i) { // 发送 8 位控制字 ADC_DI (ctrl 0x80) ? 1 : 0; ctrl 1; ADC_CLK 1; _nop_(); ADC_CLK 0; // 下降沿让 ADC 采样 DI } ADC_DI 1; // 释放 DI51 写 1 为输入 for (i 0; i 8; i) { // 读 8 位数据MSB 在前 ADC_CLK 1; _nop_(); dat 1; if (ADC_DO) dat | 0x01; ADC_CLK 0; } ADC_CS 1; // 拉高片选结束 return dat; }这段函数返回 0255 的 ADC 码值。参数ch选 0 或 1对应 CH0、CH1。_nop_()用来拉长时钟高电平12MHz 晶振下一条_nop_()约 1μsADC0832 支持 250kHz所以这个速度没问题。注意如果 DO 和 DI 分开接读循环里不要写ADC_DI1如果并线必须先释放。还有一点CS 拉低后要保持到转换结束中途拉高会复位接口。读到的码值不是浓度下一步要做零点校正和线性换算。3.3 从 ADC 码值到 ppm换算与滑动平均滤波ADC0832 是 8 位参考 5V码值 0255。理论关系是电压 码值 × 5000mV / 256。再根据 4.7mV/ppm得到ppm 电压 / 4.7。但实际有零点偏移所以要先在洁净空气中读一个zero_adc再用(adc - zero_adc)算增量。如果量程标定过用两点法算斜率更准。代码里先做 8 次滑动平均抑制传感器噪声和电源尖峰。#define FILTER_LEN 8 unsigned char adc_buf[FILTER_LEN]; unsigned char buf_idx 0; unsigned int adc_to_ppm(unsigned char adc) { unsigned long mv; unsigned int ppm; unsigned char zero_adc 12; // 洁净空气下标定得到的零点码值 if (adc zero_adc) return 0; mv (unsigned long)(adc - zero_adc) * 5000UL / 256UL; // 转成 mV ppm (unsigned int)(mv / 4.7); // 按 4.7mV/ppm 换算 return ppm; } unsigned int filter_and_convert(unsigned char adc) { unsigned long sum 0; unsigned char i; adc_buf[buf_idx] adc; buf_idx (buf_idx 1) % FILTER_LEN; for (i 0; i FILTER_LEN; i) { sum adc_buf[i]; } return adc_to_ppm((unsigned char)(sum / FILTER_LEN)); }zero_adc是零点码值上电后在洁净空气里读几十次取平均写进程序或存到外部 EEPROM。FILTER_LEN取 8 是折中再大响应变慢报警会迟再小读数跳。mv / 4.7用浮点会拉长代码可以改成mv * 10 / 47用整数算。注意传感器 T90 小于 50 秒滤波窗口不要超过 1 秒否则真实浓度上升时报警延迟明显。如果读数总在 0 附近跳先查运放输出是否真的随传感器变化再查 ADC 参考电压是否稳。4. 74HC595 数码管显示与 P1.0 蜂鸣器报警阈值设置和超限逻辑4.1 74HC595 串进并出驱动数码管AT89C52 的 IO 要接 ADC、按键、蜂鸣器直接驱动 4 位数码管不够用。74HC595 是 8 位串入并出移位寄存器带输出锁存三根线就能扩展 8 位输出。常见接法两片 595 级联一片管段选一片管位选或者一片 595 管段选位选用 P2 口。动态扫描时定时器每 2ms 换一位人眼看起来是同时亮的。段码表按共阴或共阳写共阴高电平点亮共阳反过来。74HC595 引脚连接说明SERP3.7串行数据输入SRCLKP3.5移位时钟上升沿移入RCLKP3.6锁存时钟上升沿输出OEGND输出使能低有效Vcc5V加 0.1μF 去耦QAQH段选/位选经限流电阻接数码管sbit SRCLK P3^5; sbit RCLK P3^6; sbit SER P3^7; void hc595_send(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { SER (dat 0x80) ? 1 : 0; dat 1; SRCLK 0; _nop_(); SRCLK 1; _nop_(); // 上升沿移入 } RCLK 0; _nop_(); RCLK 1; _nop_(); // 上升沿锁存到输出 }hc595_send先发最高位发完 8 位再拉 RCLK 锁存。如果级联两片就连续发两个字节先发的会移到后面那片。段选限流电阻一般 220Ω470Ω位选电流大时用三极管或 ULN2003 驱动。注意595 的 OE 不要悬空悬空会乱亮RCLK 和 SRCLK 不要接反接反了数据会移进去但锁存不住现象是数码管闪一下就没。4.2 阈值按键设置与掉电保存原文说操作人员可以通过单片机编程设置报警上限值。演示阶段最省事的是把阈值放在 RAM 里两个按键加/减比如默认 50ppm按一下加 5长按连加。但 AT89C52 没有内部 EEPROM掉电就丢。要保存常见做法是外挂 24C02用 I2C 读写如果手头是 STC 系列单片机直接用内部 EEPROM 存阈值比外挂芯片省事很多蓝桥杯单片机板子也是这个套路。阈值不要设得太低传感器零点漂移和 ADC 跳变会让它频繁报警公共场所按标准设室内一般 3050ppm 起步具体看当地规范。sbit P1_0 P1^0; sbit KEY_ADD P3^2; #define ALARM_DEFAULT 50 unsigned int alarm_ppm ALARM_DEFAULT; void check_alarm(unsigned int ppm) { if (ppm alarm_ppm) { P1_0 1; // 三极管导通蜂鸣器响 } else { P1_0 0; // 低于阈值停止报警 } } void key_scan(void) { static unsigned char last 1; if (KEY_ADD 0 last 1) { // 简单消抖按下沿 if (alarm_ppm 500) alarm_ppm 5; } last KEY_ADD; }check_alarm放在主循环里每算完一次 ppm 就比一次。key_scan只做了最简消抖实际要加 10ms 延时或定时器扫描。阈值上限建议不超过 500ppm再高就失去预警意义。如果报警后浓度降下来但蜂鸣器还响检查是不是用了锁存报警标志有些场合需要手动复位报警有些场合要求自动恢复看需求定。4.3 三极管驱动的单音频报警电路报警电路用 P1.0 接 NPN 三极管基极基极串 1kΩ4.7kΩ 电阻发射极接地集电极接蜂鸣器蜂鸣器另一端接 5V。P1.0 输出高电平时三极管导通蜂鸣器响输出低电平截止。压电蜂鸣器工作电流约 10mA三极管用 9013、8050 都行。如果要发 3kHz 单音频可以用定时器翻转 P1.0输出方波如果用有源蜂鸣器直接给高电平就响。注意蜂鸣器两端反并一个续流二极管防止关断时反向电动势打坏三极管。软件上报警时不要让数码管扫描停掉否则显示会灭把蜂鸣器翻转放在定时器中断里和显示扫描共用时基逻辑更稳。5. 标定、零点漂移补偿与现场排错从 ADC 码值到稳定报警5.1 两点标定与零点补偿传感器个体差异和运放失调会让同一浓度下 ADC 码值不同所以必须标定。常见两点法第一点洁净空气读zero_adc第二点用标准气体比如 500ppm读span_adc。斜率k 500 / (span_adc - zero_adc)浓度ppm (adc - zero_adc) × k。没有标准气体时可以用已知浓度的打火机气体或专业标定站但不要用烟熏烟里成分复杂标出来不准。零点漂移要定期校准代码里可以加按键进入校准模式把当前 ADC 值写入zero_adc。unsigned int zero_adc 12; // 洁净空气下标定 unsigned int span_adc 128; // 500ppm 标准气下标定 unsigned int span_ppm 500; unsigned int adc_to_ppm_cal(unsigned char adc) { long diff; if (adc zero_adc) return 0; diff (long)(adc - zero_adc) * span_ppm / (span_adc - zero_adc); return (unsigned int)diff; }span_adc必须大于zero_adc否则除零或负数。如果标定后读数线性差可能是运放饱和或 ADC 参考电压不稳先量运放输出。温度补偿方面电化学传感器输出随温度变化常见做法加 DS18B20 或热敏电阻做分段查表修正。原文提到信号补偿实际项目里先做零点补偿再考虑温度顺序不要反。5.2 常见故障与排查表现象可能原因排查方法数码管乱亮595 接反、OE 悬空查 SRCLK/RCLKOE 接地ADC 读数不变CS 没拉低、CLK 没翻转示波器看 CS 和 CLK读数满量程运放饱和、Rf 太大量运放输出是否到 5V零点漂移电源纹波、地线干扰模拟数字地单点连接报警不响三极管基极电阻太大量 P1.0 高电平查蜂鸣器响应迟钝滤波窗口太长减小滤波长度到 4 或 8把 ADC 原始码值和换算后的 ppm 通过串口每隔 200ms 打印一次用定时器发盯着看 10 分钟比在数码管上猜快得多。串口能看出是零点在爬还是报警阈值算错也能看出电源开关瞬间的跳变。本文还有配套的精品资源点击获取
返回列表