迪文科技论坛

 找回密码
 立即注册
搜索
查看: 44|回复: 0

【分享】+张正炀+基于STC89C52的多量程电阻测量系统设计

[复制链接]

1

主题

0

回帖

20

积分

新手上路

Rank: 1

积分
20
发表于 2026-1-20 12:10:00 | 显示全部楼层 |阅读模式
一、设计简介
1.1 项目背景与目标在电子实验、设备维修及教学实践中,电阻测量是一项基础且频繁的操作。传统万用表在超高阻值测量时精度下降,且无法实现自动化数据记录。本项目旨在开发一款基于嵌入式系统的电阻测量仪,具备宽量程、自动切换、直观显示和低成本的特点,作为传统仪表的补充或嵌入式测控模块。本项目设计并实现了一种高性价比、宽量程的智能电阻测量系统。系统以STC89C52RC单片机为核心,复用XPT2046ADC芯片实现模数转换,采用CD4051BE模拟开关切换参考电阻网络,从而将测量量程扩展至10kΩ20MΩ。测量结果通过迪文串口屏实时显示,系统具备自动量程切换功能。设计报告详细阐述了方案选择、硬件电路设计、软件算法开发、系统调试与校准的全过程。最终测试表明,系统在各量程内测量误差可控制在±5%以内,运行稳定,达到了设计要求。
1.2 系统功能概述第一,宽量程测量:覆盖1kΩ20MΩ范围,分为三个子量程。
第二,自动量程切换:根据被测电阻大小,自动选择最佳测量档位。
第三,实时显示:通过彩色串口屏直观显示电阻值(两位整数、两位小数)及当前量程。
第四,基础通信:预留串口调试接口,可输出原始ADC数据,便于校准与诊断。
1.3 设计指标
量程范围:1kΩ ~ 20MΩ (10kΩ100kΩ1MΩ三档)
显示分辨率:0.01kΩ (10Ω)
测量精度:≤ ±5% (在标准电源与校准后)
显示界面:迪文屏,显示电阻值与量程状态。
供电电压:DC 5V (±5%)
核心控制器:STC89C52RC




二、方案选择
2.1 核心控制器选择
选用STC89C52RC单片机。理由:其资源丰富(8KB Flash, 512B RAM, 32个I/O口),完全满足本项目对SPI模拟、UART通信和逻辑控制的需求;且价格低廉,开发资料齐全,易于快速实现。
2.2 ADC转换方案选择
选用XPT2046芯片。理由:作为四线电阻触摸屏控制器,其内部集成了一个12位、125kHz采样率的SAR ADC。通过将其模拟输入通道复用为电阻分压测量点,可以极低成本获得精密ADC功能,无需额外采购专用ADC芯片,大大简化了硬件设计
2.3 量程扩展方案选择
选用CD4051BE 8通道模拟开关。理由:与被考虑的继电器方案相比,模拟开关具有无机械触点、寿命长、切换速度快、体积小、驱动简单的优点。CD4051BE的导通电阻(约100Ω)在测量较大电阻时引入的误差百分比很小,在可接受范围内。通过其切换不同的参考电阻(1MΩ, 100kΩ, 10kΩ),即可实现量程扩展。
2.4 人机交互方案选择
选用迪文DGUS串口屏。理由:与传统LCD屏+按键方案相比,串口屏将显示驱动和图形处理封装在屏内,单片机只需通过串口发送简单的指令和数据即可控制显示,极大地减轻了单片机的负担,简化了软件设计,并能实现更美观的界面。
2.5 最终系统流程
系统工作流程如下:待测电阻接入由CD4051公共输出端和参考电阻构成的分压电路。单片机通过P2.0-P2.2控制CD4051,选择接入1MΩ、100kΩ或10kΩ参考电阻。分压点电压送入XPT2046的IN3通道。单片机通过P3.4-P3.7模拟SPI协议读取XPT2046转换的ADC值。单片机内部程序根据ADC值计算电阻,并通过P3.1(TXD)以115200波特率将结果和量程信息发送给迪文屏显示。


三.硬件部分
3.1 核心控制电路
采用典型的STC89C52最小系统:11.0592MHz晶振(为产生精确串口波特率)、上电复位电路、EA引脚接VCC。电源入口处并联100μF电解电容和0.1μF陶瓷电容进行退耦。
3.2 电阻测量与ADC电路
这是系统的核心模拟前端。
分压原理:基本测量原理为电阻分压法。待测电阻(Rx)与已知参考电阻(Rref)串联于VCC与GND之间,测量两者的连接点电压(Vmeasure)。公式为:V_measure = (Rx / (Rref + Rx)) × VCC。由此可反推:Rx = (V_measure × Rref) / (VCC - V_measure)。
XPT2046连接:其IN3通道(Pin 7)连接至分压测量点。VREF引脚(Pin 12)连接独立的5V参考源,决定ADC量程。单片机通过P3.4-P3.7引脚模拟SPI协议与其通信。
3.3 量程切换电路
这是实现多量程的关键。
CD4051BE配置:工作在单电源模式。VDD接+5V,VSS和VEE接GND,INH使能端接地。地址线A、B、C(Pins 11,10,9)由单片机P2.0、P2.1、P2.2控制。
参考电阻网络:三个精密金属膜电阻(1MΩ, 100kΩ, 10kΩ, 精度1%)一端共同接VCC,另一端分别接CD4051的通道输入端Ch2(Pin13)、Ch1(Pin14)、Ch0(Pin15)。
输出连接:CD4051的公共输出端COM(Pin 3)连接至测量点(即XPT2046的IN3和待测电阻Rx的一端)。
切换逻辑:目标量程1(10kΩ档)对应参考电阻10kΩ,CD4051控制CBA=000,单片机P2.2,P2.1,P2.0输出(0,0,0)。目标量程2(100kΩ档)对应参考电阻100kΩ,控制CBA=001,输出(0,0,1)。目标量程3(1MΩ档)对应参考电阻1MΩ,控制CBA=010,输出(0,1,0)。
3.4 人机交互电路
迪文屏的TX、RX分别接单片机的P3.0 (RXD)、P3.1 (TXD)。屏与单片机共地。单片机串口通过定时器2产生115200波特率与迪文屏通信。

四.软件设计
4.1 软件开发环境与整体流程
环境:Keil μVision5 C51编译器。
整体流程:系统上电初始化后,进入主循环。循环内依次执行:设置量程通道->读取ADC->判断并执行量程切换->计算电阻->发送数据至迪文屏。
4.2 底层驱动模块
第一,XPT2046_ReadADC():模拟SPI时序,向XPT2046发送控制字0xE4(选择IN3通道,单端输入,12位模式),并读取12位转换结果。关键修正:最初误用0x93(差分模式),导致通道选择错误。
第二,SetCD4051Channel():根据传入的通道号(0,1,2),设置P2.0-P2.2的电平,并延时约10ms等待模拟开关稳定
// 设置CD4051通道 (0, 1, 2 对应三个参考电阻)void SetCD4051Channel(BYTE channel) {
    CD4051_A = channel & 0x01;
    CD4051_B = (channel >> 1) & 0x01;
    CD4051_C = (channel >> 2) & 0x01;
    DelayMs(10); // 等待模拟开关稳定}
// ==================== XPT2046 读取函数 ====================
DWORD XPT2046_ReadADC(void) {
    BYTE i;
    DWORD adc_value = 0;
    BYTE command = 0xE4; // IN3通道,单端模式

    XPT2046_CS = 0;
    for(i=0; i<8; i++) {
        XPT2046_DIN = (command >> (7-i)) & 0x01;
        XPT2046_DCLK = 0; _nop_(); _nop_();
        XPT2046_DCLK = 1; _nop_(); _nop_();
    }
    XPT2046_DCLK = 0; _nop_(); _nop_();
    XPT2046_DCLK = 1; _nop_(); _nop_();
    XPT2046_DCLK = 0; _nop_(); _nop_();
    for(i=0; i<12; i++) {
        XPT2046_DCLK = 1; _nop_(); _nop_();
        XPT2046_DCLK = 0; _nop_(); _nop_();
        adc_value <<= 1;
        if(XPT2046_DOUT) adc_value |= 0x01;
    }
    for(i=0; i<4; i++) {
        XPT2046_DCLK = 1; _nop_(); _nop_();
        XPT2046_DCLK = 0; _nop_(); _nop_();
    }
    XPT2046_CS = 1;
    return adc_value;}

4.3 核心算法模块
第一,电阻计算函数 CalculateResistance():
voltage_mv = (adc_value * VREF_CAL) / ADC_MAX; // 计算测量点电压
if(voltage_mv >= VCC) return OVER_RANGE;
resistance_ohm = (ref_resistor * voltage_mv) / (VCC - voltage_mv);
return resistance_ohm / 1000.0;
float CalculateResistance(DWORD adc_value, DWORD ref_resistor) {
    float voltage_mv, resistance_ohm;

    if(adc_value == 0 || adc_value >= ADC_MAX) return 0.0;

    voltage_mv = (adc_value * VREF_CAL) / (float)ADC_MAX;
    if(voltage_mv >= VCC) return 99999.0; // 超量程

    resistance_ohm = (ref_resistor * voltage_mv) / (VCC - voltage_mv);
    return resistance_ohm / 1000.0; // 转换为
}

第二,自动量程切换逻辑 AutoRange():
升量程:若当前ADC值小于阈值ADC_TOO_SMALL(如100),说明分压过小,待测电阻过大,应切换至更大的参考电阻(即升量程)。
降量程:若ADC值大于阈值ADC_TOO_LARGE(如4000),说明分压过大,待测电阻过小,应切换至更小的参考电阻(即降量程)。
BYTE AutoRange(DWORD adc_value, BYTE cur_range) {
    if(adc_value < ADC_TOO_SMALL) {
        // ADC值太小,说明待测电阻太大,应切换到更大的参考电阻(升量程)
        if(cur_range < 2) return cur_range + 1; // 10k->100k, 100k->1M
    } else if(adc_value > ADC_TOO_LARGE) {
        // ADC值太大,说明待测电阻太小,应切换到更小的参考电阻(降量程)
        if(cur_range > 0) return cur_range - 1; // 1M->100k, 100k->10k
    }
    return cur_range; // 保持不变
}
防振荡:在主程序中,当量程切换后,立即continue跳回循环开始,重新采样,避免在临界点附近反复跳档。
4.4 系统校准模块
校准是保证精度的关键。系统采用两点校准法,核心是确定VREF_CAL(ADC实际参考电压)的值。
原理:在公式 Rx = (V_measure × Rref) / (VCC - V_measure) 中,V_measure = (ADC / 4095) × VREF_CAL。因此,VREF_CAL的准确性直接决定测量结果。
步骤:
a. 在10kΩ量程下,连接一个已知精密的电阻(如R_known = 5.1kΩ)。
b. 读取稳定的ADC值(如ADC_read = 2730)。
c. 根据公式反推或采用迭代逼近法计算VREF_CAL。
d. 将计算出的VREF_CAL(本项目最终值为2833)代入代码常数中。
校准常数:
#define VCC 5000 // 系统电源电压,单位mV
#define VREF_CAL 2833 // XPT2046的实际参考电压,通过校准得出,单位mV
4.5与迪文屏通信
首先设置迪文屏,在显示阻值的页面增添一个文本变量,变量地址设置为1000,使用4位长整数,2位整数,2位小数用于显示电阻数值,再添加一个图标变量,设置地址为1100用于显示电阻的单位,还需额外准备三张分别显示【1k】,【100k】,【1M】的图标并且设置为对应数据012。下面时对应的单片机代码:
void UART_SendByte_DWIN(BYTE dat) {
    SBUF = dat;
    while(!TI);
    TI = 0;}

// 发送电阻值到地址 0x1000void SendToDWIN(BYTE integer, BYTE decimal) {
    DWORD value = (DWORD)integer * 100 + decimal;
    UART_SendByte_DWIN(0x5A);
    UART_SendByte_DWIN(0xA5);
    UART_SendByte_DWIN(0x07);
    UART_SendByte_DWIN(0x82);
    UART_SendByte_DWIN(0x10); // 地址 0x1000 高字节
    UART_SendByte_DWIN(0x00); // 地址低字节
    UART_SendByte_DWIN(0x00);
    UART_SendByte_DWIN(0x00);
    UART_SendByte_DWIN((BYTE)(value >> 8));
    UART_SendByte_DWIN((BYTE)(value & 0xFF));}
// 发送量程信息到地址 0x1100 (用于迪文屏显示量程)void SendRangeToDWIN(BYTE range) {
    // 发送一个简单的数字代表量程: 1->10k, 2->100k, 3->1M
    // 迪文屏上对应设置文本显示或图标
    UART_SendByte_DWIN(0x5A);
    UART_SendByte_DWIN(0xA5);
    UART_SendByte_DWIN(0x05);
    UART_SendByte_DWIN(0x82);
    UART_SendByte_DWIN(0x11); // 地址 0x1100 高字节
    UART_SendByte_DWIN(0x00); // 地址低字节
    UART_SendByte_DWIN(0x00);
    UART_SendByte_DWIN(range);}
4.6 主程序结构
程序采用顺序执行与状态判断结合的方式,结构清晰。
主程序伪代码:
初始化(); // 包括IO、串口、变量
while(1) {
1. 设置CD4051通道 = current_range;
2. 延时等待稳定;
3. 读取ADC = XPT2046_ReadADC()多次平均;
4. 判断新量程 = AutoRange(ADC, current_range);
if(新量程 != 当前量程) {
更新当前量程和参考电阻;
切换CD4051通道;
更新迪文屏量程显示;
continue; // 重新开始循环,用新量程测量
}
5. 计算电阻值 = CalculateResistance(ADC, current_ref_r);
6. 格式转换(分离整数、小数部分);
7. 发送电阻值至迪文屏;
8. 延时;
}

void main(void) {
    DWORD adc_raw, adc_sum;
    float resistance_kohm;
    BYTE int_part, dec_part;
    BYTE i, new_range;
    DWORD ref_resistors[3] = {REF_R_10K, REF_R_100K, REF_R_1M};

    // 初始化
    DelayMs(1000);
    UART_Init();

    // 初始化CD4051 (默认选择10kΩ量程)
    CD4051_A = 0; CD4051_B = 0; CD4051_C = 0;
    current_range = 0;
    current_ref_r = ref_resistors[0];
    SetCD4051Channel(0);
    SendRangeToDWIN(1); // 通知迪文屏当前为量程1 (10k)

    // 初始化XPT2046引脚
    XPT2046_CS = 1; XPT2046_DCLK = 0;

    LED = 1; DelayMs(500); LED = 0; // 启动指示

    while(1) {
        // 1. 设置当前量程对应的参考电阻通道
        SetCD4051Channel(current_range);
        DelayMs(50); // 等待电路稳定

        // 2. 读取ADC (多次平均)
        adc_sum = 0;
        for(i=0; i<16; i++) {
            adc_sum += XPT2046_ReadADC();
            DelayMs(2);
        }
        adc_raw = adc_sum / 16;

        // 3. 自动量程判断与切换
        new_range = AutoRange(adc_raw, current_range);
        if(new_range != current_range) {
            // 量程需要切换
            current_range = new_range;
            current_ref_r = ref_resistors[current_range];
            SetCD4051Channel(current_range);
            SendRangeToDWIN(current_range + 1); // 更新迪文屏量程显示
            DelayMs(100); // 切换量程后等待更长时间稳定
            continue; // 跳过本次显示,重新读取新量程下的ADC
        }

        // 4. 计算电阻值
        resistance_kohm = CalculateResistance(adc_raw, current_ref_r);

        // 5. 处理显示
        if(resistance_kohm >= 10000.0) { // 超量程显示
            int_part = 99; dec_part = 99;
        } else if(resistance_kohm < 0.1) { // 太小或无效
            int_part = 0; dec_part = 0;
        } else {
            int_part = (BYTE)resistance_kohm;
            dec_part = (BYTE)((resistance_kohm - int_part) * 100 + 0.5); // 四舍五入
        }

        // 6. 发送到迪文屏
        LED = 1;
        SendToDWIN(int_part, dec_part);
        LED = 0;

        DelayMs(300); // 主循环延时
    }}



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|迪文科技论坛 ( 京ICP备05033781号-1 )

GMT+8, 2026-1-28 18:40 , Processed in 0.034538 second(s), 23 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表