ARTICLE DETAIL

资讯详情

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

80C51数字压力传感器设计:从信号链到标定与显示的工程实践

80C51数字压力传感器设计:从信号链到标定与显示的工程实践 简介这份Word文档面向电子信息、自动化及测控专业的学生与工程技术人员围绕基于80C51单片机的数字压力传感器设计展开可用于课程设计、毕业设计或工业自控入门参考。内容涵盖电阻应变片与压电传感器两种敏感元件的方案对比、全桥测量电路、三运放大电路、ADC0809模数转换、LED数码显示以及RS-485通信接口与协议并给出电源电路、数据处理与温度补偿等设计论证。资源包共1个doc文件约158KB为完整可编辑的Word版设计文档结构清晰、图文并茂便于直接查阅或二次修改。目前已有43人学习下载适合需要快速获取完整设计方案、理解传感器信号采集与单片机处理流程的读者参考借鉴。1. 从一颗 80C51 到数字压力表为什么老架构反而更适合入门很多人第一次接触压力测量脑子里冒出来的方案是买一个带 I2C 输出的数字压力传感器接上开发板读寄存器就完事。但如果你手上只有一颗 80C51或者你面对的是一个需要长期稳定运行、成本压到极致的工业现场思路就完全不一样了。基于 80C51 单片机的数字压力传感器设计核心不是堆算力而是用最少的资源把模拟信号采准、把数字量算对、把显示和通信做稳。它解决的是“低成本、可标定、能长期跑”的问题适合刚入门的电子工程师、做课程设计的学生以及需要给老旧设备加数字显示的现场维护人员。这颗芯片没有硬件乘法器没有浮点单元连 ADC 都要外挂但正因为资源紧你才会被迫把每一个时钟周期和每一字节内存都算清楚。我见过太多人一上来就选带 24 位 ADC 的 MCU结果连基准电压的温漂都没搞定数据跳得比心跳还快。80C51 方案反而逼你先搞明白信号链。2. 信号链怎么搭从压阻桥到 80C51 能读的数字2.1 压力传感器选型与模拟前端数字压力传感器的“数字”二字指的是最终输出形式而不是敏感元件本身。绝大多数低成本方案用的还是压阻式惠斯通电桥输出差分电压通常在几十毫伏量级。以常见的表压传感器为例满量程输出可能是 50mV 到 100mV零点偏置却可能有 ±20mV。这个信号直接进 ADC 会被噪声淹没所以模拟前端必须做两件事差分放大和电平搬移。我一般会选一款仪表放大器比如 AD620 的国产替代型号增益电阻根据传感器灵敏度和 ADC 参考电压来算。假设传感器灵敏度是 2mV/V供电 5V满量程输出就是 10mV。ADC 参考电压 2.5V想要满量程对应 2.0V增益就是 200 倍。增益电阻公式是 Rg 49.4kΩ / (G-1)算下来约 248Ω。这个电阻要用精度 1% 的金属膜电阻温漂越小越好。电平搬移用一只运放搭减法电路把差分放大后的信号抬到 ADC 量程中点。这里有个血泪经验不要用单电源运放直接处理接近地的信号输出会失真。要么用双电源要么选轨到轨输入输出的型号并且给负电源端加一个负压产生电路。如果嫌麻烦可以直接选一款带差分输入和可编程增益的 ADC比如 HX711 这类专用于桥式传感器的芯片它内部就把放大和 ADC 做完了输出直接是数字量80C51 只需要两个 IO 口模拟时序读取。但 HX711 的更新率低适合静态压力测量动态压力就别想了。2.2 80C51 最小系统与 ADC 接口80C51 本身没有 SPI 和 I2C 硬件模块所有时序都要用 IO 口模拟。如果你选的是并行输出的 ADC比如 ADC0809那就更方便直接挂在 P0 口和 P2 口上用外部中断或者查询方式读。ADC0809 是 8 位、8 通道、逐次逼近型转换时间约 100μs对于压力测量绰绰有余。它的启动信号、地址锁存、输出使能都需要用 IO 口控制。最小系统包括晶振电路、复位电路和电源。晶振选 11.0592MHz 还是 12MHz如果要做串口通信11.0592MHz 能产生更准的波特率误差为 0。如果只做显示和按键12MHz 计算定时器更方便。我一般用 11.0592MHz因为后期加串口上传数据时不用改硬件。复位电路用 10μF 电容加 10kΩ 电阻上电复位时间约 100ms足够 80C51 初始化。EA 引脚接高电平使用内部程序存储器。ADC 的参考电压直接决定测量精度。不要用 7805 的输出当基准它的温漂和噪声都很大。至少要用 TL431 这类可调基准源精度 0.5%温漂 50ppm/℃。如果预算允许用 REF3025 这类专用基准初始精度 0.05%温漂 15ppm/℃。基准电压的稳定性比 ADC 位数更重要8 位 ADC 配 0.05% 基准有效精度能到 7 位以上16 位 ADC 配 5% 基准实际精度可能只有 8 位。2.3 采样时序与数字滤波80C51 读 ADC 的时序必须严格按数据手册来。以 ADC0809 为例启动转换需要给 START 引脚一个正脉冲然后等待 EOC 变高再给 OE 高电平读取数据。用查询方式写起来简单但占用 CPU 时间。用中断方式把 EOC 反相后接到 INT0 或 INT1转换完成触发中断在中断服务程序里读数据。采样频率不用太高压力变化通常很慢。我一般设 10Hz 到 100Hz 的采样率用定时器 0 产生 10ms 中断在中断里启动 ADC下次中断读结果。这样 CPU 占用率很低还能留出时间做显示和按键扫描。数字滤波是必须的。8 位 ADC 的原始数据跳动可能有 ±2LSB直接显示会闪。最简单的办法是滑动平均开一个 8 个元素的环形缓冲区每次新数据覆盖最旧数据然后求平均。求平均时不要用浮点80C51 做浮点除法很慢。用移位代替除法8 个数据求和后右移 3 位就是平均值。如果数据跳动还是大可以加中值滤波连续采 5 次去掉最大和最小剩下 3 个求平均。中值滤波对脉冲干扰特别有效比如电机启停时产生的尖峰。// 滑动平均滤波8 点用移位代替除法 unsigned char filter_buf[8]; unsigned char filter_idx 0; unsigned int filter_sum 0; unsigned char filter_avg(unsigned char new_data) { filter_sum - filter_buf[filter_idx]; // 减去最旧数据 filter_buf[filter_idx] new_data; // 存入新数据 filter_sum new_data; // 累加新数据 filter_idx (filter_idx 1) 0x07; // 环形索引0-7循环 return (unsigned char)(filter_sum 3); // 除以8右移3位 }这段代码的关键在于 filter_sum 用 unsigned int因为 8 个 8 位数据求和最大 2040不会溢出。filter_idx 用按位与代替取模因为 8 是 2 的幂这样写编译出来的代码更短。每次调用返回平均值主循环里直接拿去显示或计算压力值。注意初始化时要把缓冲区清零否则上电后前 8 次采样会混入随机值。3. 标定与压力计算没有浮点单元怎么算得准3.1 两点标定法的实操步骤压力传感器的输出和压力值之间理论上是线性的但实际有零点偏置和灵敏度误差。两点标定是最实用的方法在零压力和满量程压力下各采一组数据算出斜率和截距。具体操作是先不给压力等读数稳定后记录 ADC 值记为 adc_zero然后加满量程标准压力记录 adc_full。压力计算公式就是P (adc - adc_zero) * P_full / (adc_full - adc_zero)其中 P_full 是满量程压力值比如 100kPa。这个公式里有一次除法和一次乘法80C51 做 16 位除法大概需要几十个机器周期对于 10Hz 的采样率完全够用。但要注意 adc_full - adc_zero 不能太小否则除法误差大。如果传感器灵敏度低放大后的满量程 ADC 变化量至少要有 100 个码值对应 8 位 ADC 就是 40% 量程。如果只有 20 个码值标定出来的精度会很差这时候要么加大增益要么换 12 位 ADC。标定时要用标准压力源不要用打气筒加机械表凑合。机械表的精度通常只有 2.5% 满量程标出来的数字表不会比它更准。如果实在没有标准源可以用水柱法1 米水柱约 9.8kPa用卷尺量水位差精度能到 1%。但水柱法只适合低压高压还是得用活塞式压力计。3.2 定点数运算与查表法80C51 没有浮点单元用 float 类型会调用库函数代码体积大、执行慢。我一般用定点数把压力值放大 10 倍或 100 倍用 unsigned int 或 unsigned long 存储。比如压力范围 0 到 100.0kPa就用 0 到 1000 表示单位是 0.1kPa。计算时先乘后除避免中间结果溢出。如果压力范围不大查表法更快。把 ADC 值 0 到 255 对应的压力值预先算好存到 code 区运行时直接查表。表的大小是 256 字节对于 80C51 的 4KB 程序空间来说可以接受。查表法没有计算误差但标定后要重新生成表。生成表可以用 Excel 或者 Python 脚本把公式算好导出成 C 数组。// 定点数压力计算压力单位 0.1kPa满量程 1000 unsigned int adc_zero 20; // 零点 ADC 值 unsigned int adc_full 230; // 满量程 ADC 值 unsigned int p_full 1000; // 满量程压力 100.0kPa unsigned int calc_pressure(unsigned char adc) { unsigned long temp; if (adc adc_zero) return 0; if (adc adc_full) return p_full; temp (unsigned long)(adc - adc_zero) * p_full; temp temp / (adc_full - adc_zero); return (unsigned int)temp; }这段代码里 temp 用 unsigned long因为 (adc - adc_zero) 最大 255乘以 1000 等于 255000超过 16 位范围。除法用 32 位除以 16 位Keil C51 会调用库函数执行时间约 200 个机器周期在 11.0592MHz 下约 18μs对 10Hz 采样率来说微不足道。注意 adc_zero 和 adc_full 要存在非易失存储器里比如 AT24C02这样标定一次就不用每次上电重来。3.3 温度补偿的简化处理压阻式压力传感器有温漂零点温漂和灵敏度温漂都有。工业级传感器内部会做补偿输出已经比较稳定。但如果用的是裸片或者低成本传感器温漂可能达到 0.5%FS/℃。80C51 方案里加温度补偿最简单的办法是外挂一颗数字温度传感器比如 DS18B20测环境温度然后在软件里做一阶线性补偿。补偿公式P_corrected P_raw K_zero * (T - T_ref) K_span * (T - T_ref) * P_rawK_zero 和 K_span 需要实验测定。把传感器放在温箱里分别在 0℃、25℃、50℃ 下测零点和满量程输出算出系数。没有温箱的话可以用冰箱和热水浴粗略测但精度没法保证。如果温度变化不大比如室内环境 15℃ 到 35℃可以只做零点补偿灵敏度补偿忽略。DS18B20 用单总线80C51 模拟时序要小心读写时序的延时用 NOP 微调不同晶振频率下要重新算。4. 显示与通信把数字压力值送出去4.1 数码管驱动与动态扫描数字压力表最直观的显示是 4 位数码管显示 0.0 到 100.0。共阳数码管用 P0 口输出段码P2 口输出位选。动态扫描频率要大于 50Hz否则会闪。用定时器 1 产生 2ms 中断每次中断点亮一位4 位轮流刷新率 125Hz肉眼完全看不出闪烁。段码表用 code 关键字存在程序存储器共阳和共阴的段码不一样写代码前先用万用表测一下。限流电阻每段一个220Ω 到 470Ω根据数码管亮度调。位选三极管用 S8550 或 S8050注意共阳数码管的位选是低电平有效三极管要接成射极跟随器还是集电极开路取决于数码管共阳端的接法。// 共阳数码管段码表0-9不带小数点 unsigned char code seg_code[] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 }; // 显示缓冲区4 位分别对应千、百、十、个 unsigned char disp_buf[4] {0, 0, 0, 0}; // 定时器1中断服务程序2ms 一次 void timer1_isr() interrupt 3 { static unsigned char pos 0; P2 0xFF; // 先消隐关闭所有位 P0 seg_code[disp_buf[pos]]; // 输出段码 P2 ~(1 pos); // 打开当前位低电平有效 pos (pos 1) 0x03; // 0-3 循环 }这段代码里 P2 先写 0xFF 消隐再写段码最后打开位选顺序不能反否则会有鬼影。pos 用按位与循环和前面滤波代码一个套路。disp_buf 里的数据在主循环里更新把压力值拆成 4 位数字。拆位用除法和取余80C51 做 16 位除法稍慢但 10Hz 更新一次完全没压力。4.2 串口上传与上位机协议如果需要把数据传到电脑80C51 的串口是现成的。用定时器 1 做波特率发生器11.0592MHz 晶振下9600bps 的定时器初值是 0xFD。串口工作在模式 18 位数据可变波特率。发送用查询方式就行压力数据更新慢不用中断。协议我一般用简单的 ASCII 帧帧头 0xAA然后 4 字节压力值 ASCII 码最后 0x55 结尾。上位机收到后解析。ASCII 的好处是调试方便用串口助手直接能看。二进制协议更紧凑但调试时得对着十六进制看容易出错。// 串口发送一个字节查询方式 void uart_send(unsigned char dat) { SBUF dat; while (!TI); // 等待发送完成 TI 0; // 清除发送标志 } // 发送压力值格式0xAA 4字节ASCII 0x55 void send_pressure(unsigned int p) { uart_send(0xAA); uart_send(p / 1000 0); // 千位 uart_send(p / 100 % 10 0); // 百位 uart_send(p / 10 % 10 0); // 十位 uart_send(p % 10 0); // 个位 uart_send(0x55); }发送前要确保 p 不超过 9999否则会发乱。如果压力值可能超过先做限幅。串口发送和数码管扫描都在主循环里调用注意不要互相阻塞。数码管扫描在定时器中断里串口发送在主循环两者不冲突。5. 避坑与排查80C51 压力表最容易翻车的 5 个地方5.1 ADC 读数跳动大显示最后一位乱闪现象压力稳定时数码管最后一位在 0 和 1 之间跳或者跳动超过 3 个码值。原因基准电压不稳、模拟地和数字地混在一起、滤波不充分。80C51 的 IO 口翻转时会产生电流尖峰通过地线耦合到模拟部分。解决基准源加 10μF 钽电容和 0.1μF 陶瓷电容退耦位置尽量靠近基准芯片。模拟地和数字地单点连接在 ADC 下方用 0Ω 电阻或磁珠连接。软件上把滑动平均从 8 点加到 16 点或者加中值滤波。如果还跳检查传感器信号线是否太长用屏蔽线并单端接地。5.2 标定后线性度差中间点误差大现象零点和满量程都准但 50% 压力时误差超过 2%。原因传感器本身非线性或者放大器在中间电平处失真。也可能是 ADC 的积分非线性INL指标差。解决先查传感器数据手册的非线性指标如果传感器本身非线性就 1%那系统不可能优于 1%。如果是放大器问题检查输出是否在轨到轨范围内中间电平处是否进入非线性区。ADC 的 INL 通常 8 位的是 ±0.5LSB影响不大。如果要求高用三点标定把中间点也纳入用分段线性插值。5.3 上电后数码管显示乱码或全亮现象一上电所有段都亮或者显示无意义的字符。原因80C51 上电复位时间不够IO 口状态不确定。或者 disp_buf 未初始化里面是随机值。解决复位电路电容加大到 22μF确保复位时间大于 100ms。在 main 函数开头先把 disp_buf 清零P0 和 P2 写 0xFF 消隐。如果还乱检查 EA 引脚是否接高程序是否从 0000H 开始执行。有时候是晶振没起振用示波器看 XTAL2 引脚有没有波形。5.4 串口通信丢包或乱码现象上位机收到的数据偶尔多一个字节或少一个字节或者全是乱码。原因波特率误差大、地线没接、发送时被中断打断。解决11.0592MHz 晶振下 9600bps 误差为 0如果用了 12MHz 晶振误差约 8.5%会丢包。检查串口线是否共地TX 和 RX 不要接反。发送函数里 while(!TI) 是死等如果发送时来了中断TI 标志可能被意外清除。解决办法是在发送前关中断发送完再开中断或者用中断方式发送。5.5 长期运行后压力值漂移现象跑几个小时或几天后零压力时显示不为零慢慢变大或变小。原因基准电压温漂、传感器温漂、放大器失调电压漂移。也可能是电源纹波导致 ADC 参考不稳。解决基准源换低温漂型号传感器如果温漂大就加温度补偿。放大器选低失调漂移的比如 OP07 的失调漂移是 0.5μV/℃足够用。电源加 LC 滤波7805 后面接 100μH 电感和 100μF 电容。软件上可以加自动零点跟踪如果压力值连续 1 分钟小于满量程的 1%就认为当前是零点自动更新 adc_zero。但这个方法有风险如果实际压力就是很小会被误判为零点。我一般只在开机后前 10 秒做自动零点之后锁定。6. 进阶技巧用 80C51 的剩余资源做自诊断80C51 的 IO 口和定时器资源有限但做完压力测量、显示和串口后通常还能剩几个 IO。我习惯留一个 IO 做传感器断线检测一个 IO 做超量程报警。断线检测的原理很简单在传感器桥臂上并一个高阻值电阻正常时桥臂电阻低该点电压被拉低断线时桥臂开路电压被高阻拉高。用 ADC 读这个电压超过阈值就报警。另一个技巧是用定时器 2 做看门狗。80C51 没有内置看门狗但可以用定时器 2 的溢出中断模拟。在主循环里定时喂狗如果程序跑飞定时器 2 溢出后触发复位。具体做法是定时器 2 设成 100ms 溢出溢出中断里把 RST 引脚拉低 10μs。主循环里每 50ms 清一次定时器 2 的计数器。如果程序卡死定时器 2 溢出系统复位。// 定时器2看门狗初始化100ms溢出 void wdt_init() { RCAP2H 0x3C; // 11.0592MHz 下 100ms 初值 RCAP2L 0xB0; TH2 0x3C; TL2 0xB0; ET2 1; // 使能定时器2中断 EA 1; TR2 1; // 启动定时器2 } // 定时器2中断溢出说明程序跑飞复位 void timer2_isr() interrupt 5 { TF2 0; // 清除溢出标志 // 拉低复位引脚需要硬件配合用一个IO口控制RST RST_PIN 0; // 等待复位 while(1); } // 主循环里喂狗 void wdt_feed() { TR2 0; // 停止定时器 TH2 0x3C; // 重装初值 TL2 0xB0; TR2 1; // 重新启动 }这个看门狗需要硬件上把 RST 引脚通过一个二极管或三极管接到某个 IO 口不能直接接因为 80C51 的 RST 是高电平复位而 IO 口拉低是复位不了的。正确做法是用一个 NPN 三极管基极接 IO 口集电极接 RST发射极接地。IO 口输出高电平时三极管导通RST 被拉低但 80C51 的 RST 需要高电平复位所以这个电路反了。应该用 PNP 三极管发射极接 VCC基极接 IO 口集电极接 RSTIO 口输出低电平时三极管导通RST 得到高电平。这个细节容易翻车画板子前先用万用表测一下。最后说一个我自己的习惯每次改完代码先不接传感器用信号发生器给 ADC 输入一个已知电压看显示值对不对。确认信号链没问题了再接传感器标定。这样能把硬件问题和软件问题分开省得来回猜。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表