黄色线就是中断中取反的信号,粉色线是变动的占空比。
核心思想是通过中心对齐模式+启动前调整DIR方向,然后设置RCR为1,即可实现可控的固定中心触发
HSPWM_ADC_示例工程.zip
(18.03 KB, 下载次数: 12)
黄色是ADC中断(P01),蓝色是下管的PWM(P25),红色是上管的PWM(P24)
- /* ============================================================
- * STC32G12K128 HSPWM + ADC 下管低电平中点触发 DEMO
- * ------------------------------------------------------------
- * 客户要求:
- * - HSPWM 20 kHz,PWM_DTR=250,P2.4/P2.5 互补
- * - ADC 在 P2.5(下管)低电平中心触发
- * - ADC 中断翻转 P0.1,用于和 PWM 波形对比
- *
- * 关键:
- * 启动前预置 DIR,再进入 CMS=01,让其按 DIR 方向上下计数:
- * 1. 中央对齐模式 1:CMS=01;
- * 2. RCR=1:每两个半周期一次更新事件;
- * 3. 中心选择由 PWM_ADC_CENTER_LOW 宏决定:
- * 1 = P2.5 低电平中心(DIR=1)
- * 0 = P2.5 高电平中心(DIR=0)
- * 3. PWMA_CR2.MMS=010:更新事件作为 TRGO 触发 ADC;
- * 4. ADC_CONTR.ADC_EPWMT=1:ADC 由 PWM 硬件触发。
- *
- * 异步访问规则:
- * - 一次异步写入只写一个完整值,不做读改写、与或非;
- * - 先写 RCR;
- * - 先在 CMS=00 下预置 DIR=1;
- * - 保持 DIR=1 切入 CMS=01;
- * - 最后 CEN=1;
- * - 不写 EGR。
- *
- * ADC 采样时间也缩短:
- * ADCCFG=0x00(SPEED=0)
- * ADCTIM=0x2A(CSSETUP=0, CSHOLD=1, SMPDUTY=10)
- * 约 1.8 us 转换,P0.1 翻转更贴近中点。
- *
- * 时钟:
- * IRC 24M → PLL 144M → HSPWM 144M
- * PWMA 计数时钟 72M,ARR=1800 → 20 kHz
- * 系统时钟 28.8M
- * ============================================================ */
-
- #include "STC32G.h"
- #include "intrins.h"
-
- typedef unsigned char u8;
- typedef unsigned int u16;
- typedef unsigned long u32;
-
- #define FOSC 28800000UL
- #define PWM_ARR 1800
- #define PWM_DTR_VALUE 250
-
- #define DUTY_MIN10 220
- #define DUTY_MAX10 8100
- #define DUTY_STEP10 8
-
- /* ADC 触发中心选择:
- * 1 = P2.5 低电平中心(默认)
- * 0 = P2.5 高电平中心
- *
- * 只改这一个宏,重新编译即可切换中心。
- */
- #define PWM_ADC_CENTER_LOW 1
-
- #if PWM_ADC_CENTER_LOW
- /* DIR=1,CMS=00 / CMS=01 / CEN=1 三种完整值 */
- #define PWM_CR1_DIR_PRESET 0x90
- #define PWM_CR1_DIR_CMS 0xB0
- #define PWM_CR1_START 0xB1
- #else
- /* DIR=0,CMS=00 / CMS=01 / CEN=1 三种完整值 */
- #define PWM_CR1_DIR_PRESET 0x80
- #define PWM_CR1_DIR_CMS 0xA0
- #define PWM_CR1_START 0xA1
- #endif
-
- volatile u16 duty10 = DUTY_MIN10;
- volatile u8 duty_dir = 1;
- volatile u32 adc_beat = 0;
- volatile u32 tick_ms = 0;
- volatile u8 duty_tick = 0; /* Timer0 只置标志,占空比在 main 中更新 */
-
- static void Duty_Update(void);
-
- static void Delay_ms(u16 ms)
- {
- u16 i, j;
- for (i = 0; i < ms; i++)
- for (j = 0; j < (u16)(FOSC / 12000); j++);
- }
-
- static void WritePWMA(u8 addr, u8 dat)
- {
- while (HSPWMA_ADR & 0x80);
- HSPWMA_DAT = dat;
- HSPWMA_ADR = addr & 0x7F;
- while (HSPWMA_ADR & 0x80); /* 等待本次异步写完成 */
- }
-
- static void System_Init(void)
- {
- EAXFR = 1;
-
- CLKSEL &= ~0x80;
- CLKSEL |= 0x80;
- USBCLK &= ~0x60;
- USBCLK |= 0x20;
- USBCLK |= 0x80;
- Delay_ms(1);
-
- HSCLKDIV = 0;
- CLKSEL |= 0x40;
-
- CLKDIV = 5;
- CLKSEL &= 0xF0;
- CLKSEL |= 0x04;
- }
-
- static void GPIO_Init(void)
- {
- P2M1 &= ~0x30;
- P2M0 |= 0x30;
-
- P0M1 &= ~0x02;
- P0M0 |= 0x02;
- P01 = 0;
-
- P1M1 |= 0x3B;
- P1M0 &= ~0x3B;
- }
-
- static void PWM_Init(void)
- {
- u16 cc;
-
- HSPWMA_CFG = 0x07; /* INTEN=1, ASYNCEN=1 */
- PWMA_PS = 0x10;
-
- WritePWMA((u8)&PWMA_CCER2, 0x00);
- WritePWMA((u8)&PWMA_CCMR3, 0x68);
-
- WritePWMA((u8)&PWMA_PSCRH, 0x00);
- WritePWMA((u8)&PWMA_PSCRL, 0x01);
- WritePWMA((u8)&PWMA_ARRH, (u8)(PWM_ARR >> 8));
- WritePWMA((u8)&PWMA_ARRL, (u8)(PWM_ARR & 0xFF));
-
- cc = (u16)(duty10 / 10);
- WritePWMA((u8)&PWMA_CCR3H, (u8)(cc >> 8));
- WritePWMA((u8)&PWMA_CCR3L, (u8)(cc & 0xFF));
-
- WritePWMA((u8)&PWMA_DTR, PWM_DTR_VALUE);
-
- /* 更新事件触发 ADC:MMS=010 */
- WritePWMA((u8)&PWMA_CR2, 0x20);
-
- WritePWMA((u8)&PWMA_ENO, 0x30);
- WritePWMA((u8)&PWMA_CCER2, 0x05);
-
- WritePWMA((u8)&PWMA_BKR, 0x80);
-
- /* 先设置 RCR = 1 */
- WritePWMA((u8)&PWMA_RCR, 1);
-
- /* 启动前预置 DIR(此时还在 CMS=00) */
- WritePWMA((u8)&PWMA_CR1, PWM_CR1_DIR_PRESET);
-
- /* 保持 DIR,切入 CMS=01 */
- WritePWMA((u8)&PWMA_CR1, PWM_CR1_DIR_CMS);
-
- WritePWMA((u8)&PWMA_SR1, 0xFE); /* 清 UIF */
-
- /* 启动前写入 CNTR = 0,随后按 DIR=1 的方向开始上下计数 */
- WritePWMA((u8)&PWMA_CNTRH, 0x00);
- WritePWMA((u8)&PWMA_CNTRL, 0x00);
-
- WritePWMA((u8)&PWMA_CR1, PWM_CR1_START);
- }
-
- static void ADC_Init(void)
- {
- /* 缩短 ADC 转换时间:SPEED=0,SMPDUTY=10,约 1.8us */
- ADCCFG = 0x00;
- ADCTIM = 0x2A;
-
- ADC_CONTR = 0x90; /* ADC_POWER=1, ADC_EPWMT=1 */
- ADC_CONTR = (ADC_CONTR & 0xF0) | 0x04; /* ADC 通道 4 */
- ADC_CONTR &= ~0x20; /* 清 ADC_FLAG */
- EADC = 1;
- }
-
- static void Timer0_Init(void)
- {
- TMOD &= 0xF0;
- TMOD |= 0x00;
- TM0PS = 0;
- TL0 = (u8)((65536UL - FOSC / 12 / 1000) & 0xFF);
- TH0 = (u8)(((65536UL - FOSC / 12 / 1000) >> 8) & 0xFF);
- ET0 = 1;
- TR0 = 1;
- }
-
- void main(void)
- {
- EA = 0;
-
- System_Init();
- GPIO_Init();
- PWM_Init();
- ADC_Init();
- Timer0_Init();
-
- /* ADC 高优先级,Timer0 低优先级:避免 Timer0 阻塞 ADC 中断 */
- PADC = 1; PADCH = 1;
- PT0 = 0; PT0H = 0;
- PPWMA = 0; PPWMAH = 0;
-
- EA = 1;
-
- while (1)
- {
- if (duty_tick)
- {
- duty_tick--;
- Duty_Update();
- }
- }
- }
-
- void Timer0_ISR(void) interrupt 1
- {
- tick_ms++;
- if (duty_tick < 255) duty_tick++;
- }
-
- /* 占空比更新放在 main 中,Timer0 中断只置标志。
- * 这样 Timer0 中断很短,不会用 HSPWM 异步写去阻塞 ADC 中断。
- */
- static void Duty_Update(void)
- {
- u16 cc;
-
- if (duty_dir)
- {
- duty10 += DUTY_STEP10;
- if (duty10 >= DUTY_MAX10)
- {
- duty10 = DUTY_MAX10;
- duty_dir = 0;
- }
- }
- else
- {
- if (duty10 >= (DUTY_MIN10 + DUTY_STEP10))
- duty10 -= DUTY_STEP10;
- else
- {
- duty10 = DUTY_MIN10;
- duty_dir = 1;
- }
- }
-
- cc = (u16)(duty10 / 10);
- WritePWMA((u8)&PWMA_CCR3H, (u8)(cc >> 8));
- WritePWMA((u8)&PWMA_CCR3L, (u8)(cc & 0xFF));
- }
-
- void ADC_ISR(void) interrupt 5
- {
- ADC_CONTR &= ~0x20; /* 清 ADC_FLAG */
- P01 = ~P01; /* 与 PWM 波形对比 */
- adc_beat++;
- }
复制代码
|