Guía práctica de funciones principales de la HAL para STM32

Manipulación de GPIO

En la familia STM32F103, los registros BSRR y BRR permiten modificar el nivel lógico de un pin de forma atómica.

  • GPIOB->BSRR |= GPIO_PIN_7: escribe un 1 en el bit 7 del registro BSRR, lo que pone el pin PB7 a nivel alto.
  • GPIOB->BRR |= GPIO_PIN_7: escribe un 1 en el bit 7 del registro BRR, lo que pone el pin PB7 a nivel bajo.

El acceso mediante |= es habitual aunque, dado que escribir en BSRR/BRR no requiere leer previamente, también es válido una asignación directa como GPIOB->BSRR = GPIO_PIN_7.

// Activa y desactiva PB7 de forma sencilla
#define LED_PIN  GPIO_PIN_7

void led_on(void)  { GPIOB->BSRR = LED_PIN; }
void led_off(void) { GPIOB->BRR  = LED_PIN; }

Temporizadores (TIM)

Contador y captura de entrada

__HAL_TIM_SetCounter(&htim_x, valor) carga un valor en el contador del temporizador. Usar 0 reinicia la cuenta, lo cual resulta útil para sincronizar capturas o medidas de tiempo.

__HAL_TIM_GET_COUNTER(&htim_x) devuelve el valor actual del contador. Se emplea normalmente para medir anchos de pulso o intervalos.

TIM_HandleTypeDef htim_x;

uint32_t leer_cuenta(void)
{
    return __HAL_TIM_GET_COUNTER(&htim_x);
}

void reiniciar_cuenta(void)
{
    __HAL_TIM_SetCounter(&htim_x, 0);
}

Frecuencia máxima medible en captura (aproximada):

frecuencia_max ≈ frecuencia_reloj_TIM / resolución. Para un TIM de 16 bits a 168 MHz, el resultado es aproximadamente 2.56 kHz, aunque la configuración real del prescaler y del modo de captura puede modificar este límite.

Gestión de interrupciones periódicas

HAL_TIM_PeriodElapsedCallback() es el callback ejecutado cuando el contador alcanza el valor de recarga automática (ARR) y se produce el evento de actualización.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2)
    {
        // Tarea periódica de 1 ms, por ejemplo
    }
}

Para arrancar o detener el temporizador con interrupciones se usan:

  • HAL_TIM_Base_Start_IT(&htim_x)
  • HAL_TIM_Base_Stop_IT(&htim_x)

Dentro del ISR se puede comprobar y limpiar el flag de actualización con:

if (__HAL_TIM_GET_FLAG(&htim_x, TIM_FLAG_UPDATE) != RESET)
{
    __HAL_TIM_CLEAR_FLAG(&htim_x, TIM_FLAG_UPDATE);
}

Configuración dinámica del periodo

__HAL_TIM_SET_AUTORELOAD(&htim_x, nuevo_arr) cambia el valor de recarga automática. Para que el cambio sea efectivo de inmediato conviene habilitar la precarga del ARR y, si es necesario, reiniciar el contador.

void cambiar_periodo(uint32_t nuevo_arr)
{
    __HAL_TIM_SET_AUTORELOAD(&htim_x, nuevo_arr);
    __HAL_TIM_SET_COUNTER(&htim_x, 0);
}

__HAL_TIM_SET_PRESCALER(&htim_x, psc) modifica el prescaler, lo que permite ajustar la frecuencia de cuenta sin tocar el ARR. Ambos parámetros definen la frecuencia de salida:

frecuencia = frecuencia_reloj / ((PSC + 1) · (ARR + 1))

Modo encoder

HAL_TIM_Encoder_Start(&htim_x, TIM_CHANNEL_ALL) inicia el contador en modo encoder, útil para leer cuadratura de encoders rotatorios.

Generación de PWM

Función Descripción
HAL_TIM_PWM_Start() Inicia la salida PWM sin interrupciones.
HAL_TIM_PWM_Start_IT() Inicia la salida PWM y habilita la interrupción de actualización.
HAL_TIM_PWM_Stop_IT() Detiene la salida PWM y deshabilita la interrupción.
__HAL_TIM_SetCompare() Modifica el registro CCR para variar el ciclo de trabajo.

Ejemplo de ajuste de duty cycle:

void ajustar_pwm(uint32_t canal, uint32_t ciclo_trabajo)
{
    __HAL_TIM_SetCompare(&htim_pwm, canal, ciclo_trabajo);
}

El callback HAL_TIM_PWM_PulseFinishedCallback() se invoca al finalizar un pulso de PWM, lo que resulta útil para actualizar el siguiente ciclo de trabajo de forma síncrona.

Comunicación CAN

El periférico CAN se gestiona con estas funciones clave:

  • HAL_CAN_Start(&hcan_x): activa el módulo CAN tras configurar el bit-rate.
  • HAL_CAN_Receive_IT(&hcan_x, CAN_FIFO0): habilita la recepción por interrupción.
  • HAL_CAN_ActivateNotification(): activa las notificaciones de interrupción deseadas (mensaje pendiente, FIFO lleno, eror, etc.).
  • HAL_CAN_AddTxMessage(): encola un mensaje para su transmisión.
  • HAL_CAN_GetRxMessage(): extrae un mensaje recibido del FIFO indicado.
  • HAL_CAN_GetRxFifoFillLevel(): indica cuántos mensajes hay pendientes en el FIFO.
  • HAL_CAN_GetTxMailboxLevel(): devuelve el número de buzones de transmisión disponibles.
  • HAL_CAN_GetError(): lee el estado de error del controlador.

Configuración de filtros:

CAN_FilterTypeDef filtro;

filtro.FilterBank = 0;
filtro.FilterMode = CAN_FILTERMODE_IDMASK;
filtro.FilterScale = CAN_FILTERSCALE_32BIT;
filtro.FilterIdHigh = 0x0000;
filtro.FilterIdLow = 0x0000;
filtro.FilterMaskIdHigh = 0x0000;
filtro.FilterMaskIdLow = 0x0000;
filtro.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filtro.FilterActivation = ENABLE;

HAL_CAN_ConfigFilter(&hcan_x, &filtro);

Comunicación UART

Función Modo de operación
HAL_UART_Receive() Bloqueatne con timeout.
HAL_UART_Receive_IT() Por interrupción.
HAL_UART_Receive_DMA() Por DMA.
HAL_UART_Transmit() Transmisión bloqueante.

Antes de usar una UART es necesario habilitar su reloj:

__HAL_RCC_USART2_CLK_ENABLE();

Para habilitar interrupciones específicas y consultar flags se usan:

__HAL_UART_ENABLE_IT(&huart_debug, UART_IT_RXNE);

if (__HAL_UART_GET_FLAG(&huart_debug, UART_FLAG_RXNE))
{
    // Dato disponible en el buffer de recepción
}

Para recepción por DMA con detección de línea inactiva se emplea:

HAL_UARTEx_ReceiveToIdle_DMA(&huart_debug, buffer_rx, TAM_BUFFER);

Comunicación I2C

La HAL ofrece funciones tanto para maestro como para esclavo:

  • HAL_I2C_Master_Transmit(): envío desde maestro.
  • HAL_I2C_Master_Receive(): recepción como maestro.
  • HAL_I2C_Slave_Transmit(): envío como esclavo.
  • HAL_I2C_Mem_Write(): escritura en un registro/dispositivo con dirección interna.
  • HAL_I2C_Mem_Read(): lectura desde un registro/dispositivo con dirección interna.
uint8_t datos_tx[] = {0xAA, 0x55};
uint8_t datos_rx[4];

HAL_I2C_Master_Transmit(&hi2c_bus, 0xA0, datos_tx, 2, 100);
HAL_I2C_Master_Receive(&hi2c_bus, 0xA0, datos_rx, 4, 100);

Comunicación SPI

Para transferencias full-duplex simultáneas:

uint8_t tx[] = {0x01, 0x02};
uint8_t rx[2];

HAL_SPI_TransmitReceive(&hspi_x, tx, rx, sizeof(tx), HAL_MAX_DELAY);

Para envío simple:

HAL_SPI_Transmit(&hspi_x, tx, sizeof(tx), HAL_MAX_DELAY);

Transferencias DMA

HAL_DMA_Start() inicia una transferencia memoria-memoria o periférico-memoria.

HAL_DMA_Start(&hdma_x,
              (uint32_t)origen,
              (uint32_t)destino,
              sizeof(origen));

Para deshabilitar una interrupción DMA concreta o consultar flags:

__HAL_DMA_DISABLE_IT(&hdma_x, DMA_IT_TC);

if (__HAL_DMA_GET_FLAG(&hdma_x, DMA_FLAG_TC1))
{
    __HAL_DMA_CLEAR_FLAG(&hdma_x, DMA_FLAG_TC1);
}

Gestión de interrupciones

Habilitación de una IRQ:

HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

El handler de la USART1 se declara como:

void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&huart1);
}

Control global de interrupciones en Cortex-M:

__set_PRIMASK(1); // Deshabilita todas las interrupciones
__set_PRIMASK(0); // Habilita interrupciones

__set_CONTROL(0x02); // Modo no privilegiado + process stack
__set_CONTROL(0x00); // Modo privilegiado + main stack

FSMC y AFIO

En algunos dispositivos STM32 se dispone de la macro __HAL_AFIO_FSMCNADV_DISCONNECTD(), que desconecta la señal NADV del FSMC para permitir que el pin se reasigne a otra función.

void configurar_fsmc(void)
{
    // Configuración previa del FSMC...
    __HAL_AFIO_FSMCNADV_DISCONNECTD();
    // Resto de configuración de GPIO
}

Etiquetas: STM32 HAL GPIO TIM CAN

Publicado el 7-1 21:41