¿Cómo usar los temporizadores en la placa STM32 y la biblioteca HAL para medir la velocidad del motor?

Necesito medir la velocidad de un motor giratorio instalado en un disco codificador con 20 ranuras. El sensor basado en LM393 se mantiene en el lugar correcto, de modo que el disco del codificador gira entre la parte de detección.

Creo que necesitaría tres temporizadores provistos por la placa STM32: uno para proporcionar PWM (TIM4), segundo (TIM1) para medir el tiempo de (1000 ms ~ 1 seg) y tercer temporizador (TIM2) para medir la cantidad de pulsos recibidos del codificador disco en 1 seg.

El período del contador de TIM1 (registro de recarga automática) se fija en 1000 y la frecuencia en 1000 Hz. La frecuencia de TIM2 es la misma que la frecuencia PWM-10kHz. La salida del sensor está conectada a PC15 y PWM a PD12.

El siguiente código usa la biblioteca HAL. Tengo dudas sobre cómo usar estos dos temporizadores/contadores dentro del ciclo while, qué funciones de la biblioteca HAL usar aquí y cómo usarlas. También debe colocarse el código para el control PWM y el cálculo de velocidad en el mismo bucle.

int main(void)
{
        volatile GPIO_PinState level; // stores input state from PC15, sensor output is connected to PC15
        uint16_t dutycycle=70;
        int speed=0;                  //speed variable
        int constant=(2*3*1)/20;      //roughly circumference/20 slots

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();       //for measuring 1 second
  MX_TIM4_Init();       //for PWM
  MX_TIM2_Init();      //for counting output pulses from PC15 pin

  HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);    //Start PWM signal
  level=HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15); //HAL function is called to read pin status

  while (1)
  {
              counter1=__HAL_TIM_GetCounter(&tim2);
              speed=(counter1)*(constant);          //speed variable, counter 1 related to TIM2  

            htim4.Instance->CCR1=dutycycle;   //setting the duty cycle of PWM signal
            HAL_Delay(1000);dutycycle+=10;    //1 sec delay and then increases dutycyle by 10%
            if(dutycycle==90)dutycycle=50;    //resets to 50% duty cycle
  }

La placa de desarrollo es STM32F407 , CubeMX es el generador de código fuente e IDE es Eclipse . El sistema operativo es Linux (Ubuntu 15.04) . Amablemente sugiera.

¿Qué tipo de STM32 tienes? La mayoría de ellos, si no todos, tienen soporte de codificador de cuadratura incorporado. Solo se necesita un temporizador con 2 canales.
Hola Bence, gracias por tomarte tu valioso tiempo. La placa de desarrollo es stm32F407, CubeMX se usa para generar el código fuente, también se usa la biblioteca HAL y IDE es Eclipse. También he editado esto en cuestión.

Respuestas (4)

La mejor manera es configurar un temporizador en modo codificador para contar los pulsos de su sensor. Si tiene pulsos A y B, elija el modo de codificador TIM_ENCODERMODE_TI12si solo tiene pulso A y luego TIM_ENCODERMODE_TI1. (Este código funciona bien en una placa Discovery STM32F4).

void MX_TIM3_Init(void)
{
    TIM_Encoder_InitTypeDef encoderConfig;

    __TIM3_CLK_ENABLE();

    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 0;
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim3.Init.Period = 0xFFFF;
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    encoderConfig.EncoderMode = TIM_ENCODERMODE_TI12;

    encoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
    encoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
    encoderConfig.IC1Prescaler = TIM_ICPSC_DIV1;
    encoderConfig.IC1Filter = 0x00;

    encoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
    encoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
    encoderConfig.IC2Prescaler = TIM_ICPSC_DIV1;
    encoderConfig.IC2Filter = 0x00;

    if(HAL_TIM_Encoder_Init(&htim3, &encoderConfig) != HAL_OK)
    {

    }
}

/**TIM3 GPIO Configuration - Encoder
PB4     ------> TIM3_CH1
PB5     ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

En realidad, no nos dijo el tipo exacto de su STM32, por lo que debe verificar el temporizador, el pin y los valores de función alternativos para el suyo. Después de configurar el temporizador, se pueden usar las siguientes funciones simples.

Luego configure otro temporizador con interrupción periódica y llame a la Encoder_Readfunción en el ISR para consultar el valor de incremento actual.

uint32_t Encoder_Read(void)
{
    return TIM3->CNT;
}

void Encoder_Start(void)
{
    HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
}

void Encoder_Stop(void)
{
    HAL_TIM_Encoder_Stop(&htim3, TIM_CHANNEL_ALL);
}

En cada ISR puede calcular la diferencia entre el estado del contador anterior y el actual. Esa será la cantidad de pulsos durante el período a partir del cual puede calcular la velocidad (dado que sabe cuánto significa la distancia 1 incremento). Tenga en cuenta los posibles desbordamientos del contador.

actualización:
configuración de canal combinado: modo de codificador. Aquí hay una foto.ingrese la descripción de la imagen aquí

Hola Bence, gracias nuevamente por conocer las funciones de la biblioteca HAL. Encontré en el manual que el modo codificador es compatible con TIM1 y TIM8 de STM32F407. Ahora, estoy procediendo a seguir el método del codificador. Necesito saber algunas cosas: 1. El modo codificador se puede usar como un contador como se usa en el modo normal, ¿verdad? 2. el primer segmento de código, es decir, la función de inicialización del temporizador; se puede usar para medir el tiempo y, como se indica, los cambios se pueden cambiar en declaraciones "htim3.Init.Prescaler = 16000;"//para 1 milisegundo" y "htim3.Init.Period = 1000;"// para contar hasta 1000 milisegundos .es decir, 1 segundo
@abinjacob El código anterior se usó en un STM32F407VG. 1. Bueno, básicamente, pero si usa el modo codificador, el registro del contador del temporizador se incrementará/disminuirá por los pulsos de entrada y no por una frecuencia de reloj regular. 2. Si desea medir 1 ms, eso ya está listo para usted, Systick Interrupt se genera en cada ms, puede usar eso. En stm32f4xx_it.c debe haber un SysTick_Handlerllamado con un período de 1 ms.
La imagen es más clara ahora, tengo algunas preguntas más, corríjame también si me equivoco. 1. La idea es que el codificador se usará para contar el número de pulsos del sensor, ¿verdad? luego, cómo conectar PC15 con este contexto, porque la señal de entrada proviene de PC15. 2. Cómo utilizar la función "void SysTick_Handler(void)", de modo que la interrupción generada después de 1 ms se pueda utilizar para reiniciar el registro del contador del temporizador utilizado en el modo codificador.
@abinjacob 1. Sí. Pero si no puede cambiar que la señal de entrada esté conectada a PC15, me temo que no puede usar un temporizador en el modo codificador. Para eso, debe conectar la señal de entrada a un pin que está conectado a un temporizador apropiado. 2. En el SysTick_Handlerpuede llamar a su función de reinicio o simplemente borrar el registro del contador del temporizador.
Gracias Bence, ¿la función del codificador se generó usando CubeMX? No encontré la configuración en CubeMX, que dice directamente indica sobre el modo codificador, ¿cómo genero el segmento de código como lo muestra usted en el modo Codificador, usando Cube MX para TIM3? Además, leí en el manual de referencia para stm32f407 que el modo de codificador se puede seleccionar configurando bits de Selección de modo esclavo en 001, 010 o 011 para el registro de control de modo esclavo de REgister TIMx (TIMx_SMCR) . Pero eso es en el nivel de registro, aquí estaba usando CubeMx, entonces, ¿cómo se puede usar?
@abinjacob Configuración de canal combinado: modo de codificador. Aquí hay una foto imgur.com/a/4LCPz .
Eso es amable de tu parte, todo el mediodía busqué la opción de codificador, oh, solo estaba allí. Bence, aquí hay un código basado en la idea, por favor comente. ¿Se llamará el ISR cada milisegundo? y cuenta el número de pulsos del pin PA6? ¿Es este el uso correcto?
Cómo escalar SysTick_Handler hasta 1000ms=1seg. Entonces, ese codificador lee la entrada del sensor durante 1000 milisegundos hasta que se genera la siguiente interrupción para contarla nuevamente.

Puede usar un solo temporizador en el modo de captura de entrada para medir la velocidad de un motor a partir de los pulsos de tacómetro generados por el sensor. La señal del sensor debe enviarse a uno de los canales de captura de entrada del temporizador (es decir, un pin con una función alternativa para la captura de entrada del temporizador).

Configure el contador para contar a una velocidad constante que sea adecuada para el rango de velocidades que necesita medir. La frecuencia del contador debe ser lo suficientemente lenta como para no volcarse en el período entre los pulsos del tacómetro para la velocidad más lenta que pretende medir. Y la frecuencia del contador debe ser lo suficientemente rápida como para proporcionar suficiente resolución de tiempo entre pulsos de tacómetro para la velocidad más rápida que pretende medir.

En el modo de captura de entrada, el temporizador contará hasta el rango completo del valor del contador. Y el temporizador capturará el valor del contador y proporcionará una interrupción cada vez que se reciba un pulso de tacómetro. En el controlador de interrupciones, debe comparar el valor del temporizador recién capturado con el valor capturado anteriormente para determinar la cantidad de tics que se han producido entre los pulsos del tacómetro. Luego, con el número de tics entre los pulsos del tacómetro conocido, puede calcular la velocidad de rotación del motor.

Aquí hay un código de ejemplo. Deberá elegir el TACH_TMR_INSTANCE, TACH_TMR_TICK_RATE, TACH_TMR_IC_CHANNEL y la polaridad que sean apropiados para usted. Este ejemplo es para un contador de 16 bits, por lo que si está utilizando un contador de 32 bits, deberá ajustar el Periodvalor y el tipo de tach_timer_values, this_tach_timer_valuey prev_tach_timer_value. Tenga en cuenta que este ejemplo no muestra la HAL_TIM_IC_MspInit()función en la que se habilitan el temporizador y los relojes GPIO y el pin GPIO se configura para la función alternativa adecuada.

TIM_HandleTypeDef tach_timer_handle;
TIM_IC_InitTypeDef tach_timer_config;

HAL_StatusTypeDef TachInit()
{
    HAL_StatusTypeDef hal_status;

    tach_timer_handle.Instance = TACH_TMR_INSTANCE;

    tach_timer_handle.Init.Prescaler = (SystemCoreClock / TACH_TMR_TICK_RATE) - 1;
    tach_timer_handle.Init.Period = 0xFFFF;
    tach_timer_handle.Init.ClockDivision = 0;
    tach_timer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    tach_timer_handle.Init.RepetitionCounter = 0;

    hal_status = HAL_TIM_IC_Init(&tach_timer_handle);
    if (hal_status != HAL_OK)
    {
        return hal_status;
    }

    tach_timer_config.ICPolarity = TIM_ICPOLARITY_RISING;
    tach_timer_config.ICSelection = TIM_ICSELECTION_DIRECTTI;
    tach_timer_config.ICPrescaler = TIM_ICPSC_DIV1;
    tach_timer_config.ICFilter = 0;

    hal_status = HAL_TIM_IC_ConfigChannel(&tach_timer_handle, &tach_timer_config, TACH_TMR_IC_CHANNEL);
    if (hal_status != HAL_OK)
    {
        return hal_status;
    }

    HAL_TIM_IC_Start_IT(&tach_timer_handle, TACH_TMR_IC_CHANNEL);
    if (hal_status != HAL_OK)
    {
        return hal_status;
    }

    return HAL_OK;
}


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *tim_handle)
{
    // An array for storing the two most recent captured counter values.
    static uint16_t tach_timer_values[2] = {0, 0};
    // An array index to identify the most recently captured counter value.
    static int tach_timer_index = 0;

    if (tim_handle == &tach_timer_handle)
    {
        uint16_t this_tach_timer_value = HAL_TIM_ReadCapturedValue(tim_handle, TACH_TMR_IC_CHANNEL);
        tach_timer_values[tach_timer_index++] = this_tach_timer_value;
        tach_timer_index &= 0x01;
        uint16_t prev_tach_timer_value = tach_timer_values[tach_timer_index];

        unsigned int tach_period_ticks = this_tach_timer_value - prev_tach_timer_value;
    }
}

No necesita preocuparse por el traspaso del valor del contador al calcular tach_period_ticksde esta manera porque es matemática sin signo y el traspaso desaparece.

La velocidad del motor se puede calcular tach_period_ticksasí.

speed_rpm = (60 * TACH_TMR_TICK_RATE) / (tach_period_ticks * PULSES_PER_REVOLUTION);

TIM1 no parece estar haciendo nada. Su bucle while se ejecuta a una velocidad determinada por HAL_Delay, que (¿creo?) Está utilizando algún otro temporizador no definido explícitamente por usted (o eso o espera ocupado).

Si su intención es ejecutar a una velocidad determinada por TIM1, debe configurar una interrupción basada en la condición de coincidencia de este temporizador (o desbordamiento, o como lo llame ST).

En general, puedo creer que la idea de contar pulsos dado un período de tiempo fijo sería un indicador de la velocidad del motor, pero no sé lo suficiente sobre su hardware en particular para decir más.

Hola @Mitchell, TIM1 se usará para medir 1000 milisegundos de duración, en el código no se muestra porque no estoy seguro de cómo usar esta funcionalidad de la biblioteca HAL, el hardware es STM32F407 (placa de descubrimiento, una placa de desarrollo) y se basa en Microprocesador ARM cortex m4

¿Cómo usar los temporizadores en la placa STM32 y la biblioteca HAL para medir la velocidad del motor?

para ese tipo de cosas, primero piense en cómo lo mediría y luego cómo lo mediría con un enfoque/biblioteca particular.

en términos generales, necesitará un contador y una base de tiempo: para tener la cantidad de pulsos en un período de tiempo conocido.

dependiendo de la velocidad del motor, puede usar la base de tiempo para activar el contador, o viceversa. por ejemplo, puede precargar el contador con una compensación, iniciar la base de tiempo y luego interrumpir el desbordamiento del contador. O al revés.

Entonces, como máximo, se necesitan dos temporizadores. en algunos casos, un temporizador es suficiente.

Una vez que descubra el concepto, cómo implementarlo en su entorno de software es simple.

gracias por contestar, entiendo tu idea, pero como incorporarla en códigos, me cuesta; Estoy usando el generador de código fuente CUbeMX y Eclipse IDE, la placa es STM32f407.