前言:
高级定时器具有通用定时器的所有功能,我们在这里面只说它不一样的地方。(通用定时器不具备的功能)
一:高级定时器
1:介绍
2:重复计数器
在我们普通的定时器中当CNT=CCR时直接发生溢出。然而我们的高级定时器多了一个重复计数器。
3:中断源
中断号的选择
在STM32微控制器的中断向量表中,你会看到很多与TIM1(定时器1)相关的中断处理程序(IRQHandler)的条目。这些处理程序对应于TIM1的不同中断源。根据你提供的内容,我们可以逐一解释这些中断并讨论如何选择它们:
- TIM1_BRK_IRQHandler:
- 描述:这个处理程序用于处理TIM1的断点中断。当定时器达到预定义的断点值时,这个中断会被触发。
- 用途:通常用于安全相关的应用,比如当某个条件满足时,需要立即停止定时器或执行某些安全操作。
- TIM1_UP_IRQHandler:
- 描述:这个处理程序用于处理TIM1的更新中断。当定时器计数达到其重载值(即一个周期完成)时,这个中断会被触发。
- 用途:通常用于需要周期性执行的任务,如PWM信号的产生、定时任务等。
- TIM1_TRG_COM_IRQHandler:
- 描述:这个处理程序用于处理TIM1的触发和换向中断。这通常与外部触发事件或电机控制中的换向操作相关。
- 用途:在电机控制或其他需要外部触发或换向的应用中使用。
- TIM1_CC_IRQHandler:
- 描述:这个处理程序用于处理TIM1的捕获比较中断。当定时器的当前计数值与某个比较寄存器匹配时,这个中断会被触发。
- 用途:用于精确的时间测量(输入捕获)或生成复杂的PWM信号(输出比较)。
4:互补死区
不能同时为高电平
5:死区时间计算
6:刹车
二:功能配置
1:输出PWM实验
注意:MOE默认为0。
A:HAL配置
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);
通过软件产生事件,我们这里面选跟新事件
调用这个函数的一个常见原因是为了立即触发定时器的更新事件,而不是等待定时器自然溢出。这对于需要立即应用新的PWM参数或更改定时器的状态特别有用。例如,如果你在代码中更改了PWM的占空比或频率,并希望这些更改立即生效,你可以调用这个函数来强制定时器更新其状态。
2:刹车和死区
三:代码
A:互补通道--DMA方式的
不能同时为高电平 :会烧毁MOS管!!!!!!
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "key.h"
#include "IC.h"
uint16_t num=0;
uint16_t zone_bit=1;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
Timer1_Init(1000-1,36000-1,0); // 0.5
while (1)
{
}
}
#include "stm32f1xx_hal.h"
DMA_HandleTypeDef DMA_Handle;
TIM_HandleTypeDef TIM1_Handle;
//把捕获的CCR的值放入这个数组里面 0ccr1 1CCR2
uint16_t tim1_dmabuff[4]={200,300,400,500}; //内存
void Timer1_Init(uint16_t arr, uint16_t psc, uint8_t rep)
{
TIM_OC_InitTypeDef TIM_OC_Init;
TIM1_Handle.Instance = TIM1;
TIM1_Handle.Init.Prescaler = psc;
TIM1_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM1_Handle.Init.Period = arr;
TIM1_Handle.Init.RepetitionCounter = rep; //重复计数器
TIM1_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //自动重装载值
HAL_TIM_PWM_Init(&TIM1_Handle);
__HAL_TIM_CLEAR_FLAG(&TIM1_Handle, TIM_FLAG_UPDATE);
//PWM1 --CNT <CCRX :输出有效电平
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; //输出比较的模式
TIM_OC_Init.Pulse=100; //比较值(CCR)
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH; //输出极性 (高电平有效)
TIM_OC_Init.OCNPolarity=TIM_OCNPOLARITY_HIGH; //互补通道的极性
TIM_OC_Init.OCFastMode=TIM_OCFAST_DISABLE; //快速模式
HAL_TIM_PWM_ConfigChannel(&TIM1_Handle,&TIM_OC_Init,TIM_CHANNEL_1);
HAL_TIM_PWM_Start_DMA(&TIM1_Handle,TIM_CHANNEL_1,(uint32_t *)tim1_dmabuff,4);
/*在一个定时器中如果开启多个通道,需要在开启每个通道后面都加上下面的代码*/
//TIM1_Handle.State=HAL_TIM_STATE_READY
/*互补通道需要单独开启*/
HAL_TIMEx_PWMN_Start(&TIM1_Handle,TIM_CHANNEL_1);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE() ;
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Pin = GPIO_PIN_8;
GPIO_InitType.Mode = GPIO_MODE_AF_PP;
GPIO_InitType.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Pin = GPIO_PIN_13;
GPIO_InitType.Mode = GPIO_MODE_AF_PP;
GPIO_InitType.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
DMA_Handle.Instance=DMA1_Channel2; //
//传输方向:内存--》外设
DMA_Handle.Init.Direction=DMA_MEMORY_TO_PERIPH;
//CCR1=16位。一个半子=2个字节 1个字节=8位。
DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //内存宽度
DMA_Handle.Init.MemInc=DMA_MINC_ENABLE; //内存地址是否自增(上面的数组)
DMA_Handle.Init.Mode=DMA_NORMAL; //模式 :循环
DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD; //外设宽度
DMA_Handle.Init.PeriphInc=DMA_MINC_DISABLE; //外设地址是否自增
DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM; //优先级
//建立连接关系
__HAL_LINKDMA(&TIM1_Handle,hdma[TIM_DMA_ID_CC1],DMA_Handle);
HAL_DMA_Init(&DMA_Handle);
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn,3,0); //设置优先级
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn); //打开中断
}
}
void DMA1_Channel2_IRQHandler()
{
HAL_DMA_IRQHandler(&DMA_Handle);
}
//由于没有开启捕获中断,所以进入这个函数的是:4次转运完成进入这个回调函数
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
}
}
}
关于极性反转问题:
按道理来说,我们正通道和互补通道的极性应该配置为相反的,才会输出互补的波形。
但是不是这个样子的,我们正通道和互补通道同时打开,我们的互补通道的极性会反转,我们我们应该吧正通道和互补通道配置为相同的极性它才输出输出互补的波形。
实验现象:
因为没有示波器,所以我们采用了2个LED灯
高级定时器--互补通道
关于互补通道:
通道4没有互补通道。
关于在一个定时器开启多个PWM--MDA
*在一个定时器中如果开启多个通道,需要在开启每个通道后面都加上下面的代码*/
TIM1_Handle.State=HAL_TIM_STATE_READY
因为
HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length)
{
if (htim->State == HAL_TIM_STATE_BUSY)
{
return HAL_BUSY;
}
else if (htim->State == HAL_TIM_STATE_READY)
{
if ((pData == NULL) && (Length > 0U))
{
return HAL_ERROR;
}
else
{
htim->State = HAL_TIM_STATE_BUSY;
}
}
}
eg:
HAL_TIM_PWM_Start_DMA(&tim1,TIM_CHANNEL_1,(uint32_t *)tim1_oc1_pwmdmabuff,4); //DMA方式 通道1 4次传输
tim1.State = HAL_TIM_STATE_READY; //设置定时器的状态为就绪态
HAL_TIM_PWM_Start_DMA(&tim1,TIM_CHANNEL_2,(uint32_t *)tim1_oc2_pwmdmabuff,4); //DMA方式 通道2 4次传输
tim1.State = HAL_TIM_STATE_READY; //设置定时器的状态为就绪态
HAL_TIM_PWM_Start_DMA(&tim1,TIM_CHANNEL_3,(uint32_t *)tim1_oc3_pwmdmabuff,4); //DMA方式 通道3 4次传输
互补通道需要单独打开:
HAL_TIMEx_PWMN_Start(&TIM1_Handle,TIM_CHANNEL_1);
B:互补通道+刹车+死区
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "key.h"
#include "IC.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
uint16_t num=0;
uint16_t zone_bit=1;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
Uart_Init(115200);
Timer1_Init(1000-1,36000-1,0); // 0.5
while (1)
{
}
}
#include "stm32f1xx_hal.h"
#include "OLED.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
DMA_HandleTypeDef DMA_Handle;
TIM_HandleTypeDef TIM1_Handle;
//把捕获的CCR的值放入这个数组里面 0ccr1 1CCR2
uint16_t tim1_dmabuff[4]={200,300,400,500}; //内存
void Timer1_Init(uint16_t arr, uint16_t psc, uint8_t rep)
{
TIM_OC_InitTypeDef TIM_OC_Init;
TIM_BreakDeadTimeConfigTypeDef TIM_BreakDeadTime;
TIM1_Handle.Instance = TIM1;
TIM1_Handle.Init.Prescaler = psc;
TIM1_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM1_Handle.Init.Period = arr;
TIM1_Handle.Init.RepetitionCounter = rep; //重复计数器
TIM1_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
TIM1_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //自动重装载值
HAL_TIM_PWM_Init(&TIM1_Handle);
__HAL_TIM_CLEAR_FLAG(&TIM1_Handle, TIM_FLAG_UPDATE);
//PWM1 --CNT <CCRX :输出有效电平
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; //输出比较的模式
TIM_OC_Init.Pulse=100; //比较值(CCR)
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH; //输出极性 (高电平有效)
TIM_OC_Init.OCNPolarity=TIM_OCNPOLARITY_HIGH; //互补通道的极性
TIM_OC_Init.OCFastMode=TIM_OCFAST_DISABLE; //快速模式
/*
空闲下的电平状态:如果使用刹车功能的话,需要配置
当刹车的时候,定时器通道的电平输出状态处于空闲下的配置
*/
TIM_OC_Init.OCIdleState=TIM_OCIDLESTATE_SET; //正通道空闲下的电平状态
TIM_OC_Init.OCNIdleState=TIM_OCNIDLESTATE_RESET; //互补通道空闲下的电平状态
HAL_TIM_PWM_ConfigChannel(&TIM1_Handle,&TIM_OC_Init,TIM_CHANNEL_1);
HAL_TIM_PWM_Start_DMA(&TIM1_Handle,TIM_CHANNEL_1,(uint32_t *)tim1_dmabuff,4);
/*在一个定时器中如果开启多个通道,需要在开启每个通道后面都加上下面的代码*/
//TIM1_Handle.State=HAL_TIM_STATE_READY
TIM_BreakDeadTime.OffStateRunMode=TIM_OSSR_DISABLE; //OSSR:0
TIM_BreakDeadTime.OffStateIDLEMode=TIM_OSSI_DISABLE; //OSSI:0
TIM_BreakDeadTime.LockLevel=TIM_LOCKLEVEL_OFF; //锁
TIM_BreakDeadTime.DeadTime=0xFF; //死区时间
TIM_BreakDeadTime.BreakState=TIM_BREAK_ENABLE; //刹车使能
TIM_BreakDeadTime.BreakPolarity=TIM_BREAKPOLARITY_HIGH; //刹车极性
TIM_BreakDeadTime.BreakFilter=0x8; //刹车滤波
/*
MOE自动功能 :打开--在刹车时间定时器的通道输出上面配置的空闲电平,
在取消刹车后,通道自动输出原来的电平。
*/
TIM_BreakDeadTime.AutomaticOutput=TIM_AUTOMATICOUTPUT_ENABLE; //MOE自动功能
HAL_TIMEx_ConfigBreakDeadTime(&TIM1_Handle,&TIM_BreakDeadTime);
/*互补通道需要单独开启*/
HAL_TIMEx_PWMN_Start(&TIM1_Handle,TIM_CHANNEL_1);
//开启刹车中断
__HAL_TIM_ENABLE_IT(&TIM1_Handle,TIM_IT_BREAK);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE() ;
__HAL_RCC_GPIOB_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Pin = GPIO_PIN_8;
GPIO_InitType.Mode = GPIO_MODE_AF_PP;
GPIO_InitType.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Pin = GPIO_PIN_13;
GPIO_InitType.Mode = GPIO_MODE_AF_PP;
GPIO_InitType.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
GPIO_InitType.Pin = GPIO_PIN_12;
GPIO_InitType.Mode = GPIO_MODE_AF_INPUT;
GPIO_InitType.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
HAL_NVIC_SetPriority(TIM1_BRK_IRQn,3,0); //设置刹车中断优先级
HAL_NVIC_EnableIRQ(TIM1_BRK_IRQn); //打开刹车中断
DMA_Handle.Instance=DMA1_Channel2;
//传输方向:内存--》外设
DMA_Handle.Init.Direction=DMA_MEMORY_TO_PERIPH;
//CCR1=16位。一个半子=2个字节 1个字节=8位。
DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //内存宽度
DMA_Handle.Init.MemInc=DMA_MINC_ENABLE; //内存地址是否自增(上面的数组)
DMA_Handle.Init.Mode=DMA_CIRCULAR; //模式 :循环
DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD; //外设宽度
DMA_Handle.Init.PeriphInc=DMA_MINC_DISABLE; //外设地址是否自增
DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM; //优先级
//建立连接关系
__HAL_LINKDMA(&TIM1_Handle,hdma[TIM_DMA_ID_CC1],DMA_Handle);
HAL_DMA_Init(&DMA_Handle);
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn,3,0); //设置优先级
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn); //打开中断
}
}
void DMA1_Channel2_IRQHandler()
{
HAL_DMA_IRQHandler(&DMA_Handle);
}
void TIM1_BRK_IRQHandler()
{
HAL_TIM_IRQHandler(&TIM1_Handle);
}
//DMA:进入这个函数的是:4次转运完成进入这个回调函数
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
printf("DMA4次传输完成,进入DMA完成中断\r\n");
}
}
}
//刹车的中断回调函数
void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
/*刹车是针对整个定时器的,不是某个通道的。
所以不需要判断是通道几的。
*/
printf("刹车回调函数\r\n");
}
}
空闲电平:
当刹车时间的时候,定时器的通道输出空闲配置的电平。在下面的刹车实验现象中可以看到。
/*
空闲下的电平状态:如果使用刹车功能的话,需要配置
当刹车的时候,定时器通道的电平输出状态处于空闲下的配置
*/
TIM_OC_Init.OCIdleState=TIM_OCIDLESTATE_SET; //正通道空闲下的电平状态
TIM_OC_Init.OCNIdleState=TIM_OCNIDLESTATE_RESET; //互补通道空闲下的电平状态
HAL_TIM_PWM_ConfigChannel(&TIM1_Handle,&TIM_OC_Init,TIM_CHANNEL_1);
2个位
TIM_BreakDeadTime.OffStateRunMode=TIM_OSSR_DISABLE; //OSSR:0
TIM_BreakDeadTime.OffStateIDLEMode=TIM_OSSI_DISABLE; //OSSI:0
锁
死区:
死区实际上就是为了规避,电平同时为高电平而发明的。
2个通道必须为高有效。(第有效会事得其反出现同时为高电平的存在。)
红色为没有加入死区时的波形。
正通道:它的上升沿相对于参考信号的上升沿有一个延迟。
互补通道:它的上升沿相对于参考信号的下降沿有一个延迟,
死区时间的计算:
TIM_BreakDeadTime.DeadTime=0xFF;
根据我们的时钟分频因子,决定我们的CKD。T=1/f
TIM1_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
刹车实验现象:
刹车的针对我们整个定时器的,不能指定刹车具体通道,刹车会刹整个定时器的通道。
说明:按键按下为高电平
当我们按下按键时,触发刹车功能,刹车的时候定时器的正通道和互补通道,输出的电平为我们空闲状态配置的电平状态。我们配置为一高一低,所以一个灭,一个亮。
TIM_OC_Init.OCNPolarity=TIM_OCNPOLARITY_HIGH; 互补通道的极性
TIM_OC_Init.OCFastMode=TIM_OCFAST_DISABLE; 快速模式
高级定时器刹车功能完整版
为什么在松开按下的时候可以恢复以前的状态?
我们使能的下面的(MOE自动功能 ):
/*
MOE自动功能 :打开--在刹车时间定时器的通道输出上面配置的空闲电平,
在取消刹车后,通道自动输出原来的电平。
*/
TIM_BreakDeadTime.AutomaticOutput=TIM_AUTOMATICOUTPUT_ENABLE; //MOE自动功能
使能相当于我们的AOE为=1 ; 在下一个更新事件中 ,自动把MOE置1。
这个也就是我们脱离刹车的时候,没有立刻恢复正通道和互补通道的输出的原因。如果定时器设置的更新事件的周期(T=arr*psc/72000 000)的T很大,恢复也的间隔也会很大。(只有在下一个跟新事件发送的时候置1--计数器溢出)
更多高级定时器的功能: