赞
踩
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桥相当于两组推挽半桥组成的电路,负载跨在中间,在一个半周期左上臂到右下臂(Arm A)导通,在另外一个半周期右上臂和左下臂(Arm B)导通,从而经过负载的是交流信号,可以驱动交流负载如超声波雾化片,超声波清洗器等。在一个上臂和一个下臂导通时,另外的一个上臂和下臂必须处于关闭状态,否则会产生短路冲击。
H桥实现器件有两种,一种用三极管来实现,一种用MOS管来实现.原理是相似的,只是对电流的控制方式不同,一种是流控,一种是压控。
H桥实现方式有里两种,一种上臂是两个PMOS, 下臂是两个NMOS:
另一种通过浮压控制,实现上臂两个MOS管也用NMOS,所以上臂每个NMOS管到桥驱动端会多接一根线:
一般用集成芯片做桥驱动端具有更好的可靠性,而桥驱动端的输入来自外部相位脉冲。这里就是用STM32的PWM功能产生2组互补PWM输出给桥驱动端。
H桥的驱动时序要点为:
这里以STM32F030K6T6为例介绍中阶的互补PWM输出方式,采用STM32CUBEIDE工具。
在应用PWM时,考虑到计算的便捷性和整除性,可以将芯片的时钟配置为10的倍数,如最大84MHz的芯片配置为80MHz,最大72MHz的芯片配置为70MHz,最大48MHz的芯片配置为40MHz。
STM32F030K6T6频率最大48MHz,所以可以配置为40MHz,采用内部和外部时钟倍频都可以。这里配置为采用内部时钟倍频到40MHz。
这里配置输出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–
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。