STM32F1定时器编码器模式,cubeMX配置LL库

任务:使用定时器的编码器功能,采集旋转编码器的信号并处理,使用LL库

网上似乎没查到关于LL库配置定时器编码器模式,这篇博客用于分享我解决问题的过程,以及提出一种实现方案。

作者只是大四学生一枚,水平有限,如有错误,还请您指出,不胜感激。

核心思路是:不管是什么库,最终都是操作单片机寄存器,先了解清楚HAL库的配置方法,再依照其核心实现去匹配LL库相关函数。KEIL左侧的function中可以快速查到文件内函数,如图所示。

根据经验,一般c文件内的函数接口是需要一系列操作的函数(如初始化),h文件中的函数一般是宏函数,可以快速操作某一寄存器(比如使能,获取数据)。

目录

一、硬件介绍

二、cubeMX配置

三、代码实现


一、硬件介绍

开发板采用的正点原子stm32F1RCT6  mini板,20线AB相旋转编码器模块如图,

这个模块有五根线,正负电源线(用于上拉),CLK和DT(也就是AB输出线),KEY(按下旋钮与gnd接通)

二、cubeMX配置

参考资料:http://www.eemaker.com/stm32cubemx-encoder.html

我这里没有分频,但实际上选择的encoderMode是  TI1和TI2这种模式下,AB两相的上升沿和下降沿都会计数,所以计数值是采集到脉冲个数的4倍。

配置为LL库,为了高效。配置完成、生成代码后会产生初始化函数:

void MX_TIM2_Init(void)
{
  LL_TIM_InitTypeDef TIM_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
  
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
  /**TIM2 GPIO Configuration  GPIO口信息
  PA0-WKUP   ------> TIM2_CH1
  PA1   ------> TIM2_CH2 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0|LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

//配置模式
  LL_TIM_SetEncoderMode(TIM2, LL_TIM_ENCODERMODE_X4_TI12);
  LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
  LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
  LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV2_N8);
  LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
  LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
  LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
  LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV2_N8);
  LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
  TIM_InitStruct.Prescaler = 0;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 65535;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM2, &TIM_InitStruct);
  LL_TIM_DisableARRPreload(TIM2);
  LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
  LL_TIM_DisableMasterSlaveMode(TIM2);

}

三、代码实现

已知HAL库实现定时器编码器需要几个核心步骤

  1. 初始化外设(上面已讲)
  2. 使能(一层一层剥开,追其根本就是操作CCER 、 CR1)
    HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL) ;  
    
    具体内容:即使能两个通道,以及使能定时器
    HAL_StatusTypeDef HAL_TIM_Encoder_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
    {
      /* Check the parameters */
      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
    
      /* Enable the encoder interface channels */
      switch (Channel)
      {
        case TIM_CHANNEL_1:
      {
        TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
          break;
      }
        case TIM_CHANNEL_2:
      {
        TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
          break;
      }
        default :
      {
         TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
         TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
         break;
        }
      }
      /* Enable the Peripheral */
      __HAL_TIM_ENABLE(htim);
    
      /* Return function status */
      return HAL_OK;
    }
    
    //使能操作,追其根本就是操作CCER
    
    void TIM_CCxChannelCmd(TIM_TypeDef* TIMx, uint32_t Channel, uint32_t ChannelState)
    {
      uint32_t tmp = 0U;
    
      /* Check the parameters */
      assert_param(IS_TIM_CC1_INSTANCE(TIMx));
      assert_param(IS_TIM_CHANNELS(Channel));
    
      tmp = TIM_CCER_CC1E << Channel;
    
      /* Reset the CCxE Bit */
      TIMx->CCER &= ~tmp;
    
      /* Set or reset the CCxE Bit */
      TIMx->CCER |=  (uint32_t)(ChannelState << Channel);
    }
    
    
    //定时器使能实际是操作CR1
    #define __HAL_TIM_ENABLE(__HANDLE__)                 ((__HANDLE__)->Instance->CR1|=(TIM_CR1_CEN))
  3. 获取数据(获取CNT)

    i= __HAL_TIM_GET_COUNTER(&htim2);
    
    //也就是获得CNT
    
    #define __HAL_TIM_GET_COUNTER(__HANDLE__) \
       ((__HANDLE__)->Instance->CNT)
    

根据这个流程,筛选LL库中相关函数:

  1. 初始化(cubemx已自动生成)
  2. 使能通道(操作CCER)
    LL_TIM_CC_EnableChannel(TIM2,LL_TIM_CHANNEL_CH1);
    LL_TIM_CC_EnableChannel(TIM2,LL_TIM_CHANNEL_CH2);
    
    //可以看到也是操作CCER
    __STATIC_INLINE void LL_TIM_CC_EnableChannel(TIM_TypeDef *TIMx, uint32_t Channels)
    {
      SET_BIT(TIMx->CCER, Channels);
    }

    使能计数(操作CR1)

     LL_TIM_EnableCounter(TIM2);
    
    __STATIC_INLINE void LL_TIM_EnableCounter(TIM_TypeDef *TIMx)
    {
      SET_BIT(TIMx->CR1, TIM_CR1_CEN);
    }
  3. 获取数据(获取CNT)

    i= LL_TIM_GetCounter(TIM2);
    
    //获取CNT
    
    __STATIC_INLINE uint32_t LL_TIM_GetCounter(TIM_TypeDef *TIMx)
    {
      return (uint32_t)(READ_REG(TIMx->CNT));
    }
  4. 然后功能就能实现了(可以看到我编码器转过一个刻度,数值增加了4(因为没分频),顺时针转增加,逆时针转减少)

具体代码就不上传了,时间用来写博客了,抓紧做毕设了。

如果有错误请您支出。如果觉得有帮助,打赏一下我这个穷学生吧,啊哈哈,开玩笑的。

猜你喜欢

转载自blog.csdn.net/qq_40550914/article/details/105167794