王昱顺 发表于 2025-10-22 11:39:32

STC32G144K246,高速PWM@240Mhz 运行测试

高速PWM@240MHz 运行测试, STC32G144K246:
程序使用CHIPID参数固定内部时钟为24Mhz,
且通过HPLL功能升频至480MHz
其中,给CPU的为120MHz分频时钟,
给PWM外设的为240MHz分频时钟
串口使用115200波特率,
内部带有异步写入PWMA@240MHz和异步读取示例
效果是P60~P67上输出一个互补且循环自增的占空比,
表现为LED灯从最暗到最亮然后再继续循环

实际测量程序输出为:
58Khz,PWMA无预分频,PWMA_ARR设置为4096,
所以58Khz*4096=237Mhz,与理论值240Mhz接近

以下为程序代码和编译通过的程序文件:
这个是与CPU同频的PWM输出示例:
这个是与CPU不同频率的PWM输出示例:
程序内容:
#include "STC32G.H"
#include "stdio.h"
#include "stdarg.h"


//本例程使用CHIPID内预置参数,设置HIRC为24MHz
//使用HPLL1,提供60Mhz,80Mhz,120Mhz的设置例程
#define Fosc_60Mhz 0 //系统时钟为60Mhz
#define Fosc_80Mhz 1 //系统时钟为80Mhz
#define Fosc_120Mhz 2//系统时钟为120Mhz

#define Main_Fosc Fosc_120Mhz //设置系统时钟为120Mhz

void CLK_Init(void);                              //设置系统时钟,由Main_Fosc定义设置
void Timer0_Init(void);                        //定时器0初始化函数
void Io_Init(void);                                        //I/O口初始化函数,设置P32为开漏+打开内部上拉电阻模式
void Uart1_Init(void);                        //串口初始化函数,500000bps
void Pwm_Init(void);                              //PWM初始化函数
void UpdatePwm(void);                              //更新PWM占空比函数
void UpdateHSPwm(void);                        //更新高速PWM占空比函数
void WritePWMA(char addr, char dat);      //异步方式写入PWM寄存器
char ReadPWMA(char addr);                                                      //异步方式读出PWM寄存器
void uart_send(int num);
bit P32_OUT = 1;                                                //用于确定输出电平
char uart_buff = {0};
unsigned int PWM1_Duty = 100, PWM2_Duty = 500, PWM3_Duty = 2000, PWM4_Duty = 4000, Duty_All = 0;

void Delay100ms(void)      //@120MHz
{
      unsigned long edata i;

      _nop_();
      _nop_();
      i = 2999998UL;
      while (i) i--;
}


unsigned int read_pwmh = 0, read_pwml = 0;
void main(void)
{
      EAXFR = 1;                                        //使能访问扩展RAM区特殊功能寄存器(XFR)
      CKCON &= ~0x07;                        //清空,设置外部数据总线等待时钟为0(最快),默认为7
      CLK_Init();                                        //设置HPLL时钟为指定频率
      Timer0_Init();                        //初始化定时器0,50毫秒@120MHz
      Uart1_Init();                              //串口初始化函数,115200bps
      Pwm_Init();                                        //PWM初始化函数
      HSPWMA_CFG = 0x03;      //使能 PWMA 相关寄存器异步访问功能
      HPLL2CR &= ~(3<<5);      //清空高速外设时钟选择
      HPLL2CR |= (0<<5);      //选择HPLL1/2
      Io_Init();                                        //初始化I/O口,设置P32等效为原准双向口模式(开漏模式+打开内部上拉电阻)
      EA = 1;                                                      //打开总中断
      while(1)
      {
                //用户程序
                Duty_All = (Duty_All+150)&0xfff;//限制最大值4095
                PWM1_Duty = PWM2_Duty = PWM3_Duty = PWM4_Duty = Duty_All;//调整占空比
                //UpdatePwm();//更新占空比
                UpdateHSPwm();
                read_pwmh = (unsigned char)ReadPWMA((char)&PWMA_CCR1H);
                read_pwml = (unsigned char)ReadPWMA((char)&PWMA_CCR1L);
                uart_send(sprintf(uart_buff, "read_pwm_cnt:0x%x%x, duty:%u\r\n",read_pwmh, read_pwml, Duty_All));//串口回传当前占空比
                Delay100ms();
      }
}


bit uart_flag = 0;
void send_dat(char c)
{
      uart_flag = 1;
      SBUF = c;
      while(uart_flag);
}
int data dat_len = 0;
void uart_send(int num)
{
      for(dat_len = 0; dat_len<num; dat_len++)
      {
                send_dat(uart_buff);
      }
}
void Uart1_Isr(void) interrupt 4
{
      if (TI)                              //检测串口1发送中断
      {
                TI = 0;                        //清除串口1发送中断请求位
                uart_flag = 0;
      }
      if (RI)                              //检测串口1接收中断
      {
                RI = 0;                        //清除串口1接收中断请求位
      }
}

void Uart1_Init(void)      //115200bps@120MHz
{
      SCON = 0x50;                //8位数据,可变波特率
      AUXR |= 0x40;                //定时器时钟1T模式
      AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
      TMOD &= 0x0F;                //设置定时器模式
      TL1 = 0xFC;                        //设置定时初始值
      TH1 = 0xFE;                        //设置定时初始值
      ET1 = 0;                        //禁止定时器中断
      TR1 = 1;                        //定时器1开始计时
      ES = 1;                              //使能串口1中断
}


char data off_t0_cnt = 0;
void Timer0_Isr(void) interrupt 1
{
      if(P32_OUT == 1&&P32 == 0&&off_t0_cnt<100)off_t0_cnt++;      //判断外部P32按键按下一定时间时,关闭定时器0
      if(off_t0_cnt>5)TR0 = 0;//注:仅在P32输出为1的时候,外部的按键按下才能被读到
      P32_OUT = ~P32_OUT;//每隔10ms亮/灭切换一次
      P32 = P32_OUT;                //将输出电平给P32管脚
}

void Timer0_Init(void)                //50毫秒@120MHz
{
      TM0PS = 0x5B;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
      AUXR |= 0x80;                        //定时器时钟1T模式
      TMOD &= 0xF0;                        //设置定时器模式
      TL0 = 0x3F;                              //设置定时初始值
      TH0 = 0x01;                              //设置定时初始值
      TF0 = 0;                              //清除TF0标志
      TR0 = 1;                              //定时器0开始计时
      ET0 = 1;                              //使能定时器0中断
      T0CLKO = 1;                        //使能P35输出定时器溢出时钟
}

void Io_Init(void)
{
      P3M0 = 0x26; P3M1 = 0xdd;
      P3PU = 0x05;
      P3SR = 0xdc;
      P3DR = 0xdc;
      P6M0 = 0xff; P6M1 = 0x00; //推挽输出,P6,PWM
      P6SR = 0x00; //转换速度和驱动电流最大,P6
      P6DR = 0x00;
}

void Delay10ms(void)      //@120MHz
{
      unsigned long edata i;

      _nop_();
      _nop_();
      i = 299998UL;
      while (i) i--;
}

void CLK_Init(void)
{
      #if Main_Fosc == Fosc_120Mhz
      WTST = 4;CLKDIV = 2;               //设置系统时钟=480MHz/2/2=120MHz,(因为CLKSEL选择时,已经将HPLL/2了)
      #elif Main_Fosc == Fosc_80Mhz
      WTST = 3;CLKDIV = 3;         //设置系统时钟=480MHz/2/3=80MHz
      #elif Main_Fosc == Fosc_60Mhz
      WTST = 2;CLKDIV = 4;         //设置系统时钟=480MHz/2/4=60MHz
      #endif
      //以下为超过60MHz时,系统时钟使用HPLL方式提供
      VRTRIM = CHIPID22;                //载入27MHz频段的VRTRIM值
      IRTRIM = CHIPID12;                //指定当前HIRC为24MHz,此时会覆盖掉ISP设置的时钟频率
      IRCBAND &= ~0x03;                        //清空频段选择
      IRCBAND |= 0x01;                        //选择27Mhz频段
      HPLLCR &= ~0x10;            //选择HPLL输入时钟源为HIRC
      HPLLPDIV = 4;                                        //24MHz/4=6MHz,需要保证输入HPLL的时钟在6MHz附近
      HPLLCR |= 0x0e;             //HPLL=6MHz*80=480MHz
      HPLLCR |= 0x80;             //使能HPLL
      Delay10ms();
      CLKSEL &= ~0x03;                        //BASE_CLK选择为HIRC,用以提供给HPLL
      CLKSEL &= ~0x0c;                        //清空主时钟源选择
      CLKSEL |= 1<<2;                              //设置主时钟源为内部 HPLL1 输出/2
}


#define PWM1_1      0x00    //P:P1.0N:P1.1
#define PWM1_2      0x01    //P:P2.0N:P2.1
#define PWM1_3      0x02    //P:P6.0N:P6.1

#define PWM2_1      0x00    //P:P1.2/P5.4N:P1.3
#define PWM2_2      0x04    //P:P2.2N:P2.3
#define PWM2_3      0x08    //P:P6.2N:P6.3

#define PWM3_1      0x00    //P:P1.4N:P1.5
#define PWM3_2      0x10    //P:P2.4N:P2.5
#define PWM3_3      0x20    //P:P6.4N:P6.5

#define PWM4_1      0x00    //P:P1.6N:P1.7
#define PWM4_2      0x40    //P:P2.6N:P2.7
#define PWM4_3      0x80    //P:P6.6N:P6.7
#define PWM4_4      0xC0    //P:P3.4N:P3.3

#define ENO1P       0x01
#define ENO1N       0x02
#define ENO2P       0x04
#define ENO2N       0x08
#define ENO3P       0x10
#define ENO3N       0x20
#define ENO4P       0x40
#define ENO4N       0x80

void Pwm_Init(void)
{
      PWMA_CCER1 = 0x00;//写 CCMRx 前必须先清零 CCxE 关闭通道
      PWMA_CCER2 = 0x00;
      PWMA_CCMR1 = 0x60;//通道模式配置
      PWMA_CCMR2 = 0x60;
      PWMA_CCMR3 = 0x60;
      PWMA_CCMR4 = 0x60;
      PWMA_CCER1 = 0x55;//配置通道输出使能和极性
      PWMA_CCER2 = 0x55;

      PWMA_ARRH = 0x0f;   //设置周期时间
      PWMA_ARRL = 0xff;

      PWMA_DTR = 0x40;   //设置死区时间
      PWMA_PSCRH = 0;    //设置预分频器
      PWMA_PSCRL = 0;    //设置预分频器

      PWMA_ENO = 0x00;
      PWMA_ENO |= ENO1P; //使能输出
      PWMA_ENO |= ENO1N; //使能输出
      PWMA_ENO |= ENO2P; //使能输出
      PWMA_ENO |= ENO2N; //使能输出
      PWMA_ENO |= ENO3P; //使能输出
      PWMA_ENO |= ENO3N; //使能输出
      PWMA_ENO |= ENO4P; //使能输出
      PWMA_ENO |= ENO4N; //使能输出

      PWMA_PS = 0x00;//高级 PWM 通道输出脚选择位
      PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
      PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
      PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
      PWMA_PS |= PWM4_3; //选择 PWM4_3 通道
      
      UpdatePwm();
      
      PWMA_BKR = 0x80; //使能主输出
      PWMA_CR1 |= 0x01; //开始计时
}

void UpdatePwm(void)
{
      PWMA_CCR1H = (unsigned char)(PWM1_Duty >> 8); //设置占空比时间
      PWMA_CCR1L = (unsigned char)(PWM1_Duty);
      PWMA_CCR2H = (unsigned char)(PWM2_Duty >> 8); //设置占空比时间
      PWMA_CCR2L = (unsigned char)(PWM2_Duty);
      PWMA_CCR3H = (unsigned char)(PWM3_Duty >> 8); //设置占空比时间
      PWMA_CCR3L = (unsigned char)(PWM3_Duty);
      PWMA_CCR4H = (unsigned char)(PWM4_Duty >> 8); //设置占空比时间
      PWMA_CCR4L = (unsigned char)(PWM4_Duty);
}

void UpdateHSPwm(void)
{
      WritePWMA((char)&PWMA_CCR1H,(unsigned char)(PWM1_Duty >> 8));
      WritePWMA((char)&PWMA_CCR1L,(unsigned char)(PWM1_Duty));
      WritePWMA((char)&PWMA_CCR2H,(unsigned char)(PWM2_Duty >> 8));
      WritePWMA((char)&PWMA_CCR2L,(unsigned char)(PWM2_Duty));
      WritePWMA((char)&PWMA_CCR3H,(unsigned char)(PWM3_Duty >> 8));
      WritePWMA((char)&PWMA_CCR3L,(unsigned char)(PWM3_Duty));
      WritePWMA((char)&PWMA_CCR4H,(unsigned char)(PWM4_Duty >> 8));
      WritePWMA((char)&PWMA_CCR4L,(unsigned char)(PWM4_Duty));
}

char ReadPWMA(char addr)
{
      char dat;
      while (HSPWMA_ADR & 0x80);//等待前一个异步读写完成
      HSPWMA_ADR = addr | 0x80;//设置间接访问地址,只需要设置原 XFR 地址的低 7 位。HSPWMA_ADR 寄存器的最高位写 1,表示读数据
      while (HSPWMA_ADR & 0x80);//等待当前异步读取完成
      dat = HSPWMA_DAT;//读取异步数据
      return dat;
}

void WritePWMA(char addr, char dat)
{
      while (HSPWMA_ADR & 0x80);//等待前一个异步读写完成
      HSPWMA_DAT = dat;//准备需要写入的数据
      HSPWMA_ADR = addr & 0x7f;//设置间接访问地址,只需要设置原 XFR 地址的低 7 位。HSPWMA_ADR 寄存器的最高位写 0,表示写数据
}





国芯人工智能 发表于 2025-10-22 11:43:00


https://www.stcaimcu.com/data/download/Datasheet/STC32G144K246.pdf

感谢分享关于 STC32G144K246 的高速 PWM 测试案例。以下是对该测试内容的专业分析与技术解读,供参考与交流。

一、测试背景与目标

本次测试基于 STC32G144K246 单片机,重点验证其在 240MHz 高速 PWM 输出能力下的运行稳定性与精度。该芯片具备 HPLL(高速锁相环) 功能,可将内部时钟从默认的 24MHz(HIRC) 提升至 480MHz,为 CPU 和外设提供灵活的时钟配置。

测试目标如下:

1. 验证在 240MHz 时钟频率下 PWM 输出的准确性;
2. 测试 PWM 占空比的动态变化与 LED 显示效果;
3. 验证串口通信与 PWM 异步操作的协同工作;
4. 验证不同外设时钟配置下的 PWM 输出能力。

二、系统时钟配置分析

根据测试说明,系统使用 CHIPID 参数 固定 HIRC 为 24MHz,并通过 HPLL 模块将主频提升至 480MHz。随后通过分频机制,为 CPU 和 PWM 外设分别提供:
CPU 时钟:120MHz(即 480MHz ÷ 4);
PWM 外设时钟:240MHz(即 480MHz ÷ 2);

这种配置方式体现了 STC32 系列芯片在时钟管理上的灵活性。CPU 和外设可以使用不同的时钟频率,既保证了处理性能,又实现了高精度的 PWM 输出。

三、PWM 输出配置与实测分析

1. PWM 配置参数
PWM 时钟源:240MHz(无预分频);
PWMARR(自动重载寄存器)值:4096;
输出频率计算:

$$
f{PWM} = \frac{f{PWM\CLK}}{ARR} = \frac{240\,MHz}{4096} \approx 58.59375\,kHz
$$

实际测量结果为 58kHz,与理论值相符,误差较小,说明系统时钟配置准确,PWM 控制器运行稳定。

2. PWM 输出波形特性

测试中使用 PWMA 模块输出互补波形,并在 P60~P67 引脚上实现占空比循环自增,驱动 LED 实现从最暗到最亮的渐变效果。
互补输出:表明使用了高级 PWM 模式,支持死区时间控制,适用于电机控制、电源转换等场景;
占空比自增:通过软件控制或定时器触发,动态更新 PWM 比较寄存器值,实现 LED 明暗渐变;
循环更新:确保 PWM 波形连续无中断,避免闪烁或抖动。

四、串口通信与异步操作测试

测试中还包含了串口通信(115200 波特率)与 PWM 异步读写操作的验证。
异步写入 PWMA:说明 PWM 的占空比设置通过串口指令异步更新,具备远程控制能力;
异步读取:可用于实时反馈 PWM 状态,实现闭环控制;
波特率 115200:满足高速通信需求,且在 240MHz 系统时钟下能稳定运行,说明串口模块时钟配置合理。

五、代码结构与模块化设计

从提供的代码结构来看,程序使用了标准头文件(如 STC32GH、stdioh、stdargh),并定义了多个时钟配置宏,便于快速切换 CPU 频率。
#define Fosc60Mhz 0:通过宏定义控制不同的时钟配置;
模块化设计:便于维护和扩展,适合多场景复用;
使用标准 I/O 函数(如 printf)进行调试输出,提升了开发效率。

建议在后续开发中进一步封装 PWM 控制函数与串口通信协议,提升代码的可读性与可移植性。

六、测试附件分析

附件中提供了两个 PWM 输出示例程序:
与 CPU 同频的 PWM 输出示例(附件 118435);
与 CPU 不同频的 PWM 输出示例(附件 118434);

这两个程序分别验证了:

1. 同步时钟配置下,PWM 与 CPU 的协调工作;
2. 异步时钟配置下,外设独立于 CPU 的运行能力。

这体现了 STC32G 系列芯片在多时钟域管理方面的优势,适用于复杂嵌入式系统的时钟优化设计。

七、结论与建议

本次测试充分展示了 STC32G144K246 在以下方面的性能优势:
高速 PWM 输出能力:支持 240MHz 时钟输入,输出频率精度高;
灵活的时钟管理机制:CPU 与外设可独立配置时钟频率;
强大的外设协同能力:PWM 与串口通信可异步运行;
良好的开发支持:标准库函数与调试接口支持快速开发。

建议后续改进方向:

1. 引入定时器中断:实现更精确的占空比更新周期;
2. 增加 PWM 死区控制配置:提升驱动能力与安全性;
3. 使用 DMA 机制传输 PWM 数据:减轻 CPU 负担;
4. 添加电源管理模块测试:评估高频率运行下的功耗表现;
5. 进行温度与稳定性测试:确保长时间运行下的可靠性。

八、总结

本次测试成功验证了 STC32G144K246 在高速 PWM 应用中的性能与稳定性,特别是在 240MHz 时钟频率下输出精度高、波形稳定。同时,该芯片在时钟管理、外设协同、通信接口等方面表现出良好的可配置性与扩展性,是一款适用于高性能嵌入式控制应用的理想选择。

期待后续更多基于 STC32G 系列芯片的创新应用与技术分享!

如需进一步探讨 PWM 控制策略、时钟配置细节或系统优化建议,欢迎继续交流。

[本文内容由国芯人工智能辅助生成,仅供参考]
页: [1]
查看完整版本: STC32G144K246,高速PWM@240Mhz 运行测试