找回密码
 立即注册
查看: 561|回复: 13

帮训练 STC 【AI 助手】一次能将 PWM / 串口 搞对,通知 STC 【AI 助手】他错的原因

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 2026-6-18 10:10:32 | 显示全部楼层 |阅读模式
训练 STC 【AI 助手】一次能将 PWM / 串口 搞对,
通知 STC 【AI 助手】他错的原因,帮他前进

STC8H1K08-36I-TSSOP20, PWM应用,
根据接收到的串口命令,
调整PWM输出的占空比和频率的PWM通用演示程序,
要方便修改,使用 KEIL C51编译器

截图202606181012045988.jpg

截图202606181013031255.jpg

截图202606181013445404.jpg

截图202606181014288463.jpg

截图202606181015291352.jpg

截图202606181015545133.jpg








截图202606181018309569.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 2026-6-18 10:22:39 | 显示全部楼层
截图202606181019314805.jpg


截图202606181019575648.jpg

截图202606181020532754.jpg

截图202606181021143687.jpg

截图202606181021387882.jpg

截图202606181022104274.jpg

截图202606181022372882.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:586
  • 最近打卡:2026-07-04 12:34:03
已绑定手机

112

主题

4958

回帖

1万

积分

荣誉版主

无情的代码机器

积分
10758
发表于 2026-6-18 10:43:48 | 显示全部楼层

结论:AI输出的代码编译运行一把通
======================
提问:
STC8H1K08-36I-TSSOP20, PWM应用,
根据接收到的串口命令,
调整PWM输出的占空比和频率的PWM通用演示程序


截图202606181040557387.jpg

  1. /******************************************************************************
  2. * 文件名:main.c
  3. * MCU:STC8H1K08-36I-TSSOP20
  4. * 功能:通过串口命令动态调整PWM的频率和占空比
  5. * 主频:11.0592MHz(内部IRC)
  6. * PWM输出:P1.0 (PWMA 通道1,PWM1P)
  7. * 串口:UART1, P3.0/P3.1, 115200-8-N1
  8. *
  9. * 命令格式(字符串,以 0x0D 或 0x0A 结尾):
  10. *   F=1000  → 设置频率为 1000 Hz(范围:10 ~ 500000)
  11. *   D=50    → 设置占空比为 50%   (范围:0 ~ 100)
  12. *   ?       → 查询当前频率和占空比
  13. *
  14. * 编译环境:Keil C51,需包含 stc8h.h 头文件
  15. *****************************************************************************/
  16. #include "stc8h.h"
  17. #include "intrins.h"
  18. // ========================== 用户可修改宏 ==================================
  19. #define MAIN_Fosc         11059200L      // 系统主频(Hz),可修改
  20. #define UART_BAUD         115200         // 串口波特率
  21. #define PWM_DEFAULT_FREQ  1000           // 默认PWM频率(Hz)
  22. #define PWM_DEFAULT_DUTY  50             // 默认占空比(%)
  23. // ========================================================================
  24. // 波特率重装载值(T2做波特率发生器,4倍采样,1T模式)
  25. //   BRT = 65536 - SYSCLK / 4 / Baud
  26. #define BRT_VAL  (65536UL - (MAIN_Fosc / 4 / UART_BAUD))
  27. // ---------- 类型定义 ----------
  28. typedef unsigned char  u8;
  29. typedef unsigned int   u16;
  30. typedef unsigned long  u32;
  31. // ---------- 全局变量 ----------
  32. volatile u8  Rx_Ready;              // 收到完整命令标志
  33. volatile u8  Rx_Buffer[32];         // 接收缓冲区
  34. volatile u8  Rx_Index;              // 当前接收位置
  35. u32  PWM_Freq  = PWM_DEFAULT_FREQ;  // 当前PWM频率(Hz)
  36. u16  PWM_Duty  = PWM_DEFAULT_DUTY;  // 当前占空比(0~100)
  37. // ---------- 函数声明 ----------
  38. void UART1_Init(void);
  39. void PWM_Init(void);
  40. void PWM_Update(u32 freq, u16 duty);
  41. void Cmd_Parse(void);
  42. void Print_String(u8 *p);
  43. void Print_Num(u32 val);
  44. void Print_Prompt(void);
  45. // ============================ 主函数 ====================================
  46. void main(void)
  47. {
  48.     u8 i;
  49.     // 使能XFR访问(PWM寄存器在XFR区)
  50.     P_SW2 |= 0x80;
  51.     // 所有IO设置为准双向口
  52.     P0M0 = 0x00; P0M1 = 0x00;
  53.     P1M0 = 0x00; P1M1 = 0x00;
  54.     P2M0 = 0x00; P2M1 = 0x00;
  55.     P3M0 = 0x00; P3M1 = 0x00;
  56.     P4M0 = 0x00; P4M1 = 0x00;
  57.     P5M0 = 0x00; P5M1 = 0x00;
  58.     // 初始化外设
  59.     UART1_Init();
  60.     PWM_Init();
  61.     // 使能中断
  62.     ES = 1;         // 串口中断
  63.     EA = 1;         // 总中断
  64.     // 输出开机提示
  65.     Print_Prompt();
  66.     while(1)
  67.     {
  68.         if(Rx_Ready)
  69.         {
  70.             Rx_Ready = 0;
  71.             Cmd_Parse();                    // 解析并执行命令
  72.         }
  73.     }
  74. }
  75. // ======================== 串口1初始化 ===================================
  76. // 使用定时器T2作为波特率发生器,模式1(8位可变波特率)
  77. void UART1_Init(void)
  78. {
  79.     SCON = 0x50;                            // 模式1, 8位UART, REN使能接收
  80.     // 定时器2配置:1T模式,作为波特率发生器
  81.     AUXR &= ~0x10;                          // T2停止
  82.     AUXR &= ~0x08;                          // T2设为定时器
  83.     AUXR |=  0x04;                          // T2设为1T模式
  84.     T2H = (u8)(BRT_VAL >> 8);              // 装载波特率重装值
  85.     T2L = (u8)(BRT_VAL);
  86.     AUXR |=  0x10;                          // T2启动
  87.     AUXR |=  0x01;                          // 串口1选择T2做波特率发生器
  88.     Rx_Index = 0;
  89.     Rx_Ready = 0;
  90. }
  91. // ======================== PWM初始化 =====================================
  92. // 使用 PWMA 通道1 (PWM1P),默认输出在 P1.0
  93. // 边沿对齐,向上计数模式,PWM模式1
  94. void PWM_Init(void)
  95. {
  96.     // ---- 关闭所有通道,写CCMRx前必须先清零CCERx ----
  97.     PWMA_CCER1 = 0x00;
  98.     // ---- 配置通道1为PWM模式1 ----
  99.     // OC1M[2:0] = 110 (PWM模式1: CNT<CCR时输出有效电平)
  100.     // OC1PE     = 1   (预装载使能)
  101.     PWMA_CCMR1 = 0x68;                      // 0110 1000
  102.     // ---- 通道1使能,高电平有效 ----
  103.     PWMA_CCER1 = 0x01;                      // CC1E=1, CC1P=0
  104.     // ---- 使能 PWM1P 端口输出 ----
  105.     PWMA_ENO  = 0x01;                       // ENO1P=1
  106.     // ---- 通道输出脚选择:默认 C1PS=00 → PWM1P = P1.0 ----
  107.     PWMA_PS  &= ~0x03;                      // C1PS[1:0] = 00
  108.     // ---- 设置初始频率和占空比 ----
  109.     PWM_Update(PWM_Freq, PWM_Duty);
  110.     // ---- 使能主输出,启动计数 ----
  111.     PWMA_BKR  = 0x80;                       // MOE=1 主输出使能
  112.     PWMA_CR1 |= 0x01;                       // CEN=1 启动计数
  113. }
  114. // ======================== 更新PWM参数 ===================================
  115. // freq: 目标频率(Hz)    范围:10 ~ 500000
  116. // duty: 占空比百分比    范围:0 ~ 100
  117. //
  118. // 公式:
  119. //   频率 = SYSCLK / (PSC+1) / (ARR+1)
  120. //   占空比 = CCR / (ARR+1) * 100%
  121. //   自动选择 PSC 使 ARR 不溢出16位
  122. void PWM_Update(u32 freq, u16 duty)
  123. {
  124.     u16 arr, ccr;
  125.     u16 psc;
  126.     if(freq == 0) freq = 1;                 // 防除零
  127.     // ---- 自动计算预分频 ----
  128.     // 先计算不分频(PSC=0)时的ARR
  129.     // ARR = SYSCLK / freq - 1
  130.     if(freq < (MAIN_Fosc / 65536UL))
  131.     {
  132.         // 不分频时ARR会超16位,需要分频
  133.         // 让ARR尽量大以保证精度
  134.         psc = (u16)(MAIN_Fosc / freq / 65536UL);
  135.         if(psc > 0) psc--;
  136.         arr = (u16)(MAIN_Fosc / freq / (psc + 1) - 1);
  137.     }
  138.     else
  139.     {
  140.         psc = 0;
  141.         arr = (u16)(MAIN_Fosc / freq - 1);
  142.     }
  143.     // 边界保护
  144.     if(arr > 65535) arr = 65535;
  145.     if(arr < 1)     arr = 1;
  146.     // ---- 占空比计算 ----
  147.     if(duty == 0)
  148.         ccr = 0;                            // 0%:CCR=0
  149.     else if(duty >= 100)
  150.         ccr = arr + 1;                      // 100%:CCR > ARR
  151.     else
  152.         ccr = (u16)((u32)(arr + 1) * duty / 100);
  153.     // ---- 写入寄存器 ----
  154.     PWMA_PSCRH = (u8)(psc >> 8);
  155.     PWMA_PSCRL = (u8)(psc);
  156.     PWMA_ARRH  = (u8)(arr >> 8);
  157.     PWMA_ARRL  = (u8)(arr);
  158.     PWMA_CCR1H = (u8)(ccr >> 8);
  159.     PWMA_CCR1L = (u8)(ccr);
  160.     // 产生更新事件,使新值立即生效
  161.     PWMA_EGR |= 0x01;                       // UG=1
  162.     // 保存当前值
  163.     PWM_Freq = freq;
  164.     PWM_Duty = duty;
  165. }
  166. // ======================== 串口中断服务 ==================================
  167. void UART1_ISR(void) interrupt 4
  168. {
  169.     u8 dat;
  170.     if(RI)
  171.     {
  172.         RI = 0;
  173.         dat = SBUF;
  174.         // 回车或换行 → 结束一帧命令
  175.         if(dat == 0x0D || dat == 0x0A)
  176.         {
  177.             if(Rx_Index > 0)
  178.             {
  179.                 Rx_Buffer[Rx_Index] = '\0';
  180.                 Rx_Ready = 1;
  181.                 Rx_Index = 0;
  182.             }
  183.         }
  184.         else
  185.         {
  186.             // 防止缓冲区溢出
  187.             if(Rx_Index < sizeof(Rx_Buffer) - 1)
  188.             {
  189.                 Rx_Buffer[Rx_Index++] = dat;
  190.             }
  191.         }
  192.     }
  193.     if(TI)
  194.     {
  195.         TI = 0;
  196.     }
  197. }
  198. // ======================== 命令解析与执行 ================================
  199. void Cmd_Parse(void)
  200. {
  201.     u8 *p = (u8 *)Rx_Buffer;
  202.     u32 val = 0;
  203.     // ---- 跳过前导空格 ----
  204.     while(*p == ' ') p++;
  205.     // ---- 查询命令 ----
  206.     if(*p == '?' || *p == 'H' || *p == 'h')
  207.     {
  208.         Print_String("\r\n--- PWM Status ---\r\n");
  209.         Print_String("Freq: ");
  210.         Print_Num(PWM_Freq);
  211.         Print_String(" Hz\r\n");
  212.         Print_String("Duty: ");
  213.         Print_Num(PWM_Duty);
  214.         Print_String(" %\r\n");
  215.         Print_String("CLK : ");
  216.         Print_Num(MAIN_Fosc);
  217.         Print_String(" Hz\r\n");
  218.         Print_String("------------------\r\n");
  219.         return;
  220.     }
  221.     // ---- 频率命令: F=xxxx ----
  222.     if((*p == 'F' || *p == 'f') && *(p+1) == '=')
  223.     {
  224.         p += 2;
  225.         val = 0;
  226.         while(*p >= '0' && *p <= '9')
  227.         {
  228.             val = val * 10 + (*p - '0');
  229.             p++;
  230.         }
  231.         if(val >= 10 && val <= 500000)      // 范围保护:10Hz ~ 500kHz
  232.         {
  233.             PWM_Update(val, PWM_Duty);
  234.             Print_String("\r\nFreq set to ");
  235.             Print_Num(val);
  236.             Print_String(" Hz\r\n");
  237.         }
  238.         else
  239.         {
  240.             Print_String("\r\nError: Freq out of range (10~500000)\r\n");
  241.         }
  242.         return;
  243.     }
  244.     // ---- 占空比命令: D=xx ----
  245.     if((*p == 'D' || *p == 'd') && *(p+1) == '=')
  246.     {
  247.         p += 2;
  248.         val = 0;
  249.         while(*p >= '0' && *p <= '9')
  250.         {
  251.             val = val * 10 + (*p - '0');
  252.             p++;
  253.         }
  254.         if(val <= 100)
  255.         {
  256.             PWM_Update(PWM_Freq, (u16)val);
  257.             Print_String("\r\nDuty set to ");
  258.             Print_Num(val);
  259.             Print_String(" %\r\n");
  260.         }
  261.         else
  262.         {
  263.             Print_String("\r\nError: Duty out of range (0~100)\r\n");
  264.         }
  265.         return;
  266.     }
  267.     // ---- 未知命令 ----
  268.     Print_String("\r\nUnknown cmd. Usage: F=1000  D=50  ?\r\n");
  269. }
  270. // ======================== 辅助输出函数 ==================================
  271. void Print_String(u8 *p)
  272. {
  273.     while(*p)
  274.     {
  275.         SBUF = *p++;
  276.         while(!TI);
  277.         TI = 0;
  278.     }
  279. }
  280. void Print_Num(u32 val)
  281. {
  282.     u8 buf[12];
  283.     u8 i = 0;
  284.     if(val == 0)
  285.     {
  286.         SBUF = '0';
  287.         while(!TI); TI = 0;
  288.         return;
  289.     }
  290.     while(val > 0)
  291.     {
  292.         buf[i++] = (u8)(val % 10) + '0';
  293.         val /= 10;
  294.     }
  295.     while(i > 0)
  296.     {
  297.         i--;
  298.         SBUF = buf[i];
  299.         while(!TI); TI = 0;
  300.     }
  301. }
  302. void Print_Prompt(void)
  303. {
  304.     Print_String("\r\n========================================\r\n");
  305.     Print_String(" STC8H1K08 PWM Ctrl via UART\r\n");
  306.     Print_String(" CLK: "); Print_Num(MAIN_Fosc);
  307.     Print_String(" Hz, BAUD: "); Print_Num(UART_BAUD);
  308.     Print_String("\r\n");
  309.     Print_String(" Commands:\r\n");
  310.     Print_String("  F=1000  set Freq (10~500000 Hz)\r\n");
  311.     Print_String("  D=50    set Duty (0~100 %%)\r\n");
  312.     Print_String("  ?       Query status\r\n");
  313.     Print_String("========================================\r\n");
  314. }
复制代码
效果测试:
截图202606181041502265.jpg


截图202606181042235794.jpg

修改占空比:
截图202606181042475795.jpg
截图202606181042572244.jpg
修改频率:
截图202606181043358447.jpg
截图202606181043454810.jpg
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:586
  • 最近打卡:2026-07-04 12:34:03
已绑定手机

112

主题

4958

回帖

1万

积分

荣誉版主

无情的代码机器

积分
10758
发表于 2026-6-18 11:01:28 | 显示全部楼层


结论:AI首次输出的代码编译正常,逻辑有点小问题,通过对话可让其纠正。
======================


难度提升,带死区设置的互补pwm输出,提问:
STC8H, PWM应用,PWM1P,1N输出互补PWM
根据接收到的串口命令,
调整PWM输出的占空比、频率、死区的PWM通用演示程序


编译下载默认:
截图202606181050089308.jpg
截图202606181050237385.jpg

截图202606181053113164.jpg

更新死区时间:
截图202606181053382581.jpg
设置100时串口提示大约18000 ns,实际测量只有9us
错误分析:
截图202606181058148163.jpg
修正后:
截图202606181059471276.jpg
截图202606181100064691.jpg
截图202606181101034511.jpg

截图202606181101134219.jpg

截图202606181103541180.jpg

截图202606181104122648.jpg
  1. /******************************************************************************
  2. * 文件名:main.c
  3. * MCU:STC8H1K08-36I-TSSOP20(兼容所有STC8H系列)
  4. * 功能:通过串口命令动态调节互补PWM输出的频率、占空比和死区时间
  5. * 主频:11.0592MHz(内部IRC,宏可修改)
  6. * PWM输出:P1.0 (PWM1P) + P1.1 (PWM1N) 互补带死区
  7. * 串口:UART1, P3.0/P3.1, 115200-8-N1
  8. *
  9. * 命令格式(以 0x0D 或 0x0A 结尾):
  10. *   F=1000  → 设置频率为 1000 Hz(10 ~ 500000)
  11. *   D=50    → 设置占空比为 50%   (0 ~ 100)
  12. *   T=20    → 设置死区寄存器值   (0 ~ 255)
  13. *   ?       → 查询当前频率、占空比、死区值及实际死区时间
  14. *
  15. * 编译环境:Keil C51,需包含 stc8h.h 头文件
  16. *****************************************************************************/
  17. #include "stc8h.h"
  18. #include "intrins.h"
  19. // ========================== 用户可修改宏 ==================================
  20. #define MAIN_Fosc         11059200L      // 系统主频(Hz)
  21. #define UART_BAUD         115200         // 串口波特率
  22. #define PWM_DEFAULT_FREQ  1000           // 默认PWM频率(Hz)
  23. #define PWM_DEFAULT_DUTY  50             // 默认占空比(%)
  24. #define PWM_DEFAULT_DTR   20             // 默认死区寄存器值 (0~255)
  25. // ========================================================================
  26. // 波特率重装载值(T2做波特率发生器,4倍采样,1T模式)
  27. #define BRT_VAL  (65536UL - (MAIN_Fosc / 4 / UART_BAUD))
  28. // ---------- 类型定义 ----------
  29. typedef unsigned char  u8;
  30. typedef unsigned int   u16;
  31. typedef unsigned long  u32;
  32. // ---------- 全局变量 ----------
  33. volatile u8  Rx_Ready;              // 收到完整命令标志
  34. volatile u8  Rx_Buffer[32];         // 接收缓冲区
  35. volatile u8  Rx_Index;              // 当前接收位置
  36. u32  PWM_Freq  = PWM_DEFAULT_FREQ;  // 当前PWM频率(Hz)
  37. u16  PWM_Duty  = PWM_DEFAULT_DUTY;  // 当前占空比(0~100)
  38. u16  PWM_Psc   = 0;                 // 当前预分频值(保存用于死区计算)
  39. u16  PWM_Arr   = 0;                 // 当前自动重装载值
  40. u8   PWM_Dtr   = PWM_DEFAULT_DTR;   // 当前死区寄存器值
  41. // ---------- 函数声明 ----------
  42. void UART1_Init(void);
  43. void PWM_Init(void);
  44. void PWM_Update(u32 freq, u16 duty, u8 dtr);
  45. void Cmd_Parse(void);
  46. void Print_String(u8 *p);
  47. void Print_Num(u32 val);
  48. void Print_Prompt(void);
  49. u32  Calc_DeadTime_ns(u8 dtr, u16 psc);
  50. // ============================ 主函数 ====================================
  51. void main(void)
  52. {
  53.     // 使能XFR访问(PWM寄存器在XFR区)
  54.     P_SW2 |= 0x80;
  55.     // 所有IO设置为准双向口
  56.     P0M0 = 0x00; P0M1 = 0x00;
  57.     P1M0 = 0x00; P1M1 = 0x00;
  58.     P2M0 = 0x00; P2M1 = 0x00;
  59.     P3M0 = 0x00; P3M1 = 0x00;
  60.     P4M0 = 0x00; P4M1 = 0x00;
  61.     P5M0 = 0x00; P5M1 = 0x00;
  62.     // 初始化外设
  63.     UART1_Init();
  64.     PWM_Init();
  65.     // 使能中断
  66.     ES = 1;         // 串口中断
  67.     EA = 1;         // 总中断
  68.     // 输出开机提示
  69.     Print_Prompt();
  70.     while(1)
  71.     {
  72.         if(Rx_Ready)
  73.         {
  74.             Rx_Ready = 0;
  75.             Cmd_Parse();                    // 解析并执行命令
  76.         }
  77.     }
  78. }
  79. // ======================== 串口1初始化 ===================================
  80. // 使用定时器T2作为波特率发生器,模式1(8位可变波特率)
  81. void UART1_Init(void)
  82. {
  83.     SCON = 0x50;                            // 模式1, 8位UART, REN使能接收
  84.     // 定时器2配置:1T模式,作为波特率发生器
  85.     AUXR &= ~0x10;                          // T2停止
  86.     AUXR &= ~0x08;                          // T2设为定时器
  87.     AUXR |=  0x04;                          // T2设为1T模式
  88.     T2H = (u8)(BRT_VAL >> 8);              // 装载波特率重装值
  89.     T2L = (u8)(BRT_VAL);
  90.     AUXR |=  0x10;                          // T2启动
  91.     AUXR |=  0x01;                          // 串口1选择T2做波特率发生器
  92.     Rx_Index = 0;
  93.     Rx_Ready = 0;
  94. }
  95. // ======================== PWM初始化 =====================================
  96. // 使用 PWMA 通道1,P1.0(PWM1P) + P1.1(PWM1N) 互补输出带死区
  97. // 边沿对齐,向上计数模式,PWM模式1
  98. void PWM_Init(void)
  99. {
  100.     // ---- 关闭所有通道,写CCMRx前必须先清零CCERx ----
  101.     PWMA_CCER1 = 0x00;
  102.     // ---- 配置通道1为PWM模式1(带预装载) ----
  103.     PWMA_CCMR1 = 0x68;                      // 0110 1000
  104.     // ---- 通道1输出脚选择:C1PS=00 → PWM1P=P1.0, PWM1N=P1.1 ----
  105.     PWMA_PS  &= ~0x03;
  106.     // ---- 使能 PWM1P 和 PWM1N 端口输出 ----
  107.     PWMA_ENO  = 0x03;                       // ENO1P=1 (bit0), ENO1N=1 (bit1)
  108.     // ---- 使能互补输出,高电平有效 ----
  109.     // CC1E=1 (bit0), CC1P=0 (bit1)
  110.     // CC1NE=1 (bit2), CC1NP=0 (bit3)
  111.     PWMA_CCER1 = 0x05;                      // 0000 0101
  112.     // ---- 设置初始参数 ----
  113.     PWM_Update(PWM_Freq, PWM_Duty, PWM_Dtr);
  114.     // ---- 使能主输出,启动计数 ----
  115.     PWMA_BKR  = 0x80;                       // MOE=1 主输出使能
  116.     PWMA_CR1 |= 0x01;                       // CEN=1 启动计数
  117. }
  118. // ======================== 更新PWM参数 ===================================
  119. // freq: 目标频率(Hz)    范围:10 ~ 500000
  120. // duty: 占空比百分比    范围:0 ~ 100
  121. // dtr:  死区寄存器值    范围:0 ~ 255
  122. //
  123. // 频率公式:freq = SYSCLK / (PSC+1) / (ARR+1)
  124. // 自动选择 PSC 使 ARR 不溢出16位
  125. void PWM_Update(u32 freq, u16 duty, u8 dtr)
  126. {
  127.     u16 arr, ccr;
  128.     u16 psc;
  129.     if(freq == 0) freq = 1;
  130.     // ---- 自动计算预分频 ----
  131.     if(freq < (MAIN_Fosc / 65536UL))
  132.     {
  133.         psc = (u16)(MAIN_Fosc / freq / 65536UL);
  134.         if(psc > 0) psc--;
  135.         arr = (u16)(MAIN_Fosc / freq / (psc + 1) - 1);
  136.     }
  137.     else
  138.     {
  139.         psc = 0;
  140.         arr = (u16)(MAIN_Fosc / freq - 1);
  141.     }
  142.     if(arr > 65535) arr = 65535;
  143.     if(arr < 1)     arr = 1;
  144.     // ---- 占空比计算 ----
  145.     if(duty == 0)
  146.         ccr = 0;
  147.     else if(duty >= 100)
  148.         ccr = arr + 1;          // 100%:CCR > ARR
  149.     else
  150.         ccr = (u16)((u32)(arr + 1) * duty / 100);
  151.     // ---- 写入寄存器 ----
  152.     PWMA_PSCRH = (u8)(psc >> 8);
  153.     PWMA_PSCRL = (u8)(psc);
  154.     PWMA_ARRH  = (u8)(arr >> 8);
  155.     PWMA_ARRL  = (u8)(arr);
  156.     PWMA_CCR1H = (u8)(ccr >> 8);
  157.     PWMA_CCR1L = (u8)(ccr);
  158.     PWMA_DTR   = dtr;                       // 死区寄存器
  159.     // 产生更新事件,使新值立即生效
  160.     PWMA_EGR |= 0x01;                       // UG=1
  161.     // 保存当前值
  162.     PWM_Freq = freq;
  163.     PWM_Duty = duty;
  164.     PWM_Dtr  = dtr;
  165.     PWM_Psc  = psc;
  166.     PWM_Arr  = arr;
  167. }
  168. // ======================== 串口中断服务 ==================================
  169. void UART1_ISR(void) interrupt 4
  170. {
  171.     u8 dat;
  172.     if(RI)
  173.     {
  174.         RI = 0;
  175.         dat = SBUF;
  176.         // 回车或换行 → 结束一帧命令
  177.         if(dat == 0x0D || dat == 0x0A)
  178.         {
  179.             if(Rx_Index > 0)
  180.             {
  181.                 Rx_Buffer[Rx_Index] = '\0';
  182.                 Rx_Ready = 1;
  183.                 Rx_Index = 0;
  184.             }
  185.         }
  186.         else
  187.         {
  188.             if(Rx_Index < sizeof(Rx_Buffer) - 1)
  189.             {
  190.                 Rx_Buffer[Rx_Index++] = dat;
  191.             }
  192.         }
  193.     }
  194.     if(TI)
  195.     {
  196.         TI = 0;
  197.     }
  198. }
  199. // ======================== 命令解析与执行 ================================
  200. void Cmd_Parse(void)
  201. {
  202.     u8 *p = (u8 *)Rx_Buffer;
  203.     u32 val = 0;
  204.     while(*p == ' ') p++;
  205.     // ---- 查询命令 ----
  206.     if(*p == '?' || *p == 'H' || *p == 'h')
  207.     {
  208.         u32 dt_ns;
  209.         Print_String("\r\n--- Complementary PWM Status ---\r\n");
  210.         Print_String("Freq : ");
  211.         Print_Num(PWM_Freq);
  212.         Print_String(" Hz\r\n");
  213.         Print_String("Duty : ");
  214.         Print_Num(PWM_Duty);
  215.         Print_String(" %\r\n");
  216.         Print_String("DTR  : ");
  217.         Print_Num(PWM_Dtr);
  218.         Print_String(" (0x");
  219.         if(PWM_Dtr < 16) Print_String("0");
  220.         Print_Num(PWM_Dtr);
  221.         Print_String(")\r\n");
  222.         dt_ns = Calc_DeadTime_ns(PWM_Dtr, PWM_Psc);
  223.         Print_String("DeadT: ");
  224.         Print_Num(dt_ns);
  225.         Print_String(" ns\r\n");
  226.         Print_String("PSC  : ");
  227.         Print_Num(PWM_Psc);
  228.         Print_String("\r\n");
  229.         Print_String("ARR  : ");
  230.         Print_Num(PWM_Arr);
  231.         Print_String("\r\n");
  232.         Print_String("CLK  : ");
  233.         Print_Num(MAIN_Fosc);
  234.         Print_String(" Hz\r\n");
  235.         Print_String("Output: P1.0(PWM1P) + P1.1(PWM1N) 互补\r\n");
  236.         Print_String("----------------------------------\r\n");
  237.         return;
  238.     }
  239.     // ---- 频率命令: F=xxxx ----
  240.     if((*p == 'F' || *p == 'f') && *(p+1) == '=')
  241.     {
  242.         p += 2;
  243.         val = 0;
  244.         while(*p >= '0' && *p <= '9')
  245.         {
  246.             val = val * 10 + (*p - '0');
  247.             p++;
  248.         }
  249.         if(val >= 10 && val <= 500000)
  250.         {
  251.             PWM_Update(val, PWM_Duty, PWM_Dtr);
  252.             Print_String("\r\nFreq set to ");
  253.             Print_Num(val);
  254.             Print_String(" Hz\r\n");
  255.         }
  256.         else
  257.         {
  258.             Print_String("\r\nError: Freq out of range (10~500000)\r\n");
  259.         }
  260.         return;
  261.     }
  262.     // ---- 占空比命令: D=xx ----
  263.     if((*p == 'D' || *p == 'd') && *(p+1) == '=')
  264.     {
  265.         p += 2;
  266.         val = 0;
  267.         while(*p >= '0' && *p <= '9')
  268.         {
  269.             val = val * 10 + (*p - '0');
  270.             p++;
  271.         }
  272.         if(val <= 100)
  273.         {
  274.             PWM_Update(PWM_Freq, (u16)val, PWM_Dtr);
  275.             Print_String("\r\nDuty set to ");
  276.             Print_Num(val);
  277.             Print_String(" %\r\n");
  278.         }
  279.         else
  280.         {
  281.             Print_String("\r\nError: Duty out of range (0~100)\r\n");
  282.         }
  283.         return;
  284.     }
  285.     // ---- 死区命令: T=xx ----
  286.     if((*p == 'T' || *p == 't') && *(p+1) == '=')
  287.     {
  288.         p += 2;
  289.         val = 0;
  290.         while(*p >= '0' && *p <= '9')
  291.         {
  292.             val = val * 10 + (*p - '0');
  293.             p++;
  294.         }
  295.         if(val <= 255)
  296.         {
  297.             PWM_Update(PWM_Freq, PWM_Duty, (u8)val);
  298.             Print_String("\r\nDTR set to ");
  299.             Print_Num(val);
  300.             Print_String(" (0x");
  301.             if(val < 16) Print_String("0");
  302.             Print_Num(val);
  303.             Print_String(")\r\n");
  304.             {
  305.                 u32 ns = Calc_DeadTime_ns((u8)val, PWM_Psc);
  306.                 Print_String("DeadTime ~ ");
  307.                 Print_Num(ns);
  308.                 Print_String(" ns\r\n");
  309.             }
  310.         }
  311.         else
  312.         {
  313.             Print_String("\r\nError: DTR out of range (0~255)\r\n");
  314.         }
  315.         return;
  316.     }
  317.     // ---- 未知命令 ----
  318.     Print_String("\r\nUnknown cmd.\r\n");
  319.     Print_String("Usage: F=1000  D=50  T=20  ?\r\n");
  320. }
  321. // ======================== ★★★ 修正后的死区计算函数 ★★★ ===================
  322. // 根据 PWMA_DTR 寄存器的分段规则计算实际死区时间
  323. // tCK_PSC = (PSC + 1) / SYSCLK  (单位:秒)
  324. // 返回值为纳秒
  325. //
  326. // 手册 25.9.34 节分段规则:
  327. //   DTG[7:5]=000~011 (DTR 0~127) : DT = DTR × tCK_PSC
  328. //   DTG[7:5]=100~101 (DTR 128~191): DT = (64 + DTR[6:0]) × 2 × tCK_PSC
  329. //   DTG[7:5]=110     (DTR 192~223): DT = (32 + DTR[5:0]) × 8 × tCK_PSC
  330. //   DTG[7:5]=111     (DTR 224~255): DT = (32 + DTR[4:0]) × 16 × tCK_PSC
  331. u32 Calc_DeadTime_ns(u8 dtr, u16 psc)
  332. {
  333.     u32 dt_cycles;              // 死区占用的 tCK_PSC 周期数
  334.     u32 tck_ns;                 // tCK_PSC 的纳秒数
  335.     // 计算 tCK_PSC(纳秒)
  336.     // tCK_PSC = (psc+1) / SYSCLK 秒
  337.     // tck_ns  = (psc+1) * 1,000,000,000 / SYSCLK
  338.     tck_ns = ((u32)(psc + 1) * 1000000000UL) / MAIN_Fosc;
  339.     // 判断 DTR 所在的分段范围(依据 bit7 和 bit6)
  340.     if(dtr < 128)                       // DTG[7]=0 → 第一段
  341.     {
  342.         dt_cycles = dtr;                // 0~127 个 tCK_PSC
  343.     }
  344.     else if(dtr < 192)                  // DTG[7:5]=100~101 → 第二段
  345.     {
  346.         dt_cycles = (u32)(64 + (dtr & 0x7F)) * 2;  // 128~254 个 tCK_PSC
  347.     }
  348.     else if(dtr < 224)                  // DTG[7:5]=110 → 第三段
  349.     {
  350.         dt_cycles = (u32)(32 + (dtr & 0x3F)) * 8;  // 256~504 个 tCK_PSC
  351.     }
  352.     else                                // DTG[7:5]=111 → 第四段
  353.     {
  354.         dt_cycles = (u32)(32 + (dtr & 0x1F)) * 16; // 512~1008 个 tCK_PSC
  355.     }
  356.     return dt_cycles * tck_ns;
  357. }
  358. // ========================================================================
  359. // ======================== 辅助输出函数 ==================================
  360. void Print_String(u8 *p)
  361. {
  362.     while(*p)
  363.     {
  364.         SBUF = *p++;
  365.         while(!TI);
  366.         TI = 0;
  367.     }
  368. }
  369. void Print_Num(u32 val)
  370. {
  371.     u8 buf[12];
  372.     u8 i = 0;
  373.     if(val == 0)
  374.     {
  375.         SBUF = '0';
  376.         while(!TI); TI = 0;
  377.         return;
  378.     }
  379.     while(val > 0)
  380.     {
  381.         buf[i++] = (u8)(val % 10) + '0';
  382.         val /= 10;
  383.     }
  384.     while(i > 0)
  385.     {
  386.         i--;
  387.         SBUF = buf[i];
  388.         while(!TI); TI = 0;
  389.     }
  390. }
  391. void Print_Prompt(void)
  392. {
  393.     Print_String("\r\n============================================\r\n");
  394.     Print_String(" STC8H Complementary PWM Ctrl via UART\r\n");
  395.     Print_String(" CLK: "); Print_Num(MAIN_Fosc);
  396.     Print_String(" Hz, BAUD: "); Print_Num(UART_BAUD);
  397.     Print_String("\r\n");
  398.     Print_String(" Output: P1.0(PWM1P) + P1.1(PWM1N)\r\n");
  399.     Print_String(" Commands:\r\n");
  400.     Print_String("  F=1000  set Freq (10~500000 Hz)\r\n");
  401.     Print_String("  D=50    set Duty (0~100 %%)\r\n");
  402.     Print_String("  T=20    set DeadTime Reg (0~255)\r\n");
  403.     Print_String("  ?       Query all params\r\n");
  404.     Print_String("============================================\r\n");
  405. }
复制代码



三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:586
  • 最近打卡:2026-07-04 12:34:03
已绑定手机

112

主题

4958

回帖

1万

积分

荣誉版主

无情的代码机器

积分
10758
发表于 2026-6-18 11:09:01 | 显示全部楼层


结论:同样的问题,AI没有保留上一次对话的经验,并且这次首次输出的代码编译不通过。
======================


关闭当前网页,重新打开AI助手,提问相同问题:

STC8H, PWM应用,PWM1P,1N输出互补PWM
根据接收到的串口命令,
调整PWM输出的占空比、频率、死区的PWM通用演示程序


输出代码报错:
截图202606181108572285.jpg




  1. /******************************************************************************
  2. * 文件名:main.c
  3. * MCU:STC8H 系列(如 STC8H8K64U、STC8H1K08 等)
  4. * 功能:PWM1P/PWM1N 互补输出,串口命令调频、调占空比、调死区
  5. * 主频:11.0592MHz(内部IRC)
  6. * PWM 输出:P1.0 (PWM1P) / P1.1 (PWM1N)
  7. * 串口:UART1, P3.0/P3.1, 115200-8-N1
  8. *
  9. * 命令格式(字符串,以 0x0D 或 0x0A 结尾):
  10. *   F=1000  → 设置频率为 1000 Hz(范围 10~500000)
  11. *   D=50    → 设置占空比为 50%   (范围 0~100)
  12. *   T=500   → 设置死区为 500 ns  (范围 0~12700)
  13. *   ?       → 查询当前参数
  14. *
  15. * 编译环境:Keil C51,需包含 stc8h.h 头文件
  16. *****************************************************************************/
  17. #include "stc8h.h"
  18. #include "intrins.h"
  19. // ========================== 用户可修改宏 ==================================
  20. #define MAIN_Fosc          11059200UL      // 系统主频(Hz)
  21. #define UART_BAUD          115200          // 串口波特率
  22. #define PWM_DEFAULT_FREQ   1000            // 默认频率(Hz)
  23. #define PWM_DEFAULT_DUTY   50              // 默认占空比(%)
  24. #define PWM_DEFAULT_DT_NS  1000            // 默认死区(ns),1000ns = 1us
  25. // PWM 引脚选择:C1PS[1:0]
  26. //   00 -> PWM1P=P1.0, PWM1N=P1.1
  27. //   01 -> PWM1P=P2.0, PWM1N=P2.1
  28. //   10 -> PWM1P=P6.0, PWM1N=P6.1
  29. #define PWM_C1PS           0x00
  30. // ========================================================================
  31. // 波特率重装载值(T2做波特率发生器,4倍采样,1T模式)
  32. //   BRT = 65536 - SYSCLK / 4 / Baud
  33. #define BRT_VAL  (65536UL - (MAIN_Fosc / 4 / UART_BAUD))
  34. // ---------- 类型定义 ----------
  35. typedef unsigned char  u8;
  36. typedef unsigned int   u16;
  37. typedef unsigned long  u32;
  38. // ---------- 全局变量 ----------
  39. volatile u8  Rx_Ready;              // 收到完整命令标志
  40. volatile u8  Rx_Buffer[32];         // 接收缓冲区
  41. volatile u8  Rx_Index;              // 当前接收位置
  42. u32  PWM_Freq     = PWM_DEFAULT_FREQ;   // 当前频率(Hz)
  43. u16  PWM_Duty     = PWM_DEFAULT_DUTY;   // 当前占空比(%)
  44. u16  PWM_DeadNS   = PWM_DEFAULT_DT_NS;  // 当前死区(ns)
  45. u16  PWM_PSC      = 0;                  // 当前预分频值
  46. u16  PWM_ARR      = 0;                  // 当前自动重装载值
  47. // ---------- 函数声明 ----------
  48. void UART1_Init(void);
  49. void PWM_Init(void);
  50. void PWM_Update(u32 freq, u16 duty);
  51. void PWM_SetDeadTime_ns(u16 ns);
  52. u8   PWM_CalcDTG(u32 ns, u32 tCK, u8 *pDTG);
  53. void Cmd_Parse(void);
  54. void Print_String(u8 *p);
  55. void Print_Num(u32 val);
  56. void Print_Prompt(void);
  57. // ============================ 主函数 ====================================
  58. void main(void)
  59. {
  60.     // 使能XFR访问(PWM寄存器在XFR区)
  61.     P_SW2 |= 0x80;
  62.     // 所有IO设置为准双向口
  63.     P0M0 = 0x00; P0M1 = 0x00;
  64.     P1M0 = 0x00; P1M1 = 0x00;
  65.     P2M0 = 0x00; P2M1 = 0x00;
  66.     P3M0 = 0x00; P3M1 = 0x00;
  67.     P4M0 = 0x00; P4M1 = 0x00;
  68.     P5M0 = 0x00; P5M1 = 0x00;
  69.     // 初始化外设
  70.     UART1_Init();
  71.     PWM_Init();
  72.     // 设置默认死区
  73.     PWM_SetDeadTime_ns(PWM_DeadNS);
  74.     // 使能中断
  75.     ES = 1;         // 串口中断
  76.     EA = 1;         // 总中断
  77.     // 输出开机提示
  78.     Print_Prompt();
  79.     while(1)
  80.     {
  81.         if(Rx_Ready)
  82.         {
  83.             Rx_Ready = 0;
  84.             Cmd_Parse();                    // 解析并执行命令
  85.         }
  86.     }
  87. }
  88. // ======================== 串口1初始化 ===================================
  89. // 使用定时器T2作为波特率发生器,模式1(8位可变波特率)
  90. void UART1_Init(void)
  91. {
  92.     SCON = 0x50;                            // 模式1, 8位 UART, REN使能接收
  93.     // 定时器2配置:1T模式,作为波特率发生器
  94.     AUXR &= ~0x10;                          // T2停止
  95.     AUXR &= ~0x08;                          // T2设为定时器
  96.     AUXR |=  0x04;                          // T2设为1T模式
  97.     T2H = (u8)(BRT_VAL >> 8);              // 装载波特率重装值
  98.     T2L = (u8)(BRT_VAL);
  99.     AUXR |=  0x10;                          // T2启动
  100.     AUXR |=  0x01;                          // 串口1选择T2做波特率发生器
  101.     Rx_Index = 0;
  102.     Rx_Ready = 0;
  103. }
  104. // ======================== PWM初始化 =====================================
  105. // PWMA 通道1 (PWM1P/PWM1N) 互补输出
  106. // 边沿对齐,向上计数模式,PWM模式1
  107. void PWM_Init(void)
  108. {
  109.     // ---- 关闭通道1输出,才能配置CCMR1的CC1S位 ----
  110.     PWMA_CCER1 = 0x00;
  111.     // ---- 配置通道1为PWM模式1 ----
  112.     // OC1M[2:0] = 110 (PWM模式1: CNT<CCR时输出有效电平)
  113.     // OC1PE     = 1   (预装载使能)
  114.     // CC1S[1:0] = 00  (输出模式)
  115.     PWMA_CCMR1 = 0x68;                      // 0110 1000
  116.     // ---- 通道1引脚选择 ----
  117.     PWMA_PS = (PWMA_PS & ~0x03) | (PWM_C1PS & 0x03);
  118.     // ---- 使能 PWM1P 和 PWM1N 端口输出 ----
  119.     PWMA_ENO = 0x03;                        // ENO1P=1, ENO1N=1
  120.     // ---- 通道1互补输出使能,高电平有效 ----
  121.     // CC1E=1, CC1NE=1, CC1P=0, CC1NP=0
  122.     PWMA_CCER1 = 0x05;                      // 0000 0101 (CC1E|CC1NE)
  123.     // ---- 边沿对齐,向上计数,预装载使能 ----
  124.     PWMA_CR1 = 0x80;                        // ARPE=1 (自动重装载预装载)
  125.     // ---- 设置初始频率和占空比 ----
  126.     PWM_Update(PWM_Freq, PWM_Duty);
  127.     // ---- 使能主输出(总开关),启动计数 ----
  128.     PWMA_BKR = 0x80;                        // MOE=1 主输出使能
  129.     PWMA_CR1 |= 0x01;                       // CEN=1 启动计数器
  130. }
  131. // ======================== 更新PWM频率和占空比 ============================
  132. // freq: 目标频率(Hz)    范围:10 ~ 500000
  133. // duty: 占空比百分比    范围:0 ~ 100
  134. //
  135. // 公式:
  136. //   频率 = SYSCLK / (PSC+1) / (ARR+1)
  137. //   占空比 = CCR / (ARR+1) * 100%
  138. void PWM_Update(u32 freq, u16 duty)
  139. {
  140.     u16 arr, ccr;
  141.     u16 psc;
  142.     if(freq == 0) freq = 1;                 // 防除零
  143.     // ---- 自动计算预分频,使ARR尽量大以保证精度 ----
  144.     if(freq >= MAIN_Fosc)
  145.     {
  146.         psc = 0;
  147.         arr = 1;
  148.     }
  149.     else
  150.     {
  151.         // 目标:ARR 在 100~65535 之间
  152.         // 先估算不分频时的ARR
  153.         u32 tmp = MAIN_Fosc / freq;
  154.         if(tmp <= 65536UL)
  155.         {
  156.             psc = 0;
  157.             arr = (u16)(tmp - 1);
  158.             if(arr < 2) arr = 2;
  159.         }
  160.         else
  161.         {
  162.             // 需要分频,让ARR尽量大
  163.             psc = (u16)(tmp / 2000);        // 粗略分频
  164.             if(psc > 65535) psc = 65535;
  165.             arr = (u16)(tmp / (psc + 1) - 1);
  166.             if(arr > 65535) { arr = 65535; psc = (u16)(tmp / (arr + 1) - 1); }
  167.             if(arr < 2)   { arr = 2;  psc = (u16)(tmp / (arr + 1) - 1); }
  168.         }
  169.     }
  170.     // 保存当前PSC和ARR,供死区计算使用
  171.     PWM_PSC = psc;
  172.     PWM_ARR = arr;
  173.     // ---- 占空比计算 ----
  174.     if(duty == 0)
  175.         ccr = 0;                            // 0%:CCR=0
  176.     else if(duty >= 100)
  177.         ccr = arr + 1;                      // 100%:CCR > ARR
  178.     else
  179.         ccr = (u16)((u32)(arr + 1) * duty / 100);
  180.     // ---- 写入寄存器(先写高8位,再写低8位) ----
  181.     PWMA_PSCRH = (u8)(psc >> 8);
  182.     PWMA_PSCRL = (u8)(psc);
  183.     PWMA_ARRH  = (u8)(arr >> 8);
  184.     PWMA_ARRL  = (u8)(arr);
  185.     PWMA_CCR1H = (u8)(ccr >> 8);
  186.     PWMA_CCR1L = (u8)(ccr);
  187.     // 产生更新事件,使新值立即生效
  188.     PWMA_EGR |= 0x01;                       // UG=1
  189.     // 保存当前值
  190.     PWM_Freq = freq;
  191.     PWM_Duty = duty;
  192.     // 频率改变后,重新计算死区(因为tCK_PSC变了)
  193.     PWM_SetDeadTime_ns(PWM_DeadNS);
  194. }
  195. // ======================== 设置死区时间(ns为单位)=======================
  196. // 根据当前预分频,将用户输入的纳秒值转换为 DTR 寄存器值
  197. void PWM_SetDeadTime_ns(u16 ns)
  198. {
  199.     u32 tCK;            // PWM时钟周期(ps),用皮秒避免浮点
  200.     u32 target_ps;      // 目标死区(ps)
  201.     u8  dtg = 0;
  202.     u8  found;
  203.     if(ns == 0)
  204.     {
  205.         PWMA_DTR = 0x00;    // 死区=0
  206.         PWM_DeadNS = 0;
  207.         return;
  208.     }
  209.     // tCK = 1 / (SYSCLK / (PSC+1)) = (PSC+1) / SYSCLK (秒)
  210.     // 转皮秒:tCK_ps = (u32)(PSC+1) * 1000000000000UL / SYSCLK
  211.     // 但为了不溢出,用微秒级计算:tCK_ns 精确到纳秒
  212.     // tCK (ns) = (u32)(PWM_PSC + 1) * 1000000000UL / MAIN_Fosc
  213.     // 但避免32位溢出,用下面方式:
  214.     // tCK_ns = (u32)(PWM_PSC + 1) * 1000 / (MAIN_Fosc / 1000000)
  215.     // 这样精度到纳秒级别,对于死区设置足够
  216.     {
  217.         u32 clk_mhz = MAIN_Fosc / 1000000UL;       // 主频(MHz)
  218.         u32 tCK_ns;
  219.         if(clk_mhz == 0) clk_mhz = 1;
  220.         // tCK_ns = (PSC+1) * 1000 / clk_mhz
  221.         tCK_ns = (u32)(PWM_PSC + 1) * 1000UL / clk_mhz;
  222.         // 如果tCK_ns=0(高频情况),按1ns处理
  223.         if(tCK_ns == 0) tCK_ns = 1;
  224.         target_ps = (u32)ns * 1000UL;       // 转皮秒
  225.         // ---- 尝试匹配最佳 DTG 值 ----
  226.         // DTG[7:0] 分4种模式
  227.         // 模式1: DTG[7:5]=000, dead = DTG[7:0] * tCK
  228.         // 模式2: DTG[7:5]=100, dead = (64 + DTG[6:0]) * 2 * tCK
  229.         // 模式3: DTG[7:5]=110, dead = (32 + DTG[5:0]) * 8 * tCK
  230.         // 模式4: DTG[7:5]=111, dead = (32 + DTG[4:0]) * 16 * tCK
  231.         found = 0;
  232.         // 模式1:0~255 * tCK_ns
  233.         if(!found && ns <= (255UL * tCK_ns))
  234.         {
  235.             dtg = (u8)(ns / tCK_ns);
  236.             if(dtg > 0 && (ns - dtg * tCK_ns) > (tCK_ns / 2))
  237.                 dtg++;      // 四舍五入
  238.             // DTG[7:5] 保持为 000
  239.             found = 1;
  240.         }
  241.         // 模式2:(64~191) * 2 * tCK_ns => 128~382 * tCK_ns
  242.         if(!found && ns <= (191UL * 2UL * tCK_ns))
  243.         {
  244.             u16 val = (u16)(ns / (2UL * tCK_ns));
  245.             if(val < 64) val = 64;
  246.             if(val > 191) val = 191;
  247.             dtg = (u8)(0x80 | (val - 64));  // DTG[7:5]=100
  248.             found = 1;
  249.         }
  250.         // 模式3:(32~63) * 8 * tCK_ns => 256~504 * tCK_ns
  251.         if(!found && ns <= (63UL * 8UL * tCK_ns))
  252.         {
  253.             u16 val = (u16)(ns / (8UL * tCK_ns));
  254.             if(val < 32) val = 32;
  255.             if(val > 63) val = 63;
  256.             dtg = (u8)(0xC0 | (val - 32));  // DTG[7:5]=110
  257.             found = 1;
  258.         }
  259.         // 模式4:(32~63) * 16 * tCK_ns => 512~1008 * tCK_ns
  260.         if(!found && ns <= (63UL * 16UL * tCK_ns))
  261.         {
  262.             u16 val = (u16)(ns / (16UL * tCK_ns));
  263.             if(val < 32) val = 32;
  264.             if(val > 63) val = 63;
  265.             dtg = (u8)(0xE0 | (val - 32));  // DTG[7:5]=111
  266.             found = 1;
  267.         }
  268.         if(!found)
  269.         {
  270.             // 超出范围,取最大值
  271.             dtg = 0xFF;     // 模式4最大值:63*16*tCK_ns
  272.             // 计算实际可达的最大死区
  273.             // 上限在模式4:DTG=0xFF => (32+31)*16*tCK = 1008*tCK_ns
  274.         }
  275.     }
  276.     PWMA_DTR = dtg;
  277.     PWM_DeadNS = ns;
  278.     // 产生更新事件,使新死区生效
  279.     PWMA_EGR |= 0x01;                       // UG=1
  280. }
  281. // ======================== 串口中断服务 ==================================
  282. void UART1_ISR(void) interrupt 4
  283. {
  284.     u8 dat;
  285.     if(RI)
  286.     {
  287.         RI = 0;
  288.         dat = SBUF;
  289.         // 回车或换行 → 结束一帧命令
  290.         if(dat == 0x0D || dat == 0x0A)
  291.         {
  292.             if(Rx_Index > 0)
  293.             {
  294.                 Rx_Buffer[Rx_Index] = '\0';
  295.                 Rx_Ready = 1;
  296.                 Rx_Index = 0;
  297.             }
  298.         }
  299.         else
  300.         {
  301.             // 防止缓冲区溢出
  302.             if(Rx_Index < sizeof(Rx_Buffer) - 1)
  303.             {
  304.                 Rx_Buffer[Rx_Index++] = dat;
  305.             }
  306.         }
  307.     }
  308.     if(TI)
  309.     {
  310.         TI = 0;
  311.     }
  312. }
  313. // ======================== 命令解析与执行 ================================
  314. void Cmd_Parse(void)
  315. {
  316.     u8 *p = (u8 *)Rx_Buffer;
  317.     u32 val = 0;
  318.     // ---- 跳过前导空格 ----
  319.     while(*p == ' ') p++;
  320.     // ---- 查询命令 ----
  321.     if(*p == '?' || *p == 'H' || *p == 'h')
  322.     {
  323.         Print_String("\r\n--- PWM Status ---\r\n");
  324.         Print_String("Freq : "); Print_Num(PWM_Freq);
  325.         Print_String(" Hz\r\n");
  326.         Print_String("Duty : "); Print_Num(PWM_Duty);
  327.         Print_String(" %\r\n");
  328.         Print_String("Dead : "); Print_Num(PWM_DeadNS);
  329.         Print_String(" ns\r\n");
  330.         Print_String("PSC  : "); Print_Num(PWM_PSC);
  331.         Print_String("\r\n");
  332.         Print_String("ARR  : "); Print_Num(PWM_ARR);
  333.         Print_String("\r\n");
  334.         Print_String("DTR  : 0x");
  335.         { u8 d = PWMA_DTR;
  336.           u8 hex[2] = { (d>>4)&0x0F, d&0x0F };
  337.           for(u8 i=0;i<2;i++) {
  338.             if(hex[i]<10) SBUF = hex[i]+'0'; else SBUF = hex[i]-10+'A';
  339.             while(!TI); TI=0;
  340.           }
  341.         }
  342.         Print_String("\r\nCLK  : "); Print_Num(MAIN_Fosc);
  343.         Print_String(" Hz\r\n");
  344.         Print_String("------------------\r\n");
  345.         return;
  346.     }
  347.     // ---- 频率命令: F=xxxx ----
  348.     if((*p == 'F' || *p == 'f') && *(p+1) == '=')
  349.     {
  350.         p += 2;
  351.         val = 0;
  352.         while(*p >= '0' && *p <= '9')
  353.         {
  354.             val = val * 10 + (*p - '0');
  355.             p++;
  356.         }
  357.         if(val >= 10 && val <= 500000)      // 范围保护:10Hz ~ 500kHz
  358.         {
  359.             PWM_Update(val, PWM_Duty);
  360.             Print_String("\r\nFreq set to "); Print_Num(val);
  361.             Print_String(" Hz\r\n");
  362.         }
  363.         else
  364.         {
  365.             Print_String("\r\nError: Freq out of range (10~500000)\r\n");
  366.         }
  367.         return;
  368.     }
  369.     // ---- 占空比命令: D=xx ----
  370.     if((*p == 'D' || *p == 'd') && *(p+1) == '=')
  371.     {
  372.         p += 2;
  373.         val = 0;
  374.         while(*p >= '0' && *p <= '9')
  375.         {
  376.             val = val * 10 + (*p - '0');
  377.             p++;
  378.         }
  379.         if(val <= 100)
  380.         {
  381.             PWM_Update(PWM_Freq, (u16)val);
  382.             Print_String("\r\nDuty set to "); Print_Num(val);
  383.             Print_String(" %\r\n");
  384.         }
  385.         else
  386.         {
  387.             Print_String("\r\nError: Duty out of range (0~100)\r\n");
  388.         }
  389.         return;
  390.     }
  391.     // ---- 死区命令: T=xxx (ns) ----
  392.     if((*p == 'T' || *p == 't') && *(p+1) == '=')
  393.     {
  394.         p += 2;
  395.         val = 0;
  396.         while(*p >= '0' && *p <= '9')
  397.         {
  398.             val = val * 10 + (*p - '0');
  399.             p++;
  400.         }
  401.         if(val <= 12700)
  402.         {
  403.             PWM_SetDeadTime_ns((u16)val);
  404.             Print_String("\r\nDeadTime set to "); Print_Num(val);
  405.             Print_String(" ns\r\n");
  406.             // 显示实际的DTR寄存器值
  407.             Print_String("DTR=0x");
  408.             { u8 d = PWMA_DTR;
  409.               u8 hex[2] = { (d>>4)&0x0F, d&0x0F };
  410.               for(u8 i=0;i<2;i++) {
  411.                 if(hex[i]<10) SBUF = hex[i]+'0'; else SBUF = hex[i]-10+'A';
  412.                 while(!TI); TI=0;
  413.               }
  414.             }
  415.             Print_String("\r\n");
  416.         }
  417.         else
  418.         {
  419.             Print_String("\r\nError: DeadTime out of range (0~12700 ns)\r\n");
  420.         }
  421.         return;
  422.     }
  423.     // ---- 未知命令 ----
  424.     Print_String("\r\nUnknown cmd. Usage: F=1000  D=50  T=500  ?\r\n");
  425. }
  426. // ======================== 辅助输出函数 ==================================
  427. void Print_String(u8 *p)
  428. {
  429.     while(*p)
  430.     {
  431.         SBUF = *p++;
  432.         while(!TI);
  433.         TI = 0;
  434.     }
  435. }
  436. void Print_Num(u32 val)
  437. {
  438.     u8 buf[12];
  439.     u8 i = 0;
  440.     if(val == 0)
  441.     {
  442.         SBUF = '0';
  443.         while(!TI); TI = 0;
  444.         return;
  445.     }
  446.     while(val > 0)
  447.     {
  448.         buf[i++] = (u8)(val % 10) + '0';
  449.         val /= 10;
  450.     }
  451.     while(i > 0)
  452.     {
  453.         i--;
  454.         SBUF = buf[i];
  455.         while(!TI); TI = 0;
  456.     }
  457. }
  458. void Print_Prompt(void)
  459. {
  460.     Print_String("\r\n===========================================\r\n");
  461.     Print_String(" STC8H PWM1P/PWM1N Complementary Output\r\n");
  462.     Print_String(" CLK: "); Print_Num(MAIN_Fosc);
  463.     Print_String(" Hz, BAUD: "); Print_Num(UART_BAUD);
  464.     Print_String("\r\n");
  465.     Print_String(" PWM1P=");
  466.     switch(PWM_C1PS) {
  467.         case 0: Print_String("P1.0"); break;
  468.         case 1: Print_String("P2.0"); break;
  469.         case 2: Print_String("P6.0"); break;
  470.         default: Print_String("---"); break;
  471.     }
  472.     Print_String(", PWM1N=");
  473.     switch(PWM_C1PS) {
  474.         case 0: Print_String("P1.1"); break;
  475.         case 1: Print_String("P2.1"); break;
  476.         case 2: Print_String("P6.1"); break;
  477.         default: Print_String("---"); break;
  478.     }
  479.     Print_String("\r\n");
  480.     Print_String(" Commands:\r\n");
  481.     Print_String("  F=1000  set Freq (10~500000 Hz)\r\n");
  482.     Print_String("  D=50    set Duty (0~100 %%)\r\n");
  483.     Print_String("  T=500   set DeadTime (0~12700 ns)\r\n");
  484.     Print_String("  ?       Query all params\r\n");
  485.     Print_String("===========================================\r\n");
  486. }
复制代码


三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 2026-6-18 11:35:48 | 显示全部楼层
请问楼上,您 2个问题,
STC【AI助手】,第几次搞对了 ?
还是有时对,有时错 ?
可能的原因 ?

帮提供思路


点评

提问了三次,后两个重复问题: 1.STC8H1K08-36I-TSSOP20, PWM应用,根据接收到的串口命令,调整PWM输出的占空比和频率的PWM通用演示程序 AI输出的代码编译运行一把通 2.STC8H, PWM应用,PWM1P,1N输出互补PWM,根据  详情 回复 发表于 2026-6-18 11:50
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:586
  • 最近打卡:2026-07-04 12:34:03
已绑定手机

112

主题

4958

回帖

1万

积分

荣誉版主

无情的代码机器

积分
10758
发表于 2026-6-18 11:50:47 | 显示全部楼层
神*** 发表于 2026-6-18 11:35
请问楼上,您 2个问题,
STC【AI助手】,第几次搞对了 ?
还是有时对,有时错 ?

提问了三次,后两个重复问题:

1.STC8H1K08-36I-TSSOP20, PWM应用,根据接收到的串口命令,调整PWM输出的占空比和频率的PWM通用演示程序
AI输出的代码编译运行一次就对, 满分

2.STC8H, PWM应用,PWM1P,1N输出互补PWM,根据接收到的串口命令,调整PWM输出的占空比、频率、死区的PWM通用演示程序
AI首次输出的代码编译正常,逻辑点小问题,通过对话可让其纠正。70分

3.关闭网页,重新打开,重复提问2
同样的问题,AI没有保留上一次对话的经验,并且这次首次输出的代码编译不通过


三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:492
  • 最近打卡:2026-07-05 09:21:10
已绑定手机

6

主题

429

回帖

2187

积分

金牌会员

积分
2187
发表于 2026-6-18 14:35:30 | 显示全部楼层
这个好玩
学习
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:409
  • 最近打卡:2026-07-05 00:22:08
已绑定手机

28

主题

257

回帖

2195

积分

金牌会员

积分
2195
发表于 2026-6-19 13:07:01 | 显示全部楼层
erci*** 发表于 2026-6-18 11:09
结论:同样的问题,AI没有保留上一次对话的经验,并且这次首次输出的代码编译不通过。
================= ...

使用中也发现与你类似的问题。
同样的问题多问几次展示的内容有变化。
还有:
1、登录论坛的用户提问问题历史记录最好能保留。
2、回答结果、产生的源代码等能压缩后一键下载,这样方便。
学习MCU
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:299
  • 最近打卡:2026-07-04 00:01:55
已绑定手机

66

主题

808

回帖

3037

积分

论坛元老

DIY玩家

积分
3037
发表于 2026-6-20 08:46:57 | 显示全部楼层
我心*** 发表于 2026-6-19 13:07
使用中也发现与你类似的问题。
同样的问题多问几次展示的内容有变化。
还有:

保留历史对话
下次还可以翻阅
无需重复提问消耗token
永怀这学徒心
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-7-5 14:04 , Processed in 0.084552 second(s), 92 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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