You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PIC18F452的ADC可调PWM异常问题求助

电位器控制PWM输出故障排查与修复

故障现象

  • 电位器输入0–3V时,PWM输出正常变化;但输入超过3.15V时,示波器输出变为平线,CCP1引脚的LED也无变化。
  • 启动时输入3.15V及以上电压,ADC无法正常工作,但PORTB和PORTD(用于查看10位ADC输出)能显示正确值,此时调节电位器无响应。
  • Proteus的DEBUG窗口显示0-5V输入对应的ADC值(CCPR1L范围0-255)正常,但CCP1引脚的LED不工作。

原代码

int rd_adc()
{
    int x;
    int y;
    TRISB = 0;
    TRISD = 0;
    //trise.b1 = 1;
    ADCON0 = 0x81;   // Fosc/64, Channel 0, A/D is on
    ADCON1 = 0xCE;   // Right justified, Fosc/64, AN0 =Analog

    ADCON0.GO = 1;  //Start Converting
    while (ADCON0.GO == 1)  //wait for completion of conversion
        ;
    PORTB = ADRESH;  //Display low byte on PORT C
    PORTD = ADRESL;  //Display high byte on PORT D
    x = ADRESH;
    x = (ADRESH << 8) | (ADRESL);

    y = x * 0.2492; //for 0.00488x51(for making 10 bit x value within range of 0-255
    //x = x * 51;
    return y;
}

void main()
{
    TRISA.F0 = 1;
    TRISC.F2 = 0;
    PR2 = 156;
    CCP1CON = 0x0C;
    T2CON = 0x02;
    TMR2 = 0;            //clr timer reg
    T2CON.TMR2ON = 1;
    while(1)
    {
        CCPR1L = rd_adc();
        PIR1.TMR2IF = 0;         //clr timer flag
        while (PIR1.TMR2IF == 0)        // wait for end of period
            ;
    }
}

问题分析

  1. ADC重复初始化:rd_adc函数每次调用都重新配置ADCON0和ADCON1,导致ADC在运行中频繁重置,高电压启动时容易触发ADC工作异常,出现调节无响应的情况。
  2. PWM占空比处理不完整:PIC的CCP1 PWM是10位精度,由CCPR1L(高8位)和CCP1CON的CCP1X/CCP1Y位(低2位)共同决定占空比。原代码只设置CCPR1L,丢失低2位信息,当ADC值超过645(对应3.15V)时,计算后的CCPR1L值会导致占空比逻辑错误,出现平线输出。
  3. 浮点数计算精度损失:用y = x * 0.2492进行缩放,浮点数运算会带来截断误差,导致占空比映射不准确。
  4. 端口配置重复设置:rd_adc中每次设置TRISB=0和TRISD=0属于冗余操作,且可能干扰端口状态。

修复后的代码

#include <pic16f877a.h> // 根据实际使用的MCU型号调整

int rd_adc()
{
    int adc_val;
    ADCON0.GO = 1;          // 启动ADC转换
    while (ADCON0.GO);      // 等待转换完成
    PORTB = ADRESH;         // 输出ADC高字节到PORTB
    PORTD = ADRESL;         // 输出ADC低字节到PORTD
    adc_val = (ADRESH << 8) | ADRESL; // 组合10位ADC值
    return adc_val;
}

void main()
{
    // 端口方向初始化
    TRISA.F0 = 1;    // AN0设为输入(电位器连接端)
    TRISB = 0;       // PORTB设为输出(显示ADC高字节)
    TRISD = 0;       // PORTD设为输出(显示ADC低字节)
    TRISC.F2 = 0;    // CCP1引脚设为输出(PWM输出端)

    // ADC初始化(仅执行一次)
    ADCON0 = 0x81;   // Fosc/64分频,选择AN0通道,使能ADC
    ADCON1 = 0xCE;   // 结果右对齐,仅AN0为模拟输入,其余为数字IO

    // PWM初始化
    PR2 = 156;       // 设置PWM周期((156+1)*4*Tosc*4 ≈10ms,基于10MHz晶振)
    CCP1CON = 0x0C;  // 配置CCP1为PWM模式
    T2CON = 0x02;    // TMR2预分频4
    TMR2 = 0;        // 重置TMR2计数器
    T2CON.TMR2ON = 1;// 启动TMR2

    int adc_value;
    while(1)
    {
        adc_value = rd_adc();
        // 将10位ADC值映射到10位PWM占空比
        CCPR1L = adc_value >> 2;  // 取ADC值高8位给CCPR1L
        // 更新CCP1CON的低2位(CCP1X/CCP1Y)
        CCP1CON = (CCP1CON & 0xF0) | ((adc_value & 0x03) << 4);
        // 等待TMR2溢出,确保占空比更新同步到PWM周期
        PIR1.TMR2IF = 0;
        while(!PIR1.TMR2IF);
    }
}

修复说明

  • ADC初始化移至main:避免每次转换重置ADC,解决高电压启动时的无响应问题。
  • 完整处理10位PWM:同时设置CCPR1L和CCP1CON的低2位,确保占空比从0到100%完整映射,解决高电压输入时的平线问题。
  • 整数运算替代浮点数:直接通过位操作完成10位到10位的映射,消除精度损失。
  • 冗余代码移除:去掉rd_adc中重复的端口方向设置,优化代码逻辑。

Proteus仿真额外检查点

  • 确认MCU型号与代码匹配(如PIC16F877A),ADCON1配置与模拟通道对应。
  • CCP1引脚需串联220Ω电阻后连接LED,根据引脚电平极性选择LED连接方式(共阳/共阴)。
  • 当ADC输入满量程5V时,PWM输出为100%占空比,LED应常亮而非无反应。

内容的提问来源于stack exchange,提问作者Zerox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:15:21