STM32 TIM PWM中阶操作详解:互补PWM输出

STM32 TIM可以输出管脚PWM信号适合多种场景使用,功能包括单线/非互补PWM输出,双线/互补PWM输出,以及死区时间和刹车控制等。

实际上,因为早期IP Core的缺陷,早期的芯片包括STM32F1, STM32F2, STM32F3, STM32F4, STM32F7在应用于多路互补PWM时存在缺陷,所以在后期的芯片包括STM32F0, STM32H7,STM32G0, STM32C0,STM32L等系列,增加了TIM16和TIM17可以输出互补PWM信号,原因会在本文里做介绍。

STM32输出3组互补PWM的场景为无刷直流电机的三相驱动,STM32输出2组互补PWM的场景为H桥驱动。本文里以H桥全桥驱动超声波设备为例,说明驱动的方式以及早期芯片存在的问题。

H桥介绍

H桥相当于两组推挽半桥组成的电路,负载跨在中间,在一个半周期左上臂到右下臂(Arm A)导通,在另外一个半周期右上臂和左下臂(Arm B)导通,从而经过负载的是交流信号,可以驱动交流负载如超声波雾化片,超声波清洗器等。在一个上臂和一个下臂导通时,另外的一个上臂和下臂必须处于关闭状态,否则会产生短路冲击。

H桥实现器件有两种,一种用三极管来实现,一种用MOS管来实现.原理是相似的,只是对电流的控制方式不同,一种是流控,一种是压控。

H桥实现方式有里两种,一种上臂是两个PMOS, 下臂是两个NMOS:

另一种通过浮压控制,实现上臂两个MOS管也用NMOS,所以上臂每个NMOS管到桥驱动端会多接一根线:

一般用集成芯片做桥驱动端具有更好的可靠性,而桥驱动端的输入来自外部相位脉冲。这里就是用STM32的PWM功能产生2组互补PWM输出给桥驱动端。

H桥驱动时序

H桥的驱动时序要点为:

  1. 在半臂导通前,另外半臂必须提前关闭,否则会产生强短路冲击,这个提前关闭的时间就是死区时间
  2. 在同半周期导通的半臂里,下半臂必须早于或等于上半臂导通,否则会产生弱短路冲击

这里以STM32F030K6T6为例介绍中阶的互补PWM输出方式,采用STM32CUBEIDE工具。

STM32 PWM系统时钟配置

在应用PWM时,考虑到计算的便捷性和整除性,可以将芯片的时钟配置为10的倍数,如最大84MHz的芯片配置为80MHz,最大72MHz的芯片配置为70MHz,最大48MHz的芯片配置为40MHz。

STM32F030K6T6频率最大48MHz,所以可以配置为40MHz,采用内部和外部时钟倍频都可以。这里配置为采用内部时钟倍频到40MHz。

STM32 互补PWM输出介绍及问题说明

这里配置输出TIM1的PWM通道1, 选择内部时钟:

可以看到,在TIM1里,就可以配置多个互补PWM输出通道。如选择了Channel1为PWM Generation CH1 CH1N, 则是配置了2个输出管脚,并且参数里需要对这2个输出管脚的输出特性进行配置。

这里我们也配置Channel2为PWM Generation CH2 CH2N, 总共就是配置里2组互补PWM输出,即4用4个管脚输出PWM信号。

Active-Break-Input是使能刹车功能,这里不使能,会在《STM32 TIM PWM高阶操作:刹车及状态约束》里做介绍。

通常将PWM输出的管脚驱动能力调高:

这里的输出不涉及DMA和中断使用,直接进行参数部分的配置,如配置为40KHz频率。

这里不介绍刹车部分的功能控制,但需要控制死区时间:

死区时间控制是互补PWM输出的一项重要功能, 首先要注意的要点是:STM32的互补PWM输出设计概念上,以自身管脚最终输出高电平作为外部MOS(也可以是三极管,这里不做区分,以MOS管做描述)导通的逻辑,也就是说,对于上臂的MOS管,STM32管脚输出高电平,则MOS管输出高电平,对于下臂的MOS管,STM32管脚输出高电平,则MOS管输出低电平,所以当上臂一个MOS管和下臂一个MOS管都接收到MCU控制的高电平,则形成一条流过负载的电流导通路径,而当经过半个时钟周期,这两个MOS管接收到MCU控制的低电平,则是都截止,而另外半臂一上一下两个MOS管会导通,想成对负载反向的电流导通。Dead Time的设置,则是将STM32管脚输出的脉冲的高电平的前沿缩回一定时间,从而保证要导通的半臂,MCU输出的两个管脚的高电平前有一段低电平时间,这就是死区时间,在这个时间里保证前面的半臂完全关闭后,MCU再释放出高电平。Dead Time参数的设置,可以查阅文档进行计算,但更好的方式是设置后直接用示波器测试调整,避免计算失误。
如下为同一个管脚无死区时间保持50%占空比的波形和有死区时间占空比变化的波形:

这样,对于不同半臂轮流截止导通时,MCU输出给两个半臂的波形关系如下,可见,实现了死区时间(dt)的效果。

而实现H桥的配置方式有两种,问题将出现。
1. 将同一个通道的互补PWM设置成管脚同相输出。这样实现控制的连接关系将是:

注意这里“LOAD"不是指单纯的负载,而是由H桥驱动芯片,H桥和负载组成。H桥驱动芯片可以用一颗全桥驱动芯片或2颗半桥驱动芯片实现。H桥驱动芯片和H桥的MOS管配合,提供给负载足够的电压和功率。STM32输出的低电压信号控制的是时序。典型的高压半桥驱动芯片有IR2110等。
参数配置为:

Mode选为PWM Mode1, 40KHz的占空比选择为50%,则脉冲高电平时间为125us,因此Pulse里填写124。Output compare preload设置程序运行时实时修改Pulse参数立即生效(disable)还是当前周期完成后生效(enable),这里选enable或disable都可以。Fast Mode用于管脚配置为open drain模式时的快速驱动模式选择,这里用disable。
注意初始输出相位电平CH Polarity和CHN Polarity的配置,在PWM Mode1下,配置CH Polarity为High,则CH对应的管脚输出高电平,配置CH Polarity为Low, 则CH对应的管脚输出低电平,而配置CHN Polarity则是反电平配置,即,配置CHN Polarity为High,则CHN对应的管脚输出低电平,而配置CHN Polarity为Low,则CHN对应的管脚输出高电平。因此将CH1和CH1N配置为High和Low,则是同相电平输出,初始输出从高电平开始,而配置CH2和CH2N为Low和High,也是同相电平输出,初始输出从低电平开始。
CH Idle State和CHN Idle State这里不用,随意设置,会在《STM32 TIM PWM高阶操作:刹车及状态约束》里做介绍。
保存生成初始工程后,在main文件里的while循环之前执行PWM通道使能:

  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2);

测得STM32输出给ARM A一个控制信号和给ARM B一个控制信号的信号相位为:

测得的STM32输出给ARM A两个控制的信号(或STM32输出给ARM B两个控制的信号)相位为:

这个时候,输出的信号相位是正常的,但没有死区时间,控制H桥需要加入死区时间,然后问题就会出现:

保存升级工程代码后,进行测试。
测得STM32输出给ARM A一个控制信号和给ARM B一个控制信号的信号相位为:

死区时间出现,相位没有问题。

测得的STM32输出给ARM A两个控制的信号相位为:

输出相位出现不对齐,蓝线波形是输出给上臂MOS管的信号,黄线波形是输出给下臂MOS管的信号,但输出给下臂MOS管的信号高电平宽于输出给上臂MOS管的信号高电平,时序能用。


测得的STM32输出给ARM B两个控制的信号相位为:

输出相位出现了问题
,输出给下臂MOS管的信号高电平应该等于或宽于输出给上臂MOS管的信号高电平,否则会出现弱短路冲击。

@1 : 因此在配置了死区时间后,对于CH CHN配置为High和Low时,输出的高电平时间长度,CHN大于CH,而CH CHN配置为Low和High时,输出的高电平时间长度,CHN小于CH。

@2: 一个需要注意的重要概念是,TIM1的某一个通道使能,则所有配置的通道输出的相位关系就按照配置的初始相位关系生效了,举例而言,当使能TIM1的CH1输出后(HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);),无论延时多少时间使能CH1N输出(HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);),CH1N管脚输出的信号和CH管脚输出的信号的相位关系不变,也就是说,CH1N使能输出的时候,并不是输出一个脉冲周期的高电平部分的最前沿,而可能是一个从一个脉冲周期的任何一个时间点输出。

因为@2这样一个原理概念,所以当在多互补PWM通道的TIM如TIM1要输出两组互补PWM信号控制H桥时,两组互补PWM的输出相位,从一开始就要配置好,所以一组的CH CHN配置为High Low时,另外一组的CH CHN就需要配置为Low High,以实现一组关闭另一组打开的时序配置。而由于@1的bug原因,有一个半桥导通的时候,上管先于下管导通,会造成弱短路冲击。

因此,将同一个通道的互补PWM设置成管脚同相输出,由于芯片IP Core bug的存在,不能实现H桥的良好控制。

2. 将同一个通道的互补PWM设置成管脚反向输出,这样实现的连接关系将是:

对应的配置为:

同样进行测试 ,测得的STM32输出给ARM A两个控制信号的相位为:

蓝线波形是输出给上臂MOS管的信号,黄线波形是输出给下臂MOS管的信号,相位没有问题,但应该有所预感了。
测得的STM32输出给ARM B两个控制信号的相位为:

蓝线波形是输出给上臂MOS管的信号,黄线波形是输出给下臂MOS管的信号,相位同样出现问题。上管先于下管导通,会造成弱短路冲击。

结论:在同一个TIM里,设置2个互补PWM通道输出的时序,不能良好的符合H桥驱动原理。

解决方案

采用互补PWM输出的解决方案是,两路互补PWM输出在不同的TIM里,因此,在STM32后来的STM32F0, STM32L, STM32H, STM32G0, STM32C0各系列里,都有TIM16和TIM17,可以配置互补PWM输出,实现了bug的修正。也就是有3个TIM,每个都可以配置互补PWM输出。但是对于老型号的STM32F1, STM32F2, STM32F3, STM32F4, STM32F7, 则没有具有互补PWM输出的TIM16, TIM17。

前面已经展示,在两个互补PWM配置里,当通道初始态配置不相同时,会出现时序问题,如CH1 CH1N配置为High Low,CH2 CH2N配置为Low High; 或CH1 CH1N配置为High High,CH2 CH2N配置为Low Low,都会出现时序问题。那么,当有多个可产生互补PWM的TIM时,如何实现解决方案?

答案就是把TIMA和TIMB的CH CHN配置成同样的状态,然后,因为互补PWM是在不同的TIM里,所以可以把TIMB延迟1/2周期使能,从而实现正确的时序配合。

有TIM1, TIM16, TIM17可以选择,这里用TIM16作为TIMA, TIM17作为TIMB。

对应如下连接的配置为:

保存升级代码后,首先使能TIM16, 再延迟半个周期使能TIM17, 即:

  HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);HAL_TIMEx_PWMN_Start(&htim16,TIM_CHANNEL_1);PY_Delay_us_t(16);i>>=1;i>>=1;HAL_TIMEx_PWMN_Start(&htim17,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);

PY_Delay_us_t()是微秒级延时函数,参考 STM32 HAL us delay(微秒延时)的指令延时实现方式及优化 。i>>=1;实现纳秒级延时,参考STM32 纳秒级延时 (ns delay) 的指令延时实现方式及测定。由于不同TIM通道启动时从寄存器配置到管脚输出信号的延时有偏差,要用示波器实际测试调整延时,以实现更准确的半周期延时。

测得的STM32输出给ARM A两个控制信号的相位为:


上图蓝色为下臂控制信号,黄色为上臂控制信号。这里要注意,下臂控制信号上升沿要早于或等于上臂控制信号上升沿。而下降沿对应关断MOS管,所以下降沿的对位偏差是允许的,哪一个臂早一点关断影响微小。所以导通时的上臂和下臂时序符合要求。

测得的STM32输出给ARM A下臂和ARM B下臂的相位为:

一个臂在导通前(上升沿),和另外一个臂有共同的关断时间(低电平),体现出了死区时间,时序也正常。实际上,把死区时间设置大一些可以显示得更明显。

以上解决方案输出了40KHz的H桥控制逻辑,可用于驱动超声波设备。

主函数代码及工程

主函数代码:

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2022 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************Written by Pegasus Yu in 2022*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim16;
TIM_HandleTypeDef htim17;/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM16_Init(void);
static void MX_TIM17_Init(void);
/* USER CODE BEGIN PFP */
__IO float usDelayBase;
void PY_usDelayTest(void)
{__IO uint32_t firstms, secondms;__IO uint32_t counter = 0;firstms = HAL_GetTick()+1;secondms = firstms+1;while(uwTick!=firstms) ;while(uwTick!=secondms) counter++;usDelayBase = ((float)counter)/1000;
}void PY_Delay_us_t(uint32_t Delay)
{__IO uint32_t delayReg;__IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);delayReg = 0;while(delayReg!=usNum) delayReg++;
}void PY_usDelayOptimize(void)
{__IO uint32_t firstms, secondms;__IO float coe = 1.0;firstms = HAL_GetTick();PY_Delay_us_t(1000000) ;secondms = HAL_GetTick();coe = ((float)1000)/(secondms-firstms);usDelayBase = coe*usDelayBase;
}void PY_Delay_us(uint32_t Delay)
{__IO uint32_t delayReg;__IO uint32_t msNum = Delay/1000;__IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);if(msNum>0) HAL_Delay(msNum);delayReg = 0;while(delayReg!=usNum) delayReg++;
}uint8_t i=0;
/* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**    PA6(T16C1)          PA7(T17C1)*    |                    |*    |                    |*    ----------|-|-----------*    |                    |*    |                    |*    PB6(T16C1N)         PB7(T17C1N)**    H bridge indication*//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM16_Init();MX_TIM17_Init();/* USER CODE BEGIN 2 */PY_usDelayTest();PY_usDelayOptimize();__disable_irq();HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);HAL_TIMEx_PWMN_Start(&htim16,TIM_CHANNEL_1);PY_Delay_us_t(16); i>>=1;i>>=1;HAL_TIMEx_PWMN_Start(&htim17,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL10;RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV2;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK){Error_Handler();}
}/*** @brief TIM16 Initialization Function* @param None* @retval None*/
static void MX_TIM16_Init(void)
{/* USER CODE BEGIN TIM16_Init 0 *//* USER CODE END TIM16_Init 0 */TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};/* USER CODE BEGIN TIM16_Init 1 *//* USER CODE END TIM16_Init 1 */htim16.Instance = TIM16;htim16.Init.Prescaler = 3;htim16.Init.CounterMode = TIM_COUNTERMODE_UP;htim16.Init.Period = 249;htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim16.Init.RepetitionCounter = 0;htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim16) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim16) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 124;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(&htim16, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime = 20;sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(&htim16, &sBreakDeadTimeConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM16_Init 2 *//* USER CODE END TIM16_Init 2 */HAL_TIM_MspPostInit(&htim16);}/*** @brief TIM17 Initialization Function* @param None* @retval None*/
static void MX_TIM17_Init(void)
{/* USER CODE BEGIN TIM17_Init 0 *//* USER CODE END TIM17_Init 0 */TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};/* USER CODE BEGIN TIM17_Init 1 *//* USER CODE END TIM17_Init 1 */htim17.Instance = TIM17;htim17.Init.Prescaler = 3;htim17.Init.CounterMode = TIM_COUNTERMODE_UP;htim17.Init.Period = 249;htim17.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim17.Init.RepetitionCounter = 0;htim17.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim17) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim17) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 124;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(&htim17, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime = 20;sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(&htim17, &sBreakDeadTimeConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM17_Init 2 *//* USER CODE END TIM17_Init 2 */HAL_TIM_MspPostInit(&htim17);}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{/* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

范例下载

基于STM32F030的H桥驱动范例下载(STM32CUBEIDE开发平台HAL库工程)

–End–

STM32 TIM PWM中阶操作:互补PWM输出相关推荐

  1. STM32 TIM PWM高阶操作:刹车及状态约束

    STM32 TIM PWM高阶操作:刹车及状态约束 刹车及状态约束是STM32 TIM PWM控制里面比较复杂的一部分,涉及到PWM波形产生前,中,后的管脚状态输出. 这里先引入两个描述,一个是&qu ...

  2. C语言中文件操作使用fgetc()输出出现多余空格问题

    #include<stdio.h> int main() { //定义文件指针 FILE *fp; char ch; int word=0,num=0,oth; if((fp=fopen( ...

  3. STM32 基础系列教程 8 - 互补PWM

    前言 PWM及互补PWM是电机控制的基础,前面我们已经学习了PWM的产生,今天来学习一下互补PWM的产生,互补PWM就是两个互补的PWM的组合,用stm32 的高级定时器可以直接产生两路互补的PWM( ...

  4. 普通定时器输出互补PWM带死区

    普通定时器输出互补PWM带死区 一般来说三相半桥电路需要互补PWM驱动,而且互补PWM需要带死区,死区的大小跟管子的性能参数有关.公司为了省成本,在云台上把电机驱动芯片替换成三相半桥.用6个MOS驱动 ...

  5. STM32: 利用高级定时器产生6路互补PWM波形输出在BLDC中H-PWM-L-ON驱动方式下驱动无刷电机

    原文网址:http://jingyan.eeboard.com/article/73847#/prettyPhoto%5Bpp_gal%5D/6/ http://jingyan.eeboard.com ...

  6. STM32互补PWM输出使能控制

    STM32的高级定时器TIM1可以产生互补的PWM,并且可以通过相关寄存器的设置使能或关闭PWM的输出.在编写BLDC的驱动程序时,本人利用TIM1的channel1,2,3三个通道生成了三路互补的P ...

  7. stm32关于带死区时间的互补PWM输出

    1.互补PWM输出简介 在使用stm32输出PWM波形时,笔者所用的stm32f103zet6中的高级定时器TIM1和TIM8可以输出互补的PWM波形,使用互补的PWM波常见与一些半桥电路和全桥电路控 ...

  8. STM32高级定时器TIM1生成互补PWM

    硬件:stm32f103zet6 开发工具:Keil uVision V5.26.2.0 下载调试工具:J-Link 最近在研究三相无刷电机FOC控制,肯定要对互补PWM了解透彻.记录一下学习过程. ...

  9. STM32通用定时器输出带死区互补PWM/任意移相PWM

    项目上遇到了多个通道的带死区的互补功能,单纯靠H7的TIM1/TIM8/TIM15/TIM16/TIM17几个硬件自带的死区互补还不够,本文就使用通用定时器TIM2/TIM3/TIM4/TIM5来实现 ...

最新文章

  1. 2017-2018-1 20155202 《信息安全系统设计基础》第9周学习总结
  2. 1106C程序语法树
  3. B组前导码功率偏移(messagePowerOffsetGroupB)
  4. Python协同过滤推荐算法(Collaborative Filtering)1.概念和思想以及大致步骤
  5. Api文档生成工具与Api文档的传播(pdf)
  6. java增删改查_Java程序员你真的甘心只做增删改查吗
  7. Ubuntu16.04安装MySQL5.7
  8. mysql上机实验报告3_SQL入门随笔(上机实验报告)
  9. 什么是立即执行函数,有什么作用?
  10. ewebeditor高版本=5.50day
  11. 【转】腾讯云-解决Winscp permission denied的问题
  12. mysql kill 很多_MySQL--批量KILL连接
  13. C语言winform中
  14. 矩阵转置matlab的函数,【ZZ】Matlab矩阵操作
  15. 操作系统4.1.4 文件的物理结构(文件分配方式)
  16. html中怎么去除多余的代码,去除HTML代码中所有标签的两种方法
  17. u盘变o字节怎么修复_U盘变成0字节了数据怎么恢复
  18. 计算机硬件找不到网络适配器,图文学习网络适配器不见了 【操作教程】 的恢复方法_...
  19. An工具介绍之宽度工具、变形工具与套索工具
  20. IMX.6ULL_Linux_基础篇(6) soc资源介绍

热门文章

  1. 【内网穿透】使用神卓互联和Nginx搭建局域网应用反向代理
  2. 盲校计算机教学策略,浅谈盲校计算机教学策略.doc
  3. mfgtools工具BootStrap阶段使用文件的介绍
  4. 字符定位(最后一次找到的字符)
  5. 都在讲数据仓库,你知道它的作用是什么吗?
  6. 上海亚商投顾:沪指延续调整 机器人概念股掀涨停潮
  7. 从Windows 10共享文件夹给Docker虚拟机中的Docker的神奇bug
  8. PostgreSQL安装教程
  9. Interspeech 2022丨FFSVC 2022 远场说话人识别比赛
  10. [kali]--攻击浏览器漏洞