关于基本定时器参考:
STM32实战总结:HAL之基本定时器_路溪非溪的博客-CSDN博客
关于PWM(通用定时器)参考:
STM32实战总结:HAL之PWM蜂鸣器_路溪非溪的博客-CSDN博客
高级定时器
基本定时器,只有计数定时,产生中断的功能。
通用定时器,在基本定时器的基础上,增加了输入捕获、PWM波生成等功能。
到了高级定时器,又在通用定时器的基础上增加了一些功能。
重复计数器
通俗来说就是:
在基本定时器和通用定时器中,计数值到了之后就会产生中断。而重复计数可以使得在经过了n次重复计数后才产生中断。
这样的好处有很多,举个简单的例子,之前的每次计数是5ms,为了实现每1s灯闪一下,我们需要在中断函数中重复200次中断。而有了这个重复次数计数器,我们可以很容易地解决这个问题,那就是重复计数次数达到200之后再产生中断,而不必在用户程序中处理。
重复次数计数器是8位的,最高能产生256的重复次数。
CNT是16位的,重复次数计数器是8位的,那么这两个加起来一共就是24位的。
互补输出
互补输出比较好理解,就是波形相反输出。
原来低电平变成高电平,原来高电平变成低电平。
如果原来的占空比是25%,那么互补输出后的占空比就是75%。
互补输出可以有如下应用场景:
比如两个MOS管,要求同一时间,一个导通,一个关闭。
死区
举例说明。
比如两个MOS管,要求同一时间,一个导通,一个关闭。
但是因为硬件没有这么完美,有时候导通和关闭并没有那么及时。可能会导致同一时间,两个MOS管都处于导通状态,这样可能会导致短路。
所以,可以通过波形延迟一小段时间,再去改变极性,这样可以确保同一时间,一个导通,一个关闭。
以下为带死区的PWM波:
理想情况下,互补的PWM波在同一时间,肯定是一个高电平一个低电平,状态同时切换。但是不可能这么理想,所以,就设置一个死区时间。
如以上,在PWM2变成低电平的时候,PW1延迟一段时间后再变成高电平,有同时关闭的状态并没有什么关系,关键是,不能同时导通。
在PWM2变成高电平时,如果PWM1同时变到低电平,有可能因为PWM2已经变高电平,但是PWM1还没来得及变成低电平,导致两个MOS都被导通,从而短路。所以,解决方法是让PWM2延迟变高电平。
所以,像这种通过延迟一段时间来避免同时导通的延迟区间,就叫死区时间。
完美的理解MOS就只有开与关两个状态, 但现实并没有这种美事, 在状态转完时(很短的时间), 第三种部分导通的状态出现。当控制两MOS转变电流方向时, 在很短的时间内, 两MOS都处于第三种状态, 而后果是, 很短时间内有电流从上面的MOS流经下面的MOS到地,而不是流到负载去。这种不是流到负载去的电流是损耗, 费工这还是小事, 大事是它会让MOS产生很大的功耗, 比起在关与闭的两种状态下大得多得多,而这功耗将会转化为热能。
为了避免第三种状态引起的问题, 就发明了死区这东东, 意思是上下两管的控制信号不完全是相反的状态, 而是在关闭一MOS的信号后,Delay一段时间, 后才打开另一MOS,这是错开两MOS管的第三种状态的方法,以避免电流直接流经两MOS管,这就是死区的用意。
死区主要防止桥驱动的时候,上管在开的时候,下管也处在开的状态,这样电流就会从上管直接留到下管。而理想状态下,MOS开关没有延时,这种情况下是可以不用死区的。但是实际情况是MOS开关有一个延时,而死区的作用就是确保这个区间不要出现上下管同时打开的情况。而一般这个死区是设置在下管。
带死区的两个波形的特点是,一个波形的高电平永远在另一个波形低电平的范围内。
刹车功能
举例说明。
当电机出现异常的时候,可以通过设置中断服务程序将其关停。
但是软件的响应速度太慢了,所以,可以将异常检测引脚连接到刹车端口,这样,直接触发硬件来关停电机。
这就是刹车功能,讲究实时性。
配置MX
这里用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输入高电平时,就会触发中断进行保护,即停止输出波形控制。
?
|