Saltar al contenido

Controlador STM32F4 UART HAL

La guía paso a paso o código que verás en este artículo es la resolución más fácil y efectiva que encontramos a esta inquietud o dilema.

Solución:

Decidí ir con DMA para que la recepción funcionara. Estoy usando un búfer circular de 1 byte para manejar los datos tal como están escritos en el terminal serial del transmisor. Aquí está mi código final (solo la parte de recepción, más información sobre transmisión en la parte inferior).

Algunas definiciones y variables:

#define BAUDRATE              9600
#define TXPIN                 GPIO_PIN_6
#define RXPIN                 GPIO_PIN_7
#define DATAPORT              GPIOB
#define UART_PRIORITY         6
#define UART_RX_SUBPRIORITY   0
#define MAXCLISTRING          100 // Biggest string the user will type

uint8_t rxBuffer = '00'; // where we store that one character that just came in
uint8_t rxString[MAXCLISTRING]; // where we build our string from characters coming in
int rxindex = 0; // index for going though rxString

Configurar IO:

__GPIOB_CLK_ENABLE();
__USART1_CLK_ENABLE();
__DMA2_CLK_ENABLE();

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Pin = TXPIN | RXPIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(DATAPORT, &GPIO_InitStruct);

Configure la UART:

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;

huart1.Instance = USART1;
huart1.Init.BaudRate = BAUDRATE;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);

Configurar DMA:

extern DMA_HandleTypeDef hdma_usart1_rx; // assuming this is in a different file

hdma_usart1_rx.Instance = DMA2_Stream2;
hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_rx.Init.MemInc = DMA_MINC_DISABLE;
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_usart1_rx);

__HAL_LINKDMA(huart, hdmarx, hdma_usart1_rx);

HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, UART_PRIORITY, UART_RX_SUBPRIORITY);
HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);

Configure la interrupción DMA:

extern DMA_HandleTypeDef hdma_usart1_rx;

void DMA2_Stream2_IRQHandler(void)

    HAL_NVIC_ClearPendingIRQ(DMA2_Stream2_IRQn);
    HAL_DMA_IRQHandler(&hdma_usart1_rx);

Iniciar DMA:

__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Receive_DMA(&huart1, &rxBuffer, 1);

DMA recibe devolución de llamada:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
 rxBuffer == 'r') // If Enter
    
        executeSerialCommand(rxString);
        rxString[rxindex] = 0;
        rxindex = 0;
        for (i = 0; i < MAXCLISTRING; i++) rxString[i] = 0; // Clear the string buffer
    

    else
    
        rxString[rxindex] = rxBuffer; // Add that character to the string
        rxindex++;
        if (rxindex > MAXCLISTRING) // User typing too much, we can't have commands that big
        
            rxindex = 0;
            for (i = 0; i < MAXCLISTRING; i++) rxString[i] = 0; // Clear the string buffer
            print("rnConsole> ");
        
    

Eso es prácticamente todo el código para recibir caracteres y crear un string (carbonizarse array) que muestra lo que ha introducido el usuario. Si el usuario pulsa la tecla de retroceso o del, el último carácter de la array se sobrescribe y si presionan enter, ese array se envía a otra función y se procesa como un comando.

Para ver cómo funciona el comando de análisis y transmisión de código, vea mi proyecto aquí

¡Gracias a @Flip y @Dormen por sus sugerencias!

Recibir datos mientras el Registro de datos (DR) está lleno resultará en un error de saturación. El problema es que la función UART_Receive_IT(UART_HandleTypeDef*) dejará de leer el registro DR una vez que haya recibido suficientes datos. Cualquier dato nuevo provocará el error de saturación.

Lo que hice fue utilizar una estructura de recepción circular DMA. A continuación, puede utilizar currentPosInBuffer - uart->hdmarx->Instance->NDTR para determinar cuántos datos se recibieron que aún no ha procesado.

Es un poco más complicado porque, si bien el DMA realiza el almacenamiento en búfer circular, debe implementar manualmente el bucle de retorno al principio si pasa del final del búfer.

También encontré una falla en la que el controlador dice que ha transferido los datos (es decir, NDTR ha disminuido) pero los datos aún no están en el búfer. Puede ser un problema de contención de acceso al bus / DMA, pero es molesto.

Los controladores STM32 UART son un poco inestables. La única forma en que funcionan de inmediato es si conoce la cantidad exacta de caracteres que va a recibir. Si desea recibir una cantidad no especificada de caracteres, hay un par de soluciones que encontré y probé:

  1. Establezca la cantidad de caracteres a recibir en 1 y construya una string. Esto funciona pero tiene problemas al recibir datos muy rápido, porque cada vez que el controlador lee el rxBuffer desactiva la interrupción, por lo que se pueden perder algunos caracteres.

  2. Establezca la cantidad de caracteres que se recibirán con el mayor tamaño de mensaje posible e implemente un tiempo de espera, después del cual se leerá todo el mensaje.

  3. Escriba su propia función UART_Receive_IT, que escribe directamente en un búfer circular. Esto es más trabajo, pero es lo que encontré que funciona mejor al final. Sin embargo, debe cambiar algunos de los controladores hal, por lo que el código es menos portátil.

Otra forma es usar DMA como sugirió @Flip.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *