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

2023-05-16

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管的信号
,输出给下臂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–

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 TIM PWM中阶操作:互补PWM输出 的相关文章

  • 初始化 ST-Link 设备时出错 - 无法连接到设备

    我目前正在使用 ST Link 调试器对我的 STM32F3 Discovery 板进行编程 我使用的IDE是Atollic TrueStudio 5 5 2 现在我面临一个非常奇怪的问题 那就是我不断收到消息 初始化 ST Link 设备
  • c项目makefile多重定义错误

    这个问题是一个对应于创建的repexthis问题 在我的嵌入式 C 项目中 我有两个独立的板 我想为每个板创建两个 c 文件 master c 和 Slave c 其中包含自己的特定main 功能 我使用 stm32cumbemx 生成带有
  • 以字符串形式接收数字(uart)

    我正在尝试通过 uart 接收一个包装为字符串的数字 我发送数字 1000 所以我得到 4 个字节 空字符 但是 当我使用 atoi 将数组转换为数字并将整数与 1000 进行比较时 我并不总是得到正确的数字 这是我用于接收号码的中断处理函
  • STM32 F072上的软件如何跳转到bootloader(DFU模式)?

    STM32应用笔记2606对此进行了讨论 但没有简单的代码示例 该答案已使用 IAR EWARM 在 STM32F072 Nucleo 板上进行了测试 这个答案使用 STM32标准外设库 仅此而已 请注意 验证您是否成功进入引导加载程序模式
  • 优化 ARM Cortex M3 代码

    我有一个 C 函数 它尝试将帧缓冲区复制到 FSMC RAM 这些函数将游戏循环的帧速率降低至 10FPS 我想知道如何分析反汇编的函数 我应该计算每个指令周期吗 我想知道CPU把时间花在哪里 在哪个部分 我确信该算法也是一个问题 因为它的
  • 140-基于stm32单片机智能晾衣杆控制系统Proteus仿真+源程序

    资料编号 140 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 光敏传感器 蜂鸣器 LED灯 制作一个基于stm32单片机智能晾衣杆控制系统Proteus仿真 2 通过光敏传感器
  • 135-基于stm32单片机超声波非接触式感应水龙头控制系统Proteus仿真+源程序

    资料编号 135 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 电机 超声波传感器 制作一个基于stm32单片机超声波非接触式感应水龙头控制系统Proteus仿真 2 通过DHT11传感器检测当前
  • HAL库STM32常用外设教程(二)—— GPIO输入\输出

    HAL库STM32常用外设教程 二 GPIO输入 输出 文章目录 HAL库STM32常用外设教程 二 GPIO输入 输出 前言 一 GPIO功能概述 二 GPIO的HAl库驱动 三 GPIO使用示例 1 示例功能 四 代码讲解 五 总结
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

    我问这个问题是因为可以在这里找到类似问题的答案 通过应用程序跳转到 STM32 中的引导加载程序 即从用户闪存在引导模式下使用引导 0 和引导 1 引脚 用户 JF002 JF002回答 当我想跳转到引导加载程序时 我在其中一个备份寄存器中
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • STM32超声波——HC_SR04

    文章目录 一 超声波图片 二 时序图 三 超声波流程 四 单位换算 五 取余计算 六 换算距离 七 超声波代码 一 超声波图片 测量距离 2cm 400cm 二 时序图 1 以下时序图要先提供一个至少10us的脉冲触发信号 告诉单片机我准备
  • 毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计

    基于STM32的智能温室控制蓝牙声光报警APP系统设计 1 项目简介 1 1 系统构成 1 2 系统功能 2 部分电路设计 2 1 stm32f103c8t6单片机最小系统电路设计 2 2 LCD1602液晶显示电路设计 2 2 风
  • SHT10温湿度传感器——STM32驱动

    实验效果 硬件外观 接线 3 3V供电 IIC通讯 代码获取 查看下方 END
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • BeagleBone Black (v4.14) 上的 PWM 芯片到引脚映射

    关于如何在较新版本的内核上配置 Beaglebone 进行 PWM 的信息非常少 我按照以下指示进行操作BeagleBone Black v4 14 上的 PWM https stackoverflow com questions 5020
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • 无法使用 OpenOCD 找到脚本文件

    我正在尝试按照本教程将 OpenOCD 与我的 ST 发现板一起使用 https japaric github io discovery README html https japaric github io discovery READM
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • 使用 STM32 USB 设备库将闪存作为大容量存储设备

    我的板上有这个闪存IC 它连接到我的STM32F04 ARM处理器 处理器的USB端口可供用户使用 我希望我的闪存在通过 USB 连接到 PC 时被检测为存储设备 作为第一步 我在程序中将 USB 类定义为 MSC 效果很好 因为当我将主板

随机推荐

  • ubuntu 安装图形界面 tasksel不成功

    1 执行tasksel相关的安装命令一直报错 xff0c 经过改源等操作都不行 sudo tasksel install ubuntu desktop 2 参考下面的博客Ubuntu安装图形界面 xff08 桌面 xff09 yangxin
  • 【ChatGPT】比尔·盖茨最新分享:ChatGPT的发展,不止于此

    作者简介 xff1a 在读博士 xff0c 伪程序媛 xff0c 人工智能领域学习者 xff0c 深耕机器学习 xff0c 交叉学科实践者 xff0c 周更前沿文章解读 xff0c 提供科研小工具 xff0c 分享科研经验 xff0c 欢迎
  • 51单片机(十).避障与循迹小车

    实验简介 使用STC89C53单片机作为主控制器芯片实现了带全向轮的两轮驱动避障以及循线小车 xff0c 感知模块为超声波和红外传感器 本实验分为3个独立的部分 xff1a 1 利用超声波实现小车的避障功能 xff1b 2 利用红外传感器实
  • 一点小小的感悟

    从2017年发表了第一篇CSDN文章 xff0c 到现在已经过去了快五年的时间 五年前的我是一名学生 xff0c 五年后的今天我依旧是一名学生 xff0c 区别是从一名大二学生成为了现在的研二学生 这一路走来 xff0c 经历了很多很多事情
  • Arduino单片机(五).基于OpenWrt模块的无线视频遥控小车设计

    1 功能介绍 本项目所设计的机器人需要实现的功能为 xff1a 操作员在操作终端 xff08 手机或PC端 xff09 通过Wi Fi连接到小车上Wi Fi模块所在的网络中 xff0c 向其发出相关操作指令 xff0c 在Wi Fi模块接收
  • STM32单片机(二).STM32系列单片机的介绍

    STM32系列芯片是为要求高性能 低成本 低功耗的嵌入式应用设计的ARM Cortex M0 M0 43 M3 M4和M7内核 xff0c 按照内核架构分成不同的产品 其主流的产品有STM32F0 STM32F1和STM32F3 xff1b
  • STM32单片机(六). 传感器的使用

    传感器是单片机外围电路中最常见的模块 xff0c 在搭配了各种形式的传感器电路后 xff0c 就可以采集到的更多的环境信息 在本章节中 xff0c 主要介绍呼吸灯 温度传感器 RTC实时时钟以及红外遥控模块的控制使用 1 PWM实现呼吸灯的
  • STM32单片机(七). USART串口、IIC和CAN通信

    在简单的学习过了STM32中的简单外设以及中断系统后 xff0c 在本章节中开始介绍STM32芯片中各个通信接口的配置 在计算机中 xff0c 按数据传输方式可分为串行通信以及并行通信 xff1b 按数据同步方式可分为异步通信和同步通信 x
  • STM32单片机(八). ADC、DAC和DMA数据传输

    对于STM32的ADC模数转换器的介绍以及配置在文章 STM32单片机 xff08 六 xff09 传感器的使用 中已经详细介绍 xff0c 在本章节中主要介绍DAC数模转换器以及DMA的使用 1 DAC转换 1 1 数模转换器的介绍 DA
  • 虚拟机中安装Ubuntu 20.04,Liunx系统的常用命令以及工具安装

    因工作学习 需求 xff0c 在虚拟机软件VirtualBox安装了Ubuntu 20 04系统 xff0c 本章节中主要介绍一些环境的安装以及Liunx中常用的一些命令 1 软件和系统的安装 1 1 VirtualBox VirtualB
  • 51单片机(二).STC89C52单片机的引脚功能

    1 主电源引脚VCC和VSS 电源VCC接 43 5V xff0c VSS接地端 xff0c 即公共参考地 2 时钟电路引脚XTAL1和XTAL2 STC89C52芯片内部设置了一个反相放大器 xff0c XTAL1为放大器输入端 xff1
  • 51单片机(四).C51编程语言

    一 xff0e 常量与变量 1 常量 又称为标量 xff0c 在程序执行过程中不能改变 xff0c 常量数据类型有整形 xff08 int xff09 浮点型 xff08 float xff09 字符型和字符串型 应用中一般使用 defin
  • 【动态绘图】python可视化--丝滑版

    作者简介 xff1a 在读博士 xff0c 伪程序媛 xff0c 人工智能领域学习者 xff0c 深耕机器学习 xff0c 交叉学科实践者 xff0c 周更前沿文章解读 xff0c 提供科研小工具 xff0c 分享科研经验 xff0c 欢迎
  • TI DSP TMS320C66x学习笔记之DSP优化经验(八-1)

    此文是在http blog csdn net guanchanghui article details 1181851基础上 xff0c 通过自己的学习理解修改而来 暂且算作是自己的原创吧 如有侵权 xff0c 联系 xff0c 立刻改转载
  • Qt connect信号连接的几种写法

    概述 Qt 中的信号和槽应该是最熟悉不过的了 xff0c 连接信号和槽的关键字 connect有五种连接类型 xff0c 今天不是介绍这五种连接类型 xff0c 而是简单的总结一下 connect 的几种新旧写法 xff0c 其实在新版本中
  • ubuntu20.04系统的英文语言换成中文语言

    1 ubuntu20 04下载安装成功后 xff0c 默认系统语言为英文 英文界面切换到中文界面 xff0c 点击设置 2 选择对应的区域和语言 3 点击 Manage Installed Languages 上面那个 43 xff0c 然
  • he packaging for this project did not assign a file to the build artifact

    一 maven 工具中 xff0c 项目目录下 xff0c 点击运行Plugins install install install 抛出出错误 xff1a The packaging for this project did not ass
  • USB的枚举过程

    所谓USB设备与主机是通过检测Vcc上拉电阻的变化来确定是否有设备连接的 在D12内部集成了1 5k 的上拉电阻 xff0c 默认状态下不与Vcc相连 xff0c 程序运行时可以向D12发送连接命令使1 5k 电阻连接到Vcc xff0c
  • VNC远程登录树莓派的图形界面

    使用树莓派的一种经典方式 xff0c 是远程登陆进行操作 常用的方法有两种 xff0c 一种是通过SSH协议 xff0c 一种是使用VNC工具 这两种方法在Raspbian xff08 至迟2017年3月版 xff09 上都是集成的 xff
  • STM32 TIM PWM中阶操作:互补PWM输出

    STM32 TIM PWM中阶操作详解 xff1a 互补PWM输出 STM32 TIM可以输出管脚PWM信号适合多种场景使用 xff0c 功能包括单线 非互补PWM输出 xff0c 双线 互补PWM输出 xff0c 以及死区时间和刹车控制等