web-dev-qa-db-fra.com

STM32: implémentation UART dans DMA mode

J'essaie d'implémenter UART en mode DMA pour transmettre une chaîne simple chaque fois que vous appuyez sur un bouton-poussoir.

Donc, j'ai utilisé CubeMX pour générer le code et j'ai configuré UART2 TX DMA en mode normal (non circulaire) et également pas de FIFO ni de rafale.

Chaque fois que j'exécute le code en mode débogage, je vois que la première fois que je tente d'envoyer la chaîne, cela fonctionne correctement et envoie la chaîne, mais dans le gestionnaire d'IRQ DMA, il appelle TxHalfCpltCallback et non TxCpltCallback et aussi UART gState restera en mode BUSY, je ne peux donc pas l'utiliser pour ne plus transmettre de chaîne.

Ma question est pourquoi il appelle TxHalfCpltCallback et non TxCpltCallback? et comment dois-je le gérer (car la référence HAL indique qu'il attend l'envoi de la seconde moitié de la mémoire tampon! quoi?)

Et aussi, est-ce que l'envoi de la prochaine moitié de données effacerait le gState de UART?

J'aimerais demander à quelqu'un de nous donner un exemple de configuration de UART dans un projet.

3
Nixmd

Si vous utilisez DMA, vous aurez alors deux interruptions: une lorsque la moitié du tampon sera transmise et une autre lorsque la seconde moitié sera transmise (entière).

Les deux devraient être renvoyés si tout va bien. La raison derrière cela est que lors de l'envoi d'une grande quantité de données, vous pouvez commencer à charger de nouvelles données dans la première moitié du tampon dans la variable TxHalfCpltCallback pendant que la seconde moitié du tampon est transmise par le DMA. Et encore une fois, vous pouvez charger les nouvelles données dans la seconde moitié de la mémoire tampon dans la TxCpltCallback pendant la transmission de la première moitié.

L'avantage est que vous n'avez pas besoin d'attendre la fin de la transmission pour copier le bloc de données suivant dans la mémoire tampon, mais vous pouvez déjà commencer à le charger pendant que la transmission est en cours.

Voici un exemple:

Dans cet exemple, 2000 octets seront transférés à l'aide de DMA, Transmit Half Complete et Transmit Complete interrompront pour obtenir les meilleures performances.

La première moitié du tampon de transmission est chargée de nouvelles données par la CPU dans le rappel d'interruption Transmit Half Complete, tandis que la seconde moitié du tampon est transmise par le DMA en arrière-plan.

Ensuite, dans Transmit Complete, la seconde moitié du tampon de transmission est chargée par les nouvelles données par la CPU, tandis que la première moitié (précédemment mise à jour) est transmise par le DMA en arrière-plan.

#include "stm32f4xx.h"

uint8_t dma_buffer[2000];
volatile uint8_t toggle = 0;

UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_tx;

void uart_gpio_init()
{
  GPIO_InitTypeDef GPIO_InitStruct;

  __GPIOA_CLK_ENABLE();

  /**USART2 GPIO Configuration
  PA2     ------> USART2_TX
  PA3     ------> USART2_RX
  */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void uart_dma_init()
{
  /* DMA controller clock enable */
  __DMA1_CLK_ENABLE();

  /* Peripheral DMA init*/
  hdma_usart2_tx.Instance = DMA1_Stream6;
  hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4;
  hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_usart2_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_usart2_tx.Init.Mode = DMA_NORMAL;
  hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
  hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  HAL_DMA_Init(&hdma_usart2_tx);

  __HAL_LINKDMA(&huart2,hdmatx,hdma_usart2_tx);

  /* DMA interrupt init */
  HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
}

void uart_init()
{
  __USART2_CLK_ENABLE();

  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  HAL_UART_Init(&huart2);

  /* Peripheral interrupt init*/
  HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(USART2_IRQn);
}

/* This function handles DMA1 stream6 global interrupt. */
void DMA1_Stream6_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_usart2_tx);
}

void USART2_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart2);
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  uint16_t i;
  toggle = !toggle;

  for(i = 1000; i < 1998; i++)
  {
    if(toggle)
      dma_buffer[i] = '&';
    else
      dma_buffer[i] = 'z';
  }

  dma_buffer[1998] = '\r';
  dma_buffer[1999] = '\n';
}

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
  uint16_t i;

  for(i = 0; i < 1000; i++)
  {
    if(toggle)
      dma_buffer[i] = 'y';
    else
      dma_buffer[i] = '|';
  }
}

int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  uart_gpio_init();
  uart_dma_init();
  uart_init();

  uint16_t i;

  for(i = 0; i < 1998; i++)
  {
    dma_buffer[i] = 'x';
  }

  dma_buffer[1998] = '\r';
  dma_buffer[1999] = '\n';

  while(1)
  {
    HAL_UART_Transmit_DMA(&huart2, dma_buffer, 2000);
  }
}

L'exemple a été écrit pour une carte STM32F4 Discovery (STM32F407VG). L'instance DMA appropriée, le canal UART-DMA, le GPIO et les autres paramètres de fonction doivent être modifiés en fonction du microcontrôleur STM32 utilisé.

8
Bence Kaulics

Votre problème a le même aspect que https://community.st.com/thread/40080-dma-uart-with-hal-remain-busy-bug

Vous devez activer HAL_UART_IRQHandler () 

i.e . à l'intérieur de "main.c" (ou où que vous initialisiez votre matériel), ajoutez:

HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);                                        
HAL_NVIC_EnableIRQ(USART2_IRQn);

à l'intérieur de "stm32f4xx_it.c":

void USART2_IRQHandler(void)
{ 
  HAL_UART_IRQHandler(&huart2);
}
2
Jonas Kibildis

Pour moi, je recevais une erreur de transmission lorsque j'utilisais le DMA. Le problème a été résolu en activant l'interruption TXE:

void sas_write(char* buf, uint16_t size)
{
    HAL_UART_Transmit_DMA(&uart_handle, buf, size) ;
    __HAL_UART_ENABLE_IT(&uart_handle, UART_IT_TXE) ;
}
0
user3575270

Si vous utilisez la fonction 

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 

dans la bibliothèque CubeMX, cela activera toutes les interruptions DMA. Vous pouvez désactiver l'interruption de demi-transfert en effaçant le bit HTIE dans le registre DMA_SxCR

0
Jackson