欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32学习之路——通用定时器库函数分析(为产生PWM)

程序员文章站 2022-03-13 17:18:53
...

产生PWM的必要流程

一、使能GPIOA和TIM2外设。
两个外设别挂接在APB1和APB2总线上,所以需要分别调用下面两条库函数指令。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,DISABLE);

分析:该指令的作用是APB总线上的外设的使能

二、对GPIOA进行配置。
我使用的是TIM2,TIM2有四个输出通道(TIM2_CH1,TIM2CH2,TIM2_CH3,TIM2_CH4)分别于PA.0,PA.1,PA.2,PA.3引脚复用,所以对GPIOA进行配置如下:

	GPIO_Structure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_Structure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Structure.GPIO_Pin=GPIO_Pin_0;
	GPIO_Init(GPIOA,&GPIO_Structure);

这里只对TIM2_CH1通道进行输出,所以把PA.0配置成为复用输出

三、讲TIM2进行初始化。

	TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV4;
	TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TimeBaseStructure.TIM_Period=899;
	TimeBaseStructure.TIM_Prescaler=0;
	TIM_TimeBaseInit(TIM2,&TimeBaseStructure);

分析:下面是TIM_TimeBaseInit函数定义

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
  uint16_t tmpcr1 = 0;

  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx)); //
  assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
  assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
//检验以上三个变量是否符合规定,符合就往下执行,不符合就报错
  tmpcr1 = TIMx->CR1;//控制寄存器1,主要控制着计数器的工作方式

  if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5)) 
  {
    /* Select the Counter Mode */
    tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));//控制寄存器1上的DIR于CMS位,分别控制计数器的计数方向和对齐方式
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;//把我们定义的TIM_CounterMode_Up实际上就是0x0000,与其相或,进而使DIR和CMS位达到我们的想要的设定。此时的设定为位边缘对齐模式和向上计数
  }
 
  if((TIMx != TIM6) && (TIMx != TIM7))
  {
    /* Set the clock division */
    tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));//控制寄存器1上的CKD位,是选择时钟分频因子
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;//对CKD位进行赋值,
  }

  TIMx->CR1 = tmpcr1;//将设定的值赋值到控制寄存器1中

  /* Set the Autoreload value */
  TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;//ARR为自动重装载寄存器,将值赋予其中,在这里的值为899,功能:计数器从零开始计数,在达到899是归零,在重新计数,以此往复
 
  /* Set the Prescaler value */
  TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;//PSC为预分频器,功能:控制计数器计数的频率,计数频率=fCK_PSC/(PSC存储的数值+1)
    
  if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))  
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
  }//我们的定时器选择的是TIM2,所以这段语句直接跳过

  /* Generate an update event to reload the Prescaler and the Repetition counter
     values immediately */
  TIMx->EGR = TIM_PSCReloadMode_Immediate;//EGR为时间产生寄存器,这里使用的是默认值,也就是0x0000,没有使用    。   
}

四、设置 TIM2_CH1 的 PWM 模式及通道方向, 使能 TIM1 的 CH1 输出。

	Tim_OCInitypeStructure.TIM_OCMode=TIM_OCMode_PWM2;
	Tim_OCInitypeStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	Tim_OCInitypeStructure.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OC1Init(TIM2,&Tim_OCInitypeStructure);

分析:下面是TIM_OC1Init函数定义。

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)
{
  uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0;
   
  /* Check the parameters */
  assert_param(IS_TIM_LIST8_PERIPH(TIMx));
  assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode));
  assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState));
  assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity));   
 /* Disable the Channel 1: Reset the CC1E Bit */
  TIMx->CCER &= (uint16_t)(~(uint16_t)TIM_CCER_CC1E);//将CCER(捕获/比较使能寄存器)上的CC1E位置1,开启- OC1信号输出到对应的输出引脚。
  /* Get the TIMx CCER register value */
  tmpccer = TIMx->CCER;
  /* Get the TIMx CR2 register value */
  tmpcr2 =  TIMx->CR2;//CR2为控制寄存器2
  
  /* Get the TIMx CCMR1 register value */
  tmpccmrx = TIMx->CCMR1;//CCMR1为捕获/比较模式寄存器1
    
  /* Reset the Output Compare Mode Bits */
  tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_OC1M));
  tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_CC1S));
  //CCMR1上的OC1M三位控制的是输出比较模式,CC1S两位控制的是通道的方向(输入/输出),及输入脚的选择

  /* Select the Output Compare Mode */
  tmpccmrx |= TIM_OCInitStruct->TIM_OCMode;
  
  /* Reset the Output Polarity level */
  tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1P));
  /* Set the Output Compare Polarity */
  tmpccer |= TIM_OCInitStruct->TIM_OCPolarity;
  
  /* Set the Output State */
  tmpccer |= TIM_OCInitStruct->TIM_OutputState;
  //将我们的设定值分别赋值给这几个变量  
  if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)||
     (TIMx == TIM16)|| (TIMx == TIM17))
  {
    assert_param(IS_TIM_OUTPUTN_STATE(TIM_OCInitStruct->TIM_OutputNState));
    assert_param(IS_TIM_OCN_POLARITY(TIM_OCInitStruct->TIM_OCNPolarity));
    assert_param(IS_TIM_OCNIDLE_STATE(TIM_OCInitStruct->TIM_OCNIdleState));
    assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState));
    
    /* Reset the Output N Polarity level */
    tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NP));
    /* Set the Output N Polarity */
    tmpccer |= TIM_OCInitStruct->TIM_OCNPolarity;
    
    /* Reset the Output N State */
    tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NE));    
    /* Set the Output N State */
    tmpccer |= TIM_OCInitStruct->TIM_OutputNState;
    
    /* Reset the Output Compare and Output Compare N IDLE State */
    tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1));
    tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1N));
    
    /* Set the Output Idle state */
    tmpcr2 |= TIM_OCInitStruct->TIM_OCIdleState;
    /* Set the Output N Idle state */
    tmpcr2 |= TIM_OCInitStruct->TIM_OCNIdleState;
  }//我们选择的是TIM2,所以该条件跳过
  /* Write to TIMx CR2 */
  TIMx->CR2 = tmpcr2;
  
  /* Write to TIMx CCMR1 */
  TIMx->CCMR1 = tmpccmrx;

  /* Set the Capture Compare Register value */
  TIMx->CCR1 = TIM_OCInitStruct->TIM_Pulse; 
 
  /* Write to TIMx CCER */
  TIMx->CCER = tmpccer;
  //分别将变量里的值赋值给这几寄存器,完成设定
}

五、前面只是完成了计数器的计数模式的配置,输出通道的输出模式配置,此时需要对计数器进行使能操作,开始进行计数。

	TIM_Cmd(TIM2,ENABLE);

分析:下面是TIM_Cmd函数定义

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  
  if (NewState != DISABLE)
  {
    /* Enable the TIM Counter */
    TIMx->CR1 |= TIM_CR1_CEN;//将控制寄存器1上的CEN为置1,功能:使能计数器。
  }
  else
  {
    /* Disable the TIM Counter */
    TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
  }
}

六、需要了解的PWM输出原理。
在这里,我们设定的TIM2_CH1输出模式为PWM模式2-,在该模式下就有,当计数器内的值<CCR1(捕获/比较寄存器1)内的值是,输出为低电平,当计数器内的值>=CCR1(捕获/比较寄存器1)内的值是,输出为高电平。
所以此时我们想要得到脉冲输出的话,就需要对CCR1进行赋值,此时我选择的是计数器设定值得一半,也就是450,调用的函数如下:

TIM_SetCompare1(TIM2,450);

七、得到的PWM输出
STM32学习之路——通用定时器库函数分析(为产生PWM)

相关标签: 嵌入式 stm32