结论:AI首次输出的代码编译正常,逻辑有点小问题,通过对话可让其纠正。
======================
难度提升,带死区设置的互补pwm输出,提问:
STC8H, PWM应用,PWM1P,1N输出互补PWM
根据接收到的串口命令,
调整PWM输出的占空比、频率、死区的PWM通用演示程序
编译下载默认:
更新死区时间:
设置100时串口提示大约18000 ns,实际测量只有9us
错误分析:
修正后:
- /******************************************************************************
- * 文件名:main.c
- * MCU:STC8H1K08-36I-TSSOP20(兼容所有STC8H系列)
- * 功能:通过串口命令动态调节互补PWM输出的频率、占空比和死区时间
- * 主频:11.0592MHz(内部IRC,宏可修改)
- * PWM输出:P1.0 (PWM1P) + P1.1 (PWM1N) 互补带死区
- * 串口:UART1, P3.0/P3.1, 115200-8-N1
- *
- * 命令格式(以 0x0D 或 0x0A 结尾):
- * F=1000 → 设置频率为 1000 Hz(10 ~ 500000)
- * D=50 → 设置占空比为 50% (0 ~ 100)
- * T=20 → 设置死区寄存器值 (0 ~ 255)
- * ? → 查询当前频率、占空比、死区值及实际死区时间
- *
- * 编译环境:Keil C51,需包含 stc8h.h 头文件
- *****************************************************************************/
-
- #include "stc8h.h"
- #include "intrins.h"
-
- // ========================== 用户可修改宏 ==================================
- #define MAIN_Fosc 11059200L // 系统主频(Hz)
- #define UART_BAUD 115200 // 串口波特率
- #define PWM_DEFAULT_FREQ 1000 // 默认PWM频率(Hz)
- #define PWM_DEFAULT_DUTY 50 // 默认占空比(%)
- #define PWM_DEFAULT_DTR 20 // 默认死区寄存器值 (0~255)
- // ========================================================================
-
- // 波特率重装载值(T2做波特率发生器,4倍采样,1T模式)
- #define BRT_VAL (65536UL - (MAIN_Fosc / 4 / UART_BAUD))
-
- // ---------- 类型定义 ----------
- typedef unsigned char u8;
- typedef unsigned int u16;
- typedef unsigned long u32;
-
- // ---------- 全局变量 ----------
- volatile u8 Rx_Ready; // 收到完整命令标志
- volatile u8 Rx_Buffer[32]; // 接收缓冲区
- volatile u8 Rx_Index; // 当前接收位置
-
- u32 PWM_Freq = PWM_DEFAULT_FREQ; // 当前PWM频率(Hz)
- u16 PWM_Duty = PWM_DEFAULT_DUTY; // 当前占空比(0~100)
- u16 PWM_Psc = 0; // 当前预分频值(保存用于死区计算)
- u16 PWM_Arr = 0; // 当前自动重装载值
- u8 PWM_Dtr = PWM_DEFAULT_DTR; // 当前死区寄存器值
-
- // ---------- 函数声明 ----------
- void UART1_Init(void);
- void PWM_Init(void);
- void PWM_Update(u32 freq, u16 duty, u8 dtr);
- void Cmd_Parse(void);
- void Print_String(u8 *p);
- void Print_Num(u32 val);
- void Print_Prompt(void);
- u32 Calc_DeadTime_ns(u8 dtr, u16 psc);
-
- // ============================ 主函数 ====================================
- void main(void)
- {
- // 使能XFR访问(PWM寄存器在XFR区)
- P_SW2 |= 0x80;
-
- // 所有IO设置为准双向口
- P0M0 = 0x00; P0M1 = 0x00;
- P1M0 = 0x00; P1M1 = 0x00;
- P2M0 = 0x00; P2M1 = 0x00;
- P3M0 = 0x00; P3M1 = 0x00;
- P4M0 = 0x00; P4M1 = 0x00;
- P5M0 = 0x00; P5M1 = 0x00;
-
- // 初始化外设
- UART1_Init();
- PWM_Init();
-
- // 使能中断
- ES = 1; // 串口中断
- EA = 1; // 总中断
-
- // 输出开机提示
- Print_Prompt();
-
- while(1)
- {
- if(Rx_Ready)
- {
- Rx_Ready = 0;
- Cmd_Parse(); // 解析并执行命令
- }
- }
- }
-
- // ======================== 串口1初始化 ===================================
- // 使用定时器T2作为波特率发生器,模式1(8位可变波特率)
- void UART1_Init(void)
- {
- SCON = 0x50; // 模式1, 8位UART, REN使能接收
-
- // 定时器2配置:1T模式,作为波特率发生器
- AUXR &= ~0x10; // T2停止
- AUXR &= ~0x08; // T2设为定时器
- AUXR |= 0x04; // T2设为1T模式
- T2H = (u8)(BRT_VAL >> 8); // 装载波特率重装值
- T2L = (u8)(BRT_VAL);
- AUXR |= 0x10; // T2启动
- AUXR |= 0x01; // 串口1选择T2做波特率发生器
-
- Rx_Index = 0;
- Rx_Ready = 0;
- }
-
- // ======================== PWM初始化 =====================================
- // 使用 PWMA 通道1,P1.0(PWM1P) + P1.1(PWM1N) 互补输出带死区
- // 边沿对齐,向上计数模式,PWM模式1
- void PWM_Init(void)
- {
- // ---- 关闭所有通道,写CCMRx前必须先清零CCERx ----
- PWMA_CCER1 = 0x00;
-
- // ---- 配置通道1为PWM模式1(带预装载) ----
- PWMA_CCMR1 = 0x68; // 0110 1000
-
- // ---- 通道1输出脚选择:C1PS=00 → PWM1P=P1.0, PWM1N=P1.1 ----
- PWMA_PS &= ~0x03;
-
- // ---- 使能 PWM1P 和 PWM1N 端口输出 ----
- PWMA_ENO = 0x03; // ENO1P=1 (bit0), ENO1N=1 (bit1)
-
- // ---- 使能互补输出,高电平有效 ----
- // CC1E=1 (bit0), CC1P=0 (bit1)
- // CC1NE=1 (bit2), CC1NP=0 (bit3)
- PWMA_CCER1 = 0x05; // 0000 0101
-
- // ---- 设置初始参数 ----
- PWM_Update(PWM_Freq, PWM_Duty, PWM_Dtr);
-
- // ---- 使能主输出,启动计数 ----
- PWMA_BKR = 0x80; // MOE=1 主输出使能
- PWMA_CR1 |= 0x01; // CEN=1 启动计数
- }
-
- // ======================== 更新PWM参数 ===================================
- // freq: 目标频率(Hz) 范围:10 ~ 500000
- // duty: 占空比百分比 范围:0 ~ 100
- // dtr: 死区寄存器值 范围:0 ~ 255
- //
- // 频率公式:freq = SYSCLK / (PSC+1) / (ARR+1)
- // 自动选择 PSC 使 ARR 不溢出16位
- void PWM_Update(u32 freq, u16 duty, u8 dtr)
- {
- u16 arr, ccr;
- u16 psc;
-
- if(freq == 0) freq = 1;
-
- // ---- 自动计算预分频 ----
- if(freq < (MAIN_Fosc / 65536UL))
- {
- psc = (u16)(MAIN_Fosc / freq / 65536UL);
- if(psc > 0) psc--;
- arr = (u16)(MAIN_Fosc / freq / (psc + 1) - 1);
- }
- else
- {
- psc = 0;
- arr = (u16)(MAIN_Fosc / freq - 1);
- }
-
- if(arr > 65535) arr = 65535;
- if(arr < 1) arr = 1;
-
- // ---- 占空比计算 ----
- if(duty == 0)
- ccr = 0;
- else if(duty >= 100)
- ccr = arr + 1; // 100%:CCR > ARR
- else
- ccr = (u16)((u32)(arr + 1) * duty / 100);
-
- // ---- 写入寄存器 ----
- PWMA_PSCRH = (u8)(psc >> 8);
- PWMA_PSCRL = (u8)(psc);
- PWMA_ARRH = (u8)(arr >> 8);
- PWMA_ARRL = (u8)(arr);
- PWMA_CCR1H = (u8)(ccr >> 8);
- PWMA_CCR1L = (u8)(ccr);
- PWMA_DTR = dtr; // 死区寄存器
-
- // 产生更新事件,使新值立即生效
- PWMA_EGR |= 0x01; // UG=1
-
- // 保存当前值
- PWM_Freq = freq;
- PWM_Duty = duty;
- PWM_Dtr = dtr;
- PWM_Psc = psc;
- PWM_Arr = arr;
- }
-
- // ======================== 串口中断服务 ==================================
- void UART1_ISR(void) interrupt 4
- {
- u8 dat;
-
- if(RI)
- {
- RI = 0;
- dat = SBUF;
-
- // 回车或换行 → 结束一帧命令
- if(dat == 0x0D || dat == 0x0A)
- {
- if(Rx_Index > 0)
- {
- Rx_Buffer[Rx_Index] = '\0';
- Rx_Ready = 1;
- Rx_Index = 0;
- }
- }
- else
- {
- if(Rx_Index < sizeof(Rx_Buffer) - 1)
- {
- Rx_Buffer[Rx_Index++] = dat;
- }
- }
- }
-
- if(TI)
- {
- TI = 0;
- }
- }
-
- // ======================== 命令解析与执行 ================================
- void Cmd_Parse(void)
- {
- u8 *p = (u8 *)Rx_Buffer;
- u32 val = 0;
-
- while(*p == ' ') p++;
-
- // ---- 查询命令 ----
- if(*p == '?' || *p == 'H' || *p == 'h')
- {
- u32 dt_ns;
-
- Print_String("\r\n--- Complementary PWM Status ---\r\n");
-
- Print_String("Freq : ");
- Print_Num(PWM_Freq);
- Print_String(" Hz\r\n");
-
- Print_String("Duty : ");
- Print_Num(PWM_Duty);
- Print_String(" %\r\n");
-
- Print_String("DTR : ");
- Print_Num(PWM_Dtr);
- Print_String(" (0x");
- if(PWM_Dtr < 16) Print_String("0");
- Print_Num(PWM_Dtr);
- Print_String(")\r\n");
-
- dt_ns = Calc_DeadTime_ns(PWM_Dtr, PWM_Psc);
- Print_String("DeadT: ");
- Print_Num(dt_ns);
- Print_String(" ns\r\n");
-
- Print_String("PSC : ");
- Print_Num(PWM_Psc);
- Print_String("\r\n");
-
- Print_String("ARR : ");
- Print_Num(PWM_Arr);
- Print_String("\r\n");
-
- Print_String("CLK : ");
- Print_Num(MAIN_Fosc);
- Print_String(" Hz\r\n");
- Print_String("Output: P1.0(PWM1P) + P1.1(PWM1N) 互补\r\n");
- Print_String("----------------------------------\r\n");
- return;
- }
-
- // ---- 频率命令: F=xxxx ----
- if((*p == 'F' || *p == 'f') && *(p+1) == '=')
- {
- p += 2;
- val = 0;
- while(*p >= '0' && *p <= '9')
- {
- val = val * 10 + (*p - '0');
- p++;
- }
- if(val >= 10 && val <= 500000)
- {
- PWM_Update(val, PWM_Duty, PWM_Dtr);
- Print_String("\r\nFreq set to ");
- Print_Num(val);
- Print_String(" Hz\r\n");
- }
- else
- {
- Print_String("\r\nError: Freq out of range (10~500000)\r\n");
- }
- return;
- }
-
- // ---- 占空比命令: D=xx ----
- if((*p == 'D' || *p == 'd') && *(p+1) == '=')
- {
- p += 2;
- val = 0;
- while(*p >= '0' && *p <= '9')
- {
- val = val * 10 + (*p - '0');
- p++;
- }
- if(val <= 100)
- {
- PWM_Update(PWM_Freq, (u16)val, PWM_Dtr);
- Print_String("\r\nDuty set to ");
- Print_Num(val);
- Print_String(" %\r\n");
- }
- else
- {
- Print_String("\r\nError: Duty out of range (0~100)\r\n");
- }
- return;
- }
-
- // ---- 死区命令: T=xx ----
- if((*p == 'T' || *p == 't') && *(p+1) == '=')
- {
- p += 2;
- val = 0;
- while(*p >= '0' && *p <= '9')
- {
- val = val * 10 + (*p - '0');
- p++;
- }
- if(val <= 255)
- {
- PWM_Update(PWM_Freq, PWM_Duty, (u8)val);
- Print_String("\r\nDTR set to ");
- Print_Num(val);
- Print_String(" (0x");
- if(val < 16) Print_String("0");
- Print_Num(val);
- Print_String(")\r\n");
-
- {
- u32 ns = Calc_DeadTime_ns((u8)val, PWM_Psc);
- Print_String("DeadTime ~ ");
- Print_Num(ns);
- Print_String(" ns\r\n");
- }
- }
- else
- {
- Print_String("\r\nError: DTR out of range (0~255)\r\n");
- }
- return;
- }
-
- // ---- 未知命令 ----
- Print_String("\r\nUnknown cmd.\r\n");
- Print_String("Usage: F=1000 D=50 T=20 ?\r\n");
- }
-
- // ======================== ★★★ 修正后的死区计算函数 ★★★ ===================
- // 根据 PWMA_DTR 寄存器的分段规则计算实际死区时间
- // tCK_PSC = (PSC + 1) / SYSCLK (单位:秒)
- // 返回值为纳秒
- //
- // 手册 25.9.34 节分段规则:
- // DTG[7:5]=000~011 (DTR 0~127) : DT = DTR × tCK_PSC
- // DTG[7:5]=100~101 (DTR 128~191): DT = (64 + DTR[6:0]) × 2 × tCK_PSC
- // DTG[7:5]=110 (DTR 192~223): DT = (32 + DTR[5:0]) × 8 × tCK_PSC
- // DTG[7:5]=111 (DTR 224~255): DT = (32 + DTR[4:0]) × 16 × tCK_PSC
- u32 Calc_DeadTime_ns(u8 dtr, u16 psc)
- {
- u32 dt_cycles; // 死区占用的 tCK_PSC 周期数
- u32 tck_ns; // tCK_PSC 的纳秒数
-
- // 计算 tCK_PSC(纳秒)
- // tCK_PSC = (psc+1) / SYSCLK 秒
- // tck_ns = (psc+1) * 1,000,000,000 / SYSCLK
- tck_ns = ((u32)(psc + 1) * 1000000000UL) / MAIN_Fosc;
-
- // 判断 DTR 所在的分段范围(依据 bit7 和 bit6)
- if(dtr < 128) // DTG[7]=0 → 第一段
- {
- dt_cycles = dtr; // 0~127 个 tCK_PSC
- }
- else if(dtr < 192) // DTG[7:5]=100~101 → 第二段
- {
- dt_cycles = (u32)(64 + (dtr & 0x7F)) * 2; // 128~254 个 tCK_PSC
- }
- else if(dtr < 224) // DTG[7:5]=110 → 第三段
- {
- dt_cycles = (u32)(32 + (dtr & 0x3F)) * 8; // 256~504 个 tCK_PSC
- }
- else // DTG[7:5]=111 → 第四段
- {
- dt_cycles = (u32)(32 + (dtr & 0x1F)) * 16; // 512~1008 个 tCK_PSC
- }
-
- return dt_cycles * tck_ns;
- }
- // ========================================================================
-
- // ======================== 辅助输出函数 ==================================
- void Print_String(u8 *p)
- {
- while(*p)
- {
- SBUF = *p++;
- while(!TI);
- TI = 0;
- }
- }
-
- void Print_Num(u32 val)
- {
- u8 buf[12];
- u8 i = 0;
-
- if(val == 0)
- {
- SBUF = '0';
- while(!TI); TI = 0;
- return;
- }
-
- while(val > 0)
- {
- buf[i++] = (u8)(val % 10) + '0';
- val /= 10;
- }
- while(i > 0)
- {
- i--;
- SBUF = buf[i];
- while(!TI); TI = 0;
- }
- }
-
- void Print_Prompt(void)
- {
- Print_String("\r\n============================================\r\n");
- Print_String(" STC8H Complementary PWM Ctrl via UART\r\n");
- Print_String(" CLK: "); Print_Num(MAIN_Fosc);
- Print_String(" Hz, BAUD: "); Print_Num(UART_BAUD);
- Print_String("\r\n");
- Print_String(" Output: P1.0(PWM1P) + P1.1(PWM1N)\r\n");
- Print_String(" Commands:\r\n");
- Print_String(" F=1000 set Freq (10~500000 Hz)\r\n");
- Print_String(" D=50 set Duty (0~100 %%)\r\n");
- Print_String(" T=20 set DeadTime Reg (0~255)\r\n");
- Print_String(" ? Query all params\r\n");
- Print_String("============================================\r\n");
- }
复制代码
|