找回密码
 立即注册
查看: 20|回复: 4

pwm的占空比怎么根据ADC值更新

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-10-22 17:13:44
已绑定手机

4

主题

6

回帖

56

积分

注册会员

积分
56
发表于 昨天 17:13 | 显示全部楼层 |阅读模式
大佬们好, 我在使用STC8H3K32S2-LQPF32 的PWMB6-3(P01脚)做PWM 固定频率可变占空比输出,

一直调试不出来, 下面是对应的代码,帮忙看看问题出在哪里?

sbit PWM_OUTPUT_PIN =P0^1;
#define Timer1_Reload (65536UL-((MAIN_FOSC)/5000))       //tim 1 interrupt freq 5000HZ


void Config_ADC()
{       
        P0M0 &= ~0x08;    // P03 for adc input port
        P0M1 |= 0x08;  
       
        P_SW2 |= 0X80;
        ADCTIM = 0X3F;    // BIN 0 01 11111
        P_SW2 &= 0X7F;

        ADCCFG = 0X2F;    // align to right side, BIN 0010 1111(for lowest speed)
  ADC_CONTR = 0X8B; // enable the adc 11# function  (P03 port)
}


u16 Get_Self_ADC_DATA()
{
//  Config_ADC();
        ADC_CONTR |= 0X40;      // start the adc reading
        _nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();
       
        while (!(ADC_CONTR & 0x20));            //when adc done
  ADC_CONTR &= ~0x20;                     //clean the adc flag
        self_adc_value = ADC_RES * 256 + ADC_RESL;
       
        ADC_RES = 0;
        ADC_RESL = 0;
       
        ADC_CONTR |= 0X4B;      // recycle adc
       
        return self_adc_value;
}




void Config_TMR1()
{
  AUXR |= 0x40;                       
        TMOD &= 0x0F;                       
        TL1 = (u8) (Timer1_Reload % 256);                       
        TH1 = (u8) (Timer1_Reload / 256);                               
        TF1 = 0X00;                               
        TR1 = 0X01;
        ET1 = 0X01;
}


void PWM_OUTPUT_DutyUpdate()
{
        PWM6_Duty = (u32)(self_adc_value_mean) * PWM_PERIOD / 4096;
}



void Timer1_ISR() interrupt 3
{
        u16 pwm_cnt = 0;
        pwm_cnt++;
      if(pwm_cnt >= 8000 )
        {        pwm_cnt = 0;}
       
                if(pwm_cnt < PWM6_Duty)
                {PWM_OUTPUT_PIN = 1;}
        else
        {
           PWM_OUTPUT_PIN = 0;
        }
}



void main()
{       
        Config_ADC();
        PWM_OUTPUT_DutyUpdate();
        Config_TMR1();
        EA  = 1;
       

       
        while(1)
        {
                       
           self_adc_value_mean = ADC_value_mean();
           PWM_OUTPUT_DutyUpdate();                                 
       }       
}

回复

使用道具 举报 送花

3

主题

1859

回帖

566

积分

等待验证会员

积分
566
发表于 昨天 17:18 | 显示全部楼层
你好!你当前的问题是关于如何根据 ADC 采集的值来动态更新 PWM 的占空比,并且你在使用 STC8H3K32S2-LQFP32 单片机的 PWMB6-3(P01)引脚进行 PWM 输出时遇到了问题。以下我将从几个方面来分析你的问题并给出建议和修改方案。

一、PWM 基本原理与占空比控制

PWM(脉宽调制)是一种通过调节高电平持续时间与周期的比例来控制输出功率的技术。占空比(Duty Cycle)定义为:

$$
\text{占空比} = \frac{\text{高电平时间}}{\text{周期时间}} \times 100\%
$$

在 STC8H 系列单片机中,PWMB 模块支持多通道 PWM 输出,通常通过寄存器设置周期(频率)和占空比。

二、你的问题分析

你提到使用的是 PWMB6-3,输出在 P01 引脚上,固定频率、可变占空比。但调试不出来。从你提供的代码片段来看,主要问题可能集中在以下几个方面:

1. 缺少 PWM 初始化代码

你贴出的代码中没有看到关于 PWMB 的初始化配置,例如:
设置 PWM 模块的频率(周期)
设置初始占空比
配置对应的 IO 引脚为 PWM 输出功能
启动 PWM 模块

这些是 PWM 输出的基础配置,缺少这些设置,PWM 是不会输出的。

2. ADC 采集函数未完整

你提供的 GetSelfADCDATA() 函数只返回了 ADC 的转换结果,但没有返回值处理,也没有用于更新 PWM 占空比的逻辑。

三、建议的代码结构

1. PWM 初始化函数(示例)
  1. c
  2. void ConfigPWM(void)
  3. {
  4.     // 设置 PWMB6-3(P01)为 PWM 输出
  5.     P0M1 &= ~0x01;  // P01 设为 PWM 输出
  6.     P0M0 |= 0x01;
  7.     // 设置 PWM 频率:假设系统时钟为 24MHz,预分频为 12
  8.     // 周期 = (PWMxT1 + 1)  12 / Fosc
  9.     PWMBT1 = 0xFF;  // 设置周期寄存器(频率)
  10.     // 设置占空比寄存器
  11.     PWMB63 = 0x80;  // 初始占空比为 50%
  12.     // 启动 PWM 模块
  13.     PWMBCTRL |= 0x80;  // 启动 PWMB 模块
  14. }
复制代码

2. ADC 初始化函数(你已有,略作补充)
  1. c
  2. void ConfigADC(void)
  3. {
  4.     P0M0 &= ~0x08;    // P03 输入
  5.     P0M1 |= 0x08;
  6.     PSW2 |= 0x80;
  7.     ADCTIM = 0x3F;
  8.     PSW2 &= 0x7F;
  9.     ADCCFG = 0x2F;        // 右对齐
  10.     ADCCONTR = 0x8B;     // 选择 P03 为 ADC 输入
  11. }
复制代码

3. ADC 获取函数(补充返回值)
  1. c
  2. u16 GetADCValue(void)
  3. {
  4.     ADCCONTR |= 0x40;        // 启动一次转换
  5.     nop(); nop();
  6.     while (!(ADCCONTR & 0x20));  // 等待转换完成
  7.     ADCCONTR &= ~0x20;      // 清除完成标志
  8.     return ADCRES;          // 返回 ADC 高 8 位(右对齐)
  9. }
复制代码

4. 主函数中动态更新占空比
  1. c
  2. void main(void)
  3. {
  4.     u16 adcvalue;
  5.     ConfigADC();
  6.     ConfigPWM();
  7.     while (1)
  8.     {
  9.         adcvalue = GetADCValue();  // 获取 ADC 值(0~255)
  10.         // 将 ADC 值映射为占空比(假设为 8 位 ADC)
  11.         PWMB63 = adcvalue;
  12.         DelayMs(10);  // 简单延时
  13.     }
  14. }
复制代码

四、常见问题排查点

1. IO 引脚配置错误

确保 P01 被正确配置为 PWM 输出功能,而不是通用 IO 或其他功能。

2. PWM 模块未启动

检查 PWMBCTRL 寄存器是否设置了启动位(如 PWMBCTRL |= 0x80)。

3. 占空比寄存器写入错误

某些型号的单片机要求在写入占空比前关闭 PWM 输出,或需要等待更新完成标志。

4. ADC 采集不准确

可以尝试在 ADC 初始化后加一个延时,或在 GetADCValue() 中加入软件延时,确保 ADC 有足够时间稳定。

五、总结

要实现根据 ADC 值动态更新 PWM 占空比,关键步骤如下:

1. 初始化 PWM 模块:设置频率和初始占空比,配置 IO 引脚。
2. 初始化 ADC 模块:选择正确的输入通道,配置采样时间。
3. 读取 ADC 值:确保读取准确,并根据需要进行滤波或平均处理。
4. 动态更新占空比:将 ADC 值映射为占空比,并写入对应寄存器。
5. 调试验证:使用示波器查看 PWM 输出是否随 ADC 值变化而变化。

如果你能提供完整的代码(包括 PWM 初始化部分),我可以进一步帮你详细分析和修改。

祝你调试顺利!如果还有其他问题,欢迎继续提问。

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

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:591
  • 最近打卡:2025-10-22 06:39:01
已绑定手机

86

主题

6331

回帖

1万

积分

超级版主

积分
11887
发表于 昨天 17:41 | 显示全部楼层
这是使用定时器做PWM直接操作IO口了,并没有使用硬件PWM,建议使用硬件PWM,写PWMA_CCR1寄存器即可更新PWM占空比,例程如下:
  1. #include <AI8H.H>
  2. #include <intrins.h>
  3. #include <absacc.h>
  4. #include <string.h>
  5. #include <stdarg.h>
  6. #include <stdio.h>
  7. #include <math.h>
  8.         
  9. #define                FOSC                        24000000UL                                        //主时钟
  10. void Delay_ms(unsigned char ms)
  11. {
  12.         unsigned int i;
  13.         do
  14.         {
  15.                 i=FOSC/10000;
  16.                 while(--i);        //10T per loop
  17.         }
  18.         while(--ms);
  19. }
  20. void Init(void)
  21. {
  22.         P_SW2|=EAXFR;
  23.         
  24.         P0M1=0x00;        P0M0=0x00;        //设置IO模式
  25.         P1M1=0x00;        P1M0=0x00;        //设置IO模式
  26.         P2M1=0x00;        P2M0=0x00;        //设置IO模式
  27.         P3M1=0x03;        P3M0=0x00;        //设置IO模式
  28.         P4M1=0x00;        P4M0=0x00;        //设置IO模式
  29.         P5M1=0x00;        P5M0=0x00;        //设置IO模式
  30.         P6M1=0x00;        P6M0=0x00;        //设置IO模式
  31.         P7M1=0x00;        P7M0=0x00;        //设置IO模式
  32.         
  33.         PWMA_PS=0x00;                //高级 PWM 通道输出脚选择位
  34.         PWMA_PS|=0x01;                //选择 PWM1_2 通道
  35.         
  36.         PWMA_CCER1 = 0x00;        //写 CCMRx 前必须先清零 CCxE 关闭通道
  37.         PWMA_CCMR1 = 0x60;        //通道模式配置
  38.         PWMA_CCER1 = 0x01;        //配置通道输出使能和极性
  39.         
  40.         PWMA_ARRH=(unsigned char)(24000>>8); //设置周期时间
  41.         PWMA_ARRL=(unsigned char)24000;
  42.         
  43.         PWMA_ENO=0x00;
  44.         PWMA_ENO|=0x01;                //使能输出
  45.         
  46.         PWMA_BKR=0x80;                //使能主输出
  47.         PWMA_CR1|=0x01;                //开始计时
  48.         
  49.         EA=1;                                //启用总中断
  50. }
  51. void main(void)
  52. {
  53.         unsigned int duty;
  54.         duty=12720;
  55.         Init();
  56.         PWMA_CCR1H = (unsigned char)(duty >> 8); //设置占空比时间
  57.         PWMA_CCR1L = (unsigned char)(duty);
  58.         while(1)
  59.         {
  60.                
  61.         }
  62. }
复制代码

例程中使用的是PWMA,如使用PWM6_3输出PWM,需要修改为PWMB,还有脚位和通道等也要同步修改
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:591
  • 最近打卡:2025-10-22 06:39:01
已绑定手机

86

主题

6331

回帖

1万

积分

超级版主

积分
11887
发表于 昨天 17:43 | 显示全部楼层
初学者建议看一下这个新手必读,初始化别忘打开EAXFR,还有设置IO模式
https://www.stcaimcu.com/thread-16535-1-1.html
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-10-22 17:13:44
已绑定手机

4

主题

6

回帖

56

积分

注册会员

积分
56
发表于 昨天 18:09 | 显示全部楼层
Debu*** 发表于 2025-10-22 17:41
这是使用定时器做PWM直接操作IO口了,并没有使用硬件PWM,建议使用硬件PWM,写PWMA_CCR1寄存器即可更新PWM ...

大哥,使用 硬件PWMB,调整ADC值占空比也没变化,代码如下,检查代码,没找到问题在哪

void Config_PCA_OUTPUT(void)
{
        P0M0 |= 0x02;
        P0M1 &= ~0x02;
       
  P_SW2 |= 0X80;           // ENABLE P_SW2 SFR

        PWMB_PS &= 0XF3;         // BIT 3=1 BIT 2=0 FOR P0.1 PWM6-3  BIN 1111 0011
        PWMB_PS |= 0X08;         // BIN 0000 1000
        PWMB_CCER1 = 0X00;       // CLEAN  
       
        PWMB_CCMR2 = 0X68;       // CC6 for pwm output, start pre-load BIN 0110 1000
  PWMB_CCER1 = 0X30;       // BIT 5 =1 , BIT4 = 1 ENABLE CC6
       
        PWMB_CCR6 = 0;
                     
        PWMB_ARRH = (u8) (PWM_PERIOD >> 8);
        PWMB_ARRL = (u8) PWM_PERIOD ;       
        PWMB_ENO = 0X04;         // BIN 0000 0100 , FOR BIT 3 SET 1 ENABLE PWM6P OUTPUT
        PWMB_BKR = 0X80;
        PWMB_CR1 |= 0X01;        
}

void PWM_OUTPUT_DutyUpdate()
{
        PWM6_Duty = (u32)(self_adc_value_mean) * PWM_PERIOD / 4096;
        PWMB_CCR6H = (u8) PWM6_Duty >> 8;
        PWMB_CCR6L = (u8) PWM6_Duty;
}


void main()
{       
        P_SW2 |= 0X80;
        Config_ADC();
        Config_PCA_OUTPUT();
        PWM_OUTPUT_DutyUpdate();

       
        EA  = 1;
       
        while(1)
        {       
                        self_adc_value_mean = ADC_value_mean();
                        PWM_OUTPUT_DutyUpdate();                                                         
      }       
}
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-10-23 05:20 , Processed in 0.128889 second(s), 73 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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