32bits encoder counter using 16bits TIM and a 32bits software counter

Dependents:   v1 v2 v2bis GestionPixy ... more

Software 32 bits quadrature encoder interface for STM32 targets

This library is designed to help interface quadrature encoders with STM32 targets. The base idea is to use a timer as a counter with several inputs corresponding to the A and B channels of a quadrature encoder. This is easily done with the new STM32 chips from STMicro, and of course with the Nucleo Boards.

Almost every Nucleo Board has 4 or 5 of those interfaces, that can be routed to several pins (I still have to check with every board to this day). This library provides an easy way to use these quadrature decoders, with an added feature : using software 32 bits integers to stock the counter, allowing a more efficient way to use them. In deed, most of the timers of the Nucleo Boards are 16 bits counters, and thus can limit the possibilities in some projects.

The solution provided here uses the update interrupt of the timer, set when the counter over- or underflows, and updates an 32 bits integer accordingly, allowing to track the encoder activity on a range of -(2^31) to (2^31)-1, or -2147483648 to 2147483647.

Plus, this interface allows to use a simple object, easily instantiated.

Example code

#include "mbed.h"
#include "Nucleo_Encoder_16_bits.h"


Nucleo_Encoder_16_bits encoder1(TIM3);

void main (void)
{
    printf("Start of Nucleo Encoder test program");

    for(;;)
    {
        wait(1.0);
        printf("Encoder count : %l", encoder1.GetCounter());
    }
}

Please check the header file corresponding to your target to get the physical pins to which you have to connect the signals (EncoderMspInitxx.h)

Also, please do not mind the name of the files or objects/methods in this library. Some are named "16 bits", but everything runs using 32 bits soft counters, as said above.

Nucleo_Encoder_16_bits.cpp

Committer:
kkoichy
Date:
2016-05-26
Revision:
1:e82009479b5c
Parent:
0:ebd170807e11

File content as of revision 1:e82009479b5c:

#include "Nucleo_Encoder_16_bits.h"

int32_t Soft_32_Counter_TIM2, Soft_32_Counter_TIM3, Soft_32_Counter_TIM4, Soft_32_Counter_TIM5;

void Overflow_Routine_TIM2()
{
    if(TIM2->SR & 0x0001)
    {
        printf("Overflow Routine");
        TIM2->SR &= 0xfffe;
        if(!(TIM2->CR1&TIM_CR1_DIR))
            Soft_32_Counter_TIM2 += 0xffff;
        else
            Soft_32_Counter_TIM2 -= 0xffff;
    }
}

void Overflow_Routine_TIM3()
{
    if(TIM3->SR & 0x0001)
    {
        printf("Overflow Routine");
        TIM3->SR &= 0xfffe;
        if(!(TIM3->CR1&TIM_CR1_DIR))
            Soft_32_Counter_TIM3 += 0xffff;
        else
            Soft_32_Counter_TIM3 -= 0xffff;
    }
}
void Overflow_Routine_TIM4()
{
    if(TIM4->SR & 0x0001)
    {
        printf("Overflow Routine");
        TIM4->SR &= 0xfffe;
        if(!(TIM4->CR1&TIM_CR1_DIR))
            Soft_32_Counter_TIM4 += 0xffff;
        else
            Soft_32_Counter_TIM4 -= 0xffff;
    }
}
void Overflow_Routine_TIM5()
{
    if(TIM5->SR & 0x0001)
    {
        printf("Overflow Routine");
        TIM5->SR &= 0xfffe;
        if(!(TIM5->CR1&TIM_CR1_DIR))
            Soft_32_Counter_TIM5 += 0xffff;
        else
            Soft_32_Counter_TIM5 -= 0xffff;
    }
}

namespace mbed
{   

    Nucleo_Encoder_16_bits::Nucleo_Encoder_16_bits(TIM_TypeDef * _TIM)
    {
        TIM = _TIM;
        // Initialisation of the TIM module as an encoder counter
        EncoderInit(&encoder, &timer, _TIM, 0xffff, TIM_ENCODERMODE_TI12);

        // Update (aka over- and underflow) interrupt enabled
        TIM->DIER |= 0x0001;
        // The initialisation process generates an update interrupt, so we'll have to clear the update flag before anything else
        TIM->SR &= 0xfffe;

        // Setting the ISR for the corresponding interrupt vector
        switch((uint32_t)TIM)
        {
            case TIM2_BASE :
            NVIC_SetVector(TIM2_IRQn, (uint32_t)&Overflow_Routine_TIM2);
            NVIC_EnableIRQ(TIM2_IRQn);
            Soft_32_Counter_TIM2 = 0;
            break;
            
            case TIM3_BASE :
            NVIC_SetVector(TIM3_IRQn, (uint32_t)&Overflow_Routine_TIM3);
            NVIC_EnableIRQ(TIM3_IRQn);
            Soft_32_Counter_TIM3 = 0;
            break;
            
            case TIM4_BASE :
            NVIC_SetVector(TIM4_IRQn, (uint32_t)&Overflow_Routine_TIM4);
            NVIC_EnableIRQ(TIM4_IRQn);
            Soft_32_Counter_TIM4 = 0;
            break;
            
            case TIM5_BASE :
            NVIC_SetVector(TIM5_IRQn, (uint32_t)&Overflow_Routine_TIM5);
            NVIC_EnableIRQ(TIM5_IRQn);
            Soft_32_Counter_TIM5 = 0;
            break;
            
            default :
            
            break;
        }
        
    }
    Nucleo_Encoder_16_bits::Nucleo_Encoder_16_bits(TIM_TypeDef * _TIM, uint32_t _maxcount, uint32_t _encmode)
    {
        TIM = _TIM;
        // Initialisation of the TIM module as an encoder counter
        EncoderInit(&encoder, &timer, _TIM, _maxcount, _encmode);

        // Update (aka over- and underflow) interrupt enabled
        TIM->DIER |= 0x0001;
        // The initialisation process generates an update interrupt, so we'll have to clear the update flag before anything else
        TIM->SR &= 0xfffe;

        // Setting the ISR for the corresponding interrupt vector
        switch((uint32_t)TIM)
        {
            case TIM2_BASE :
            NVIC_SetVector(TIM2_IRQn, (uint32_t)&Overflow_Routine_TIM2);
            NVIC_EnableIRQ(TIM2_IRQn);
            Soft_32_Counter_TIM2 = 0;
            break;
            
            case TIM3_BASE :
            NVIC_SetVector(TIM3_IRQn, (uint32_t)&Overflow_Routine_TIM3);
            NVIC_EnableIRQ(TIM3_IRQn);
            Soft_32_Counter_TIM3 = 0;
            break;
            
            case TIM4_BASE :
            NVIC_SetVector(TIM4_IRQn, (uint32_t)&Overflow_Routine_TIM4);
            NVIC_EnableIRQ(TIM4_IRQn);
            Soft_32_Counter_TIM4 = 0;
            break;
            
            case TIM5_BASE :
            NVIC_SetVector(TIM5_IRQn, (uint32_t)&Overflow_Routine_TIM5);
            NVIC_EnableIRQ(TIM5_IRQn);
            Soft_32_Counter_TIM5 = 0;
            break;
            
            default :
            
            break;
        }
        
    }
    
    Nucleo_Encoder_16_bits::Nucleo_Encoder_16_bits(TIM_Encoder_InitTypeDef * _encoder, TIM_HandleTypeDef * _timer, TIM_TypeDef * _TIM, uint32_t _maxcount, uint32_t _encmode)
    {
        timer = *_timer;
        encoder = *_encoder;
        TIM = _TIM;
        // Initialisation of the TIM module as an encoder counter
        EncoderInit(&encoder, &timer, _TIM, _maxcount, _encmode);

        // Update (aka over- and underflow) interrupt enabled
        TIM->DIER |= 0x0001;
        // The initialisation process generates an update interrupt, so we'll have to clear the update flag before anything else
        TIM->SR &= 0xfffe;

        // Setting the ISR for the corresponding interrupt vector
        switch((uint32_t)TIM)
        {
            case TIM2_BASE :
            NVIC_SetVector(TIM2_IRQn, (uint32_t)&Overflow_Routine_TIM2);
            NVIC_EnableIRQ(TIM2_IRQn);
            Soft_32_Counter_TIM2 = 0;
            break;
            
            case TIM3_BASE :
            NVIC_SetVector(TIM3_IRQn, (uint32_t)&Overflow_Routine_TIM3);
            NVIC_EnableIRQ(TIM3_IRQn);
            Soft_32_Counter_TIM3 = 0;
            break;
            
            case TIM4_BASE :
            NVIC_SetVector(TIM4_IRQn, (uint32_t)&Overflow_Routine_TIM4);
            NVIC_EnableIRQ(TIM4_IRQn);
            Soft_32_Counter_TIM4 = 0;
            break;
            
            case TIM5_BASE :
            NVIC_SetVector(TIM5_IRQn, (uint32_t)&Overflow_Routine_TIM5);
            NVIC_EnableIRQ(TIM5_IRQn);
            Soft_32_Counter_TIM5 = 0;
            break;
            
            default :
            
            break;
        }

    }

    
    int32_t Nucleo_Encoder_16_bits::GetCounter()
    {
        uint16_t count = TIM->CNT;
        switch((uint32_t)TIM)
        {
            case TIM2_BASE :
            return (int32_t)count + Soft_32_Counter_TIM2;
            break;
            
            case TIM3_BASE :
            return (int32_t)count + Soft_32_Counter_TIM3;
            break;
            
            case TIM4_BASE :
            return (int32_t)count + Soft_32_Counter_TIM4;
            break;
            
            case TIM5_BASE :
            return (int32_t)count + Soft_32_Counter_TIM5;
            break;
        }
        
        return (int32_t)count;
    } 
    
    TIM_HandleTypeDef* Nucleo_Encoder_16_bits::GetTimer()
    {
        return &timer;    
    }

    
    

    
    
}