STM32 HRTIM 电力电子逐周期保护的配置教程

介绍

本文基于ST HRTIM cookbook的第八章进行编写,相比原文,使用中文说明并提供更详细的原理介绍、配置教程、stm32cubeMX BUG 说明和解决方案

逐周期保护(cycle-by-cycle protection)常用在电力电子装置中,常见的如Boost拓扑需要电感过流逐周期保护、LLC拓扑需要谐振电感过流逐周期保护等等。

阅读本文原理部分需要对STM32和电力电子原理有基础的认知和了解,可参考系列文章Bonjour STM32 – 美味可口的STM32 CookNote。阅读本文实操部分需要具有点亮一个LED的基本能力。

建议使用芯片型号STM32F334或其进阶版STM32G474,这两款为电力电子专用芯片。本文使用STM32CubeIDE 1.15.0完成相关操作,使用新版STM32CubeMX较新版+Keil/Clion/IAR都不影响配置部分流程。

为直接获取配置信息,资深人士可直接跳到配置总结

原理

什么是逐周期保护?

在电力电子中,逐周期保护是一种用于防止电力设备损坏的方法,它通过监控和控制每个开关周期的功率来实现。这种保护机制通常应用于开关电源、电机驱动器和其他电力电子转换器中。

逐周期保护的工作原理通常如下:

  1. 监控:保护电路连续监控电力设备的一个关键参数——通常是流过它的电流。根据设计和应用的不同,它也可能监控电压或温度等其他参数。
  2. 阈值设定:为监控参数设置一个安全运行阈值。这个阈值是保证设备在安全限制内操作的最大允许值。
  3. 检测:每个周期,保护电路将监测到的参数值与其阈值进行比较。如果参数值超过阈值,就表明出现了异常或可能导致损害的情况。
  4. 干预:在检测到超限条件时,保护电路将介入调整电力设备的操作。这通常涉及在该周期剩余时间内关闭电源开关。
  5. 重置:在响应故障条件关闭设备后,电路通常允许下一个周期正常开始,再次监控以查看故障条件是否持续存在。

这种方法有效地保护设备免受由于负载突变、短路或其他故障导致的过电流损害。通过在每个周期基础上采取行动,它有助于及时处理故障条件,防止导致设备失败的热和电压应力。同时,它也有助于实现对电力处理的精确控制,从而在安全和效率方面为电力电子系统做出贡献。

什么是HRTIM?

HRTIM,即高分辨率定时器(High-Resolution Timer),是一种在微控制器和其他数字控制应用中广泛使用的高精度定时器组件。这种定时器特别适用于需要极高时间精度和复杂波形生成的应用,如电力电子控制、电机驱动以及复杂的PWM(脉冲宽度调制)应用。

HRTIM的主要特点包括:

  1. 高分辨率:HRTIM能提供极高的时间分辨率,通常在几十纳秒到几百纳秒范围内。这使得它能够精确控制事件的时间和持续时间,提供非常精确的脉冲宽度调整。
  2. 多通道输出:高分辨率定时器通常包括多个独立的定时器通道,允许同时生成多个独立的PWM信号,这对于多相电机控制等应用至关重要。
  3. 灵活的配置:HRTIM通常具有高度的配置灵活性,支持多种操作模式,包括单次定时、连续定时、PWM模式等。
  4. 复杂波形生成:它能生成复杂的波形,包括不规则波形或需要精确控制相位和频率的波形。
  5. 同步功能:HRTIM能够与系统中的其他定时器或外部事件同步,使得在复杂系统中的多个组件可以协调工作。

在电力电子和工业自动化领域,HRTIM的这些特性使其成为实现精确控制和高效能转换的关键工具。例如,它可以用于精确控制开关电源的开关频率和相位,或者控制电机的速度和扭矩。

ST公司的HRTIM有什么特点?

ST公司的高分辨率定时器(HRTIM)具有许多先进特性,非常适合需要高精度控制的电力电子应用。其主要特点包括:

  1. 高分辨率和精细的时间控制:HRTIM在STM32G4系列上能够达到184皮秒的时间分辨率,支持复杂的波形生成,如PWM和相位移波形。这种高分辨率是通过内部延迟锁定环(DLL)实现的,该环节可以将时钟周期细分为32个均匀步骤。
  2. 模块化架构:HRTIM包括多个定时器单元(最多六个),每个单元都可以独立操作或与其他定时器同步。每个定时器单元包括多达四个比较寄存器和两个捕获寄存器,允许灵活配置周期和比较事件。
  3. 多功能性:除了基本的定时和计数功能外,HRTIM还支持复杂的输出交叉配置、故障保护、和ADC触发等。这些功能使得HRTIM可以广泛应用于数字电源转换(如数字开关电源、太阳能转换器、电机控制)以及一般的定时和计量应用。
  4. 编程和配置灵活:HRTIM支持多种操作模式,如连续(自由运行)、单次可重触发和单次不可重触发。它还允许通过外部事件或其他定时器单元控制输出。
  5. 高级集成:在STM32微控制器系列中,HRTIM的集成度高,与其他系统外设(如DMA、ADC和DAC)良好配合,支持复杂的系统级应用设计。

HRTIM的这些特性使其在需要高分辨率和高精度控制的应用中,特别是在电力电子和工业自动化领域,显得尤为重要和有用。

为什么我们需要配置HRTIM而不是用HRTIM1_FLT_IRQHandler()进行响应?

在使用STM32的HRTIM配置自动响应故障与通过CPU响应故障中断之间的比较,涉及到以下几个方面:

响应速度

  • 自动响应:HRTIM在检测到故障时会立即将输出置于安全状态,几乎无延迟,适合对响应速度要求极高的场合。
  • CPU中断响应:响应速度较慢,因为需要CPU捕捉中断并执行中断服务程序。这一过程包括中断的识别、响应和处理,可能导致响应速度不如硬件直接处理快STM32F3 / HRTIM1 with Fault Interrupt

资源使用

  • 自动响应:不占用CPU资源,可以让CPU处理其他任务,适合性能要求高的应用。
  • CPU中断响应:占用CPU资源,可能影响系统中其他任务的执行。

复杂性与灵活性

  • 自动响应:通常较为简单,配置后无需软件干预,硬件级别直接执行。
  • CPU中断响应:提供更高的灵活性,允许开发者定制具体的故障处理逻辑,如添加复杂的故障恢复流程或故障日志记录。

可靠性

  • 自动响应:由于硬件直接处理,减少了软件错误引起的风险,通常更可靠。
  • CPU中断响应:虽然灵活,但增加了由软件错误导致的风险,特别是中断服务程序中的错误可能导致故障响应不当。

综上所述,选择哪种故障响应方式取决于应用的具体需求,包括对响应速度、系统资源、可靠性和灵活性的不同要求。对于高安全性和实时性要求的应用,自动响应通常是更佳的选择。而对于需要复杂处理逻辑的场景,CPU中断响应可能更合适。

实操原理

实操需要的外围电路——以Buck过流保护为例

image-20241004194228790

上图来自ST HRTIM cookbook

如图26所示,系统需配备一个Buck电路。此外,在需要逐周期保护的部分,必须进行电流采样。这一采样信号将通过比较器处理,比较器可选择使用STM32芯片内置的或外置的,根据实际需求确定。处理后的信号将触发HRTIM的外部事件EEV,以实现保护功能。

控制器与外围电路的联系

该Buck的高侧MOSFET由HRTIM_CHA1驱动,是主电源开关,控制输出电压具有固定的PWM频率和可变的占空比。低侧MOSFET由HRTIM_CHA2控制,执行同步整流。这两个晶体管通过互补PWM信号和插入死区时间来控制,以避免交叉导通。通过死区时间生成单元生成死区时间,即使在过电流保护情况下重置高侧MOSFET指令,这也意味着信号始终是互补的。在开关周期内,当出现过电流保护后,可能需要一种替代保护方案,其中两个MOSFET都被关闭。图27显示了这两种不同行为。

一些针对电力电子初学者的解释。

  • 高侧和低侧MOSFET:在一个典型的半桥驱动器配置中,高侧MOSFET连接到电源正极,而低侧MOSFET连接到电源负极。这种配置允许电流在负载间快速切换。
  • PWM信号:脉冲宽度调制(PWM)是一种有效控制模拟信号强度的方法,通过开关电源的方式调节电源总的平均电压。
  • 同步整流:这是一种效率更高的整流方法,使用开关替代传统的二极管,以减少功率损耗。
  • 互补PWM和死区时间:在PWM控制中,为防止高侧和低侧MOSFET同时导通造成短路,需要在一个MOSFET关闭与另一个MOSFET打开之间设置一个短暂的延迟,这就是死区时间。
  • 过电流保护与替代保护方案:在检测到过电流时,系统需要迅速反应以保护电路不被损坏。这通常涉及关闭所有的功率开关,防止更严重的电气故障。

其中,EEV可配置为上升沿触发和电平触发。如26图所示。其中,将EEV8配置为上升沿触发,EEV5配置为电平触发

包含死区和逐波过流保护的HRTIM配置逻辑

image-20241004210136719

上图来自ST HRTIM cookbook

为了在不使用死区时间插入单元的情况下建立一种死区和逐波过流保护机制,我们需要配置多个比较器,如图27所示。通过配置CMP1来生成头部死区时间,并在定时器A重置时 Reset HRTIM_CHA2,同时 Set HRTIM_CHA1。尾部死区时间则是通过HRTIM_EEV8触发的CMP2自延迟模式产生的,该模式以CMP3超时为条件。

STM32 HRTIM的死区时间插入单元是什么?

在STM32 HRTIM(高分辨率定时器)中,死区时间插入单元是一个关键功能,用于管理和控制与PWM(脉冲宽度调制)信号相关的开关器件,如晶体管和MOSFET。该功能主要防止在转换器或电机驱动应用中同时导通高侧和低侧开关,从而避免短路和电气故障。

死区时间的作用和重要性:

  1. 防止短路:在桥式转换器(如H桥)中,必须确保在高侧开关关闭之后再打开低侧开关,反之亦然。死区时间就是这两个动作之间的小间隔,它确保两个开关不会同时导通。
  2. 减少干扰:通过适当地设置死区时间,可以减少由于开关过渡造成的电气噪声和干扰,提高系统的整体性能和稳定性。

如何配置STM32 HRTIM的死区时间:

  • 死区时间注册器:STM32 HRTIM提供了专门的寄存器来配置死区时间。用户可以根据具体的应用需求设定合适的死区时间长度。
  • 软件设置:通过STM32的配置工具或直接通过代码,可以在初始化HRTIM时设置死区时间参数。例如,通过修改HRTIM的配置寄存器(如HRTIM_TIMx_BDTR),可以精确地控制死区时间的时长。
  • 动态调整:在某些应用中,可能需要根据不同的工作条件动态调整死区时间。STM32 HRTIM允许在运行时调整死区时间,以适应变化的电气负载和其他动态因素。

通过上述配置,STM32的HRTIM能够有效地管理PWM信号的生成和开关器件的控制,确保电力转换和电机驱动应用的安全性和效率。这使得STM32 HRTIM成为执行复杂且精确时间控制的电子设计中不可或缺的工具。

为什么不使用 STM32 HRTIM的死区时间插入单元?

不利于逐周期保护在触发保护时同时关断互补输出。

还有没有别的方法实现逐周期保护功能?

使用STM32 HRTIM的 Fault 功能。也可以实现在触发保护时同时关断互补输出。但具体的作者并没有实践过。

自延迟CMP是什么?

在HRTIM中,自延迟比较匹配(CMP)用于创建一个事件,在检测到特定条件后延迟执行,这在过电流保护中非常有用。

操作过程如下,如图28所示:

  • 在正常工作状态下(不存在过电流情况),CMP3寄存器负责设置HRTIM_CHA1的占空比。
  • 当系统没有检测到过电流时,由CMP3超时触发自延迟模式(autodelayed mode),随后在由CMP2寄存器定义的死区时间结束后激活HRTIM_CHA2输出。
  • 若出现过电流情况,HRTIM_EEV8输入将触发自延迟模式,并对HRTIM_CHA2输出发出激活请求(set),该请求将在CMP2定义的死区时间后生效。同时,系统也会使用CMP4寄存器在自延迟模式下发出重置请求(reset),此重置请求优先级高于激活请求,保证了在CMP2设置请求生效时,HRTIM_CHA2输出能够保持在低状态。

自延迟的CMP4必须由与自延迟CMP2相同的来源(这里是HRTIM_EEV8)触发,并且使用相同的比较值。必须禁用超时模式:当没有过电流情况时,CMP4事件不会生成。

自延迟的CMP2由捕获事件1触发,在HRTIM_EEV8的上升和下降沿;自延迟的CMP4由捕获事件2触发,同样在HRTIM_EEV8的上升和下降沿。

如果过电流脉冲发生在CMP1打开之前,HRTIM_EEV8会被锁存并延迟到CMP1事件,以确保过电流信息得到确认,并使两个输出都保持在零,即使在过电流提前到达的情况下也是如此。

为什么 CMP2 Set CHA2 请求和 CMP4 Reset CHA2 同时发生时 CHA2 会保持低电平?

在 HRTIM中,Reset的优先级比Set高。即当 Reset和 Set同时发生时,Reset的优先级更高,CHA2会进入no active状态,此处即低电平。

这种方案适用于开关周期内短时过电流脉冲的情况。这通常是短时过载的情况。

对于需要PWM关闭更长时间的应用(多周期过载),提出的方案需要通过第二个HRTIM_EEVx输入来补充,这里是HRTIM_EEV5,与HRTIM_EEV8并联。

HRTIM_EEV5事件被设定为电平激活状态(active on level),以便在过电流条件存在时,两个输出保持低。图29和图30展示了仅有HRTIM_EEV8事件活动时的波形。

image-20241004215204966

比较器参数设计

假设所有比较器都配置为相等时触发,而不是更大(greater)时触发。

此时包含死区的占空比为:

D=\frac{\text{CMP3}}{\text{PER+1}}  

为使得触发时间,有:

\text{CMP2}=\text{CMP1}  

为使得头部死区时间和尾部死区时间相等,有:

\text{CMP2}=\text{CMP1}  

死区时间时长为:


t_d=\frac{\text{CMP1}}{\text{PER+1}} *T_{perid}\approx\text{CMP1}*184\text{皮秒}(\text{G474})\approx\text{CMP1}*217\text{皮秒}(\text{G334})

应用上述估计公式时请注意HRTIM的时钟配置。

HRTIM G474的时钟配置最高速度为5.440GHz,即周期约为184皮秒。

HRTIM F334的时钟配置最高速度为4.608GHz,即周期约为217皮秒。

配置总结

image-20241004215224283

HRTIM 滴答时钟配置:

F334时钟配置为4.608GHz,G474时钟配置为5.440GHz。

HRTIM Timer A 时钟配置:

假设目标频率为f_s,up counting mode。

\text{PER}=\frac{5.440\text{GHz}}{f_s}-1\text{(when G474)}=\frac{4.608\text{GHz}}{f_s}-1\text{(when F334)}  

当然可以不止用在 CHA。

HRTIM EEV 配置:

EEV8:active on up and falling edges

EEV5:active on level

使用具体哪两个EEV可根据GPIO资源使用情况随意修改。

HRTIM Capture 配置:

Capture 1: capture EEV8

Capture 2: capture EEV8

HRTIM CMP 配置:

CMP1: \text{CMP1}\approx \frac{t_d}{184\text{皮秒}}(\text{G474})\approx\frac{t_d}{217\text{皮秒}}(\text{F334})

CMP2: autodelayed mode after CMP3 and a capture imediately \text{CMP2}=\text{CMP1} autodelayed timeout=0

CMP3: \text{CMP3}=D(\text{PER+1})

CMP4: autodelayed mode after a capture imediately \text{CMP4}=\text{CMP2}

HRTIM CHA 输出配置:

Output Signal 1 and Output Signal 2 are independent.

CHA1 SET: CMP1

CHA1 RESET: CMP3\EEV8 edge\EEV5 active

CHA2 SET: CMP2(autodelayed mode)

CHA2 RESET: Timer A period\CMP2(autodelayed mode)\EEV5 active

实操流程

实操流程为芯片时钟配置->HRTIM时钟配置->HRTIM输出引脚配置->外部事件EEV(external event)配置->捕获capture配置->HRTIM比较器COMP(compare)配置->HRTIM Channel 输出Set和Reset配置->保持ioc文件并生成代码->修正CubeMX配置代码的Bug并复制HRTIM配置函数至新文件->在main使能HRTIM输出

以F334为例,可照着抄,注意改有关参数:

尤其注意晶振频率、板子型号、开关周期、死区时间

image-20241004222426342

image-20241004222512366

image-20241004222701638

image-20241004222733564

image-20241004222757907

image-20241004222940756

  HAL_HRTIM_WaveformOutputStart(&hhrtim1, HRTIM_OUTPUT_TA1);  // Enable the generation of the waveform signal on the designated output  HAL_HRTIM_WaveformOutputStart(&hhrtim1, HRTIM_OUTPUT_TA2);  // Enable the generation of the waveform signal on the designated output​  HAL_HRTIM_WaveformCounterStart(&hhrtim1, HRTIM_TIMERID_TIMER_A);  // Start the counter of the Timer A operating in waveform mode​

​static void MX_HRTIM1_Init(void){​  /* USER CODE BEGIN HRTIM1_Init 0 */​  /* USER CODE END HRTIM1_Init 0 */​  HRTIM_EventCfgTypeDef pEventCfg = {0};  HRTIM_TimeBaseCfgTypeDef pTimeBaseCfg = {0};  HRTIM_TimerCfgTypeDef pTimerCfg = {0};  HRTIM_CompareCfgTypeDef pCompareCfg = {0};  HRTIM_CaptureCfgTypeDef pCaptureCfg = {0};  HRTIM_OutputCfgTypeDef pOutputCfg = {0};​  /* USER CODE BEGIN HRTIM1_Init 1 */​  /* USER CODE END HRTIM1_Init 1 */  hhrtim1.Instance = HRTIM1;  hhrtim1.Init.HRTIMInterruptResquests = HRTIM_IT_NONE;  hhrtim1.Init.SyncOptions = HRTIM_SYNCOPTION_NONE;  if (HAL_HRTIM_Init(&hhrtim1) != HAL_OK) {    Error_Handler(); }  if (HAL_HRTIM_DLLCalibrationStart(&hhrtim1, HRTIM_CALIBRATIONRATE_14) != HAL_OK) {    Error_Handler(); }  if (HAL_HRTIM_PollForDLLCalibration(&hhrtim1, 10) != HAL_OK) {    Error_Handler(); }  if (HAL_HRTIM_EventPrescalerConfig(&hhrtim1, HRTIM_EVENTPRESCALER_DIV1) != HAL_OK) {    Error_Handler(); }  pEventCfg.Source = HRTIM_EVENTSRC_1;  pEventCfg.Polarity = HRTIM_EVENTPOLARITY_HIGH;  pEventCfg.Sensitivity = HRTIM_EVENTSENSITIVITY_RISINGEDGE;  pEventCfg.Filter = HRTIM_EVENTFILTER_3;  if (HAL_HRTIM_EventConfig(&hhrtim1, HRTIM_EVENT_6, &pEventCfg) != HAL_OK) {    Error_Handler(); }  pTimeBaseCfg.Period = 46080;  pTimeBaseCfg.RepetitionCounter = 0x00;  pTimeBaseCfg.PrescalerRatio = HRTIM_PRESCALERRATIO_MUL32;  pTimeBaseCfg.Mode = HRTIM_MODE_CONTINUOUS;  if (HAL_HRTIM_TimeBaseConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, &pTimeBaseCfg) != HAL_OK) {    Error_Handler(); }  pTimerCfg.InterruptRequests = HRTIM_TIM_IT_NONE;  pTimerCfg.DMARequests = HRTIM_TIM_DMA_NONE;  pTimerCfg.DMASrcAddress = 0x0000;  pTimerCfg.DMADstAddress = 0x0000;  pTimerCfg.DMASize = 0x1;  pTimerCfg.HalfModeEnable = HRTIM_HALFMODE_DISABLED;  pTimerCfg.StartOnSync = HRTIM_SYNCSTART_DISABLED;  pTimerCfg.ResetOnSync = HRTIM_SYNCRESET_DISABLED;  pTimerCfg.DACSynchro = HRTIM_DACSYNC_NONE;  pTimerCfg.PreloadEnable = HRTIM_PRELOAD_DISABLED;  pTimerCfg.UpdateGating = HRTIM_UPDATEGATING_INDEPENDENT;  pTimerCfg.BurstMode = HRTIM_TIMERBURSTMODE_MAINTAINCLOCK;  pTimerCfg.RepetitionUpdate = HRTIM_UPDATEONREPETITION_DISABLED;  pTimerCfg.PushPull = HRTIM_TIMPUSHPULLMODE_DISABLED;  pTimerCfg.FaultEnable = HRTIM_TIMFAULTENABLE_NONE;  pTimerCfg.FaultLock = HRTIM_TIMFAULTLOCK_READWRITE;  pTimerCfg.DeadTimeInsertion = HRTIM_TIMDEADTIMEINSERTION_DISABLED;  pTimerCfg.DelayedProtectionMode = HRTIM_TIMER_A_B_C_DELAYEDPROTECTION_DISABLED;  pTimerCfg.UpdateTrigger = HRTIM_TIMUPDATETRIGGER_NONE;  pTimerCfg.ResetTrigger = HRTIM_TIMRESETTRIGGER_NONE;  pTimerCfg.ResetUpdate = HRTIM_TIMUPDATEONRESET_DISABLED;  if (HAL_HRTIM_WaveformTimerConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, &pTimerCfg) != HAL_OK) {    Error_Handler(); }  pCompareCfg.CompareValue = 4608;  if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_1, &pCompareCfg) != HAL_OK) {    Error_Handler(); }  pCompareCfg.AutoDelayedMode = HRTIM_AUTODELAYEDMODE_AUTODELAYED_TIMEOUTCMP3;  pCompareCfg.AutoDelayedTimeout = 0;  if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_2, &pCompareCfg) != HAL_OK) {    Error_Handler(); }  pCompareCfg.AutoDelayedMode = HRTIM_AUTODELAYEDMODE_AUTODELAYED_NOTIMEOUT;  pCompareCfg.AutoDelayedTimeout = 0x0000;​  if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_4, &pCompareCfg) != HAL_OK) {    Error_Handler(); }  pCompareCfg.CompareValue = 23040;  if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_3, &pCompareCfg) != HAL_OK) {    Error_Handler(); }  pCaptureCfg.Trigger = HRTIM_CAPTURETRIGGER_EEV_6;  if (HAL_HRTIM_WaveformCaptureConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_CAPTUREUNIT_1, &pCaptureCfg) != HAL_OK) {    Error_Handler(); }  if (HAL_HRTIM_WaveformCaptureConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_CAPTUREUNIT_2, &pCaptureCfg) != HAL_OK) {    Error_Handler(); }  pOutputCfg.Polarity = HRTIM_OUTPUTPOLARITY_HIGH;  pOutputCfg.SetSource = HRTIM_OUTPUTSET_TIMCMP1;  pOutputCfg.ResetSource = HRTIM_OUTPUTRESET_TIMCMP3|HRTIM_OUTPUTRESET_EEV_6;  pOutputCfg.IdleMode = HRTIM_OUTPUTIDLEMODE_NONE;  pOutputCfg.IdleLevel = HRTIM_OUTPUTIDLELEVEL_INACTIVE;  pOutputCfg.FaultLevel = HRTIM_OUTPUTFAULTLEVEL_NONE;  pOutputCfg.ChopperModeEnable = HRTIM_OUTPUTCHOPPERMODE_DISABLED;  pOutputCfg.BurstModeEntryDelayed = HRTIM_OUTPUTBURSTMODEENTRY_REGULAR;  if (HAL_HRTIM_WaveformOutputConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_OUTPUT_TA1, &pOutputCfg) != HAL_OK) {    Error_Handler(); }  pOutputCfg.SetSource = HRTIM_OUTPUTSET_TIMCMP2;  pOutputCfg.ResetSource = HRTIM_OUTPUTRESET_TIMPER|HRTIM_OUTPUTRESET_TIMCMP4;  if (HAL_HRTIM_WaveformOutputConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_OUTPUT_TA2, &pOutputCfg) != HAL_OK) {    Error_Handler(); }  /* USER CODE BEGIN HRTIM1_Init 2 */​  /* USER CODE END HRTIM1_Init 2 */  HAL_HRTIM_MspPostInit(&hhrtim1);​}

格外需要注意的CubeMX Bug

在配置CMP3的比较值时,存在配置CMP2后无法在CubeMX 输入CMP3比较值的情况,此时关注自动生成的代码,也没有CMP3比较部分,需要自己在配置代码中额外加入以下代码:

  pCompareCfg.CompareValue = 23040;
  if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_3, &pCompareCfg) != HAL_OK)
  {
    Error_Handler();
  }

加入该部分代码后复制函数内容并再写一个函数,并在main的user begin后引用,避免自动 generate code 将对应修改的代码消除。

发表回复