Problema de DMA de transmisión STM32 HAL UART

Después de configurar mi proyecto para una placa STM32F7 personalizada que incluye un convertidor FT2232H UART<->USB, tuve varios problemas al enviar (y recibir datos). El código que uso es mayormente generado por CubeMX y está al final de la publicación.

En primer lugar, no puedo hacer que el stm transmita a velocidades en baudios más altas que el estándar 115200, según las hojas de datos, tanto el FT2232H como el STM32F7 deberían ser capaces de transmitir al menos 12M baudios. Para el FT2232H funciona, ya que envío algunos caracteres desde mi terminal (lado USB) y recuperé el carácter cuando acorté los pines RX y TX en el lado de salida del FT2232H.

En segundo lugar, no puedo llamar a la función sendUART() varias veces seguidas, ¿por qué no se usa DMA Fifo para almacenar las cosas que quiero enviar?

Además, ¿cuál es la forma correcta de recuperar todos los datos recibidos pero hacer uso del fifo para que no se pierdan datos cuando no se sondean a tiempo?

Tal vez estas son preguntas tontas, pero ya traté de encontrar una solución aquí y en el resto de Internet, pero no puedo encontrar ninguna.

void MX_UART4_Init(void)
{

  huart4.Instance = UART4;
  huart4.Init.BaudRate = 115200;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart4) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(uartHandle->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspInit 0 */

  /* USER CODE END UART4_MspInit 0 */
    /* UART4 clock enable */
    __HAL_RCC_UART4_CLK_ENABLE();

    /**UART4 GPIO Configuration    
    PA0/WKUP     ------> UART4_TX
    PA1     ------> UART4_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* UART4 DMA Init */
    /* UART4_TX Init */
    hdma_uart4_tx.Instance = DMA1_Stream4;
    hdma_uart4_tx.Init.Channel = DMA_CHANNEL_4;
    hdma_uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart4_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart4_tx.Init.Mode = DMA_NORMAL;
    hdma_uart4_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_uart4_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_uart4_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_uart4_tx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_uart4_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_uart4_tx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_uart4_tx);

    /* UART4_RX Init */
    hdma_uart4_rx.Instance = DMA1_Stream2;
    hdma_uart4_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart4_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart4_rx.Init.Mode = DMA_NORMAL;
    hdma_uart4_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_uart4_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_uart4_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_uart4_rx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_uart4_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_uart4_rx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_uart4_rx);

  /* USER CODE BEGIN UART4_MspInit 1 */

  /* USER CODE END UART4_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspDeInit 0 */

  /* USER CODE END UART4_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_UART4_CLK_DISABLE();

    /**UART4 GPIO Configuration    
    PA0/WKUP     ------> UART4_TX
    PA1     ------> UART4_RX 
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1);

    /* UART4 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmatx);
    HAL_DMA_DeInit(uartHandle->hdmarx);
  /* USER CODE BEGIN UART4_MspDeInit 1 */

  /* USER CODE END UART4_MspDeInit 1 */
  }
} 

/* USER CODE BEGIN 1 */
void sendUART(char msg[]){
    //HAL_UART_Transmit(&huart4,(uint8_t *) msg, strlen(msg),10000);
    HAL_UART_Transmit_DMA(&huart4,(uint8_t *) msg, strlen(msg));
}

void echo(){
    if(HAL_UART_Receive_DMA(&huart4, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK){}
    else if(HAL_UART_Transmit_DMA(&huart4, (uint8_t*)aRxBuffer, RXBUFFERSIZE)!= HAL_OK){
    }
}

Respuestas (1)

En primer lugar, no puedo hacer que el stm transmita a velocidades en baudios más altas que el estándar 115200, según las hojas de datos, tanto el FT2232H como el STM32F7 deberían ser capaces de transmitir al menos 12M baudios.

El hardware admite velocidades de hasta 27 Mbit (bueno, no ha dicho su número de pieza, estoy mirando la hoja de datos F756), pero según , stm32f7xx_hal_uart.hHAL no aceptará una velocidad superior a 9M

#define IS_UART_BAUDRATE(BAUDRATE) ((BAUDRATE) < 9000001)

Además, depende de la velocidad del reloj del sistema, en la configuración predeterminada, cuando no toca la pestaña Configuración del reloj en STM32CubeMX, todo funciona en el reloj HSI interno de 16 MHz. Eso significa como máximo 1 Mbit mientras usa UART_OVERSAMPLING_16, o el doble si cambia a UART_OVERSAMPLING_8(pero luego perdería la detección de ruido).

En segundo lugar, no puedo llamar a la función sendUART() varias veces seguidas, ¿por qué no se usa DMA Fifo para almacenar las cosas que quiero enviar?

Aunque hay un DMA FIFO de 16 bytes, el software no puede acceder a él. No hay forma de simplemente agregar algunos datos más a una transferencia DMA en curso. HAL no hace nada más que iniciar una transferencia DMA desde la dirección de búfer proporcionada por la persona que llama.

Debe esperar hasta que finalice la transferencia, o suspender DMA, y aún esperar hasta que el FIFO esté vacío. Por supuesto, puede asignar un búfer usted mismo, agregar datos a medida que llegan y reiniciar DMA cada vez que finaliza y hay nuevos datos en el búfer.

Además, ¿cuál es la forma correcta de recuperar todos los datos recibidos pero hacer uso del fifo para que no se pierdan datos cuando no se sondean a tiempo?

Me parece que no puede tener DMA e interrupciones en cada carácter recibido. Al menos, el ISRvalor del registro de estado sería inútil y el controlador de interrupciones no podrá decidir qué hacer. Leerlo podría incluso interferir con la transferencia DMA. Por lo tanto, debe elegir uno.

Usando DMA para colocar los datos en un búfer (o dos), puede sondear el contador de transferencia regularmente en el bucle inactivo o en una interrupción del temporizador. No habrá una respuesta inmediata, pero tal vez no importe en absoluto, porque la interfaz USB también sufrirá algún retraso.

Gracias por la respuesta. Configuré los relojes y el UART se ejecuta en SYSCLK de 200 MHz, por lo que, según CubeMX, debería haber un máximo teórico. Velocidad de transmisión de 12,5 M con sobremuestreo establecido en 16 muestras.
Si configura la fuente de reloj UART en SYSCLK de 200MHz (¿Dónde lo hace? No está en el código anterior), entonces obtendrá 12.5M, correcto, pero eso no es exactamente 12M, en lo que se ejecuta la parte FTDI. Hay una diferencia de 4.167%, junto con la imprecisión del oscilador HSI, puede funcionar, cuando todo lo demás es perfecto (el controlador no está demasiado caliente, las señales son nítidas y sin ruido), pero lo intentaría con 192 MHz para conseguir un mejor partido.