Skip to main content
Visitor II
July 31, 2024
Question

USART TX DMA on STM32G031

  • July 31, 2024
  • 1 reply
  • 888 views

Hello I need to send a string via USART every 500ms. I tried to use DMA that seems to be easy to configure. USART transfer works but only without DMA (test in while main cycle). I do not see any problem in DMA configuration. Can you tell me it there is something that I do not set?

I start DMA transfer in Systick cycle. 

Thank you 

 

 

#include "main.h"
#include "stm32g0xx.h"
#include <stdint.h>


#define BAUD_RATE 9600
#define SYS_CLK 64000000

const char message[] = "Hello, world!\r\n";

void DMA_Init(void) {
 // Enable DMA clock
 RCC->AHBENR |= RCC_AHBENR_DMA1EN;

 // Configure DMA for USART1 TX (DMA1 Channel 1)
 DMA1_Channel1->CPAR = (uint32_t)&USART1->TDR;
 DMA1_Channel1->CMAR = (uint32_t)message;
 DMA1_Channel1->CNDTR = sizeof(message) - 1;
 DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;

 // Enable DMA1 interrupt
 NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

void USART1_Init(void) {

 // Enable clocks
 RCC->IOPENR |= RCC_IOPENR_GPIOBEN; // Enable GPIOB clock
 RCC->APBENR2 |= RCC_APBENR2_USART1EN; // Enable USART1 clock

 // Configure PB6 as USART1_TX and PB7 as USART1_RX
 GPIOB->MODER &= ~(GPIO_MODER_MODE6_Msk | GPIO_MODER_MODE7_Msk);
 GPIOB->MODER |= (0x2 << GPIO_MODER_MODE6_Pos) | (0x2 << GPIO_MODER_MODE7_Pos); // Set PB6 and PB7 to AF mode
 GPIOB->AFR[0] |= (0x0 << GPIO_AFRL_AFSEL6_Pos) | (0x0 << GPIO_AFRL_AFSEL7_Pos); // Set AF0 (USART1)

 // Configure USART1
 USART1->BRR = SYS_CLK / BAUD_RATE; // Set baud rate
 USART1->CR3 |= USART_CR3_DMAT; // Enable DMA for transmitter - TOTO JE NAVIC
 USART1->CR1 |= USART_CR1_TE; // Enable transmitter
 USART1->CR1 |= USART_CR1_UE; // Enable USART

}

void DMA1_Channel1_IRQHandler(void) {

 if (DMA1->ISR & DMA_ISR_TCIF1) {
 // Clear the transfer complete interrupt flag
 DMA1->IFCR |= DMA_IFCR_CTCIF1;

 // Optional: Disable DMA channel if needed, and re-enable if you need to prepare for the next transfer
 DMA1_Channel1->CCR &= ~DMA_CCR_EN; // Disable DMA channel
 DMA1_Channel1->CNDTR = sizeof(message) - 1;//1; // Reset number of data to transfer
 DMA1_Channel1->CCR |= DMA_CCR_EN; // Re-enable DMA channel
 }

}



void SysTick_Handler(void)
{
 SysTickCounter++;
 if (SysTickCounter>500) {
 	DMA1_Channel1->CCR |= DMA_CCR_EN;	
 	SysTickCounter = 0;
 }
}


void USART1_SendString(const char *str) {
 while (*str) {
 while (!(USART1->ISR & USART_ISR_TXE_TXFNF_Msk)); // Wait until TX buffer is empty
 USART1->TDR = *str++; // Transmit character
 }
}


int main(void)
{
 CLK_Config();
 GPIO_Init();
 SysTick_Init();

 DMA_Init();
 USART1_Init();


 while (1)
 {
 
 //USART1_SendString(message); - THIS WORKS, BUT NOT WITH DMA
 //for (volatile int i = 0; i < 1000000; i++);

 }

}

 

    This topic has been closed for replies.

    1 reply

    Super User
    August 1, 2024

    If your DMA target is a peripheral register, the peripheral (UART) determines the speed of the DMA transfer not to overrrun TDR. DMAMUX is used for that  like so

    DMAMUX1_Channel0->CCR = ?? << DMAMUX_CxCR_DMAREQ_ID_Pos;

     (code was for another chip, check ?? for the request ID in the ref.man.)  The line

    USART1->CR3 |= USART_CR3_DMAT;

    will trigger the very first byte transmitted, so it should be done last, when everthing else is setup.

    Some DMA registers cannto be set when DMA is enabled.  

     

    Here is my code for the other chip for comparison:

    #include <stm32c011xx.h>
    void mem2uart_dma() {
     static char src[64] = "The quick brown fox jumped over the lazy dog.\n";
    
     USART1->TDR = '!'; // send one char for connection testing (without DMA))
     while (!(USART1->ISR & USART_ISR_TXE_TXFNF)); // busy wait for TDR empty
    
     RCC->AHBENR |= RCC_AHBENR_DMA1EN; // this is good for DMA and DMAMUX
     (void)RCC->AHBENR;
    
     if( DMA1_Channel1->CCR & DMA_CCR_EN) { // channel was in use before
     while(!(DMA1->ISR & DMA_ISR_TCIF1)); // wait for transfer complete (TC) channel flag
     DMA1_Channel1->CCR &= ~DMA_CCR_EN; // disable DMA channel for setup
     }
    
     // route peripheral DMA request to DMA channel
     // Table 34: DMAMUX usart1_tx_dma == 51
     // caution: DMAMUX1_Channel0 is for DMA1_Channel1 and so on!
     DMAMUX1_Channel0->CCR = 51 << DMAMUX_CxCR_DMAREQ_ID_Pos;
     
     DMA1->IFCR = DMA_IFCR_CGIF1; // clear all (HT, TC, TE) flags for DMA channel 1
    
     DMA1_Channel1->CPAR = (uint32_t)&(USART1->TDR);
     DMA1_Channel1->CMAR = (uint32_t)src;
     DMA1_Channel1->CNDTR = sizeof(src);
     DMA1_Channel1->CCR = 
     0 << DMA_CCR_MEM2MEM_Pos // MEM2MEM 0: no memory-to-memory mode
     | 0 << DMA_CCR_PL_Pos // PL priority level 0: low.. 3: very high
     | 0 << DMA_CCR_MSIZE_Pos // MSIZE 0: 8-bit 1: 16-bit 2: 32-bit
     | 0 << DMA_CCR_PSIZE_Pos // PSIZE 0: 8-bit 1: 16-bit 2: 32-bit
     | 1 << DMA_CCR_MINC_Pos // MINC memory increment mode on (1)
     | 0 << DMA_CCR_PINC_Pos // PINC peripheral increment mode off (0)
     | 0 << DMA_CCR_CIRC_Pos // CIRC 1: circular mode
     | 1 << DMA_CCR_DIR_Pos // DIR 0: read from peripheral, 1: memory
     | 0 << DMA_CCR_TEIE_Pos // TEIE transfer error interrupt 1: enable
     | 0 << DMA_CCR_HTIE_Pos // HTIE half transfer interrupt 1: enable
     | 0 << DMA_CCR_TCIE_Pos // TCIE transfer complete interrupt 1: enable
     ;
     DMA1_Channel1->CCR |= DMA_CCR_EN; // enable DMA channel
    
     // A channel, as soon as enabled, may serve any DMA request from the peripheral 
     // connected to this channel, or may start a memory-to-memory block transfer.
    
     USART1->CR3 |= USART_CR3_DMAT; // trigger usart1_tx_dma request
    }

     hth

    KnarfB