关于基本定时器参考:
STM32实战总结:HAL之基本定时器_路溪非溪的博客-CSDN博客
关于PWM(通用定时器)参考:
高级定时器,在通用定时器的基础上增加了一些功能。
补充:
极性选择是指选择高电平有效,还是低电平有效。
有的地方可能会提到边沿对齐模式,是和计数器计数的中央对齐模式相对而言的,其实边沿对齐模式就是指向上计数或者向下计数模式。
重复计数器
通俗来说就是:
在基本定时器和通用定时器中,计数值到了之后就会触发更新事件或者中断DMA。而重复计数可以使得在经过了n次重复计数后才触发。
重复计数器在下述任一条件成立时递减:
● 向上计数模式下每次计数器溢出时,
● 向下计数模式下每次计数器下溢时,
● 中央对齐模式下每次上溢和每次下溢时
这样的好处有很多,举个简单的例子,如果计数器每轮计数是5ms,为了实现每1s灯闪一下,我们需要在中断函数中重复200次中断。而有了这个重复次数计数器,我们可以很容易地解决这个问题,那就是重复计数次数达到200之后再产生中断,而不必在用户程序中处理。
重复次数计数器是8位的,最高能产生256的重复次数。
CNT是16位的,重复次数计数器是8位的,那么这两个加起来一共就是24位的。
互补输出
互补输出比较好理解,就是波形相反输出。
原来低电平变成高电平,原来高电平变成低电平。
如果原来的占空比是25%,那么互补输出后的占空比就是75%。
互补输出可以有如下应用场景:
比如两个MOS管,要求同一时间,一个导通,一个关闭。
注意:高级定时器只有通道1/2/3有互补输出,通道4是没有的。
死区
举例说明。
比如两个MOS管,要求同一时间,一个导通(都是高电平导通),一个关闭。
理想情况下,互补的PWM波在同一时间,肯定是一个高电平一个低电平,状态同时切换。
上管开的时候,下管就是关闭的;上管关闭的时候,下管就是开的;……
但是因为硬件没有这么完美,有时候导通和关闭并没有那么及时。可能会导致同一时间,两个MOS管都处于导通状态,这样可能会导致短路。
比如这个图中,下管打开时,上管还没来得及关闭,就会导致两个管子同时导通:
为了避免这种状况发生,就需要设置一个死区时间,通过延迟一段时间来避免同时导通的延迟区间。
以下为带死区的PWM波:
直观来看就是:
当其中一个MOS管要导通的时候,另一个MOS管一定要提前关闭。
反过来说就是:
先让一个MOS管关闭后,另一个MOS管再导通。
有个明显特征就是:
一个波形的高电平(有效电平)总是处在另一个波形的低电平(无效电平)范围之内。
注意:死区时间通常是针对互补输出来说的。
带死区控制的互补输出应用之H桥:
以下为电机控制简图
刹车功能
举例说明。
当电机出现异常的时候,可以通过设置中断服务程序将其关停。
但是软件的响应速度太慢了,所以,可以将异常检测引脚连接到刹车端口,这样,直接触发硬件来关停电机。
这就是刹车功能,讲究实时性。
刹车输入为电平有效。可以根据设置选择高电平触发还是低电平触发。
这里用TIM1_CH1和TIM1_CH1N来进行(带死区的)互补输出。
使用TIM1_BKIN来做刹车功能的输入。
所以,以下重点配置TIM1和PE8/PE9/PE15管脚。
先配置PE8/PE9/PE15管脚(注意不是在GPIO分栏下,而是在Single Mapped Signals下):
输出引脚如果频率较高,比如1MHz,那么可以选择High,当然,选择低或者中等也没问题,虽然速度慢,但是节能。
注意:Modified如果修改了默认值就会打钩,没打勾用的就是默认值。
继续配置TIM1:
确认无误后生成初始化代码。
自动生成的TIM1初始化代码:
/* TIM1 init function */ void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance = TIM1; htim1.Init.Prescaler = 71; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 100; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 50; 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(&htim1, &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 = 200; sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM1_Init 2 */ /* USER CODE END TIM1_Init 2 */ HAL_TIM_MspPostInit(&htim1); }PWM开启库函数:
在stm32f1xx_hal_tim.h文件中:
/** @addtogroup TIM_Exported_Functions_Group3 TIM PWM functions * @brief TIM PWM functions * @{ */ /* Timer PWM functions ********************************************************/ HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim); HAL_StatusTypeDef HAL_TIM_PWM_DeInit(TIM_HandleTypeDef *htim); void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim); void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim); /* Blocking mode: Polling */ HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel); HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel); /* Non-Blocking mode: Interrupt */ HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel); HAL_StatusTypeDef HAL_TIM_PWM_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel); /* Non-Blocking mode: DMA */ HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length); HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);PWM波互补波形开启库函数:
在stm32f1xx_hal_tim_ex.h文件中(很多外设都有一个对应的ex扩展文件,一些较为高级的功能通常会放在扩展文件中):
/** @addtogroup TIMEx_Exported_Functions_Group3 Extended Timer Complementary PWM functions * @brief Timer Complementary PWM functions * @{ */ /* Timer Complementary PWM functions ****************************************/ /* Blocking mode: Polling */ HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel); HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel); /* Non-Blocking mode: Interrupt */ HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel); HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel); /* Non-Blocking mode: DMA */ HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length); HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);我们要用的函数是:
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel); HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);也就是两个开启函数,开启CH1和CH1N互补输出。
借助示波器查看死区
可以通过示波器查看效果。
将示波器探头分别接到CH1和CH1N,可以看到输出了带有死区的互补波形。
如果改变死区时间,会有相应的改变。
如果死区时间是0,那么会对齐:
根据测试:
设置值255-14us左右,200-4us左右,100-1.5us左右,具体死区时间和MX里填的数值之间并不是线性关系。另外死区时间是设定好后不变的,不会因为频率变化而变化。死区时间设置根据硬件的反应能力来调整,也不能设置太长,同时关闭的时间太久也可能出现一些问题。
以上占空比是50%,可以通过TIM1->CCR1去设置。
刹车功能
初始化时设置的是高电平触发刹车,那么,当PE15输入高电平时,就会触发中断进行保护,即停止输出波形控制。