ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32F103C8T6全桥逆变PWM生成:死区互补输出实战指南

STM32F103C8T6全桥逆变PWM生成:死区互补输出实战指南 第一次给全桥逆变电路上电最刺激的不是看输出电压而是祈祷MOS管别冒烟。我见过不少新手包括当年的我在写完PWM代码后满怀信心地合闸结果“啪”一声管子直接炸了原因基本都指向同一个同一桥臂的上下管直通了。STM32F103C8T6配合HAL库生成全桥逆变所需的PWM波这件事本身不复杂但里面藏着几个容易踩穿的坑定时器选型、互补输出、死区设置、刹车保护每一个都能决定你是顺利出波还是烧板子。这篇文章把整个链路从头到尾捋一遍从硬件对PWM的需求讲起到CubeMX配置、代码落地、示波器实测、再到SPWM扩展适合正在做逆变器、电机驱动或者DC-AC电源打算用F103C8T6和HAL库出四路带死区的PWM波的同学参考。1. 为什么全桥逆变不能只用普通PWM死区到底在救什么1.1 全桥电路的开关逻辑全桥逆变的基本拓扑就是两组半桥拼成一个H桥四个开关管Q1到Q4负载接在两个桥臂中点之间。要让负载两端出现交流电压最简单的策略是分两个阶段工作正半周让Q1和Q4同时导通负半周让Q2和Q3同时导通这样负载上的电流方向就能交替变化。听起来很直接但实际控制里有一个致命的物理约束同一桥臂的上下两个管子绝对不能在切换瞬间同时导通。假设Q1导通的同时下面那个管子Q3刚刚关断但还没完全截止这时候直流母线正极会通过Q1、Q3直接回到负极形成一个阻抗极低的直通回路。母线电压哪怕只有12V瞬间电流也可能冲到几十上百安培MOS管在几微秒内就过热击穿了。这个现象俗称“炸管”是逆变和电机驱动项目中最常见的硬件事故。MOS管不是理想开关它有开通延迟、关断延迟和下降时间驱动芯片也有传播延迟。你在代码里让Q1关断、Q3导通看起来是先后顺序但实际到硬件层面两个管子很可能在某一小段时间里同时处于导通状态。解决方案就是在切换瞬间插入一段“谁都不导通”的空白时间这就是死区。1.2 为什么主控必须是高级定时器有人会想既然只差一个死区那我用两个普通定时器分别产生PWM然后在软件里把其中一个延后一点不就行了理论上可行实际很难做精。死区时间通常是几百纳秒到一两微秒软件延时在这个尺度上受中断、流水线、编译器优化的影响很大温度一变时序就漂。而且两个独立定时器的相位关系需要持续对齐稍有波动就可能在某一个周期偶然出现直通这种偶发故障最难查。STM32的高级定时器TIM1在设计上就是冲着这个问题去的。它内部有一个死区发生器能在硬件上自动为主输出OCx和互补输出OCxN之间插入可编程的死区时间。你只需要设置好死区长度无论PWM频率怎么变化、占空比怎么调整硬件都会保证互补两路在一瞬间“先关后开”不存在软件时序漂移问题。高级定时器还带刹车Break功能外部过流信号一来硬件立即封锁所有输出这对于逆变项目来说基本是标配需求。1.3 为什么选F103C8T6而不是更高级的芯片F103C8T6这颗芯片在逆变这种应用里的定位很清晰主频72MHz一个高级定时器TIM1两对互补PWM输出刚好够全桥使用Flash有64KB跑一个SPWM查表加几个保护逻辑绰绰有余。和F4/G4系列比它没有高分辨率定时器、没有内置比较器DAC和模拟比较器但对于学习验证和中等精度逆变来说完全够用。一个细节要注意F103C8T6属于中等容量产品只有TIM1这一个高级定时器另一个高级定时器TIM8只在F103的高容量型号比如RCT6、VET6上才有。所以做全桥项目选C8T6TIM1是唯一选择不用纠结。2. TIM1作为“唯一解”F103C8T6的定时器资源与引脚排查2.1 TIM1的通道分布和复用关系F103C8T6的TIM1共有4个输出通道其中CH1到CH3带互补输出CH1N/CH2N/CH3NCH4没有互补输出。全桥需要两对互补PWM也就是用CH1和CH2这两对就够。默认复用映射下的引脚关系如下信号默认引脚说明TIM1_CH1PA8左桥臂上管TIM1_CH1NPA7左桥臂下管TIM1_CH2PA9右桥臂上管TIM1_CH2NPB0右桥臂下管TIM1_CH3PA10第三通道可做扩展TIM1_CH3NPB1第三通道互补这里要特别提醒第一次做F103C8T6项目的同学PA9和PA10默认是串口1的TX和RX引脚如果你既要通过串口打印调试信息又要用TIM1的CH2和CH3就会撞车。网上很多最小系统板的例程默认把串口1配在PA9/PA10上项目做到一半才发现引脚不够用回头改板子很麻烦。我个人的建议是如果确定这个项目要用TIM1做PWM输出串口调试尽量换到USART1的重映射引脚PB6/PB7或者干脆用USART3PB10/PB11。在CubeMX里把USART1的Remap勾选一下配置就能避开冲突。2.2 和最小系统板常见引脚的冲突点C8T6这块板子因为价格便宜、资料多很多人都手头有一块。它的引脚资源本身就不宽裕PWM相关引脚还要额外注意两个冲突源一是PA11/PA12如果被用作USB D-/D会和TIM1_CH4冲突但这对于全桥项目影响不大因为CH4本来没有互补输出二是PB0/PB1在某些板子上可能被占用比如有的模块把PB0/PB1接成了按键或者LED这就会导致TIM1_CH2N输出被外部电路拉到一个固定电平示波器上怎么查都看不到波形。我的建议是拿到板子先看一眼原理图把PB0/PB1、PA7、PA8、PA9这几个引脚的周围电路确认一遍尤其是有没有上拉电阻、LED、按键这些会改变电平状态的器件。之前帮一个群友排查问题他调了两天发现是板子上PB0挂了个LED到3.3V把互补输出钳位在高电平了焊掉LED立刻正常。2.3 载波频率和定时器分频的计算逆变器的PWM频率通常叫载波频率输出交流频率比如50Hz是另一回事。载波频率选择有几个考量要高于音频范围避免刺耳啸叫一般在16kHz以上同时受到开关损耗限制不能太高功率稍大点的MOSFET在40kHz以上损耗就明显了。常用的区间是18kHz到25kHz我习惯用20kHz听不见、损耗可以接受、计算也方便。TIM1挂在APB2总线上配置好时钟树之后默认是72MHz。PWM频率的计算公式是PWM频率 定时器时钟 / ((PSC 1) * (ARR 1))如果目标频率是20kHz可以直接令PSC0、ARR3599这样72MHz除以3600正好等于20kHz。注意这里计数范围是0到3599共3600个计数值所以占空比的分辨率是1/3600也就是大约0.028%。这个分辨率做SPWM正弦波调制时完全够用波形细腻程度肉眼示波器根本分辨不出来。如果PSC选大了比如PSC71定时器时钟降到1MHz要达到20kHz就得ARR49整个周期只有50个计数台阶正弦波一个周期内只能分成50级波形会有明显的阶梯感滤波后谐波也大。在做逆变PWM时尽量用小预分频、大自动重载值这是原则。3. CubeMX里TIM1的配置细节从时钟树到死区数值3.1 时钟树和调试口设置在CubeMX里新建工程选择STM32F103C8T6后第一件事不是配TIM1而是把RCC和SYS设置好。RCC里的HSE选择Crystal/Ceramic Resonator如果你用的是板载8MHz晶振。SYS里Debug选择Serial Wire这个看起来不起眼的设置如果你漏了程序烧录一次之后SWD接口可能就被禁用了下次再想连就得用串口ISP擦除非常麻烦。然后切到Clock Configuration页面把时钟树拉到72MHz。具体操作是输入HSE的8MHzPLL Source选择HSEPLLMUL选x9得到72MHz然后确认APB1预分频为/2APB2预分频为/1。TIM1在APB2上APB2预分频为1时定时器时钟就是72MHz。这里有个容易搞混的点即使APB2分频了只要APB2预分频不为1TIM1时钟会翻倍补偿。总之最终到TIM1的时钟是72MHz就对了。3.2 TIM1通道模式和占空比参数的填写进入TIM1配置页后在Slave Mode里选择Disable在Mode部分把Channel1设为PWM Generation CH1 CH1NChannel2也设为PWM Generation CH2 CH2N。这样CubeMX会自动把PA8/PA7/PA9/PB0复用为TIM1的PWM输出引脚。下面是几个关键参数和我的推荐值参数推荐值说明Prescaler0不分频保留最高分辨率Counter ModeUp向上计数模式Counter Period3599配合72MHz时钟得到20kHzAuto-reload preloadEnable使能预装载修改ARR时避免波形撕裂Pulse1800初始占空比50%CCR值CH1/CH2 PolarityHIGH主输出高有效CH1N/CH2N PolarityHIGH互补输出高有效Dead Time按功率管实测见下文详述Pulse初始值设为1800对应50%占空比是为了让第一次上电时输出不瞬间拉满降低炸管和冲击电流的风险。等代码运行起来以后再通过寄存器在线修改成自己需要的占空比这个后面代码部分会说。3.3 死区时间到底填多少死区数值的确定不能凭空拍脑袋要知道你的功率管关断特性。查看MOSFET数据手册里的Turn-Off Delay Time和Fall Time两者相加就是管子从开始关断到完全截止的大致时间MCU设置的死区必须大于这个值。如果驱动芯片本身也插入死区时间那么MCU侧死区和驱动芯片死区在逻辑上是“相加”的关系可以适当取小。举个例子常见的IRLR3410这类逻辑电平MOS管关断延迟和下降时间合计大约在60到100纳秒如果用的是IR2104这类半桥驱动芯片它内部自带大约520纳秒死区这种情况下MCU侧设个500到800纳秒就够。如果用的驱动芯片不带死区比如IR2110MCU侧就得设足1微秒以上。在CubeMX的Break and Dead Time management里有一个Dead Time输入框直接填纳秒数就行比如600。软件会自动换算成DTG寄存器的值。也可以自己验算一下72MHz时钟下TDTS约为13.9纳秒DTG寄存器值大概等于死区时间除以13.9。填600纳秒算下来大约43硬件实际插入了43乘13.9约597纳秒误差在几纳秒级完全可接受。对于大功率方案建议死区位置留一点余量宁可稍微大一点损失一点点输出电压精度也不要冒着直通风险去极限压缩死区时间。死区过大带来的问题是输出电压波形会有一个小的阶梯平台谐波增加但对学习项目来说不会致命。死区过小的后果就是炸管一次就够你心疼好久。3.4 刹车输入和自动关闭输出全桥逆变只靠死区是不够的。万一负载短路、MOSFET击穿或者其他故障导致母线电流暴增软件根本来不及响应必须在硬件层面快速把PWM输出拉到安全状态。TIM1的刹车Break功能就是干这个的。在CubeMX中Break and Dead Time management页面里Break Input可以配置成从某个引脚引入外部信号通常接一个过流比较器的输出。极性可以设成低电平有效或高电平有效具体看你的比较器电路。在Off-State Selection里可以选择刹车触发后输出是高阻态还是低电平。对于功率驱动一般建议选择高阻态因为高阻状态能避免故障状态下输出顶死某个电平。刹车是硬件行为一旦触发PWM立即封锁不需要CPU参与这是保护动作能快到纳秒级的关键。做逆变这种高危项目如果主控不支持硬件刹车功能我一般不建议直接上电。3.5 生成工程后需要手动确认的两处代码CubeMX生成代码后在main函数里能找到MX_TIM1_Init()。这个函数内部有一段Break Dead Time结构的配置如果你在CubeMX里设置了刹车它会生成类似这样的代码sBreakDeadTimeConfig.OffStateRunMode TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime 42; sBreakDeadTimeConfig.BreakState TIM_BREAK_ENABLE; sBreakDeadTimeConfig.BreakPolarity TIM_BREAKPOLARITY_LOW; sBreakDeadTimeConfig.BreakFilter 0; sBreakDeadTimeConfig.Break2State TIM_BREAK2_DISABLE; HAL_TIMEx_ConfigBreakDeadTime(htim1, sBreakDeadTimeConfig);注意看DeadTime这个字段的值。CubeMX里填的是纳秒数生成到代码里的是换算后的DTG寄存器值。如果我前面填600纳秒这里生成的可能是42或者43左右。如果发现这个值对不上反过来验算一下是不是时钟配置不对比如APB2没到72MHz。另一处要确认的是HAL_TIMEx_MasterConfigSynchronization这个函数它会把TIM1的主输出触发配置好这是高级定时器正常工作的一部分。如果CubeMX生成后这个函数被某些代码优化掉或者注释掉PWM可能完全没有输出。4. 工程代码最小落地启动函数、在线调参和初次验证4.1 HAL_TIM_PWM_Start和HAL_TIMEx_PWMN_Start的区别这是新手最常栽的一个跟头。CubeMX生成的初始化函数只负责配置定时器不会自动启动PWM输出。你需要手动开启。但全桥需要的两路PWM以CH1为例要调用两个启动函数HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1);HAL_TIM_PWM_Start启动的是主输出OC1对应PA8引脚HAL_TIMEx_PWMN_Start启动的是互补输出OC1N对应PA7引脚。两个函数必须成对调用漏掉第二个后果就是CH1有正常波形CH1N永远是一条直线上下桥缺一半。CH2同理HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_2);这里有一个背后机理要理解高级定时器有一个主输出使能位MOE在主输出未使能时所有OCx和OCxN输出都处于禁止状态。HAL_TIMEx_PWMN_Start内部会调用HAL_TIM_PWM_Start去打开对应通道的主输出同时使能MOE。所以如果你直接调用HAL_TIMEx_PWMN_Start它其实也会连带打开主输出但反过来只调用HAL_TIM_PWM_Start则不会让互补输出工作。为了保证逻辑清晰建议四行代码老老实实写上。4.2 在线修改占空比和频率的方式PWM启动以后修改占空比是用宏操作CCR寄存器__HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, 1800); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, 1800);CCR的值范围是0到ARR也就是0到3599和占空比成线性关系。占空比50%就是1800占空比25%就是900。如果改了PSC或者ARRCCR对应的占空比会变化所以变更频率后要重新计算CCR。修改频率时操作ARR寄存器__HAL_TIM_SET_AUTORELOAD(htim1, 1799);如果之前Counter Period设的是359920kHz改成1799后频率就变成40kHz。但要注意如果你在CubeMX里使能了Auto-reload preload预装载这个修改会在下一个更新事件才生效。更新事件通常由计数器溢出触发也就是完成当前这个PWM周期。这种“延迟生效”机制是好事它能避免在一个周期中途突然跳变产生很窄的脉冲但也提醒你如果你在修改ARR后立刻修改CCR两者生效的时序可能不同步最好在同一个更新中断里一起改。要专门说一句在线调参时最好在TIM1的更新中断回调里做动作void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM1) { // 在这里读取新的目标参数然后同步更新CCR/ARR } }Callback里处理的优点是你的修改永远和PWM周期边界对齐不会出现波形毛刺。4.3 一个最小可验证的测试代码框架如果你想第一次上电在示波器上看波形不需要写任何业务逻辑初始化完直接启动PWM就行。把Pulse设为1800启动四路输出用示波器探头同时量PA8和PA7应该能看到两路互补方波占空比50%频率20kHz中间有一个肉眼可见的窄缝——这就是死区。再把示波器接到PA8和PB0上看到的应该是同频同相的波形这两路分别驱动不同桥臂的上管。如果需要对占空比做一次爬坡验证可以写个简单的循环uint16_t ccr 1800; while (1) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); ccr 100; if (ccr 3400) ccr 200; __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, ccr); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, 3600 - ccr); HAL_Delay(500); }这段代码的效果是两路通道的占空比往相反方向变化正好模拟后面SPWM调制中两个桥臂“一高一低”的相互关系。用LED的闪烁作为心跳确认主循环还在跑。实际测试时我比较倾向于用示波器的“数学通道”功能把两个通道相减直接看两个桥臂间虚拟差压的波形这样能更直观地看到占空比变化带来的电压变化。5. 实测中的三个典型问题和一步步的排查路径5.1 现象一只有CHx有波形CHxN一直没有输出如果你上电后发现PA8有PWMPA7却是一根低电平直线大概率不是硬件坏了而是互补输出根本没被启动。这种问题的排查路径很固定第一步确认代码里有没有调用HAL_TIMEx_PWMN_Start。不要只看自己以为有直接搜索工程里有没有这个函数带不带参数TIM_CHANNEL_1。有时候复制旧代码漏了第二行自己都没注意到。第二步如果确认调了还是没波形检查CubeMX生成代码里PA7的GPIO初始化模式。正常应该配成GPIO_MODE_AF_PP复用推挽。如果因为引脚冲突被配置成了普通GPIO输出或者输入模式复用功能就不会被连接。直接看GPIO_InitStruct里的Mode字段一眼就能看出来。第三步把PB0也量一下。如果PA7没波形但PB0有波形那可能是PA7引脚被板子上的电路焊坏或者被占用。如果PB0也没波形说明整个定时器的互补输出链路都有问题往回查MOE使能状态和Break刹车引脚是否被误触发。刹车信号一旦有效MOE会被硬件自动清掉所有互补输出强制关断。把示波器探头放在刹车输入引脚上确认它是不是被电平干扰或者外设初始状态给拉到了触发条件。5.2 现象二死区时间不生效上下管波形交叠当你用示波器同时看PA8和PA7发现两路高电平之间有交叠也就是高到高的切换过程中两个信号同时为高的时间非零这有两种可能。第一种示波器探头地线夹和测量方式造成的假象。两个探头的地夹如果分开夹在板子不同位置地线环路会引入噪声导致波形边缘看起来模糊甚至有毛刺。正确做法是使用两个探头的弹簧接地针尽量靠近被测引脚的地把时间轴放大到死区对应的尺度比如500ns/格看上升沿和下降沿之间的间隔。很多时候参数明明正确只是测量方法不对误以为没死区。第二种真正的问题是死区寄存器没写进去。在CubeMX里虽然填了死区时间但如果生成代码后手动改动过定时器初始化函数或者使用了某些低层库直接操作寄存器把DTG值覆盖了死区自然就没了。验证方法很简单在main函数里、PWM启动前直接读一下TIM1-BDTR寄存器的值把它拆成二进制打印出来看DTG位段是否为0。如果为0说明初始化或运行过程中有人把这个寄存器清了。还有一种容易忽略的情况占空比设成了接近100%。当CCR的值和ARR1非常接近时主输出几乎整个周期都是高互补输出几乎没有有效低电平时间看起来就像两个管子同时导通但这不是死区消失是占空比本身设得不对。把CCR拉回中间值再量。5.3 现象三一上功率MOS管就发烫甚至炸管功率级出事故原因往往不是单一的。我的排查顺序是先确认信号级的波形完全干净了再把功率部分接进去。所谓“干净”包括死区正常、频率稳定、无毛刺噪声、无异常窄脉冲。信号级没问题不代表功率级安全MCU复位瞬间的引脚状态是个经典盲区。STM32上电复位到用户代码运行中间有一个短暂时间窗口GPIO处于浮空输入状态此时PA8、PA7这些引脚的电平不受控。如果驱动芯片在这个窗口期内接收到随机高电平就可能开启MOS管。因此信号级调好后建议在驱动芯片的使能脚上做硬件处理要么接下拉电阻低有效使能或上拉电阻高有效使能确保上电瞬间驱动处于关断状态要么在MCU引脚和驱动芯片之间加一个RC延时电路。软件上让PWM初始化尽量早但真正启动PWM输出放到主循环里等外围器件状态都稳定后再做。另外就是栅极驱动是否充分。全桥高边MOS管如果驱动芯片用的是自举电路自举电容容量不够或者自举二极管恢复太慢高边管导通时栅极电压不足管子会工作在线性区而不是开关区损耗急剧上升表现为管子烫得异常快。自举电容建议用10uF陶瓷电容并联100nF高频去耦自举二极管选快恢复型这点在选型时就要注意。最后强调一个电源侧的措施调试逆变器时绝对不要直接用大功率稳压电源满电压供电串联一个大功率白炽灯泡或者使用带限流功能的直流电源。如果电路有直通问题灯泡会限制故障电流把破坏范围控制在最小。我见过有人抱怨“一上电MOS管坏了三个”问了一下是拿电瓶直接怼的这不是调试方法这是毁灭式验证。6. 下一个版本把方波PWM升级成SPWM正弦输出6.1 双极性SPWM的基本调制思路方波PWM虽然能让负载两端产生交变电压但它输出的交流是方波谐波分量大很多负载根本不能用。真正要输出近似正弦的交流电得用SPWM占空比按正弦规律变化脉冲宽度从中间宽、两端窄经过LC低通滤波后负载两端就是比较干净的正弦波。双极性SPWM的实现原理不复杂。设载波周期为Tc正弦调制波在某个时刻的瞬时值为sin(ωt)范围-1到1之间调制度为m则当前PWM周期里两个桥臂的占空比分配方式是d1 0.5 * (1 m * sin(ωt)) d2 1 - d1也就是说通道1的占空比围绕50%上下波动通道2始终和通道1互补。用CCR表达就是ccr1 (ARR 1) * d1; ccr2 (ARR 1) * d2;前面第二章节我说过CH1和CH2的CCR最好互补这正是双极性SPWM的代码基础。两个桥臂的PWM占空比一高一低桥臂之间的差值电压就是一个双极性PWM脉冲串滤波后得到正弦。一个工程上的边界要处理实际配置中ccr1和ccr2必须留出死区的余量不能真的完全互补否则在相邻PWM周期切换瞬间CH1和CH2都为高的时间会超过死区设置硬件死区会强制介入导致输出波形出现畸变。通常的做法是把幅度稍微压缩几个计数让两路CCR之和略小于ARR1。6.2 查表法的代码骨架正弦计算如果每个PWM周期都实时算sin函数72MHz的F103会消耗不少CPU时间而且sin浮点运算在Cortex-M3上属于软浮点做高频控制时吃力不讨好。更好的方案是提前算好一张正弦表存到Flash里运行时查表。假设输出基波频率是50Hz载波20kHz那么每个基波周期有400个PWM脉冲。如果PWM频率是基波的整数倍查表点数400就足够。程序初始化时把400个点的正弦值转成CCR数值存入数组最后在更新中断里逐点搬运#define TABLE_LEN 400 #define ARR_MAX 3599 const uint16_t sineTable[TABLE_LEN] { /* 预计算的CCR值 */ }; volatile uint16_t index 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM1) { uint16_t ccr1 sineTable[index]; uint16_t ccr2 ARR_MAX - ccr1; __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, ccr1); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, ccr2); index; if (index TABLE_LEN) index 0; } }这张表可以在PC上生成后直接粘贴成C数组也可以用STM32上电时一次性计算。注意查表中断的时间负担其实很小HAL_TIM_PeriodElapsedCallback里就4条赋值加判断20kHz的中断频率完全不掉点。比较常见的问题是如果只配置了更新中断第一次更新事件发生在第一个PWM周期结束也就是所有输出都在初始CCR值上运行了一个完整周期。如果初始CCR设成0那第一个周期输出恒低这会让输出波形起点少一个脉冲。可以通过在初始化时把CCR设成第一个查表值来避免。6.3 闭环和保护扩展下一步还能怎么加SPWM只是逆变控制的第一步。实际产品中还要解决电压稳定和过流保护两个问题。电压闭环可以这样接输出端用分压电阻加精密整流电路采样正弦波的有效值或瞬时值送入STM32的ADC通过PID调节SPWM的调制度m来稳定输出电压。电流保护则可以继续用TIM1的刹车功能把电流检测比较器的输出直接接到刹车引脚硬件层封闭了输出CPU只负责记录故障状态和恢复逻辑。如果手头有FreeRTOS可以考虑把SPWM查表放在高优先级中断里把PID计算和显示任务放在普通任务中。这也是我在实际项目里的做法。之前看到有人讨论在F103C8T6上移植FreeRTOS跑SPWM有没有必要我的看法是如果你只是固定输出50Hz正弦查表中断自己跑就行上不上RTOS影响不大但如果你要同时做数码管显示、按键输入、通讯和故障记录一个RTOS能把代码逻辑理得清清楚楚C8T6跑FreeRTOS的资源完全够。这个项目的价值在于它把定时器高级功能、死区工程原理和功率硬件设计三条线串在了一起。F103C8T6这块板子虽然便宜但TIM1的互补输出、死区发生器和刹车机制三大功能一个不少学透一遍后面再看F4、G4的高级定时器文档会轻松很多。顺着这个思路往下做你就能从一个单纯的“点灯选手”变成一个真正理解PWM和功率控制的人。
返回列表