公司动态

STM8 Timer1互补PWM配置详解:从死区时间到电机驱动实战

📅 2026/8/13 10:55:59
STM8 Timer1互补PWM配置详解:从死区时间到电机驱动实战
1. 从需求到方案为什么需要互补PWM在电机驱动、逆变电源这些强电应用里直接用一个引脚输出的PWM波去控制一个桥臂的开关管是行不通的。想象一下一个H桥的上管和下管如果控制信号完全同步上管导通PWM高电平时下管也导通那结果就是电源正负极直接短路瞬间产生巨大的“直通”电流轻则烧毁MOS管重则炸机。为了避免这种灾难性的情况我们引入了“互补PWM”的概念。互补PWM的核心思想很简单控制同一个桥臂的两个开关管的信号必须是反相的。当上管导通时下管必须关断反之亦然。这样就能保证电流总是流经负载电机或电感而不是在电源内部短路。但是仅仅反相就够了吗还不够。半导体开关管如MOSFET、IGBT从导通到关断或从关断到导通都需要一个短暂的过渡时间。如果上一个管子还没完全关断下一个管子就开始导通了在这段重叠的时间里依然会形成瞬间的直通短路。为了解决这个问题我们必须在两路互补的PWM信号之间插入一段两者都为低电平的“死区时间”Dead Time。在这段时间里上下两个管子都处于关断状态为开关状态的切换提供了安全缓冲。所以一个完整的电机驱动PWM方案通常需要两路互补的、带可调死区时间的PWM信号。对于STM8这类微控制器这个任务通常由其高级控制定时器在STM8中通常是Timer1来承担。它内置了专门的互补输出通道和死区时间发生器硬件自动生成带死区的互补波形极大地减轻了CPU的负担并保证了时序的精确性。接下来我们就以STM8S207系列为例拆解如何配置Timer1来实现这个功能。2. STM8定时器体系与Timer1的定位STM8的定时器资源根据型号有所不同常见的有TIM1、TIM2、TIM3、TIM4等。其中TIM1是一个16位的高级控制定时器是唯一一个支持互补PWM输出和死区插入的定时器这也是我们本次实验的核心。其他的定时器如TIM2/TIM3通常用作通用定时器支持基本的PWM输出但没有互补和死区功能。理解Timer1的通道结构是关键。以最常见的三相互补输出为例Timer1拥有3个互补通道对CH1/CH1N CH2/CH2N CH3/CH3N。这里的CHx是主输出通道CHxN是其对应的互补输出通道。例如CH1和CH1N就是一对它们的波形是互补的考虑死区后。此外TIM1还有一个独立的CH4通道但它没有互补输出端。这些通道的输出引脚是复用在GPIO上的具体映射关系需要查阅对应型号的数据手册。例如在STM8S207C8T6上TIM1_CH1 对应 PC1TIM1_CH1N 对应 PC2TIM1_CH2 对应 PC3TIM1_CH2N 对应 PC4TIM1_CH3 对应 PC5TIM1_CH3N 对应 PC6配置的第一步就是将这些需要用到的GPIO引脚初始化为推挽输出模式并且设置为高速输出以提供足够的驱动能力和边沿速度。// 以配置CH1和CH1N为例 void GPIO_Config(void) { // 使能GPIOC时钟 CLK-PCKENR2 | CLK_PCKENR2_TIM1; // 配置PC1 (TIM1_CH1) 为推挽输出高速模式 PC_DDR | (1 1); // 输出模式 PC_CR1 | (1 1); // 推挽输出 PC_CR2 | (1 1); // 高速输出 // 配置PC2 (TIM1_CH1N) 为推挽输出高速模式 PC_DDR | (1 2); PC_CR1 | (1 2); PC_CR2 | (1 2); }注意STM8的GPIO配置寄存器是分端口如PC的每个端口有DDR数据方向、ODR输出数据、IDR输入数据、CR1和CR2控制寄存器。CR1和CR2的组合决定了具体模式悬浮输入、上拉输入、开漏输出、推挽输出以及输出速度。3. Timer1核心寄存器配置详解配置Timer1输出互补PWM主要围绕几个核心寄存器展开时基单元、捕获/比较模式寄存器、刹车与死区寄存器以及控制寄存器。我们一步步来。3.1 时基单元配置ARR与PSC时基单元决定了PWM波的频率。它由自动重装载寄存器ARR和预分频器PSC共同控制。PSC (TIM1_PSCRH/L)对系统时钟fMASTER进行分频得到计数器时钟CK_CNT。公式CK_CNT fMASTER / (PSC 1)。PSC是一个16位寄存器。ARR (TIM1_ARRH/L)决定了计数器的周期。计数器从0计数到ARR然后归零或向下计数形成一个周期。PWM频率的计算公式为Fpwm CK_CNT / (ARR 1)。假设主频fMASTER16MHz我们想要一个16kHz的PWM频率可以设置PSC0不分频那么CK_CNT16MHz。ARR 16MHz / 16kHz - 1 1000 - 1 999。// 配置PWM频率为16kHz (假设主频16MHz) #define PWM_FREQ 16000 #define F_MASTER 16000000 #define PWM_PERIOD (F_MASTER / PWM_FREQ - 1) // 计算ARR值 void TIM1_BaseConfig(void) { // 设置预分频器为0 (不分频) TIM1-PSCRH 0; TIM1-PSCRL 0; // 设置自动重装载值决定PWM周期 TIM1-ARRH (uint8_t)((PWM_PERIOD) 8); TIM1-ARRL (uint8_t)(PWM_PERIOD); }3.2 捕获/比较模式配置CCMR与CCER这部分配置决定了通道的工作模式此处为PWM模式和输出极性。对于每个通道x1, 2, 3都有一个对应的捕获/比较模式寄存器TIM1_CCMRx和捕获/比较使能寄存器TIM1_CCER。配置PWM模式 (TIM1_CCMRx)我们需要将通道配置为PWM模式1或模式2。这两种模式的区别在于输出电平与比较结果的关系。通常使用PWM模式1当计数器值小于比较寄存器CCRx值时输出有效电平由极性决定大于或等于时输出无效电平。需要设置OCxM位域为110PWM模式1。配置输出使能与极性 (TIM1_CCER)CCxE位使能主输出通道CHx。CCxNE位使能互补输出通道CHxN。CCxP位设置主输出通道的极性。0为高电平有效1为低电平有效。CCxNP位设置互补输出通道的极性。极性配置需要根据你驱动的硬件电路来决定。常见的用法是设置主输出高电平有效互补输出低电平有效或者都设置为高有效但靠死区错开。这里我们设置主输出高有效互补输出低有效。void TIM1_OC_Config(void) { // 以通道1为例 // 1. 配置CCMR1寄存器设置CH1为PWM模式1 TIM1-CCMR1 (0x6 4); // OC1M 110 (PWM mode 1) // 同时CCMR1中的OC1PE位需要置1开启预装载防止修改CCR时产生毛刺 TIM1-CCMR1 | (1 3); // OC1PE 1 // 2. 配置CCER寄存器使能输出并设置极性 // CC1E 1 (使能CH1输出) // CC1NE 1 (使能CH1N输出) // CC1P 0 (CH1高电平有效) // CC1NP 1 (CH1N低电平有效) TIM1-CCER1 | (1 0) | (1 2); // 使能CC1E和CC1NE TIM1-CCER1 ~(1 1); // CC1P0 TIM1-CCER1 | (1 3); // CC1NP1 // 3. 设置比较寄存器CCR1决定占空比 // 占空比 CCR1 / (ARR 1) // 例如设置50%占空比 uint16_t duty PWM_PERIOD / 2; TIM1-CCR1H (uint8_t)(duty 8); TIM1-CCR1L (uint8_t)(duty); }3.3 死区时间配置BDTR寄存器这是互补PWM安全性的核心。死区时间由TIM1_BKR刹车和死区寄存器中的DTG[7:0]位域控制。STM8的死区时间计算公式相对固定取决于DTG位的设置如果DTG[7:5] 0xx 死区时间 DTG[6:0] * T_dts。其中T_dts是定时器时钟周期。如果DTG[7:5] 10x 死区时间 (64 DTG[5:0]) * T_dts。如果DTG[7:5] 110 死区时间 (32 DTG[4:0]) * 2 * T_dts。如果DTG[7:5] 111 死区时间 (32 DTG[4:0]) * 8 * T_dts。T_dts通常是系统时钟周期如果TIM1_CKD[1:0]00。假设系统时钟16MHzT_dts 1/16M 62.5ns。我们需要根据驱动MOS管的开关速度来计算所需的死区时间。例如常用的IRF540 MOSFET其导通延迟上升时间约几十ns关断延迟下降时间也类似。为了安全通常设置死区时间为200ns到1us。我们以500ns为例进行计算所需死区周期数 500ns / 62.5ns 8个时钟周期。 查看公式DTG[7:5]000时死区时间 DTG[6:0] * T_dts。所以设置DTG[6:0] 8即可即DTG 0x08。void TIM1_DeadTime_Config(void) { // 配置死区时间DTG 0x08对应约500ns死区 (16MHz主频) TIM1-BKR | (0x08 0); // 设置DTG位域 // 同时必须使能刹车功能即使不用刹车也要置位MOE位来使能输出 TIM1-BKR | TIM1_BKR_MOE; // 主输出使能 }重要提示MOE主输出使能位必须置1否则所有TIM1的输出通道包括互补通道都会被强制禁止。这在调试时如果忘了设置会导致完全没有波形输出是一个常见的坑。3.4 控制寄存器与启动最后我们需要配置计数模式并启动定时器。TIM1_CR1配置计数模式。中心对齐模式用于电机控制减少谐波或边沿对齐模式。我们使用边沿对齐模式默认向上计数。TIM1_CR2输出空闲状态控制等暂时用默认值。TIM1_EGR通过置位UG位来产生更新事件将预装载寄存器ARR, CCR的值更新到影子寄存器。TIM1_CR1置位CEN位启动计数器。void TIM1_Start(void) { // 产生一个更新事件更新所有预装载寄存器 TIM1-EGR | TIM1_EGR_UG; // 开启自动重装载预装载使能ARPE确保修改ARR时平滑过渡 TIM1-CR1 | TIM1_CR1_ARPE; // 启动计数器 TIM1-CR1 | TIM1_CR1_CEN; }4. 完整代码整合与实测波形分析将上述所有配置函数整合并在main函数中调用一个完整的互补PWM输出程序就完成了。#include stm8s.h #define PWM_FREQ 16000 #define F_MASTER 16000000 #define PWM_PERIOD (F_MASTER / PWM_FREQ - 1) void GPIO_Config(void); void TIM1_BaseConfig(void); void TIM1_OC_Config(void); void TIM1_DeadTime_Config(void); void TIM1_Start(void); void main(void) { // 初始化系统时钟假设使用内部16MHz HSI CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 1分频16MHz GPIO_Config(); TIM1_BaseConfig(); TIM1_OC_Config(); TIM1_DeadTime_Config(); TIM1_Start(); while (1) { // 主循环中可以动态修改CCR1来改变占空比 // TIM1-CCR1H ...; // TIM1-CCR1L ...; } } // ... 上述各个配置函数的实现 ...将程序编译下载到STM8S207C8T6核心板用示波器同时测量PC1CH1和PC2CH1N的波形你应该能看到类似下图的波形CH1 (高有效): |¯¯¯|___|¯¯¯|___|¯¯¯|___| --高电平-- --死区-- --低电平-- CH1N(低有效): |___|¯¯¯|___|¯¯¯|___|¯¯¯| --低电平-- --死区-- --高电平-- 时间轴 ------------------------------------波形解读互补关系CH1为高电平时CH1N为低电平CH1为低电平时CH1N为高电平。两者相位相反。死区插入在电平跳变的边缘有一段两个信号都为低电平无效电平的区域。对于高有效的CH1在从高变低后会插入一段低电平死区然后CH1N才从低变高。反之亦然。这确保了在任何切换瞬间上下管都有一小段共同关断的时间。占空比控制通过修改TIM1_CCR1寄存器的值可以改变CH1高电平的时间即占空比CH1N的波形会自动互补调整。5. 进阶话题与避坑指南在实际项目中仅仅输出波形只是第一步。下面分享几个从实际调试中总结出来的经验和容易踩的坑。5.1 死区时间计算与实测验证理论计算死区时间是一回事实际测量又是另一回事。强烈建议用示波器测量死区时间是否与设定值相符。方法是用示波器的两个通道分别测量CH1和CH1N使用边沿触发和光标功能测量从CH1下降沿到CH1N上升沿之间的时间以及从CH1N下降沿到CH1上升沿之间的时间。这两个时间应该基本相等且接近你计算的值。如果实测死区时间偏差很大请检查系统时钟频率你代码中F_MASTER的定义是否准确STM8启动后默认是2分频的HSI8MHz如果你没有重新配置时钟那么实际主频是8MHz计算就会出错。定时器时钟使能是否通过CLK-PCKENR2寄存器使能了TIM1的时钟如果没有使能定时器根本不工作。GPIO速度如果GPIO输出模式没有设置为高速CR2相应位置1输出边沿会有延迟这可能会被误认为是死区时间的一部分或者影响死区时间的精度。5.2 动态调整占空比与同步更新在电机控制中我们需要实时调整PWM占空比。直接写入CCR1寄存器是立即生效的这可能会在PWM周期中间产生一个“断裂”的脉冲导致电流畸变。为了解决这个问题我们之前配置中开启了OC1PE输出比较预装载使能。正确做法是使用预装载功能确保TIM1_CCMR1中的OC1PE1。在修改占空比时写入的是预装载寄存器TIM1_CCR1。这个新值会在下一个更新事件UEV发生时从预装载寄存器传输到影子寄存器真正起作用的寄存器从而实现所有通道占空比的同步更新。你可以通过软件产生更新事件TIM1-EGR | TIM1_EGR_UG;但更常见的做法是等待定时器自然溢出产生更新事件。对于多通道同步例如三相电机这至关重要。5.3 刹车功能与故障安全高级定时器的“刹车”功能用于在硬件检测到故障如过流、过压时立即强制将所有输出通道置于一个安全状态通常是无输出的状态。这是硬件级别的保护响应速度极快。刹车功能通常通过一个特定的引脚BKIN来触发。当BKIN引脚检测到有效电平可配置高或低有效刹车电路会动作立即将输出置为无效状态或空闲状态由MOE、OSSI等位控制。可以产生刹车中断通知CPU处理故障。即使你不用刹车输入引脚MOE位也必须置1才能使能输出这是很多新手容易忽略的地方。如果发现配置完全正确但没有波形输出第一个要检查的就是TIM1_BKR寄存器中的MOE位。5.4 中心对齐模式与移相PWM我们上面的例子是边沿对齐模式向上计数。在电机控制中为了减小电流谐波和开关损耗经常使用中心对齐模式又称对称PWM。在中心对齐模式下计数器先向上计数到ARR再向下计数到0。PWM波形关于中心对称。这种模式下PWM频率的计算公式变为Fpwm CK_CNT / (2 * ARR)。配置方法是在TIM1_CR1寄存器中设置CMS[1:0]位为非00的值例如01为中心对齐模式1并设置DIR位为0向上计数起始。中心对齐模式的互补PWM波形更“干净”对EMI更友好。关于网络热词中提到的“STM32 3路PWM移相”这在STM8的Timer1上也能实现。移相的本质是让多个通道的PWM脉冲起点相位不同。通过设置不同通道的捕获/比较寄存器CCRx的初始值就可以实现固定的相位差。例如设置CCR10 CCR2ARR/3 CCR32*ARR/3那么三个通道的PWM波就彼此相差120度非常适合三相无刷电机的驱动。5.5 调试技巧当没有波形输出时检查时钟树确认CLK_CKDIVR时钟分频寄存器和CLK_PCKENR2外设时钟使能寄存器2的TIM1位是否配置正确。没有时钟一切免谈。检查GPIO复用STM8的引脚功能是复用的。除了配置GPIO模式还需要确保定时器功能映射到了这个引脚。对于TIM1_CH1它默认就是映射到PC1的通常不需要额外的重映射配置部分型号支持重映射需查手册。检查MOE位这是最高频的原因。确认TIM1_BKR寄存器的MOE位已经置1。检查输出使能位确认TIM1_CCER1寄存器中的CC1E和CC1NE位已经置1。检查计数器是否运行读取TIM1_CR1的CEN位或者直接读取TIM1_CNTRH/L计数器值看它是否在变化。使用调试器如果有ST-Link和调试环境如IAR for STM8可以单步运行查看上述关键寄存器的值是最直接的排查手段。配置互补PWM输出是STM8应用于电机控制类项目的基石。理解每个寄存器位背后的硬件意义而不仅仅是拷贝代码才能在各种复杂场景下游刃有余。从示波器上看到第一组干净、带死区的互补波形时那种成就感就是嵌入式开发的乐趣所在。希望这篇笔记能帮你绕过我当年踩过的那些坑。