51单片机SPWM生成:定时器+查表法原理与调试指南

发布时间:2026/10/21 18:07:20
51单片机SPWM生成:定时器+查表法原理与调试指南 简介围绕51单片机生成SPWM波形的PDF技术文档面向嵌入式开发人员、电气工程专业学生以及电机节能设计工程师解决在8位单片机平台上实现正弦脉宽调制并进行电动机节电控制的工程问题。资源为单个PDF文件压缩包仅321KB查阅方便。内容从SPWM基本原理切入讲解通过定时器中断与参考波比较生成脉宽调制波形的方法硬件部分以AT89C51为主控给出与TLC0832模数转换器、SLE4520三相PWM芯片的接口设计以及逆变器触发脉冲控制与I/O分配软件部分覆盖A/D中断服务、SPWM波形计算和负载自适应调压策略。文档结合电动机节电器案例展示空载降压、带载调压、故障保护等具体实施方案并说明了三相输出电压计算与查表生成SPWM波形的流程。已有350人学习浏览适合需要系统理解51单片机SPWM生成原理及电动机节能应用的开发者。1. 用51单片机生成SPWM定时器查表法的入门路线图51单片机生成SPWM波形听起来像是很老的教学实验但直到现在不少逆变器、单相电机驱动、调压电源的底层逻辑依然在沿用这套思路用定时器构造出占空比按正弦规律变化的脉冲序列。SPWM不是调幅它把正弦波的信息编码进脉冲宽度里再经过低通滤波还原出光滑的正弦波。这件事值得亲手做一遍是因为它同时打通了定时器中断、查表法、直流母线电压和LC滤波这几块硬骨头。做完之后再看全桥驱动、死区补偿、闭环调压这类方案会顺手很多。适合正在做电源方向项目、想从纯理论过渡到实际波形调试的工程师也适合实验室里被安排“把SPWM跑起来”的初学者。2. SPWM的本质正弦波与三角波比较51用什么思路落地2.1 正弦脉宽调制不是调幅脉冲宽度里藏着正弦信息SPWM的经典生成方法是把正弦参考波和固定频率的三角载波送进比较器比较结果就是一组等高但不等宽的脉冲。正弦值大的地方脉冲宽度就宽正弦值小的地方脉冲宽度就窄。用这组脉冲去控制开关管再让输出经过LC或RC低通滤波器滤波后的平均值就逼近原始正弦波。这里的关键是脉冲的幅度始终不变变的只是宽度。而宽度与正弦波的瞬时值呈线性关系等价于在一个固定周期里做了“面积等效”。对于一个频率为50Hz的正弦波如果载波频率是10kHz那么一个正弦周期内会塞进200个PWM周期每个PWM周期的占空比按正弦表逐步变化。载波频率越高恢复出的正弦波谐波越少但开关损耗也越大。自然采样法是让正弦波与三角波实时比较每个载波周期里比较点的位置由正弦值决定。但51单片机内部没有模拟比较器CPU也不可能在每个载波周期去算一次sin函数。所以工程上普遍采用规则采样法在三角波的顶点或底点采样正弦值用这个采样值决定当前载波周期的高电平时间。采样间隔固定正弦值可提前算好放进表格51每次中断只管查表开销极小。2.2 为什么51选择查表法而不是实时计算51单片机的指令周期在12MHz晶振下是1us执行一次浮点sin运算可能要十几到几十微秒而一个10kHz载波周期只有100us即使把中断频率降到20kHz也根本扛不住实时运算。查表法把运算全部挪到初始化阶段或PC端离线完成运行期间只有“取数组元素 装载定时器初值 翻转IO”这三件事。查表法还有一个额外好处正弦表可以做成固定精度的16位定时器初值而不是每次中断重新计算计数值。这样中断服务函数里没有乘除法没有浮点代码执行时间可以估算到us级稳定性和可移植性都更好。表格该放哪里也有讲究51的内部RAM只有128到256字节200个16位初值加起来要400字节必须用code关键字把表放进程序存储器而不是塞进RAM。另一个常被忽略的原因是查表法天然适合调制度扩展。调制度M就是正弦表峰值与最大占空比的比例调整M只需要在生成表的时候乘一个系数不需要改动中断代码。想改输出正弦波频率也只需改表长或者载波频率表格重新生成即可。相比之下实时计算方案每次改参数都要重新调试算法周期长还容易把中断卡死。3. 最小可复现工程定时器中断逐点输出SPWM的C代码3.1 引脚分配与最小硬件连接先明确我们说的是单极性SPWM输出也就是一个引脚上输出对地或对负母线的脉冲序列。硬件上只需要三样东西一个51单片机最小系统、一个IO引脚输出PWM、一个RC或LC低通滤波器接在引脚后面观察正弦波。我用P1.0做输出晶振选择12MHz定时器0工作方式116位手动重装。实际项目里如果驱动半桥或全桥还需要互补输出和死区电路本章先解决“波形能不能出来”的问题。连接滤波电容时要注意PWM引脚带载能力有限直接接电容容易把波形拉坏。我一般会在单片机和滤波器之间加一个几十欧的限流电阻或者先用示波器看PWM脉冲确认宽度变化正常再接滤波器。无论接不接滤波示波器探头的地线要尽量短不然高频脉冲会在地线上产生振铃。3.2 生成正弦表离线计算还是片上计算对于200点正弦表我建议直接在PC上算好生成C语言数组然后粘贴进工程。也可以在单片机初始化时用整数运算生成表但那样会占用几百毫秒的启动时间而且必须在RAM中临时存储。下面这段代码生成两个code数组high_init[]保存从低电平切换到高电平后定时器要装载的初值low_init[]保存从高电平切换到低电平后要装载的初值。#define FOSC 12000000UL #define TIMER_CLOCK (FOSC / 12) // 12MHz晶振标准51机器周期1us #define CARRIER_FREQ 10000UL // 载波频率10kHz #define PWM_PERIOD_US (1000000UL / CARRIER_FREQ) // 100us #define SIN_TABLE_SIZE 200UL // 一个正弦周期200个PWM周期对应50Hz code unsigned int high_init[SIN_TABLE_SIZE]; code unsigned int low_init[SIN_TABLE_SIZE]; void generate_table(void) { unsigned int i; for (i 0; i SIN_TABLE_SIZE; i) { double sinv my_sin(2 * 3.14159265 * i / SIN_TABLE_SIZE); double duty (sinv 1.0) / 2.0; // 归一化到0~1 unsigned long high_us (unsigned long)(PWM_PERIOD_US * duty); if (high_us 0) high_us 1; // 避免定时器计数为0 if (high_us PWM_PERIOD_US) high_us PWM_PERIOD_US - 1; high_init[i] (unsigned int)(65536UL - high_us); low_init[i] (unsigned int)(65536UL - (PWM_PERIOD_US - high_us)); } }这段代码生成表时使用了sin函数但实际工程里我不会在单片机里跑double而是用Excel或脚本算好之后直接生成常量数组。上面保留generate_table是为了演示计算逻辑你把my_sin换成标准库sin即可。high_us是当前占空比对应的高电平持续微秒数因为PWM周期是100us所以高电平时间在1us到99us之间。定时器初值用65536 - 时间是因为定时器从初值向上计数溢出后触发中断。时间越短初值越接近65536定时越准确。把生成好的表放进code区中断里直接查表每条语句都是整数赋值不会产生浮点运算。注意如果你把表放在RAM里200个元素两个数组就是800字节标准51的64KB程序空间没有问题但255字节的IDATA肯定放不下编译会直接报错。3.3 定时器中断服务函数与主循环定时器0中断的职责很简单如果当前输出是高电平说明高电平时间到了把引脚拉低装载low_init初值如果当前输出是低电平说明一个PWM周期结束把引脚拉高更新表格索引并装载下一周期的high_init初值。这样每个PWM周期内发生两次中断中断频率就是20kHz对应50us一次。sbit SPWM_OUT P1^0; volatile unsigned char spwm_index 0; volatile unsigned char spwm_stat 0; // 0表示当前输出低1表示当前输出高 void timer0_isr(void) interrupt 1 { if (spwm_stat 1) { // 高电平结束拉低 SPWM_OUT 0; spwm_stat 0; TL0 (unsigned char)low_init[spwm_index]; TH0 (unsigned char)(low_init[spwm_index] 8); } else { // 低电平结束开启新的PWM周期拉高 spwm_index (spwm_index 1) % SIN_TABLE_SIZE; SPWM_OUT 1; spwm_stat 1; TL0 (unsigned char)high_init[spwm_index]; TH0 (unsigned char)(high_init[spwm_index] 8); } } void main(void) { TMOD 0x01; // 定时器0方式116位 TH0 high_init[0] 8; TL0 high_init[0]; SPWM_OUT 0; spwm_stat 0; ET0 1; TR0 1; EA 1; while (1) { // 主循环可以跑数码管、按键、温度采集不影响SPWM输出 } }中断函数里只有两个分支每个分支做四次操作设置IO、更新状态、装载TL0、装载TH0。编译成汇编后大约二十多条指令在12MHz标准51上大约20us而中断间隔是50us占用率40%可以稳定跑。如果你用的还是普通12T架构且晶振只有6MHz中断间隔变宽但指令也变慢比例变化不大建议至少用11.0592MHz晶振再把载波频率降到5kHz来验证。spwm_index在中断里用%取模C51会把它编译成除法虽然只算一次但还是有点开销。为了追求极致可以把表长设定为2的整数次幂比如256然后用spwm_index (spwm_index 1) 0xFF代替取模。但256个点对应载波10kHz时正弦频率是39.06Hz不是工频50Hz。所以要么调整载波到12800Hz要么接受39Hz。我建议保持200个点工频优先中断里的20us开销完全可以接受。4. 调制度、载波频率、死区三个必须调对的参数4.1 调制度决定输出幅度0.8未必最好调制度M定义为正弦参考波的幅值与三角载波幅值的比值在查表法里它决定当前PWM周期最大占空比能到多少。上面的代码里duty (sinv 1) / 2峰值是1对应占空比接近100%这其实属于直通状态高电平时间接近整个周期。这种极限情况下输出电压的基波含量会受低电平最小时间限制而且开关管在最窄脉冲处容易失真。常见做法是给表值乘一个系数比如M0.8让占空比范围变成0.1到0.9。实际工程中M取0.8到0.9比较合适留出死区时间也给低通滤波后的谐波抑制留余地。M太低输出正弦电压幅值不够滤波后有效值不足M太高在正弦波峰值附近脉冲宽度接近极限负载突变时容易产生平顶失真。调制度不需要精确到小数点后3位0.02一个台阶就够用。4.2 载波频率上限定时器计数和中断开销的账载波频率直接决定SPWM的谐波频谱位置也决定开关管的开关损耗。10kHz载波的滤波电感可以选几毫亨5kHz就可能需要几十毫亨体积大很多。但51芯片的载波上限不是靠开关管决定的而是靠定时器精度和中断处理速度决定的。定时器计数和载波频率的关系可以用一个简单公式估算每个PWM半周期的时间 1 / (2 * fc)。12MHz标准51的机器周期是1us载波10kHz时半周期是50us也就是定时器计数50个。定时器初值要设在65536-5065486附近误差1到2us就是2%到4%的占空比误差波形上会出现肉眼可见的阶梯。要提高精度要么提高晶振到24MHz要么换1T增强型51内核的单片机。我一般会选1T主频22.1184MHz机器周期约45ns10kHz载波对应半周期有上千个计数脉冲宽度误差可以压到0.5%以内。4.3 死区插入让半桥驱动不直通上面代码输出的是单极性SPWM如果直接把这一路信号和它的反相信号分别接到上下桥臂上下管会有极短时间同时导通造成直流母线短路。死区就是在这两路互补信号之间插入的一个共同延时让上管先关断下管等一小段时间再开通。在51软实现中我常用一种简单办法在主控引脚对外接一个RC延迟电路或者用专门的驱动芯片自带死区功能。如果只用单片机引脚和普通逻辑门可以在查表时把占空比上限压低留出固定死区时间。比如10kHz载波周期100us死区设定2us那么把占空比限制在2%到98%之间这样无论何时输出上下管的最短共同导通时间最多通过电路延迟产生硬件风险可控。需要说明的是软件死区无法保证两个引脚之间的精确时序因为引脚翻转本身有延迟而且51的IO翻转速度并不快。真正做全桥逆变时强烈建议用带死区控制的专用PWM驱动芯片或者直接选择有硬件互补PWM输出的增强型51。查表法负责生成占空比死区硬件负责保护各司其职。5. SPWM的排查与避坑5个让波形变形的常见原因5.1 输出只有半波另一半凭空消失现象示波器接在SPWM输出引脚上看到一组脉冲只出现在正半周范围内负半周对应的占空比一直为0滤波后得到的是带直流偏置的半波不是正弦波。原因单极性SPWM本来就只有正脉冲如果没有加隔直电容或没有做双极性处理输出平均值包含直流分量。不是电路坏了而是接线和测量方式不对。很多人直接在PWM引脚端接示波器看到脉冲都向上就误以为只有半波。解决把PWM引脚看成“源”把滤波后的信号经过电容耦合到负载或者在示波器上用AC耦合观察。如果是驱动全桥需要双极性SPWM正半周让一对桥臂工作负半周让另一对桥臂工作。我的习惯是先确认单片机输出的是“占空比变化”而不是“极性变化”再检查后级电平移位。5.2 正弦波顶部被削平像被压限器压过现象滤波后看到正弦波的上半部分出现平顶下半部分正常或者上下都平。原因调制度过高正弦表峰值对应的占空比接近100%此时PWM脉冲的高电平时间接近整个周期最窄的低电平时间不够定时器装载和中断处理。同时后级滤波电路如果使用了稳压管或有饱和电感也会削平波峰。解决把调制度降到0.85以下重新生成正弦表。检查代码里high_us限制逻辑确保最大值不超过PWM_PERIOD_US - 2。另外测量滤波前PWM波形的最大占空比如果低电平宽度小于5us说明确实接近极限了可以考虑提高载波周期或改用1T单片机。5.3 中断太慢导致主循环卡死按键都没反应现象程序烧进去之后SPWM波形看起来能出但主循环里的按键扫描完全失灵LED切换有严重的延迟甚至死机。原因定时器中断频率高中断服务函数里有取模、乘除、甚至浮点运算导致中断占用了几乎全部CPU时间。主循环代码只能在中断间隙里插空执行一旦主循环里也有延时函数整个系统马上卡住。解决把中断函数精简成上面那种纯查表形式所有计算都移出中断。主循环不要使用delay长延时改为标志位加状态机。如果还卡降低载波到5kHz再试。另一个经验是用code数组时注意C51的存储类型如果表被默认放到外部RAM访问速度会极慢务必声明为code。5.4 频率漂移标称50Hz实测46Hz现象用示波器测量滤波后的正弦波周期发现不是20ms而是21.7ms左右频率比设定值低。原因正弦波频率等于载波频率除以表长。当载波周期由高低电平定时器初值共同决定时如果高电平和低电平时间因为整数取舍、误差累加实际PWM周期会比理论值略大。比如200个表点每个PWM周期多0.1us累计20us正弦波频率就会偏离1Hz左右。解决在装载low_init时用PWM_PERIOD_US - high_us保证总周期严格按整数值分配。同时检查定时器重装时是否写成了先读后写51的16位定时器在TL0和TH0先后写入时如果中途来了溢出会丢失一次中断。我习惯先关TR0重装后再开虽然会损失几个计数但能避免随机误差。频率要求更严的场景建议直接用硬件PWM模块软件方法只适合容忍1%误差的场合。5.5 功率管发热严重空载就烫手现象SPWM波形正常驱动半桥或全桥但空载时开关管温度明显升高手摸上去发烫。原因开关损耗太大载波频率过高或驱动电阻不合适。还有可能是死区不够上下管存在直通电流即使空载也在消耗能量。另外SPWM在正弦波零点附近有很窄的脉冲这些窄脉冲没有足够死区容易导致半桥直通。解决先用示波器看桥臂中点电压有没有贯穿性的直通毛刺。确认死区时间至少有1us调制度不要超过0.9。如果死区足够再检查栅极驱动电阻驱动电阻过大导致开关管进入放大区的时间变长开关损耗骤增。51的IO驱动能力也有限建议不要直接驱动MOS管中间加一级推挽驱动。6. 从脉冲到正弦用示波器和LC滤波器完成最终验证6.1 LC低通滤波的元件取值与带宽估计SPWM输出的是10kHz方波脉冲需要滤掉载波分量才能看到50Hz正弦波。最简单的二阶LC滤波器截止频率可以按载波频率的1/10选择也就是1kHz左右。这样50Hz信号能顺利通过10kHz谐波衰减40dB以上。电感取值1mH到10mH电容取值1uF到10uF构成几kHz的低通。计算谐振频率的公式是f 1 / (2π√(LC))例如L10mHC2.2uF算出来约1.07kHz可以在示波器上看到平滑正弦波。注意电感不能选工字电感直接竖插在面包板上周围有金属元器件容易耦合干扰。滤波电容要用CBB电容或瓷片电容不要用电解电容的极性反接。如果输出端接了电阻负载滤波效果会变差因为负载并联在电容上会拉低Q值。空载测量最漂亮带载后波形畸变有心理预期就好。6.2 测量流程先看脉冲再看滤波后正弦波上电后第一步用示波器探头直接点P1.0确认波形是等幅不等宽的脉冲按时间轴展开能看到脉冲宽度从窄到宽再到窄缓慢变化。第二步测量脉冲周期确认单个PWM周期是100us左右对应10kHz载波。第三步把探头移到LC滤波器输出端地线夹到滤波电容地观察是否出现50Hz正弦波。测量时不要用示波器的自动测量直接读频率50Hz正弦波上叠加的残余载波会干扰频率计数。我习惯把示波器时间轴调到5ms每格显示两个完整周期眼睛看波形平滑度。如果在正弦波峰值处看到明显的锯齿说明LC截止频率偏高或者载波频率太低如果正弦波幅值只有预期的一半检查调制度是否被分数除法截断成了整数。6.3 进阶玩法从查表法升级到递推正弦生成查表法的表长固定改频率要重新生成表。想在线调节频率可以用递推正弦生成器的方案每次中断按sin_value sin_value delta_theta * cos_value的方式更新相位但51做浮点乘法依然吃紧。折中做法是用表长可变的“半波查表”加相位累加器把正弦表只存四分之一周期用坐标变换推其余四分之三表占用空间降到四分之一代价是中断里增加了几个条件判断。我习惯在验证成功后先保持查表法跑稳定再逐步加入过流保护、调制度调节、软启动这些功能。每次只改一个变量比如把调制度做成可调电位器采样用ADC读取后乘到表值上或者把表长做成300点载波频率相应提高让正弦频率保持不变。这样改完一处就实测一处不会出现中断跑飞后所有功能互相牵连的惨状。做SPWM这件事最怕的不是数学而是一上来就想着全桥、滤波、稳压一把梭。先把一个PWM引脚上的占空比变化用示波器看明白再把LC滤波后的正弦波测出来剩下的控制和保护都是水到渠成的事。希望这些踩过的坑能帮到你尤其是那种波形“看起来像那么回事但细看全是问题”的阶段把定时器初值打印出来把表生成逻辑重算一遍比盲调参数靠谱得多。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询