DMA in the IDE, Part III

STM32 DMA UART Transmit M2P Demo

This is the third example of DMA usage on the STM32F405 Feather board. Programming is via the Arduino IDE. This example demonstrates the memory to peripheral (M2P) DMA Mode. We send data from memory to the UART via DMA.

For this demo to work, the Arduino serial code needs to be disabled. The instructions are included in the code comments. Also, you will note some code inside the main loop function. The purpose of this code is to reset the transmit registers so the MCU acknowledges the transmission is complete. This code would not be necessary except the Arduino STM32 code uses the USART1_IRQHandler and therefore doesn’t reset after the the UART1 transmit.

Make a serial connection to the SCL pin (PB6). Every press of the button sends a paragraph of text to the serial terminal.

The Code

// DMA USART2 transfer M2P memory to peripheral.
// Adafruit STM32F405 Feather.
//
// Add build_opt.h file with -DHAL_UART_MODULE_ENABLED
// Tools > U(S)ART Support: Disabled.
//
// SCL (PB6) TX pin.
// 10 (PB9) Button pin.
//

uint8_t data_stream[] =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure \
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt \
mollit anim id est laborum.\r\n";

uint32_t data_length{sizeof(data_stream)};

uint32_t deBounce{0};
uint32_t lastBounce{0};
constexpr uint32_t DEBOUNCE_INTERVAL{50};

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_tx;

extern "C" void DMA2_Stream7_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_tx); }

void uartInit(void) {
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 9600;
  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;

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  
  __HAL_RCC_USART1_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  
  // USART1 GPIO Configuration    
  // PB6 --> USART1_TX
  // PB7 --> USART1_RX 
  GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  hdma_usart1_tx.Instance = DMA2_Stream7;
  hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;
  hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_usart1_tx.Init.Mode = DMA_NORMAL;
  hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
  hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
      while(1);
      
  __HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);

  if (HAL_UART_Init(&huart1) != HAL_OK)
    while(1);
}

void dmaInit(void) {
  __HAL_RCC_DMA2_CLK_ENABLE();
  HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}

void buttonISR() {
  if (millis() > deBounce) {
    deBounce = millis() + DEBOUNCE_INTERVAL;
    HAL_UART_Transmit_DMA(&huart1, data_stream, (uint16_t)data_length);
  }
}

void setup(void) {
  dmaInit();
  uartInit();
  pinMode(10, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PB9), buttonISR, FALLING);
}

void loop() {
  if (deBounce != lastBounce) {
    lastBounce = deBounce;
    // UART in mode Transmision end?
    while ( !(huart1.Instance->SR & USART_SR_TC) && 
            !(huart1.Instance->CR1 & USART_CR1_TCIE) ) ;
    // Disable TXEIE and TCIE interrupts. 
    huart1.Instance->CR1 &= ~(USART_CR1_TXEIE | USART_CR1_TCIE);
    // At end of Tx process, restore huart->gState to ready. 
    huart1.gState = HAL_UART_STATE_READY;
 } 
}

About Jim Eli

µC experimenter
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s