Transmisión UART STM32F04 con interrupciones

He estado creando prototipos de mi proyecto con el STM32F103 "Bluepill" y para el producto final quiero pasar al STM32F042F6 que tiene una huella TSSOP-20 más pequeña. Necesito usar UART en mis comunicaciones con el dispositivo, así que traté de crear un programa de transmisión UART simple para verificar que el periférico UART funcionaba correctamente. Aquí está mi código.

#include "stm32f0xx.h"
#include "stm32f0xx_hal.h"
#include "stm32f0xx_hal_conf.h"

void Startup_Sequence(void);
void Error_Handler(void);
void SystemClock_Config(void);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle);

UART_HandleTypeDef UartHandle;
__IO ITStatus UartReady = RESET;

int main(void)
{
    HAL_Init();
    //SystemInit();
    SystemClock_Config();

    GPIO_InitTypeDef  GPIO_InitStruct;
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_USART1_CLK_ENABLE();

   // Setup LED Pin
   GPIO_InitStruct.Pin = GPIO_PIN_4;
   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; 
   GPIO_InitStruct.Pull = GPIO_NOPULL;
   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

   // Setup UART Tx Pin
  GPIO_InitStruct.Pin = GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  // Setup UART Rx pin
  GPIO_InitStruct.Pin = GPIO_PIN_10;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  //Setup UART Instance
  UartHandle.Instance = USART1;
  UartHandle.Init.BaudRate = 9600;
  UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
  UartHandle.Init.StopBits = UART_STOPBITS_1;
  UartHandle.Init.Parity = UART_PARITY_NONE;
  UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  UartHandle.Init.Mode = UART_MODE_TX_RX;
  UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
  UartHandle.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

  //Error handling
  if(HAL_UART_Init(&UartHandle) != HAL_OK) {
      Error_Handler();
  }

  Startup_Sequence();

  HAL_NVIC_SetPriority(USART1_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(USART1_IRQn);  

  char hello[6] = "hello\n";

  while(1) {
      if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t *)hello, 6) != HAL_OK) {
          Error_Handler();
      }     
  }
}

void Startup_Sequence(void) {
    int i;
    for (i=1; i<50;i++) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_4);
        HAL_Delay((1.0/i) * 1000);
    }
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
}

void Error_Handler(void) {
    while(1) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_4);
        HAL_Delay(1000);
    }
}

/**
  * Associates the interrupt handler with the UartHandle
 */
void USART1_IRQHandler(void) {
    HAL_UART_IRQHandler(&UartHandle);
}

/**
 * This function is called when transmitting
 * @param UartHandle [Pointer to UartHandle]
 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle) {
    UartReady = SET;
}

/**
 * This function is called when receiving
 * @param UartHandle [Pointer to UartHandle]
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) {
    UartReady = SET;
}

void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
    RCC_PeriphCLKInitTypeDef PeriphClkInit;

    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
    RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_ClkInitStruct.ClockType = 
    RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                           |RCC_CLOCKTYPE_PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI48;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != 
    HAL_OK)
    {
        Error_Handler();
    }

    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
    PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
        Error_Handler();
    }

    /**Configure the Systick interrupt time 
    */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

    /* SysTick_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
} 

El periférico UART se inicializa correctamente, porque el controlador de errores no se llama hasta después de que se haya completado Startup_Sequence(). Entonces, ¿por qué se llama a ErrorHandler cuando intento transmitir? He intentado enviar usando el modo de bloqueo, pero solo se agota el tiempo de espera cada vez.

EDITAR: he rastreado el error hasta la función de inicialización. Aquí está el registro de GDB https://hastebin.com/xopohaniyu.coffeescript pero no puedo encontrar ningún lugar donde no se devuelva HAL_OK.

¿Hay algún problema con mi configuración del periférico UART? ¿O hay un problema con el reloj del sistema? Por favor avise.

¿Por qué estás haciendo innecesariamente matemáticas de punto flotante en tu cálculo de retraso? Simplemente divide 1000 por el divisor entero. De manera más general, un problema con su código es que no está claro qué se copia de alguna fuente y qué es original. Probablemente debería tratar de hacer que algún fragmento de código existente funcione sin modificaciones antes de intentar modificaciones, de modo que tenga una mejor idea de dónde podrían originarse los problemas subsiguientes. También asegúrese de que su compilación sea exclusivamente para la serie STM32F0 y no incluya ningún archivo que pertenezca a la serie STM32F1 bastante diferente que utilizó anteriormente.
Finalmente, interrumpa el controlador de errores y luego haga un seguimiento para ver cómo llegó allí: su registro de depuración no incluye la falla real, por lo que no es muy útil.

Respuestas (1)

La configuración de GPIO es un poco más complicada y flexible en el F0. Aquí también debe establecer GPIO_InitStruct.Alternate = GPIO_AF1_USART1, de lo contrario obtendrá algún valor aleatorio, porque es una variable automática unificada. Habilite las advertencias en el compilador y preste atención a ellas, están ahí por una razón.

HAL_UART_Transmit_IT()se vuelve a llamar antes de que haya terminado la transmisión anterior. Debe esperar hasta que UartReadyse establezca la bandera y restablecerla antes de repetir.