STM32定时器学习-PWM输出

基本定时器

最基础功能定时,两个基础定时器TM6和TM7,基本定时器时钟源只来自内部时钟。
如果我们想要一个1s的定时,那么我们应该怎么设置定时器呢?

首先了解定时器的基本初始化结构体

 typedef struct {
     uint16_t Prescaler;         // 预分频器
     uint16_t CounterMode;       // 计数模式
     uint32_t Period;            // 定时器周期
     uint16_t ClockDivision;     // 时钟分频
     uint8_t RepetitionCounter;  // 重复计算器
 } TIM_Base_InitTypeDef;
  1. 首先考虑计数模式(CounterMode),定时器有五种计数模式,向上计数、向下计数、三种中心对齐模式,本实验的基本定时器只能是向上计数,从0开始计数,并且无需初始化。
  2. 定时器预分频器设置(Prescaler),设置它来取得每次计数的时间。例如每次计数的时间为100us,我们称之为时钟源周期,因为输入时钟为84MHZ,所以设置预分频器为(8400-1)
  3. 定时器周期(Period) 刚刚我们知道了时钟源时间,一个时钟源时间我们刚刚设定为100us,如果我们有10000个这样的时间我们就有了1s定时。而系统是从0开始计数的所以我们设定时需要减一(10000-1)。
  4. ClockDivision:时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟频率分频比, 基本定时器没有此功能,不用设置
  5. RepetitionCounter:重复计数器,属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。 这里不用设置

基本定时器只需要设定两个成员就行。
我们来看初始化基本定时器函数

 static void TIM_Mode_Config(void)
 {
     // 开启 TIMx_CLK,x[6,7]
     __TIM6_CLK_ENABLE();

     TIM_TimeBaseStructure.Instance = TIM6;
     /* 累计 TIM_Period 个后产生一个更新或者中断*/
     //当定时器从 0 计数到 4999,即为 5000 次,为一个定时周期
     TIM_TimeBaseStructure.Init.Period = 5000-1;

     //定时器时钟源 TIMxCLK = 2 * PCLK1
     // PCLK1 = HCLK / 4
     // => TIMxCLK=HCLK/2=SystemCoreClock/2=84MHz
     // 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=10000Hz
     TIM_TimeBaseStructure.Init.Prescaler = 8400-1;

     // 初始化定时器 TIMx, x[2,3,4,5]
     HAL_TIM_Base_Init(&TIM_TimeBaseStructure);

     // 开启定时器更新中断
     HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);
 }

最后使用 HAL_TIM_Base_Start_IT 函数开启定时器和更新中断。
主函数保持初始化就好了,之后我们设置了中断,在stm32f4xx_it.c的函数中的中断服务函数和中断回调函数。
我来解释一下,这里中断服务函数就是当定时器按照定时时间计数完成后程序进入中断服务函数进行程序运行,而这里我们通过HAL_TIM_IRQHandler调用中断回调函数,之后系统将变成双线程,一边返回原来计数继续计数,一边就行回调函数的运行。如果不用中断服务函数可能会影响定时器精准,这是我自己的理解,如果不对的话请大佬帮我指出

void TIM6_DAC_IRQn (void)
 {
     HAL_TIM_IRQHandler(&TIM_TimeBaseStructure);
 }

 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
 {
     if (htim==(&TIM_TimeBaseStructure)) {
     //添加你自己的函数
     }
 }

一个最基本的基本定时器就完成了。

TIM-高级定时器

TIM1\TIM8是高级定时器,其他为通用定时器,高级定时器包括了通用定时器的功能。
通用定时器功能比基本定时器多了:输入捕获和输出比较功能
而高级定时器又比通用定时器多了可编程死区互补输出、重复计数器、带刹车(断路)功能
我现在也不理解那功能框图有什么用,只能让我更迷惑,等着我以后会了解了再补充。
解释一下各个功能,以后用到再学:

  1. 输入捕获 输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,通常用于测量输入信号的脉宽、测量 PWM 输入信号的频率及占空比。

  2. 输出比较 定时器通过对预设的比较值与计数器的值做匹配比较之后,并依据相应的输出模式从而实现各类输出。如PWM输出、电平翻转、单脉冲输出、强制输出等。我接下来可能要做电机系统,所以应该会用到PWM输出,在下面着重讲一下。

  3. 可编程死区互补输出 死区,简单解释:通常,大功率电机、变频器等,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。解释来源:
    https://blog.csdn.net/u011456016/article/details/70238923

  4. 重复计数器 其中重复计数器意思指的是对计数器溢出(包括上溢和下溢)的次数进行计数,是递减的,每到达0时才会产生一个更新事件。而TIM1_RCR就是重复计数器减为0时重新从TIM1_RCR(重复计数器寄存器)的值开始计数.
    解释来源

  5. 带刹车(断路)功能 没有找到具体描述,但是有很通俗的解释,就是紧急刹车停止pwm波。

PWM波输出实验

PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码。
图片为周期为4毫秒的PWM波形
其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出! 但是!!!同一个定时器TIM只能产生一个频率的PWM波,你只能改变占空比。
接下来是怎么用stm32产生我们想要的pwm波。

1.初始化定时器部分

static void TIM_Mode_Config(void)
{
	TIM_OC_InitTypeDef  TIM_OCInitStructure;
  
	/*使能定时器*/
	__HAL_RCC_TIM8_CLK_ENABLE();

	TIM_AdvanceHandle.Instance = TIM8;    
	/* 累计 TIM_Period个后产生一个更新或者中断 */		
	//当定时器从0计数到TIM_PERIOD,即为TIM_PERIOD次,为一个定时周期
	TIM_AdvanceHandle.Init.Period =  0xFFFF; 
	// 高级控制定时器时钟源TIMxCLK = HCLK=168MHz 
	// 设定定时器频率为=TIMxCLK/(TIM_PRESCALER-1)
	TIM_TimeBaseStructure.Init.Period = 1000-1;
	TIM_AdvanceHandle.Init.Prescaler = 168-1;
	/* 计数方式 */
	TIM_AdvanceHandle.Init.CounterMode = TIM_COUNTERMODE_UP;            
	/* 采样时钟分频,不分频 */	
	TIM_AdvanceHandle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;   
   	/* 定时器一次一中断 */
	TIM_AdvanceHandle.Init.RepetitionCounter = 0 ;  		
	/*初始化定时器*/
  HAL_TIM_Base_Init(&TIM_TimeBaseStructure);
  
	/*PWM模式配置*/
  TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;//配置为PWM模式1
  TIM_OCInitStructure.Pulse = 500-1 ;//默认占空比为50%
  TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE;
	/*当定时器计数值小于CCR1_Val时为高电平*/
  TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;	
	
	/*配置PWM通道*/
  HAL_TIM_PWM_ConfigChannel(&TIM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
	/*开始输出PWM*/
	HAL_TIM_PWM_Start(&TIM_TimeBaseStructure,TIM_CHANNEL_1);
	
	/*配置脉宽*/
  TIM_OCInitStructure.Pulse = tim_per/2;//默认占空比为50%
	/*配置PWM通道*/
  HAL_TIM_PWM_ConfigChannel(&TIM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
	/*开始输出PWM*/
	HAL_TIM_PWM_Start(&TIM_TimeBaseStructure,TIM_CHANNEL_2);
	

  1. 了解输出比较结构体
    输出比较结构体TIM_OCInitTypeDef用于输出比较模式,与TIM_OCx_SetConfig函数配合使用完成指定定时器输出通道初始化配置。 高级控制定时器有四个定时器通道,使用时都必须单独设置。
typedef struct {
 uint32_t OCMode; // 比较输出模式
 uint32_t Pulse; // 脉冲宽度
 uint32_t OCPolarity; // 输出极性
 uint32_t OCNPolarity; // 互补输出极性
 uint32_t OCFastMode; // 比较输出模式快速使能
 uint32_t OCIdleState; // 空闲状态下比较输出状态
 uint32_t OCNIdleState; // 空闲状态下比较互补输出状态
 }TIM_OCInitTypeDef;

OCMode:比较输出模式选择,总共有八种,常用的为PWM1/PWM2。它设定CCMRx寄存器OCxM[2:0]位的值。在 PWM 模式( 1 或 2)下, TIMx_CNT 总是与 TIMx_CCRx 进行比较,以确定是TIMx_CCRx大于 TIMx_CNT 还是 TIMx_CNT小于TIMx_CCRx(取决于计数器计数方向)。

Pulse:比较输出脉冲宽度,实际设定比较寄存器CCR的值,决定脉冲宽度。可设置范围为0至65535。这个可以设置占空比,取决于你在前面的定时器预分频器设置定时器周期在407中,我们将预分频数设置为(168-1)得到1us的时钟源周期,之后将定时器周期设置为(1000-1)得到1ms的定时器周期,我们将pulse设置为(500-1)得到百分之50的占空比。

OCPolarity:比较输出极性,可选OCx为高电平有效或低电平有效。它决定着定时器通道有效电平。它设定CCER寄存器的CCxP位的值。一般设置为高电平有效。他与下面的OCNPolarity都为高电平或者都为低电平有效时输出高电平,否则为低电平。

OCNPolarity:比较互补输出极性,可选OCxN为高电平有效或低电平有效。它设定TIMx_CCER寄存器的CCxNP位的值。

OCFastMode:比较输出模式快速使能。它设定TIMx_CCMR寄存器的,OCxFE位的值可以快速使能或者禁能输出。开关作用吧。

OCIdleState:空闲状态时通道输出电平设置,可选输出1或输出0,即在空闲状态(BDTR_MOE位为0)时, 经过死区时间后定时器通道输出高电平或低电平。它设定CR2寄存器的OISx位的值。死区时间过后设置要输出高的还是输出低的,大概是这样,暂时用不到,用到的再说。

其他都是引脚初始化函数。
下面介绍设置通道占空比函数

__HAL_TIM_SET_COMPARE()

根据预分频数的大小我们来设置占空比,例如,预分频数(1000-1)我们设置占空比,100,就是百分之10的占空比。
接下来是必要的GPIO口的初始化

static void TIMx_GPIO_Config(void) 
{
 GPIO_InitTypeDef GPIO_InitStruct;
  
  /* 定时器通道功能引脚端口时钟使能 */
	
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
  
  /* 定时器通道1功能引脚IO初始化 */
	/*设置输出类型*/
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	/*设置引脚速率 */ 
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	/*设置复用*/
  GPIO_InitStruct.Alternate = GENERAL_TIM_GPIO_AF;
	
	/*选择要控制的GPIO引脚*/	
	GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;
	/*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/
  HAL_GPIO_Init(GENERAL_TIM_CH1_GPIO_PORT, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GENERAL_TIM_CH2_PIN;	
  HAL_GPIO_Init(GENERAL_TIM_CH2_GPIO_PORT, &GPIO_InitStruct);
	
}
/**
  * @brief  初始化控制通用定时器
  * @param  无
  * @retval 无
  */
void TIMx_Configuration(void)
{
	TIMx_GPIO_Config();
  
  TIM_PWMOUTPUT_Config();
}

我们之后就可以用改变占空比的函数进行修改,这个是通用定时器输出pwm波,很简单,难的是高级定时器的设置,涉及到很多模式,本实验先到这里。

22/03/07更新

PWM互补输出和输出比较吗模式区别:
PWM模式: ARR设置频率,CCR设置占空比,频率和占空比可以任意设置,起始相位不能设置。

输出比较模式:ARR设置频率,CCR设置相位,频率和起始相位可以任意设置,占空比不能设置。输出频率为理论计算值一半。
https://blog.csdn.net/qq_20222919/article/details/106564957

物联沃分享整理
物联沃-IOTWORD物联网 » STM32定时器学习-PWM输出

发表评论