找回密码
 立即注册
查看: 161|回复: 8

关于PWM关闭与打开问题

[复制链接]

48

主题

87

回帖

907

积分

高级会员

积分
907
发表于 2026-10-1 15:59:49 | 显示全部楼层 |阅读模式
前一段时间讨论过用PWMA_ENO寄存器打开关闭,当时梁老师与其他老师给我的解答是,一定要注意PWM 输出管脚初始化状态,当时我用按钮控制PWMA_ENO去打开关闭PWM,实验是成功,但现在用定时器或中断使用PWMA_ENO去打开关闭PWM,就没有pwm输出了,
          1、请各位老师看看什么原因,
          2、用PWMA_ENO寄存器打开关闭PWM,是不是不会改变pwm的任何参数、
          3、有没有其他关闭打开PWM更好的方法,(不改变pwm任何参数)

下面是我的pwm初始化和使用程序


#include "pwm.h"

u16 PWM1_Duty=0;
u16 PWM4_Duty=3000;
u16 PWM1_MAX=3110;

//========================================================================
// 函数: void Pwm_Init(void)
// 描述: pwm初始化.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void Pwm_Init(void)
{
        EAXFR = 1;
        
    PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
    PWMA_CCER2 = 0x00;
    PWMA_CCMR1 = 0x60; //通道模式配置

        
    PWMA_CCMR4 = 0x60;
    PWMA_CCER1 = 0x05; //配置通道输出使能和极性
    PWMA_CCER2 = 0x55;
        
                PWMA_PSCRH = (u8)((PWM_PSCR-1)>>8);                                //1分频
                PWMA_PSCRL = (u8)(PWM_PSCR-1);
        

    PWMA_ARRH = (u8)((PWM_PERIOD-1) >> 8); //设置周期时间
    PWMA_ARRL = (u8)(PWM_PERIOD-1);

    PWMA_ENO = 0x00;
    PWMA_ENO |= ENO1P; //使能输出
    PWMA_ENO |= ENO4P; //使能输出

    PWMA_PS = 0x00;  //高级 PWM 通道输出脚选择位
               
    PWMA_IER = 0x00;                                                // 使能更新中断

    PWMA_BKR = 0x80; //使能主输出
    PWMA_CR1 |= 0x11; //开始计时        向下计数
               
    EAXFR = 1;
        
}


void UpdatePwm(void)// 更新PWM占空比.
{
    PWMA_CCR1H = (u8)(PWM1_Duty >> 8); //设置占空比时间
    PWMA_CCR1L = (u8)(PWM1_Duty);
    PWMA_CCR4H = (u8)(PWM4_Duty >> 8); //设置占空比时间
    PWMA_CCR4L = (u8)(PWM4_Duty);
}



void PWM_Heat_Disable(void)//关闭PWM输出
{
    EAXFR = 1;
        PWMA_ENO &=~(1U << 0); //关闭CH1输出
    EAXFR = 0;
}

void PWM_Heat_Enable(void)//打开PWM输出
{
    EAXFR = 1;
    PWMA_ENO |= (1U << 0); //打开CH1输出
    EAXFR = 0;
}


下面是在中断的应用



void PWM_ISR() interrupt 26//pwm中断函数
{
        if(PWMA_SR1 & 0x01)                                        //判断更新中断标志位,是否置1
        {
                P51=1;
                PWM_Heat_Disable();//关闭PWM输出
                PWMA_SR1 &= ~0x01;                                //清零更新中断标志位
                Delay150us();
                Solder_Task();   //测温调温扫描
                PWM_Heat_Enable();//打开PWM输出                        
                P51=0;
        
        }
        
}







回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25428
发表于 2026-10-1 17:03:56 | 显示全部楼层
截图202610011703427471.jpg
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25428
发表于 2026-10-1 17:04:30 | 显示全部楼层

我已经收集到足够信息。现在结合手册和 STC 官方论坛(梁工)的解答,为您系统分析楼主这个问题。


关于 PWMA_ENO 关闭/打开 PWM 的问题分析与解答

结合手册《16位高级PWM定时器,支持正交编码器》章节内容,以及 STC 官方论坛中梁工的权威回复(见论坛帖"STC8H PWM 如何关闭--看这里"、"求助关于PWM问题,PWMA_ENO无法关闭输出问题"),对楼主三个问题逐一解答:

一、为什么用按钮可以、但用定时器/中断就"没有 PWM 输出"?

根本原因:楼主初始化代码中 PWMA_ENO 的位操作方向反了,并且对 CH4 的使能位设置有误。

看楼主的关键代码:

PWMA_ENO = 0x00;
PWMA_ENO |= ENO1P; //使能输出
PWMA_ENO |= ENO4P; //使能输出

手册中 PWMA_ENO(地址 FEB1H)的位定义如下:

符号 B7 B6 B5 B4 B3 B2 B1 B0
PWMA_ENO ENO4N ENO4P ENO3N ENO3P ENO2N ENO2P ENO1N ENO1P
  • ENO1P 在 B0 位,所以 ENO1P = 0x01;
  • ENO4P 在 B6 位,所以 ENO4P = 0x40。

再看他关闭/打开 CH1 的两个函数:

void PWM_Heat_Disable(void)   //关闭
{
    EAXFR = 1;
    PWMA_ENO &= ~(1U << 0);   //清 B0 = ENO1P  → 关闭CH1P输出 ✅ 正确
    EAXFR = 0;
}

void PWM_Heat_Enable(void)    //打开
{
    EAXFR = 1;
    PWMA_ENO |= (1U << 0);    //置 B0 = ENO1P  → 打开CH1P输出 ✅ 正确
    EAXFR = 0;
}

从位操作看,(1U << 0) 对应 B0 = ENO1P,方向是对的。所以问题不在位运算本身,而在于以下几点综合导致的"打开后仍无输出":

关键嫌疑点 1:EAXFR 关闭导致寄存器写入失败(最可能)

楼主每个函数里都写了 EAXFR = 0;(关闭 XFR 访问)。而论坛中梁工的明确回复是:

"访问位于 xdata 的扩展寄存器,一定要先设置 P_SW2 |= 0x80;,设置之后,不用清0,后面也不需要重复设置。"
"打开不用关闭,不会有问题的。"

PWMA_ENO 位于 XFR(FEB1H),必须保持 P_SW2.7 (EAXFR) = 1 才能访问。楼主在按钮控制时,可能因为主循环里其他代码仍保持 EAXFR 打开而"碰巧成功";而定时器/中断里的代码执行时机不同,若此时 EAXFR 已被别处清零,则中断中的 PWMA_ENO |= ... 实际写不进去,表现就是"关得掉、开不回"或"开了没输出"。

⚠️ 建议:删掉所有 EAXFR = 0;,程序初始化处 P_SW2 |= 0x80; 一次即可,全程不要关闭。

关键嫌疑点 2:管脚初始状态问题(梁工反复强调)

梁工在多个帖子中强调:

"PWM关闭 ENO 后,管脚状态是 GPIO 寄存器决定的,要确保 GPIO 寄存器状态不会引起管脚误动作。"
"如果你要关闭PWM输出后输出低电平,则初始化时要将对应引脚输出低电平。"

楼主用 PWMA_ENO 关闭 CH1 后,P1.x 立即变回普通 IO,输出电平由 P1 口寄存器决定。如果他之前没有把该引脚的模式/电平设置好,关闭期间引脚可能处于不确定状态;再打开 ENO 时,若中途有人改动了该 IO 的输出状态,也会影响观感。建议初始化时把对应引脚配置为准双向或推挽、并先输出低电平。

关键嫌疑点 3:CH4 的 CCER2 配置

楼主初始化中:

PWMA_CCER2 = 0x55;   // 打开 CH3、CH4 的 P/N 输出使能

而 PWMA_CCER2 用于通道3、通道4的 P/N 输出使能控制。楼主只想用 CH1、CH4,这里 0x55 同时打开了 CH3P/CH3N,若通道3没有配置相应功能,可能引入干扰。建议按实际使用通道精简配置。

二、用 PWMA_ENO 打开/关闭 PWM,会不会改变 PWM 参数?

不会改变 PWM 的参数(周期、占空比、计数方向等)。

根据手册,PWMA_ENO 是输出使能寄存器,其功能仅为:

ENO1P:PWM1P 输出控制位
0:禁止 PWM1P 输出
1:使能 PWM1P 输出

它只是控制 PWM 波形是否从引脚输出,而时基单元(ARR/PSC)、比较值(CCR)、计数模式(CR1)都不受影响,波形参数保持不变。这与论坛中"用 CCER1 开关""用 ENO 开关"在参数不变这一点上是一致的。

注意区别:ENO 是"把引脚从 PWM 功能切回普通 IO";而 PWMA_BKR 的 MOEn 位(B7)是主输出使能,一旦关闭会强制通道进入空闲/无效状态,二者机制不同。

三、有没有更好的"不改变 PWM 参数"的开关方法?

根据手册与论坛经验,推荐优先级如下:

方法 1(推荐):保持 EAXFR 常开 + PWMA_ENO 位操作

// 初始化时
P_SW2 |= 0x80;      // 打开XFR访问,之后不再关闭

// 关闭 CH1P
PWMA_ENO &= ~0x01;  // 清 B0

// 打开 CH1P
PWMA_ENO |= 0x01;   // 置 B0

不改变任何 PWM 参数,且是手册定义的"输出使能"功能,最贴切。

方法 2:用 PWMA_CCER1 的 CC1E 位开关

楼主自己已试过可行(见论坛帖 25535)。此方法同样只开关通道输出,不改变周期/占空比。但要注意 CCER 中 CCNE、极性位等的联动,操作比 ENO 稍复杂。

不推荐:用 PWMA_BKR 的 MOE 位关闭——因为 MOE 被清会触发通道进入空闲状态,恢复时行为与"仅关闭输出"不同。


给楼主的最终修改建议

  1. 删除 PWM_Heat_Disable/Enable 里的 EAXFR = 0;,改为全程 P_SW2 |= 0x80; 常开(这是梁工明确指出的关键)。
  2. 初始化时先把 CH1 对应引脚设为推挽输出并输出低电平,避免 ENO 关闭后引脚状态不定。
  3. 检查并精简 PWMA_CCER2,只打开实际用到的通道。
  4. 中断里操作 PWMA_ENO 前后无需再动 EAXFR/P_SW2,直接位操作即可。

以上依据手册《16位高级PWM定时器,支持正交编码器》章节的 PWMx_ENO、PWMx_BKR、PWMx_CR1 寄存器定义,以及 STC 官方论坛帖"STC8H PWM 如何关闭--看这里"(TID 3052)和"求助关于PWM问题,PWMA_ENO无法关闭输出问题"(TID 25535)中梁工的回复。

如果您能提供具体使用的是哪款芯片(如 STC8H8K64U / Ai8051U)以及完整的工程代码,我可以帮您更精确定位是"EAXFR 被误关"还是"引脚初始化"导致的问题。


🔍 搜索 "STC32G 单片机 技术论坛"

⏱️ 查找用时 12.7s | 总回答用时 22.1s

回复

使用道具 举报 送花

83

主题

8317

回帖

1万

积分

超级版主

积分
17932
发表于 2026-10-1 17:06:26 | 显示全部楼层
用PWMA_ENO寄存器打开、关闭PWM不会改变pwm的任何参数,这是最简单有效的方法。
你可以参考我的无刷电机的驱动程序,在主程序或中断程序里频繁使用PWMA_ENO关闭、打开PWM。
回复

使用道具 举报 送花

48

主题

87

回帖

907

积分

高级会员

积分
907
 楼主| 发表于 2026-10-1 19:33:56 | 显示全部楼层
神*** 发表于 2026-10-1 17:04
我已经收集到足够信息。现在结合手册和 STC 官方论坛(梁工)的解答,为您系统分析楼主这个问题。

关于 PW ...

非常感谢你的解答,问题就是出在你分析“关键嫌疑点 1:EAXFR 关闭导致寄存器写入失败(最可能)”我看你的解答认为最不可能EAXFR 的问题,因为写PWMA_ENO寄存器时都是先EAXFR =1,我都打开了,你还是因为EAXFR关闭造成的, 可是万万没有想到,这是豆包给我分析的,而且分析的很有道理,豆包说特殊寄存器与普通寄存器地址存在叠加,不关闭会造成写普通寄存器错误,当时我还认为扩展了知识面,最后再次表示感谢,国庆快乐,
回复

使用道具 举报 送花

48

主题

87

回帖

907

积分

高级会员

积分
907
 楼主| 发表于 2026-10-1 20:33:04 | 显示全部楼层
梁*** 发表于 2026-10-1 17:06
用PWMA_ENO寄存器打开、关闭PWM不会改变pwm的任何参数,这是最简单有效的方法。
你可以参考我的无刷电机的 ...

ok非常感谢
回复

使用道具 举报 送花

已绑定手机

2

主题

154

回帖

470

积分

中级会员

积分
470
发表于 2026-10-3 10:28:49 | 显示全部楼层
我补充一下,
void PWM_Heat_Enable(void)//打开PWM输出
{
    EAXFR = 1;    //1行
    PWMA_ENO |= (1U << 0); //2行
    EAXFR = 0;    //3行
}
这段代码,是有概率不成功的,
为什么?
分两种情况:
1,中断中调用:
如果有在多个中断中使用,并且有设置别的中断的优先级,当代码执行到第1行(上面代码后注释的行号,下同)后,
高优先级的中断抢占了,并有调用这函数,或有EAXFR = 0,抢占返回后,第2行就操作失败了,因为此时EAXFR为0。

2,主循环中调用:
同样的,当执行完第1行,有中断响应了,中断中有调用这个函数或有EAXFR = 0,中断返回后,第2行就操作失败了,因为此时EAXFR为0。

我上面说的这个原因,
并不一定是说你现在的pwm无法开启问题的原因,
因为不知道你有没有设置中断优先级,及其他中断中是否有EAXFR = 0。

另外,你的pwm中断中有:
  Delay150us();
Solder_Task();   //测温调温扫描
比较耗时,容易有其他中断抢占,尽量优化一下,
中断中一般只作标记和少量操作,
主循环根据标记再做耗时的功能操作。
回复

使用道具 举报 送花

48

主题

87

回帖

907

积分

高级会员

积分
907
 楼主| 发表于 2026-10-11 17:25:10 | 显示全部楼层
jrc*** 发表于 2026-10-3 10:28
我补充一下,
void PWM_Heat_Enable(void)//打开PWM输出
{

非常感谢你的分享,因为我想在延时150us立即执行Solder_Task();   //测温调温扫描,由于主循环里有长时间显示(几毫秒)程序,如果使用标志位,很难确保Solder_Task();及时执行,有中断置标志位,主循环立即响应的方法吗?
回复

使用道具 举报 送花

已绑定手机

2

主题

154

回帖

470

积分

中级会员

积分
470
发表于 2026-10-11 18:53:13 | 显示全部楼层
dong*** 发表于 2026-10-11 17:25
非常感谢你的分享,因为我想在延时150us立即执行Solder_Task();   //测温调温扫描,由于主循环里有长时间 ...

主循环只能按顺序执行,没有立即响应标志位的方式。
只能看看 能不能优化显示功能了。
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-11-7 12:21 , Processed in 0.077496 second(s), 78 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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