一、问题描述(背景)

        之前板卡做编码器计数的时候,用的exit外部引脚中断处理;不过这样做不仅太占用cpu资源;偶尔还会丢掉脉冲,频率也上不去。后来了解到用定时器有编码器模式,所以我改了硬件,用定时器的编码器模式,来处理编码器计数。

        改完之后,遇到了一个新的问题,那就是我想要count是一个int32_t类型的,而单片机的tim->cnt寄存器是16位的,所以不得不处理数据溢出问题。

        被卡了一天多,用了ai的代码,一直不行,心烦气躁,偶然间,看到了野火的关于Encoder的处理。豁然开朗,处理方法如下。

二、溢出问题

        因为tim->cnt寄存器是16位的的,编码器又有正反转,所以寄存器计数,就会出现上溢出,和下溢出。

        向上溢出:cnt计数到0xffff,就又会从0开始计数。

        向下溢出,cnt计数到0,又会从0ffff向下计数。

三、解决问题的方法

定时器存在溢出中断,所以我们只需要在中断里做溢出计数就可以。向上溢出我们+1,向下溢出我们-1,那么:

32位的编码器最终计数值,full_count = cnt+overflow_count*65536

最终代码如下:

static volatile int32_t overflow_count = 0;   //溢出计数(要用有符号的)
static TIM_HandleTypeDef htim3;


//中断处理程序(只做溢出计数)

void TIM3_IRQHandler(void) {
    if(__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE))
    {       
       if (TIM3->CR1 & TIM_CR1_DIR) {
            overflow_count--;
        } else {
            overflow_count++;
        }       
        __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
    }
}



//定时器3的编码器模式初始化
void MX_TIM3_Init(void)
{
 __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef gpio_init = {
        .Pin = GPIO_PIN_6 | GPIO_PIN_7,
        .Mode = GPIO_MODE_AF_INPUT,     
        .Pull = GPIO_PULLUP,
        .Speed = GPIO_SPEED_FREQ_HIGH
    };
    HAL_GPIO_Init(GPIOA, &gpio_init);

    __HAL_RCC_TIM3_CLK_ENABLE();
    htim3.Instance = TIM3;
    htim3.Init = (TIM_Base_InitTypeDef){
        .Prescaler = 0,
        .CounterMode = TIM_COUNTERMODE_UP,
        .Period = 0xFFFF,
        .ClockDivision = TIM_CLOCKDIVISION_DIV1,
        .RepetitionCounter = 0
    };
    HAL_TIM_Base_Init(&htim3);


    TIM_Encoder_InitTypeDef encoder_config = {
        .EncoderMode = TIM_ENCODERMODE_TI12,
        .IC1Filter = 4,   // 滤波设置
        .IC1Polarity = TIM_ICPOLARITY_RISING,
        .IC1Prescaler = TIM_ICPSC_DIV1,
        .IC1Selection = TIM_ICSELECTION_DIRECTTI,
        .IC2Filter = 4,   // 滤波设置
        .IC2Polarity = TIM_ICPOLARITY_RISING,
        .IC2Prescaler = TIM_ICPSC_DIV1,
        .IC2Selection = TIM_ICSELECTION_DIRECTTI
    };
    HAL_TIM_Encoder_Init(&htim3, &encoder_config);

    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
    __HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);

		TIM3->CNT = 0;        
    overflow_count = 0;      

    HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
}


//获取最终计数值full_count(这里是result,不要注意这些小细节,嘿嘿)
int32_t Encoder_GetCount(void)
{
    int32_t result;
 
    __disable_irq();
       
    result = TIM3->CNT +  overflow_count * 65536;
    __enable_irq();
    
    return result;
}




通过上述代码,我们就将full_count的值,存到了int32类型里面,这个计数值,会在-2,147,483,648 到 2,147,483,647这个范围里循环计数,可以说相当够用了。

四,结果

        实测发现,没有再出现什么稀奇古怪的问题。这么一看,逻辑也是非常的简单,代码也是非常的少,中断也就做了一件小事,可以说,非常之快乐。此帖完结(记录向)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐