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

PIC18F46K22中CCP1与CCP2同时生成PWM时频率异常问题问询

解决双CCP模块SPWM频率异常及相位差实现问题

嘿,我看你这问题是双CCP模块生成同步SPWM时遇到了频率异常的坑,这在PIC单片机里挺常见的,我来帮你捋捋原因和解决办法:

问题根源分析

单独用CCP1正常,同时开CCP2就频率跑偏,核心原因大概率是两个CCP模块依赖的TMR2时基配置有冲突,或者占空比更新不同步——毕竟PWM模式下两个CCP都是靠TMR2提供计数基准的,只要其中一个模块的配置干扰了TMR2的运行,或者两路占空比更新时机不一致,就会导致整个时基紊乱。

分步解决办法

1. 确认TMR2的核心配置完全正确

首先得确保TMR2的参数和模式完全匹配10kHz的开关频率:

  • 假设你用的是PIC系列单片机(从CCP、PR2这些寄存器判断),以8MHz晶振为例,指令周期Tcy=0.5us,PWM周期公式是PWM周期 = (PR2+1) × 预分频 × Tcy。要得到10kHz(周期100us),计算下来(199+1)×1×0.5us=100us,刚好匹配,所以:
    • 必须把T2CON寄存器的预分频位T2CKPS<1:0>设为00(预分频1)
    • 必须开启TMR2(TMR2ON=1)
    • 代码里要明确写死T2CON的配置,比如:
      T2CON = 0x04; // 对应PIC16F系列:TMR2ON=1,预分频1,后分频1
      

2. 强制两路CCP的占空比同步更新

单独更新某一路的CCPRx寄存器时,可能会在TMR2计数过程中触发异常更新,干扰时基。最好的办法是在TMR2溢出中断里同步更新两路的占空比:

  • 先开启TMR2中断:
    INTCONbits.PEIE = 1;  // 开启外设中断
    PIE1bits.TMR2IE = 1;  // 开启TMR2中断
    INTCONbits.GIE = 1;   // 开启全局中断
    
  • 然后在中断服务函数里同时更新CCP1和CCP2的占空比,这样每一个PWM周期只会更新一次,完全同步,不会干扰TMR2的计数。

3. 正确实现90°相位差的正弦/余弦SPWM

要得到相位差90°的两路波形,本质是把正弦表和余弦表错开1/4基波周期:

  • 你的基波是50Hz(周期20ms),开关频率10kHz(每个周期100us),所以一个基波周期对应200个PWM采样点(20ms/100us=200)。
  • 所以正弦表的第n个点,对应余弦表的第(n+50)%200个点(200/4=50),这样就能精准实现90°相位差。

4. 检查CCP模块的模式和引脚配置

  • 确保两个CCP模块都被设置为PWM模式:CCP1CON = 0x0C;、CCP2CON = 0x0C;(CCP模式位CCP<3:0>设为1100)
  • 确认对应引脚的TRIS位设为输出(比如CCP1一般是RC2,CCP2是RC1,所以TRISCbits.TRISC2=0; TRISCbits.TRISC1=0;),你代码里的TRISD=0; TRISC=0虽然覆盖了,但最好明确指定对应引脚,避免后续改代码出问题。

修正后的完整代码示例

#include <pic16f877a.h> // 根据你的单片机型号调整头文件

#define SINE_TABLE_LEN 200
// 预先生成的10位分辨率正弦表(范围0~1023,对应0~100%占空比)
unsigned int sine_table[SINE_TABLE_LEN] = {
    512, 537, 562, 587, 612, 636, 660, 683, // 省略其余采样点,需自行补全
    ...
};

unsigned char pwm_cnt = 0;

// 中断服务函数
void interrupt isr_handler(void) {
    if(PIR1bits.TMR2IF) {
        // 更新CCP1的正弦波占空比
        CCPR1L = sine_table[pwm_cnt] >> 2;          // 高8位
        CCP1CONbits.DC1B = sine_table[pwm_cnt] & 0x03; // 低2位
        
        // 更新CCP2的余弦波占空比(相位差90°)
        unsigned char cos_idx = (pwm_cnt + 50) % SINE_TABLE_LEN;
        CCPR2L = sine_table[cos_idx] >> 2;
        CCP2CONbits.DC2B = sine_table[cos_idx] & 0x03;
        
        // 更新采样索引
        pwm_cnt++;
        if(pwm_cnt >= SINE_TABLE_LEN) pwm_cnt = 0;
        
        PIR1bits.TMR2IF = 0; // 清除中断标志
    }
}

void main(void) {
    // 初始化IO和关闭ADC
    ANSELD = 0x00;
    ANSELC = 0x00;
    PORTD = 0x00;
    TRISD = 0x00;
    PORTC = 0x00;
    TRISCbits.TRISC2 = 0; // CCP1引脚设为输出
    TRISCbits.TRISC1 = 0; // CCP2引脚设为输出
    
    // 初始化TMR2(10kHz PWM频率)
    TMR2 = 0x00;
    PR2 = 199;
    T2CON = 0x04; // TMR2开启,预分频1,后分频1
    
    // 初始化CCP1和CCP2为PWM模式
    CCP1CON = 0x0C;
    CCP2CON = 0x0C;
    
    // 初始化中断
    PIR1bits.TMR2IF = 0;
    PIE1bits.TMR2IE = 1;
    INTCONbits.PEIE = 1;
    INTCONbits.GIE = 1;
    
    // 主循环空转,所有逻辑在中断里处理
    while(1);
}

额外注意事项

  • 正弦表的分辨率要和PWM的分辨率匹配:比如10位PWM的话,数值范围是0~1023,这样占空比才能线性变化。
  • 要是还是有问题,检查T2CON里的后分频位T2OUTPS<3:0>,设为0000(后分频1),这样每个PWM周期触发一次中断,刚好对应一次占空比更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:51:22