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

news2024/9/24 15:17:13

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设置成管脚同相输出。这样实现控制的连接关系将是:
在这里插入图片描述
参数配置为:
在这里插入图片描述
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);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(微秒延时)的指令延时实现方式及优化 。本例MCU主频只有40MHz,对于1us级延迟主频较低,因此存在一定偏差,要用示波器实际测试调整实现延迟主调,再用i>>=1;的多个指令做延迟微调, i的定义是uint8_t i。STM32纳秒级延时实现参考:STM32 纳秒级延时 (ns delay) 的指令延时实现方式及测定 。

测得的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–

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

万应低代码12月重点更新内容速递

速览版 详情版 低代码开发效率升级 01 动作流 动作编排过程中涉及到多条件判断时使用,即:满足某条件可执行一条分支,不满足则执行“其他”分支。 【使用场景】 ● 以“个人所得税计算”场景为例,不同收入水平的人输入不同的收…

人工智能对联生成 API 数据接口

人工智能对联生成 API 数据接口 基于百万数据训练,AI 训练与应答,多结果返回。 1. 产品功能 AI 基于百万历史对联数据训练应答模型;机器学习持续训练学习;一个上联可返回多个下联应答;毫秒级响应性能;数据…

关键字:final

文章目录一、final修饰类修饰方法修饰变量修饰属性修饰局部变量static final练习每日一考一、final final:最终的 final可以修饰的结构:类、方法、变量 修饰类 此类不能被其他类所继承 修饰方法 此方法不可以被重写 修饰变量 此时的“变量”就称为…

ACL论文总结

「博士毕业一年,我拿下 ACL Best Paper」 在不久前结束的自然语言处理NLP,领域顶级学术会议ACL2021上,字节跳动AL lab研究院许晶晶,完成了他的演讲。 在全球顶会做完分享后,许晶晶感到很欣慰,没想到&#…

自然语言处理重点 第11章 机器阅读理解 复习

机器阅读理解复习机器阅读理解概述机器阅读(MRC)理解与问答系统(QA)的区别:本章内容:MRC 任务分类:完形填空形式(cloze-style)选项形式片段抽取形式(span extraction)文本生成形式(free-answer/…

uCharts柱状图横向排列及不同条件下得数据颜色不同,雷达图的使用及各个参数的配置讲解

一:雷达图 1,建立一个盒子,内部存放uCharts图表 在data数据中return内部配置如下数据: chartData: {},//您可以通过修改 config-ucharts.js 文件中下标为 [radar] 的节点来配置全局默认参数,如都是默认参数,此处可以不传 opts 。实际应用过程中 opts 只需传入与全局默认…

【台式机DIY】我的第一台台式机电脑配置清单

文章目录[toc]【第一台台式机】一、电脑配置清单【电脑小白我科普】一.CPU1.选购:主流品牌2.选购:如何选择3.参数:接口4.参数:频率5.参数:核心和线程6.参数:功耗7.参数:缓存二.主板1.选购&#…

Django开发员工管理系统(Part I)

文章目录1. 准备工作1.1 创建django项目1.2 创建app1.3 配置settings.py文件,完成app注册2. 设计数据库表结构3. 在MySQL中生成表3.1 创建数据库3.2 修改配置文件,连接MySQL数据库3.3 通过django命令生成数据库表4. 编写部门列表4.1 (前段页面…

报表开发工具FastReport.NET的十大常见问题及解决方法(二)

Fastreport是目前世界上主流的图表控件,具有超高性价比,以更具成本优势的价格,便能提供功能齐全的报表解决方案,连续三年蝉联全球文档创建组件和库的“ Top 50 Publishers”奖。慧都科技是Fast Reports在中国区十余年的友好合作伙…

php宝塔搭建部署实战海洋cms视频内容管理系统源码

大家好啊,我是测评君,欢迎来到web测评。 本期给大家带来一套php开发的海洋cms视频内容管理系统源码,感兴趣的朋友可以自行下载学习。 技术架构 PHP7.2 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&#…

在动态规划的海洋中遨游(一)

前言:\textcolor{Green}{前言:}前言: 💞本专栏用于本人刷算法的过程。主要包含刷题中的感受以及知识点缺陷。对于学习者来说可以作为参考。 目前更新的算法内容会比较多,很多都是通过刷题来进行知识点的总结&#xff0…

计算机SCI论文,很难发表吗?应该如何发表? - 易智编译EaseEditing

首先,找题目需要符合国际标准,但不要缺少创新的探究题目。这个题目可以是最新的技术,也可以是最新的领域; 也可以是探索过很多次的课题。但是如果我们想成功交付,如何创新是我们需要思考的。 其次,因为英语…

为什么有的电源不是从0V开始上电的

大家可以看下,这张图片是测试XILINX 的FPGA 325T的上电时序图,其中绿色的线是FPGA 核心电源VCCINT 1.0V的波形,黄色的是BANK的电源2.5V的波形,蓝色的是辅助电源1.8V 的波形大家有没有发现这个时序图中黄色的波形,也就是…

Java之多线程详解

目录 一、线程简介 进程(Process )与 线程(Thread) 二、线程创建 1、线程Thread 1.1. 步骤 1.2 应用 1.3 案例:下载图片 2、实现Runnable接口 2.1 步骤 2.2 应用 3.小结 3. 实现Callable接口(了解…

java开发的考研系统大学生考研推荐网站考研网站源码

简介: 考研信息推荐查询。主要是管理发布管理考研的知识文章,或者上传资料,发布考研的视频。学生可以注册后下载资料,查看考研文章视频等。文章分为vip文章和普通文章,学生查看vip文章需要消耗积分。 演示视频 https…

FX5U 原点回归指令 DSZR

上一篇文章中转述了网友的文章,因回原点实在重要,再详细描述DSZR指令。 DSZR是具有自动搜索功能的原点回归指令。它对当前位置没有要求,在任意位置哪怕是停止在限位开关位置上都能完成原点回归操作。 1.指令格式 S1 原点回归速度或存储了数…

linux虚拟机搭建kafka(单节点、使用kafka自带zookeeper)

本文使用kafka单节点安装及配置,并使用kafka自带的zookeeper。一般kafka需要起三个kafka构成集群,可以连单独的zookeeper,本文不涉及。一、kafka下载解压安装包下载地址:https://archive.apache.org/dist/kafka/2.5.0/kafka_2.12-…

MyPerf4J结合Grafana和InfluxDB采集JVM以及QPS指标

MyPerf4J结合Grafana和InfluxDB采集JVM以及QPS指标 背景 ​ 需要采集现场java程序运行的状态数据(包括JVM指标以及QPS,RPS指标等)。需要采集的方式尽可能轻量化 ​ 结合实际情况采用MyPerf4J作为Java探针,InfluxDB作为数据存储端,Grafana作为数据展示…

15---整合Echarts和完善头像上传

1、完善头像上传功能 上次写的头像上传功能&#xff0c;不能实现上传保存后立刻刷新右上角头像&#xff0c;这里做一个完善。首先是在Manage.vue中&#xff08;父&#xff09;&#xff0c;写刷新User的方法 //传一个user过去到header <Header :collapseBtnClass"col…

【信息论与编码 沈连丰】第三章:离散信源

【信息论与编码 沈连丰】第三章&#xff1a;离散信源第三章 离散信源3.1 离散信源的分类及其描述3.2 离散信源的熵3.3 信源的冗余度3.4 信源符号序列分组定理3.5 平稳离散信源及其性质第三章 离散信源 3.1 离散信源的分类及其描述 信源分类&#xff1a;本质上主要基于两方面来…