最近被问得最多的一个单片机,就是 STC8G1K08A。这颗 8 脚小芯片做 PWM 控制确实香:价格低、供货稳、下载方便,小家电、舵机、电机调速、LED 灯板都能用。但问题也集中:有人照着手册配了半天,引脚没波形;有人波形出来了,占空比和示波器量出来却对不上;还有人做电机调速,一上电就飞车。折腾一圈发现,多半不是芯片不行,而是对这个 PWM 模块的原理和寄存器细节没吃透。
这篇文章我就从 STC8G1K08A 的硬件 PWM 模块入手,把频率、占空比、影子寄存器、引脚映射、故障保护这些关键点逐一讲清楚,并给出可以直接抄的配置代码和排查思路。适合刚接触 STC8G 系列、之前只玩过定时器模拟 PWM 的朋友,也适合从 STM32 转过来想快速上手的人。看完你至少能解决三件事:让引脚按你算好的频率稳定输出方波;让占空比精确落在目标值;让占空比在动态调节时平滑过渡而不是突然跳变。
1. 选这颗芯片做PWM控制,先把家底摸清楚
1.1 STC8G1K08A的硬件底子
STC8G1K08A 是宏晶的 8 位 8051 内核单片机,1T 架构,也就是一个时钟周期执行一条指令,这一点比老掉牙的 12T 传统 8051 快得多。芯片内部最高可以跑到 24MHz,8KB Flash,1KB SRAM,工作电压 2.0V 到 5.5V,常见封装是 SOP8、SOP16 和 DFN8,板子上占的位置非常小。外设方面,除了常规的定时器 T0/T1/T2、UART、SPI、I2C,还带了 10 位 ADC、比较器,以及一个完整的高级 PWM 定时器模块。
这里重点说 PWM。STC8G1K08A 内部这个高级 PWM 定时器,缩写叫 PWMA,结构上很像 STM32 的 TIM1/TIM8 高级定时器:16 位计数器、6 路 PWM 通道、支持互补输出、可编程死区、刹车输入,还带预装载的影子寄存器机制。很多从 STM32 转过来的朋友看到这组寄存器会觉得眼熟,但 STC8G 的封装方式、引脚映射规则、寄存器命名又不太一样,所以反而容易踩坑。
这颗芯片没有 DMA,也没有硬件浮点,所以在做 PWM 控制时,所有动态计算最好用整数运算。但它的 PWM 输出有硬件中断标志,更新中断、捕获中断、刹车中断都齐全,做闭环控制完全够用。1KB SRAM 虽然不大,但存几个 PSC、ARR、CCR 的调节变量绰绰有余。总的来说,这芯片适合做“只需要一路到几路 PWM,成本敏感、板子尺寸小、又需要一点硬件智能”的场景。
1.2 为什么“精准调节”必须用硬件PWM
很多人新手阶段都干过一件事:用延时函数或者定时器中断,在 GPIO 上软件翻转电平来模拟 PWM。比如周期 20ms,高电平 1.5ms,低电平 18.5ms,这样也能控制舵机,但问题非常明显。
软件模拟 PWM 的致命弱点,一是所有周期和脉宽都靠 CPU 一点一点数,中间一旦进中断、处理串口、跑 LCD 驱动,时间轴就飘了,轻则波形抖动,重则周期错乱;二是在 20kHz 这种高频下,CPU 几乎什么都不用干,全程就在翻转引脚和喂延时,其他功能全部瘫痪。所以只要谈“精准”,硬件 PWM 是唯一合理的选择。
硬件 PWM 的好处在于,计数器、比较器、输出逻辑都是芯片内部电路自动跑的,你只需要设置好周期寄存器 ARR、比较寄存器 CCR 和预分频 PSCR,CPU 就可以撒手不管。占空比在什么时候翻转、周期到什么时候重新计数,完全由硬件节拍决定,不受中断和主程序阻塞影响。我经常给朋友打比方:软件 PWM 是你在后台自己掐秒表手动开关灯,硬件 PWM 是装了一个交通信号灯控制机,你只负责告诉它“红灯亮多久、绿灯亮多久”,剩下它自己执行。
另外,STC8G1K08A 的硬件 PWM 还自带互补输出、死区、刹车保护。这些功能在电机驱动里至关重要。你用普通 GPIO 模拟,即便拼老命把频率和占空比凑出来,一旦 H 桥上下管直通,板子和 MOS 管分分钟冒烟。硬件 PWM 的死区单元和刹车输入,是软件无论如何也难以可靠实现的。
1.3 占空比调不准,通常是这三个原因
我看了不少问“PWM 占空比调不准”的代码和截图,发现绝大多数问题都可以归到三个点上。
第一,把 ARR 和 ARR+1 搞混。PWM 的完整周期不是 ARR 个数,而是 ARR+1 个计数时钟。很多人在计算频率时用 24MHz 直接除 1200,却忘了 ARR 里写的是 1199,导致算出来的频率和实际差一个计数,误差在高频时不明显,低频时肉眼可见。
第二,没搞清楚占空比公式里的分母。占空比是 CCR / (ARR+1),不是 CCR / ARR。ARR=1199、CCR=600 时,占空比是 600/1200=50%,不是 600/1199。在高分辨率场合,这一字之差就是 0.08% 的偏差,看起来小,但如果你想做精密调光或比例阀控制,这批板子的一致性就毁了。
第三,动态修改 CCR 时没有考虑更新机制。CCR 写入后,到底是立即生效还是等周期结束才生效,取决于有没有开预装载。很多人不知道有这个区别,结果发现占空比要么瞬间突变,要么改了根本没反应,然后就怀疑芯片坏了。
这三个问题,本质上都是对硬件 PWM 的数学模型不够清晰。把公式和影子寄存器吃透,占空比精准调节就完成了大半。
2. PWM频率与占空比的计算逻辑,看懂这一节就成功了一半
2.1 从公式到实例:20kHz电机PWM怎么算
STC8G1K08A 的 PWM 时基,核心就三组寄存器:预分频 PSCR、自动重装载 ARR、比较寄存器 CCR。计数时钟先经过预分频,再驱动计数器从 0 向上数到 ARR,然后溢出重新开始。计数器值和 CCR 相等时,输出引脚翻转或者保持,从而形成占空比。
基本公式是:
- 计数时钟频率 = 系统时钟 / (PSCR + 1)
- PWM 频率 = 计数时钟频率 / (ARR + 1)
- 高电平占空比 = CCR / (ARR + 1),前提是 PWM 模式 1、向上计数、有效电平为高
举个例子,系统时钟 24MHz,不分频,也就是 PSCR=0,想要 20kHz 的 PWM。那么 ARR+1 = 24000000 / 20000 = 1200,所以 ARR 寄存器写入 1199。此时如果你想输出 50% 占空比,CCR 就写 600。
这个例子里,占空比的分辨率是 1/1200,约等于 0.083%。也就是说,CCR 每加 1,占空比变化不到千分之一。对于大多数电机调速、加热控制、灯光调节,这个精度已经足够。
但注意,这里说的是“不分频”。如果你想要一个很低的频率,比如 50Hz,ARR+1 会达到 480000,远超 16 位寄存器最大值 65535。这时候就必须引入预分频,把计数时钟降下来,这也就是下一节要说的舵机场景。
2.2 50Hz舵机PWM的预分频和重装载计算
舵机控制是 PWM 占空比应用的经典场景。舵机一般需要 50Hz 的周期,也就是 20ms 一个周期,脉宽在 0.5ms 到 2.5ms 之间,对应不同的角度。我们用 24MHz 系统时钟来算一下怎么配。
如果直接不分频,ARR+1 = 24000000 / 50 = 480000,16 位 ARR 装不下。所以必须先用预分频把计数时钟降下来。我习惯把计数时钟设在 3MHz 左右,也就是 PSCR+1=8,PSCR=7。此时:
- 计数时钟 = 24MHz / 8 = 3MHz
- ARR+1 = 3000000 / 50 = 60000
- ARR 寄存器写入 59999,仍然在 16 位范围内,刚好
采用这种方式,每个计数代表的时间是 1/3000000 秒,也就是约 0.333 微秒。那么脉宽 1.5ms 对应的 CCR = 3000000 × 0.0015 = 4500,脉宽 1ms 对应 CCR=3000,脉宽 2ms 对应 CCR=6000。
这样一来,舵机在 0 度、90 度、180 度的控制值就非常清晰:分别写 CCR=3000、4500、6000。如果你要更细的角度分辨率,脉宽每变化 1 个计数,就是 0.333 微秒,绝大多数模拟舵机根本分辨不出这么细的变化,所以 3MHz 计数时钟已经相当够用。
这里有一个经常会犯的错:ARR+1=60000,但有些人把 ARR 直接写成 60000,算出来实际频率不是 50Hz,而是 50.0008Hz 倒还好,但如果用别的频率,误差会累积。习惯性把“要除的数减一”写对,后面所有计算才稳。
2.3 占空比、分辨率与频率的三角关系
PWM 调节的“精准”程度,最终取决于占空比分辨率,而分辨率跟 PWM 频率是直接冲突的。因为 ARR 是 16 位寄存器,最大到 65535,所以:
- PWM 频率 = 计数时钟 / (ARR+1)
- 占空比分辨率 = 1 / (ARR+1)
ARR 越大,频率越低,但占空比步进越小,调节越精细;反过来,频率拉高之后,ARR 就得变小,占空比分辨率随之下降。
举例,24MHz 不分频时,20kHz PWM 只有约 1200 级占空比步进;而 1kHz PWM 有 24000 级,分辨率高 20 倍。所以设计系统时,不要盲目追求高 PWM 频率。电机一般 15kHz 到 20kHz 就足够跑到人耳听不到啸叫,而 LED 调光其实 1kHz 以上人眼就基本看不出闪烁,但如果你想做低亮度的精细调光,反而应该把频率降低,换取更高分辨率。
这也是我在实际项目里经常给人提的建议:做电机调速,选 20kHz,保证静音且 MOS 管开关损耗可控;做 LED 精细调光,选 1kHz 到 4kHz,分辨率高得多;做舵机,就固定 50Hz。没有哪个频率是万能的,一切看场景。
如果你确实需要“高频率+高分辨率”,那就得考虑从提高计数时钟入手,比如把系统主频提到更高,或者用 PLL 倍频。STC8G1K08A 最高能跑到 24MHz,在 20kHz 下做到 1200 级占空比,对绝大多数消费级产品是够用的。真不够,那就是该换芯片或者加外部 DAC 的场景了。
3. 寄存器级实战:把PWM点亮并实现动态调占空比
3.1 初始化六步走,逐行解释寄存器配置
STC8G1K08A 的 PWM 初始化,我习惯按六个步骤来:配引脚、选映射、设时基、配模式、使能输出、启动计数器。下面是一份完整的初始化代码,基于 Keil C51,头文件用 STC8G.H。目标:从 P1.0 输出 20kHz 的 PWM,初始占空比 50%。
#include "STC8G.H" #define PWM_ARR 1199 // 20kHz @ 24MHz,不分频 #define PWM_CCR 600 // 50% 占空比 void PWM_Init(void) { // 1. 引脚模式:P1.0 设置为推挽输出 P1M1 &= ~0x01; P1M0 |= 0x01; // 2. 引脚映射:PWMA1P 选择默认引脚 // 具体映射取决于封装,SOP8下PWMA_PS复位值通常指向P1.0 PWMA_PS = 0x00; // 3. 时基:预分频 PSCR=0,周期 ARR=1199 PWMA_PSCRH = 0x00; PWMA_PSCRL = 0x00; PWMA_ARRH = (unsigned char)(PWM_ARR >> 8); PWMA_ARRL = (unsigned char)(PWM_ARR & 0xFF); // 4. 通道模式:PWM模式1 + 预装载使能 // CCMR1: OC1M=110(PWM模式1), OC1PE=1(预装载) PWMA_CCMR1 = 0x68; // 5. 输出配置:使能CC1通道,高电平有效 PWMA_CCER1 = 0x01; // 6. 初始占空比 50% PWMA_CCR1H = (unsigned char)(PWM_CCR >> 8); PWMA_CCR1L = (unsigned char)(PWM_CCR & 0xFF); // 7. 使能PWM1P输出 PWMA_ENO = 0x01; // 8. 主输出使能,这一步漏了引脚永远没波形 PWMA_BKR = 0x80; // 9. 启动计数器,同时打开ARR预装载 PWMA_CR1 = 0x81; }这段代码里,最容易漏的就是第 8 步。PWMA_BKR 的 bit7 是 MOEN,主输出使能。STC8G 的 PWM 设计里,即使你配好了模式、周期、比较器,只要 MOEN 是 0,PWM 引脚就是禁用的。我身边至少有三位朋友卡在这个地方,一开始都以为是芯片坏了。
另外,CCMR1 写成 0x68,二进制是 0110 1000,对应 OC1M=110、OC1PE=1。这里 OC1M=110 是 PWM 模式 1,也就是向上计数时,计数器值小于 CCR 输出有效电平。如果你想要相反的极性,可以用 PWM 模式 2,那就是 0x78。这个选择直接影响初始电平是高还是低,接 LED 还是接 MOS 管时极性问题会很关键。
3.2 动态占空比调节接口设计
初始化完成后,动态调节占空比就只需要改 CCR 寄存器。但工程上不能直接写寄存器就完事,需要封装成接口,并且做好边界保护。先看一个最简单的版本:
void SetPwmDuty(unsigned int duty) { if(duty > PWM_ARR) duty = PWM_ARR; // 限幅 PWMA_CCR1H = (unsigned char)(duty >> 8); PWMA_CCR1L = (unsigned char)(duty & 0xFF); }很多初学者会漏掉限幅。如果 duty 写进去的值大于 ARR,PWM 输出会进入一种“永远有效”或“永远无效”的状态,看波形会非常诡异。比如 ARR=1199,你却把 CCR 写成 1500,那计数器永远到不了 CCR,比较器永远不会触发翻转,输出固定在一个电平上,看起来就像 PWM 坏了。所以在所有公开接口里加限幅,是低成本高收益的习惯。
如果要按百分比设置占空比,可以这样:
void SetPwmDutyPercent(unsigned int percent) { unsigned long temp; if(percent > 100) percent = 100; temp = (unsigned long)(PWM_ARR + 1) * percent / 100; SetPwmDuty((unsigned int)temp); }注意这里要先转成 unsigned long,否则 (PWM_ARR + 1) * percent 在 16 位 int 下会溢出。8051 的 int 是 16 位,最大 65535,1199×100=119900 已经超了,不强制转换的话算出来就是错的。这个坑我见过不止一次,很多人明明公式会算,但写进 C 代码就翻车,原因就是不够重视中间变量的类型。
在 STC8G1K08A 这种资源紧张的芯片上,我一般不引入浮点运算。所有占空比计算都用整数和定点方式完成,速度和代码体积都更友好。
3.3 用Timer0中断实现平滑渐变
如果你想做呼吸灯,或者电机缓慢启动停止,那就不能让占空比一次跳到位。直接 SetPwmDuty(600) 瞬间翻转,LED 会闪一下,电机电流会猛冲。正确的做法是让占空比按固定步进逐步逼近目标值。
我通常用定时器 T0 做一个 1ms 的中断,在中断里把当前占空比向目标占空比靠近。示意代码如下:
volatile unsigned int target_duty = 0; volatile unsigned int current_duty = 0; void Timer0_Isr(void) interrupt 1 { // 重新装载T0初值,按你的系统时钟计算1ms // ... if(current_duty < target_duty) { current_duty += 8; // 每次加8,控制变化速率 if(current_duty > target_duty) current_duty = target_duty; } else if(current_duty > target_duty) { current_duty -= 8; if(current_duty < target_duty) current_duty = target_duty; } SetPwmDuty(current_duty); }步进值 8 根据实际需求调整。步进越小,变化越平滑,但到达目标的时间越长。比如 20kHz 下 ARR=1199,从 0 到 600,步进 8,需要 75 次中断,也就是 75ms,这刚好是一个比较柔和的 LED 点亮时间。电机调速也可以用同样的思路,只不过步进可以大一些,让电机快速越过启动死区。
这种渐变逻辑放在 T0 中断里,PWM 波形本身由硬件跑,不受任何影响,CPU 只负责每隔 1ms 算一次比较值,非常轻量。
4. 精准调节的进阶细节:影子寄存器、引脚映射与故障保护
4.1 影子寄存器:为什么改占空比有时会突变甚至没反应
刚玩 STC8G PWM 的人,多半会碰到一个现象:主程序里死循环不断改 CCR,但是输出波形纹丝不动,或者变了一下又跳回去。这通常就是预装载和更新事件的问题。
预装载机制其实是一套“影子寄存器”。你往 CCRH/CCRL 写入的数据,会先进入一个缓冲寄存器,并不直接作用于比较器。只有等到更新事件发生,比如计数器溢出,缓冲值才会一次性被装载到实际工作的比较寄存器里。这样做的好处是,所有参数在同一边界同步生效,避免在 PWM 周期中间出现一个奇怪宽度的脉冲。
我在初始化代码里写的 PWMA_CCMR1 = 0x68,就包含了 OC1PE=1,也就是使能了 CCR1 的预装载。同时 PWMA_CR1 = 0x81 里包含了 ARPE=1,使能 ARR 的预装载。这两个预装载开关一旦打开,周期和占空比的更新就会同步到下一个周期开始。
如果你故意不开预装载,每次写 CCR 都会立即改变比较值,那输出波形会出现在中间状态,比如一个 20ms 的周期里,前面是 50% 占空比,写寄存器后突然变成 80%,这个突变在电机上就是一次抖动,在舵机上就是一次小跳变。所以我建议:所有动态调节占空比的场合,都开预装载,让更新对齐到周期边界。
还有一个容易忽略的点:STC8G 的 CCRH 和 CCRL 是两个 8 位寄存器。我习惯先写高字节再写低字节。因为如果不确定性载荷的情况下,先改高字节或低字节,都可能让比较器中间某一个时刻拿到一个残缺值,产生毛刺。配合预装载后,这个风险会被更新事件吞掉,但仍建议养成“先高后低”的写法。
4.2 引脚映射与端口模式:波形跑到别处的隐形元凶
STC8G1K08A 的 PWM 输出引脚,并不是固死在某个 IO 上,而是由 PWMA_PS 寄存器决定映射关系。不同封装、不同型号,映射关系还不一样。比如 PWMA1P 可以选择接到 P1.0、P2.0 等引脚,具体看数据手册里的引脚映射表。很多人从网上复制代码,结果代码里配置的引脚和自己的板子不一样,波形自然出不来。
所以排查问题时,第一步不是怀疑寄存器,而是先确认你的波形到底从哪个引脚出来。把 PWMA_PS 的值和芯片手册对照一遍,确认无误再继续。
另外一个很隐蔽的问题是端口模式。PWM 外设输出信号时,引脚仍然受端口模式寄存器 PnM0/PnM1 控制。如果你把引脚设成高阻输入,那 PWM 输出会被切断或者电平被外部电路拉走;设成准双向口,驱动能力又弱,接个 LED 或者 MOS 管栅极可能波形变形。我在工程上都把 PWM 输出脚设成推挽输出,也就是 PnM1 对应位清 0,PnM0 对应位置 1,这样才能保证足够的驱动能力和边沿速度。
引脚映射和端口模式这两个问题,占了我见过的“PWM 不工作”案例里至少三成。它们不像寄存器配置那么直观,但排查起来也最简单,万用表量一下引脚电平,再翻翻手册就清楚了。
4.3 电机飞车与PWM故障保护
电机飞车是 PWM 控制里最吓人的故障之一。现象就是上电或者运行中,MOS 管开关失去控制,电机满速疯转。原因可能有几种:程序跑飞、PWM 引脚虚焊、占空比突变、MOS 管击穿。但不管哪种原因,光靠软件在主循环里补救是来不及的,必须用硬件机制兜底。
STC8G1K08A 的高级 PWM 模块带刹车功能,相关寄存器是 PWMA_BKR。BKEN 位使能刹车输入,BKFP 位选择刹车输入有效电平,当刹车信号有效时,PWM 输出会被硬件强制关闭,主输出使能 MOEN 也会被硬件清零。这个动作是毫秒级甚至微秒级的,不需要 CPU 参与。
更实用的是,STC8G 内部的比较器输出可以作为 PWM 刹车信号。你可以用采样电阻把主回路电流转换成电压,送进比较器一端,另一端设一个过流阈值。电流一旦超限,比较器立刻触发刹车,PWM 输出关断,电机停转。这比 ADC 采样再去查中断快得多,是低成本电机保护的经典做法。
另外,软件上我也建议加两道防线:一是对目标占空比做上限限制,比如 80%,防止程序错误输出全速;二是加一个看门狗喂狗任务,程序一旦卡死,看门狗复位,PWM 重新初始化成安全状态。硬件刹车管“瞬时保护”,上限和看门狗管“长期兜底”,两层都做了,飞车概率才真正降下来。
5. 常见问题排查与避坑速查表
5.1 PWM没有输出
PWM 没有输出是最常见的故障,我建议按这个顺序查:先量引脚电平,再看寄存器,最后查负载。
第一,检查 PWMA_ENO 是不是使能了对应通道。很多人配好了 CCMR、CCER,但忘了写 PWMA_ENO,输出通道根本没被打开。第二,检查 PWMA_BKR 的 MOEN 是不是 1。这个前面说过,是最容易漏的。第三,检查 PWMA_CR1 的 CEN 有没有启动计数。CEN 没置 1,计数器不跑,自然没波形。第四,检查 PWMA_PS 映射,确认你测的引脚确实是这个 PWM 通道的输出脚。第五,检查端口模式,如果是高阻输入,引脚被 PWM 外设接管也可能没信号。
这几步走完,90% 的“无输出”都能解决。剩下的可能就是芯片没有正常工作,量一下电源、复位、下载有没有成功。
5.2 实测频率与设定值对不上
如果你用示波器量出来的 PWM 频率,和心里算出来的差很多,先检查是不是把 PSCR 和 PSCR+1 写反了。预分频寄存器的值是 0 表示不分频,7 表示 8 分频。总觉得“写 7 就是 7 分频”的人不在少数。
另一个高频错误是系统时钟和你以为的不一致。STC8G1K08A 内部 IRC 频率由下载时的配置决定,可能在 STC-ISP 软件里选了 24MHz,但实际配置是 11.0592MHz,或者主程序里又改了 CLKDIV 分频。建议在初始化最前面加一个延时函数,用示波器量一下某个 GPIO 翻转频率,先确认系统时钟到底跑多少,再做 PWM 计算。
还有一种情况:实际波形不是 PWM,而是被负载压垮的衰减信号。示波器探头接在 MOS 管漏极和地之间,看到的往往叠加了振铃和 RC 充放电,频率读数会不准。这时候要测 PWM 引脚本身,或者用电子负载模拟实际工况。
5.3 占空比不对或更新异常
占空比问题,先确认你用没用对公式:占空比 = CCR / (ARR+1)。我见过太多人用 CCR / ARR 来算,结果偏了那么一点点,但批量产品就是不合格。
如果波形显示占空比是对的,但改 CCR 不生效,那十有八九是预装载位没配置对。检查 CCMR 的 OCxPE 和 CR1 的 ARPE。如果开了预装载,修改 CCR 后要到下一个更新事件才生效,这不是 bug,是特性。如果你希望“立竿见影”,可以软件置位 PWMA_EGR 的 UG 位产生更新事件,强制装载。但在电机控制里,我更推荐让它自然更新,不要人为打断周期。
还有一个细节:CCR 大于 ARR 之后,输出会固定在某个电平。如果你的“占空比不对”表现为满高或满低,优先检查是否越界。封装一个 public 接口时,一定要限幅。
5.4 波形毛刺、MOS管发热与舵机抖动
波形毛刺和 MOS 管发热,本质上是同一个问题在不同地方的体现。PWM 驱动 MOS 管时,栅极回路存在寄生电感和电容,开关瞬间会产生振铃。栅极串联一个 10 到 47 欧的电阻可以抑制振铃,代价是开关速度变慢、开关损耗略微增加。这个电阻需要根据驱动能力和实际波形去调,我在项目里一般先从 22 欧试起,用示波器看栅极波形,以没有明显过冲为准。
MOS 管发热还有两个常见原因:频率选太高导致开关损耗大,或者死区时间不够导致 H 桥上管和下管短暂直通。前者好办,把 PWM 频率降到 20kHz 甚至 15kHz;后者需要检查互补通道的死区寄存器配置,STC8G 的 PWMA_CCMR 和 BKR 相关位可以设置死区时间,具体数值按 MOS 管关断延迟来定,一般几百纳秒到一两微秒。
舵机抖动则和 PWM 周期稳定性、供电纹波关系更大。我用 STC8G1K08A 控制模拟舵机时,发现抖动的主要原因是电源瞬间跌落。舵机堵转电流很大,如果供电线太细或者没有大电容,一拉电流电压就掉,内部电路工作不稳定。解决办法是在舵机电源两端并一个 100uF 到 470uF 的电解电容,再并一个 0.1uF 陶瓷电容,效果立竿见影。
下面把这几个常见问题汇总成一张速查表,方便现场排查。
| 现象 | 可能原因 | 排查方向 | 解决办法 |
|---|---|---|---|
| PWM 无输出 | ENO/MOEN 未使能 | 检查 PWMA_ENO、PWMA_BKR | 使能通道和主输出 |
| PWM 无输出 | 引脚映射选错 | 检查 PWMA_PS | 对照手册选对引脚 |
| PWM 无输出 | 端口模式错误 | 检查 PnM0/PnM1 | 改为推挽输出 |
| 频率偏了 | PSCR 计算错误 | 核对预分频公式 | 用 PSCR+1 计算 |
| 频率偏了 | 系统时钟不一致 | 测量实际主频 | 统一系统时钟配置 |
| 占空比不对 | 公式用错 | 核对 CCR/ARR 关系 | 使用 CCR/(ARR+1) |
| 占空比不更新 | 预装载未生效 | 检查 OCxPE/ARPE | 打开预装载并确认更新事件 |
| MOS 管发热 | 频率过高/栅极振铃 | 看栅极波形 | 降频、串栅极电阻、调死区 |
| 舵机抖动 | 电源跌落 | 量舵机供电电压 | 加大电容,改善布线 |
排查的时候,我一般遵循“先硬件后软件、先电源后信号、先输出后输入”的顺序。硬件 PWM 这东西,一旦原理通了,大多数问题都是某个位没配好,而不是芯片本身有问题。
最后再聊一点个人体会。STC8G1K08A 的高级 PWM 模块,麻雀虽小五脏俱全,它和 STM32 的定时器 PWM 输出模式在思路上高度一致,所以你在 STM32 上学到的预装载、刹车、死区、互补输出这些概念,迁移过来几乎无缝。反倒是从 51 传统定时器直接跳过来的朋友,需要花点时间适应,因为 STC8G 已经不是当年那个“定时器中断翻转 IO”的玩法了。做 PWM 精准调节,核心就是四个字:算准、配稳。公式算准,寄存器配稳,剩下的交给硬件去跑。希望这篇实战记录能帮你少走几步弯路。