找回密码
 立即注册
查看: 66|回复: 14

请问这样设置PWM为什么上电会自己亮?

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 3 天前 | 显示全部楼层 |阅读模式
void UpdatePcaPwm(u8 PCA_id, u16 pwm_value)
{
        if(PCA_id == PCA0)
        {
                PCA_PWM0 = (PCA_PWM0 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
                CCAP0H = (u8)pwm_value;
        }
        else if(PCA_id == PCA1)
        {
                PCA_PWM1 = (PCA_PWM1 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
                CCAP1H = (u8)pwm_value;
        }
        else if(PCA_id == PCA2)
        {
                PCA_PWM2 = (PCA_PWM2 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
                CCAP2H = (u8)pwm_value;
        }
}
void main()
{
P3M0 |= 0x08; P3M1 &= ~0x08; //P3.3推挽模式
        if(LorH)
                P3M0 |= 0x04; P3M1 &= ~0x04; //P3.2推挽模式


      
  P_SW1 = 0x00; //ECI/P1.2, CCP0/P1.1, CCP1/P1.0, CCP2/P3.7
        //P_SW1 = 0x10; //ECI_2/P3.4, CCP0_2/P3.5, CCP1_2/P3.6, CCP2_2/P3.7
        //P_SW1 = 0x20; //ECI_3/P2.4, CCP0_3/P2.5, CCP1_3/P2.6, CCP2_3/P2.7
      
        CCON = 0x00;
        //CMOD = 0x04; //PCA 时钟为定时器0的溢出脉冲
        CMOD = 0x08; //PCA 时钟为系统时钟
        CL = 0x00;
        CH = 0x00;


        //-- 10 位 PWM--      
        if(LorH)
        {
                CCAPM0 = 0x02;          //PCA 模块 0 为 PWM 工作模式
                PCA_PWM0 = 0xc0; //PCA 模块 0 输出 10 位 PWM
                CCAP0L = 0xff;
                CCAP0H = 0x03;
        }
      
        CCAPM1 = 0x02;          //PCA 模块 1 为 PWM 工作模式
        PCA_PWM1 = 0xc0; //PCA 模块 1 输出 10 位 PWM
        CCAP1L = 0xff;
        CCAP1H = 0x03;
        CR = 1; //启动 PCA 计时器


        UpdatePcaPwm(PCA1,1023);
        if(LorH)
                UpdatePcaPwm(PCA0,1023);
while (1);

}
为什么初始化后灯是常亮的,UpdatePcaPwm(PCA1,1023);调用后灯才熄灭,导致上电后灯会闪一下才熄灭,如果把UpdatePcaPwm函数屏蔽,上电就会常亮,使用的IC是STC8G1K08A
回复

使用道具 举报 送花

3

主题

1986

回帖

820

积分

等待验证会员

积分
820
发表于 3 天前 | 显示全部楼层
在嵌入式系统中,使用PWM(脉宽调制)信号控制LED、电机或其他负载时,出现“上电即亮”的现象通常是由于初始化顺序不当或寄存器配置错误所导致的。根据你提供的代码片段,我们可以从以下几个方面分析问题的可能原因,并提出优化建议。

一、问题现象分析

“上电即亮”意味着在系统刚上电时,PWM输出已经处于高电平状态,导致LED或其他负载被驱动。这通常不是我们期望的行为,尤其是在需要精确控制启动状态的场合。

二、代码分析与潜在问题

1. PWM配置函数 UpdatePcaPwm
  1. c
  2. void UpdatePcaPwm(u8 PCAid, u16 pwmvalue)
  3. {
  4.     if(PCAid == PCA0)
  5.     {
  6.         PCAPWM0 = (PCAPWM0 & ~0x32) | (u8)((pwmvalue & 0x0300) >> 4) | (u8)((pwmvalue & 0x0400) >> 9);
  7.         CCAP0H = (u8)pwmvalue;
  8.     }
  9.     ...
  10. }
复制代码

这段代码用于设置PCA模块的PWM参数,涉及对 PCAPWMx 和 CCAPxH 寄存器的操作。

问题点分析:
寄存器位操作不清晰:PCAPWM0 &= ~0x32 这种操作可能影响了PWM输出的极性或使能位。例如,某些位可能控制PWM输出是否为反相(即高有效或低有效),若未正确设置,可能导致默认输出为高电平。
未初始化PCA模块:在调用 UpdatePcaPwm 之前,是否已对PCA模块进行全局初始化?例如,是否开启了PCA时钟?是否设置了PWM模式?
CCAPxH 仅设置高字节:pwmvalue 是16位值,但仅将低8位写入 CCAPxH,而高8位通过位操作写入 PCAPWMx。这种处理方式是否正确?是否有可能导致初始占空比异常?

2. 主函数部分代码
  1. c
  2. void main()
  3. {
  4.     P3M0 |= 0x08; P3M1 &= ~0x08; //P33推挽模式
  5.     if(LorH)
  6.         P3M0 |= ...
复制代码

这部分代码仅设置了P33为推挽输出模式,但并未对PCA模块进行任何初始化操作,也没有调用 UpdatePcaPwm 设置初始PWM值。因此,在未设置PWM值之前,硬件寄存器可能处于默认状态,导致输出高电平。

潜在问题:
未关闭PWM输出使能:某些MCU在复位后PWM输出默认是使能的,若未明确关闭,可能导致输出立即生效。
未设置初始占空比为0:在初始化时应将PWM占空比设为0%,以确保输出低电平。

三、解决方案与建议

1. 明确初始化PCA模块

确保在调用 UpdatePcaPwm 前完成以下初始化:
设置PCA模块为PWM模式。
关闭PWM输出使能,待配置完成后开启。
设置PCA时钟源和分频系数。
初始化所有相关寄存器为安全状态。

示例初始化代码(假设使用STC系列MCU):
  1. c
  2. void InitPCA(void)
  3. {
  4.     CCON = 0x00;        // 清除PCA控制寄存器
  5.     CMOD = 0x02;        // 设置PCA时钟源为系统时钟/32,无中断
  6.     CL = 0x00;          // 清空PCA低字节计数器
  7.     CH = 0x00;          // 清空PCA高字节计数器
  8.     CCAPM0 = 0x42;      // 设置PCA0为PWM模式,无中断
  9.     CR = 1;             // 启动PCA计数器
  10. }
复制代码

2. 初始化PWM占空比为0%

在系统启动时,先将占空比设为0%,防止输出高电平。
  1. c
  2. void main()
  3. {
  4.     // 设置IO模式
  5.     P3M0 |= 0x08; P3M1 &= ~0x08; // P33推挽输出
  6.     // 初始化PCA模块
  7.     InitPCA();
  8.     // 设置初始PWM占空比为0%
  9.     UpdatePcaPwm(PCA0, 0);
  10.     while(1)
  11.     {
  12.         // 主循环
  13.     }
  14. }
复制代码

3. 校验PWM设置函数的位操作逻辑

确认 PCAPWMx 寄存器的位操作是否符合芯片手册要求。例如,某些位用于设置PWM的周期长度或占空比高位,若设置错误可能导致初始值异常。

建议查阅具体MCU的数据手册,确认:
PCAPWMx 寄存器中哪些位用于设置PWM的周期/占空比高位。
是否需要设置极性控制位(决定PWM是高有效还是低有效)。

例如,若某位为“PWM输出极性控制位”,默认为高有效,则PWM输出为高电平,此时即使占空比为0%,也可能导致输出为高。

四、总结

“上电即亮”的根本原因在于:
PWM模块未正确初始化;
寄存器配置未设置初始占空比为0%;
未关闭PWM输出使能或极性设置错误。

为避免该问题,建议:

1. 在主函数中优先初始化PCA模块;
2. 设置PWM初始占空比为0%;
3. 检查寄存器配置是否符合数据手册要求;
4. 确保IO口配置与PWM输出功能一致。

通过上述优化,可以有效避免系统上电时PWM输出异常导致的“上电即亮”问题,提高系统启动的稳定性和可控性。

[本文内容由国芯人工智能辅助生成,仅供参考]
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:32
  • 最近打卡:2025-09-12 11:16:00
已绑定手机

11

主题

67

回帖

697

积分

版主

积分
697
发表于 3 天前 | 显示全部楼层
已转发技术,稍后回复
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:567
  • 最近打卡:2025-09-13 12:30:47
已绑定手机
已实名认证

116

主题

2803

回帖

7082

积分

版主

积分
7082
发表于 3 天前 | 显示全部楼层
可以将启动PCA计时器放在Update函数里面,初始化的时候不启动
这样只有在更新了占空比后才会打开输出,就不会出现闪一下的问题了。
然后是主函数最开始的部分,应该在设置I/O模式前,手动将端口的电平给一个确定值,例如0是熄灭就给0。否则端口上电默认电平为1,如果是高电平亮,则在PCA未打开前就亮了。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:558
  • 最近打卡:2025-09-14 14:21:57
已绑定手机

86

主题

6117

回帖

1万

积分

超级版主

积分
11312
发表于 3 天前 | 显示全部楼层
上电高阻,初始化先设置IO电平(禁用状态),再设置IO模式(如推挽),顺序不能错,然后初始化PWM
如果上电到运行用户初始化程序阶段状态也不正常,外接上拉/下拉电阻,拉到禁用的状态
PCA极值波形不是直线,需要设置EPCxH,以3路10位PWM为例:
  1. void PWM_Out(unsigned int pwm_0,pwm_1,pwm_2)
  2. {
  3.         unsigned char temp_l,temp_h,temp;
  4.        
  5.         temp_l=pwm_0&0x00ff;                //读pwm_0低8位赋值给temp_l
  6.         temp_h=(pwm_0&0x0300)>>4;        //读pwm_0高2位,移动到XCCAP0H(PCA_PWM0[5:4])位置,赋值给temp_h
  7.         temp=PCA_PWM0&0xcd;                        //读PCA_PWM0,XCCAP2H(PCA_PWM0[5:4])、EPC0H(PCA_PWM0[1])清零后赋值给temp
  8.         temp_h|=temp;                                //写其他位到temph
  9.         if(pwm_0==0x03ff)                        //如果pwm_0最大
  10.                 temp_h|=0x02;                        //设置EPC0H(PCA_PWM0[1])为1
  11.         PCA_PWM0=temp_h;                        //写PCA_PWM0(先写高2位)
  12.         CCAP0H=temp_l;                                //写CCAP0H(再写低8位)
  13.        
  14.         temp_l=pwm_1&0x00ff;                //读pwm_1低8位赋值给temp_l
  15.         temp_h=(pwm_1&0x0300)>>4;        //读pwm_1高2位,移动到XCCAP1H(PCA_PWM1[5:4])位置,赋值给temp_h
  16.         temp=PCA_PWM1&0xcd;                        //读PCA_PWM1,XCCAP1H(PCA_PWM2[5:4])、EPC1H(PCA_PWM1[1])清零后赋值给temp
  17.         temp_h|=temp;                                //写其他位到temph
  18.         if(pwm_1==0x03ff)                        //如果pwm_1最大
  19.                 temp_h|=0x02;                        //设置EPC1H(PCA_PWM1[1])为1
  20.         PCA_PWM1=temp_h;                        //写PCA_PWM1(先写高2位)
  21.         CCAP1H=temp_l;                                //写CCAP1H(再写低8位)
  22.        
  23.         temp_l=pwm_2&0x00ff;                //读pwm_2低8位赋值给temp_l
  24.         temp_h=(pwm_2&0x0300)>>4;        //读pwm_2高2位,移动到XCCAP2H(PCA_PWM2[5:4])位置,赋值给temp_h
  25.         temp=PCA_PWM2&0xcd;                        //读PCA_PWM2,XCCAP2H(PCA_PWM2[5:4])、EPC2H(PCA_PWM2[1])清零后赋值给temp
  26.         temp_h|=temp;                                //写其他位到temph
  27.         if(pwm_2==0x03ff)                        //如果pwm_2最大
  28.                 temp_h|=0x02;                        //设置EPC2H(PCA_PWM2[1])为1
  29.         PCA_PWM2=temp_h;                        //写PCA_PWM2(先写高2位)
  30.         CCAP2H=temp_l;                                //写CCAP2H(再写低8位)
  31. }
复制代码
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 3 天前 | 显示全部楼层
王*** 发表于 2025-9-11 15:43
可以将启动PCA计时器放在Update函数里面,初始化的时候不启动
这样只有在更新了占空比后才会打开输出,就不 ...

加了了P3 &= ~0x08; P3 &= ~0x04;,把启动PCA计时器放在Update函数后面,还是会亮,然后运行了UpdatePcaPwm(PCA1,1023);才灭
void UpdatePcaPwm(u8 PCA_id, u16 pwm_value)
{
        if(PCA_id == PCA0)
        {
                PCA_PWM0 = (PCA_PWM0 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
                CCAP0H = (u8)pwm_value;
        }
        else if(PCA_id == PCA1)
        {
                PCA_PWM1 = (PCA_PWM1 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
                CCAP1H = (u8)pwm_value;
        }
        else if(PCA_id == PCA2)
        {
                PCA_PWM2 = (PCA_PWM2 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
                CCAP2H = (u8)pwm_value;
        }
}

void main()
{
        P3 &= ~0x08;

        P3 &= ~0x04;
       

  P3M0 |= 0x08; P3M1 &= ~0x08; //P3.3推挽模式
        if(LorH)
                P3M0 |= 0x04; P3M1 &= ~0x04; //P3.2推挽模式

       
  P_SW1 = 0x00; //ECI/P1.2, CCP0/P1.1, CCP1/P1.0, CCP2/P3.7
        //P_SW1 = 0x10; //ECI_2/P3.4, CCP0_2/P3.5, CCP1_2/P3.6, CCP2_2/P3.7
        //P_SW1 = 0x20; //ECI_3/P2.4, CCP0_3/P2.5, CCP1_3/P2.6, CCP2_3/P2.7
       
        CCON = 0x00;
        //CMOD = 0x04; //PCA 时钟为定时器0的溢出脉冲
        CMOD = 0x08; //PCA 时钟为系统时钟
        CL = 0x00;
        CH = 0x00;

        //-- 10 位 PWM--       
        if(LorH)
        {
                CCAPM0 = 0x02;          //PCA 模块 0 为 PWM 工作模式
                PCA_PWM0 = 0xc0; //PCA 模块 0 输出 10 位 PWM
                CCAP0L = 0;
                CCAP0H = 0;
        }
       
        CCAPM1 = 0x02;          //PCA 模块 1 为 PWM 工作模式
        PCA_PWM1 = 0xc0; //PCA 模块 1 输出 10 位 PWM
        CCAP1L = 0;
        CCAP1H = 0;
        //CR = 1; //启动 PCA 计时器

        P5M0 &= ~0x10; P5M1 &= ~0x10;        //使能P5.4输入脚
        if(LorH)
                P3M0 &= ~0x01; P3M1 &= ~0x01; //使能P3.0输入脚

        UpdatePcaPwm(PCA1,1023);
        UpdatePcaPwm(PCA0,1023);
CR = 1; //启动 PCA 计时器
while (1);
}
       

回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 3 天前 | 显示全部楼层
简*** 发表于 2025-9-11 19:32
加了了P3 &= ~0x08; P3 &= ~0x04;,把启动PCA计时器放在Update函数后面,还是会亮,然后运行了UpdatePcaP ...

导致了上电后LED要达到最亮后再降到eeprom记忆的亮度

main.c

14.45 KB, 下载次数: 0

点评

上电设置为高阻后不要动,等待一次Update后再打开对应端口的输出 你这个程序中,上电设置了一次推挽,给高阻设置覆盖掉了。  详情 回复 发表于 3 天前
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:567
  • 最近打卡:2025-09-13 12:30:47
已绑定手机
已实名认证

116

主题

2803

回帖

7082

积分

版主

积分
7082
发表于 3 天前 | 显示全部楼层
简*** 发表于 2025-9-11 20:16
导致了上电后LED要达到最亮后再降到eeprom记忆的亮度

上电设置为高阻后不要动,等待一次Update后再打开对应端口的输出
你这个程序中,上电设置了一次推挽,给高阻设置覆盖掉了。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2025-09-12 08:47:51
已绑定手机

6

主题

21

回帖

102

积分

注册会员

积分
102
发表于 前天 08:47 | 显示全部楼层
王*** 发表于 2025-9-11 20:20
上电设置为高阻后不要动,等待一次Update后再打开对应端口的输出
你这个程序中,上电设置了一次推挽,给 ...

上电设置为高阻,Update后再打开对应端口的设置推挽输出,但是上电的时候LED还是会达到最亮后再降到eeprom记忆的亮度
        P3M0 &= ~0x08; P3M1 |= 0x08;
        P3M0 &= ~0x04; P3M1 &= ~0x04;

  P_SW1 = 0x00; //ECI/P1.2, CCP0/P1.1, CCP1/P1.0, CCP2/P3.7
        //P_SW1 = 0x10; //ECI_2/P3.4, CCP0_2/P3.5, CCP1_2/P3.6, CCP2_2/P3.7
        //P_SW1 = 0x20; //ECI_3/P2.4, CCP0_3/P2.5, CCP1_3/P2.6, CCP2_3/P2.7
       
        CCON = 0x00;
        //CMOD = 0x04; //PCA 时钟为定时器0的溢出脉冲
        CMOD = 0x08; //PCA 时钟为系统时钟
        CL = 0x00;
        CH = 0x00;

        //-- 10 位 PWM--       
        if(LorH)
        {
                CCAPM0 = 0x02;          //PCA 模块 0 为 PWM 工作模式
                PCA_PWM0 = 0xc0; //PCA 模块 0 输出 10 位 PWM
                CCAP0L = 0;
                CCAP0H = 0;
        }
       
        CCAPM1 = 0x02;          //PCA 模块 1 为 PWM 工作模式
        PCA_PWM1 = 0xc0; //PCA 模块 1 输出 10 位 PWM
        CCAP1L = 0;
        CCAP1H = 0;
        //CR = 1; //启动 PCA 计时器

        P5M0 &= ~0x10; P5M1 &= ~0x10;        //使能P5.4输入脚
        if(LorH)
                P3M0 &= ~0x01; P3M1 &= ~0x01; //使能P3.0输入脚

        UpdatePcaPwm(PCA1,1023);
        if(LorH)
                UpdatePcaPwm(PCA0,1023);
       
         P3M0 |= 0x08; P3M1 &= ~0x08; //P3.3推挽模式
        if(LorH)
                P3M0 |= 0x04; P3M1 &= ~0x04; //P3.2推挽模式
       
        CR = 1; //启动 PCA 计时器
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:513
  • 最近打卡:2025-09-12 09:04:18
已绑定手机

45

主题

2225

回帖

7806

积分

论坛元老

积分
7806
发表于 前天 10:11 | 显示全部楼层
PCA_PWMx 与 CCAPxH 跟PWM占空比有关,初始化时不要重复设置
  1.         CCON = 0x00;
  2. //        CMOD = 0x04; //PCA 时钟为定时器0的溢出脉冲
  3.         CMOD = 0x08; //PCA 时钟为系统时钟
  4.         CL = 0x00;
  5.         CH = 0x00;
  6.         //-- 10 位 PWM--
  7.         CCAPM0 = 0x42; //PCA 模块 0 为 PWM 工作模式
  8.         PCA_PWM0 = 0xc0; //PCA 模块 0 输出 10 位 PWM
  9.         //-- 10 位 PWM--
  10.         CCAPM1 = 0x42; //PCA 模块 1 为 PWM 工作模式
  11.         PCA_PWM1 = 0xc0; //PCA 模块 1 输出 10 位 PWM
  12.         UpdatePcaPwm(PCA0,duty);
  13.         UpdatePcaPwm(PCA1,duty);
  14.        
  15.         CR = 1; //启动 PCA 计时器
复制代码
回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-9-14 20:17 , Processed in 0.123801 second(s), 104 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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