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接近

程序内容:

  1. #include "STC32G.H"
  2. #include "stdio.h"
  3. #include "stdarg.h"
  4. //本例程使用CHIPID内预置参数,设置HIRC为24MHz
  5. //使用HPLL1,提供60Mhz,80Mhz,120Mhz的设置例程
  6. #define Fosc_60Mhz 0 //系统时钟为60Mhz
  7. #define Fosc_80Mhz 1 //系统时钟为80Mhz
  8. #define Fosc_120Mhz 2//系统时钟为120Mhz
  9. #define Main_Fosc Fosc_120Mhz //设置系统时钟为120Mhz
  10. void CLK_Init(void); //设置系统时钟,由Main_Fosc定义设置
  11. void Timer0_Init(void); //定时器0初始化函数
  12. void Io_Init(void); //I/O口初始化函数,设置P32为开漏+打开内部上拉电阻模式
  13. void Uart1_Init(void); //串口初始化函数,500000bps
  14. void Pwm_Init(void); //PWM初始化函数
  15. void UpdatePwm(void); //更新PWM占空比函数
  16. void UpdateHSPwm(void); //更新高速PWM占空比函数
  17. void WritePWMA(char addr, char dat); //异步方式写入PWM寄存器
  18. char ReadPWMA(char addr); //异步方式读出PWM寄存器
  19. void uart_send(int num);
  20. bit P32_OUT = 1; //用于确定输出电平
  21. char uart_buff[64] = {0};
  22. unsigned int PWM1_Duty = 100, PWM2_Duty = 500, PWM3_Duty = 2000, PWM4_Duty = 4000, Duty_All = 0;
  23. void Delay100ms(void) //@120MHz
  24. {
  25. unsigned long edata i;
  26. nop();
  27. nop();
  28. i = 2999998UL;
  29. while (i) i--;
  30. }
  31. unsigned int read_pwmh = 0, read_pwml = 0;
  32. void main(void)
  33. {
  34. EAXFR = 1; //使能访问扩展RAM区特殊功能寄存器(XFR)
  35. CKCON &= ~0x07; //清空[2:0],设置外部数据总线等待时钟为0(最快),默认为7
  36. CLK_Init(); //设置HPLL时钟为指定频率
  37. Timer0_Init(); //初始化定时器0,50毫秒@120MHz
  38. Uart1_Init(); //串口初始化函数,115200bps
  39. Pwm_Init(); //PWM初始化函数
  40. HSPWMA_CFG = 0x03; //使能 PWMA 相关寄存器异步访问功能
  41. HPLL2CR &= ~(3<<5); //清空高速外设时钟选择
  42. HPLL2CR |= (0<<5); //选择HPLL1/2
  43. Io_Init(); //初始化I/O口,设置P32等效为原准双向口模式(开漏模式+打开内部上拉电阻)
  44. EA = 1; //打开总中断
  45. while(1)
  46. {
  47. //用户程序
  48. Duty_All = (Duty_All+150)&0xfff;//限制最大值4095
  49. PWM1_Duty = PWM2_Duty = PWM3_Duty = PWM4_Duty = Duty_All;//调整占空比
  50. //UpdatePwm();//更新占空比
  51. UpdateHSPwm();
  52. read_pwmh = (unsigned char)ReadPWMA((char)&PWMA_CCR1H);
  53. read_pwml = (unsigned char)ReadPWMA((char)&PWMA_CCR1L);
  54. uart_send(sprintf(uart_buff, "read_pwm_cnt:0x%x%x, duty:%u\r\n",read_pwmh, read_pwml, Duty_All));//串口回传当前占空比
  55. Delay100ms();
  56. }
  57. }
  58. bit uart_flag = 0;
  59. void send_dat(char c)
  60. {
  61. uart_flag = 1;
  62. SBUF = c;
  63. while(uart_flag);
  64. }
  65. int data dat_len = 0;
  66. void uart_send(int num)
  67. {
  68. for(dat_len = 0; dat_len<num; dat_len++)
  69. {
  70. send_dat(uart_buff[dat_len]);
  71. }
  72. }
  73. void Uart1_Isr(void) interrupt 4
  74. {
  75. if (TI) //检测串口1发送中断
  76. {
  77. TI = 0; //清除串口1发送中断请求位
  78. uart_flag = 0;
  79. }
  80. if (RI) //检测串口1接收中断
  81. {
  82. RI = 0; //清除串口1接收中断请求位
  83. }
  84. }
  85. void Uart1_Init(void) //115200bps@120MHz
  86. {
  87. SCON = 0x50; //8位数据,可变波特率
  88. AUXR |= 0x40; //定时器时钟1T模式
  89. AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
  90. TMOD &= 0x0F; //设置定时器模式
  91. TL1 = 0xFC; //设置定时初始值
  92. TH1 = 0xFE; //设置定时初始值
  93. ET1 = 0; //禁止定时器中断
  94. TR1 = 1; //定时器1开始计时
  95. ES = 1; //使能串口1中断
  96. }
  97. char data off_t0_cnt = 0;
  98. void Timer0_Isr(void) interrupt 1
  99. {
  100. if(P32_OUT == 1&&P32 == 0&&off_t0_cnt<100)off_t0_cnt++; //判断外部P32按键按下一定时间时,关闭定时器0
  101. if(off_t0_cnt>5)TR0 = 0;//注:仅在P32输出为1的时候,外部的按键按下才能被读到
  102. P32_OUT = ~P32_OUT;//每隔10ms亮/灭切换一次
  103. P32 = P32_OUT; //将输出电平给P32管脚
  104. }
  105. void Timer0_Init(void) //50毫秒@120MHz
  106. {
  107. TM0PS = 0x5B; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
  108. AUXR |= 0x80; //定时器时钟1T模式
  109. TMOD &= 0xF0; //设置定时器模式
  110. TL0 = 0x3F; //设置定时初始值
  111. TH0 = 0x01; //设置定时初始值
  112. TF0 = 0; //清除TF0标志
  113. TR0 = 1; //定时器0开始计时
  114. ET0 = 1; //使能定时器0中断
  115. T0CLKO = 1; //使能P35输出定时器溢出时钟
  116. }
  117. void Io_Init(void)
  118. {
  119. P3M0 = 0x26; P3M1 = 0xdd;
  120. P3PU = 0x05;
  121. P3SR = 0xdc;
  122. P3DR = 0xdc;
  123. P6M0 = 0xff; P6M1 = 0x00; //推挽输出,P6,PWM
  124. P6SR = 0x00; //转换速度和驱动电流最大,P6
  125. P6DR = 0x00;
  126. }
  127. void Delay10ms(void) //@120MHz
  128. {
  129. unsigned long edata i;
  130. nop();
  131. nop();
  132. i = 299998UL;
  133. while (i) i--;
  134. }
  135. void CLK_Init(void)
  136. {
  137. #if Main_Fosc == Fosc_120Mhz
  138. WTST = 4;CLKDIV = 2; //设置系统时钟=480MHz/2/2=120MHz,(因为CLKSEL选择时,已经将HPLL/2了)
  139. #elif Main_Fosc == Fosc_80Mhz
  140. WTST = 3;CLKDIV = 3; //设置系统时钟=480MHz/2/3=80MHz
  141. #elif Main_Fosc == Fosc_60Mhz
  142. WTST = 2;CLKDIV = 4; //设置系统时钟=480MHz/2/4=60MHz
  143. #endif
  144. //以下为超过60MHz时,系统时钟使用HPLL方式提供
  145. VRTRIM = CHIPID22; //载入27MHz频段的VRTRIM值
  146. IRTRIM = CHIPID12; //指定当前HIRC为24MHz,此时会覆盖掉ISP设置的时钟频率
  147. IRCBAND &= ~0x03; //清空频段选择
  148. IRCBAND |= 0x01; //选择27Mhz频段
  149. HPLLCR &= ~0x10; //选择HPLL输入时钟源为HIRC
  150. HPLLPDIV = 4; //24MHz/4=6MHz,需要保证输入HPLL的时钟在6MHz附近
  151. HPLLCR |= 0x0e; //HPLL=6MHz*80=480MHz
  152. HPLLCR |= 0x80; //使能HPLL
  153. Delay10ms();
  154. CLKSEL &= ~0x03; //BASE_CLK选择为HIRC,用以提供给HPLL
  155. CLKSEL &= ~0x0c; //清空主时钟源选择
  156. CLKSEL |= 1<<2; //设置主时钟源为内部 HPLL1 输出/2
  157. }
  158. #define PWM1_1 0x00 //P:P1.0 N:P1.1
  159. #define PWM1_2 0x01 //P:P2.0 N:P2.1
  160. #define PWM1_3 0x02 //P:P6.0 N:P6.1
  161. #define PWM2_1 0x00 //P:P1.2/P5.4 N:P1.3
  162. #define PWM2_2 0x04 //P:P2.2 N:P2.3
  163. #define PWM2_3 0x08 //P:P6.2 N:P6.3
  164. #define PWM3_1 0x00 //P:P1.4 N:P1.5
  165. #define PWM3_2 0x10 //P:P2.4 N:P2.5
  166. #define PWM3_3 0x20 //P:P6.4 N:P6.5
  167. #define PWM4_1 0x00 //P:P1.6 N:P1.7
  168. #define PWM4_2 0x40 //P:P2.6 N:P2.7
  169. #define PWM4_3 0x80 //P:P6.6 N:P6.7
  170. #define PWM4_4 0xC0 //P:P3.4 N:P3.3
  171. #define ENO1P 0x01
  172. #define ENO1N 0x02
  173. #define ENO2P 0x04
  174. #define ENO2N 0x08
  175. #define ENO3P 0x10
  176. #define ENO3N 0x20
  177. #define ENO4P 0x40
  178. #define ENO4N 0x80
  179. void Pwm_Init(void)
  180. {
  181. PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
  182. PWMA_CCER2 = 0x00;
  183. PWMA_CCMR1 = 0x60; //通道模式配置
  184. PWMA_CCMR2 = 0x60;
  185. PWMA_CCMR3 = 0x60;
  186. PWMA_CCMR4 = 0x60;
  187. PWMA_CCER1 = 0x55; //配置通道输出使能和极性
  188. PWMA_CCER2 = 0x55;
  189. PWMA_ARRH = 0x0f; //设置周期时间
  190. PWMA_ARRL = 0xff;
  191. PWMA_DTR = 0x40; //设置死区时间
  192. PWMA_PSCRH = 0; //设置预分频器
  193. PWMA_PSCRL = 0; //设置预分频器
  194. PWMA_ENO = 0x00;
  195. PWMA_ENO |= ENO1P; //使能输出
  196. PWMA_ENO |= ENO1N; //使能输出
  197. PWMA_ENO |= ENO2P; //使能输出
  198. PWMA_ENO |= ENO2N; //使能输出
  199. PWMA_ENO |= ENO3P; //使能输出
  200. PWMA_ENO |= ENO3N; //使能输出
  201. PWMA_ENO |= ENO4P; //使能输出
  202. PWMA_ENO |= ENO4N; //使能输出
  203. PWMA_PS = 0x00; //高级 PWM 通道输出脚选择位
  204. PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
  205. PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
  206. PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
  207. PWMA_PS |= PWM4_3; //选择 PWM4_3 通道
  208. UpdatePwm();
  209. PWMA_BKR = 0x80; //使能主输出
  210. PWMA_CR1 |= 0x01; //开始计时
  211. }
  212. void UpdatePwm(void)
  213. {
  214. PWMA_CCR1H = (unsigned char)(PWM1_Duty >> 8); //设置占空比时间
  215. PWMA_CCR1L = (unsigned char)(PWM1_Duty);
  216. PWMA_CCR2H = (unsigned char)(PWM2_Duty >> 8); //设置占空比时间
  217. PWMA_CCR2L = (unsigned char)(PWM2_Duty);
  218. PWMA_CCR3H = (unsigned char)(PWM3_Duty >> 8); //设置占空比时间
  219. PWMA_CCR3L = (unsigned char)(PWM3_Duty);
  220. PWMA_CCR4H = (unsigned char)(PWM4_Duty >> 8); //设置占空比时间
  221. PWMA_CCR4L = (unsigned char)(PWM4_Duty);
  222. }
  223. void UpdateHSPwm(void)
  224. {
  225. WritePWMA((char)&PWMA_CCR1H,(unsigned char)(PWM1_Duty >> 8));
  226. WritePWMA((char)&PWMA_CCR1L,(unsigned char)(PWM1_Duty));
  227. WritePWMA((char)&PWMA_CCR2H,(unsigned char)(PWM2_Duty >> 8));
  228. WritePWMA((char)&PWMA_CCR2L,(unsigned char)(PWM2_Duty));
  229. WritePWMA((char)&PWMA_CCR3H,(unsigned char)(PWM3_Duty >> 8));
  230. WritePWMA((char)&PWMA_CCR3L,(unsigned char)(PWM3_Duty));
  231. WritePWMA((char)&PWMA_CCR4H,(unsigned char)(PWM4_Duty >> 8));
  232. WritePWMA((char)&PWMA_CCR4L,(unsigned char)(PWM4_Duty));
  233. }
  234. char ReadPWMA(char addr)
  235. {
  236. char dat;
  237. while (HSPWMA_ADR & 0x80);//等待前一个异步读写完成
  238. HSPWMA_ADR = addr | 0x80;//设置间接访问地址,只需要设置原 XFR 地址的低 7 位。HSPWMA_ADR 寄存器的最高位写 1,表示读数据
  239. while (HSPWMA_ADR & 0x80);//等待当前异步读取完成
  240. dat = HSPWMA_DAT;//读取异步数据
  241. return dat;
  242. }
  243. void WritePWMA(char addr, char dat)
  244. {
  245. while (HSPWMA_ADR & 0x80);//等待前一个异步读写完成
  246. HSPWMA_DAT = dat;//准备需要写入的数据
  247. HSPWMA_ADR = addr & 0x7f;//设置间接访问地址,只需要设置原 XFR 地址的低 7 位。HSPWMA_ADR 寄存器的最高位写 0,表示写数据
  248. }
相关推荐
禾仔仔4 小时前
USB2.0枚举流程(以鼠标为例)——从零开始学习USB2.0协议(四)
嵌入式硬件·mcu·计算机外设·1024程序员节
Despacito0o4 小时前
Keil MDK-ARM 5.42a 完整安装指南(2025.4.19最新版)
arm开发·stm32·单片机·嵌入式硬件·物联网·51单片机·嵌入式实时数据库
倔强吧!青铜4 小时前
嵌入式八股文总结(ARM篇)
单片机·嵌入式硬件
溜追4 小时前
OEC-Turbo刷群晖&Armbian流程记录
linux·经验分享·嵌入式硬件
LCMICRO-133108477465 小时前
长芯微LDUM3160完全P2P替代ADUM3160,LDUM3160是一款采用ADI公司iCoupler® 技术的USB端口隔离器
网络·stm32·单片机·嵌入式硬件·网络协议·fpga开发·硬件工程
趙小贞7 小时前
UART 串口协议详解与 STM32 实战实现
stm32·单片机·嵌入式硬件·通信协议·1024程序员节
xyx-3v7 小时前
STM32F1和STM32F4在配置硬件SPI1时有什么不同?
stm32·单片机·嵌入式硬件
小莞尔9 小时前
【51单片机】【protues仿真】基于51单片机智能温控风扇系统
c语言·单片机·嵌入式硬件·物联网·51单片机·1024程序员节
集和诚JHCTECH9 小时前
赋能边缘智能:BRAV-7722搭载全新Edge BMC模块,开启远程运维新纪元!
人工智能·嵌入式硬件