DMA with UART
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-13 11:31 AM
Hello,
I am trying to implement Modbus protocol in an efficient way. Untill now I have implemented everhting with UART interuot based Transmission and recption. Now I would like to implement with DMA to offload controller from much neavy lifting. For your refernce, I somhow managed with the below code t transfer the TX_Array from 0x00 to 0x09. I verified this in oscilloscope. But this aimed frame is appearing only once then only 0x00 is seen infinitely. even DE pin is not driven to low. Which indicateds that ISR is not being called and transfer is not stopped. Or somewhere it make the controoller to hang. Please let me know where I am makeing mistakes.
Please pardon me, I will come back again for RX implementation after fixing this issue. Thank you.
- Labels:
-
STM32F0 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-13 1:52 PM
Use the </> when posting code so it's properly formatted
TimerCallback tutorial! | UART and DMA Idle tutorial!
If you find my solution useful, please click the Accept as Solution so others see the solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-13 3:36 PM - edited 2025-05-13 3:36 PM
Here you can find even more examples of DMA use with UARTs:
https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx
Help with coding and code review is available here.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-13 9:52 PM
Hello Mr. Karl Yamashita,
Thank you for the hint. I missed that formatting option. I tried to provide minimum working code from my bigger project, but I did not prvide in readable format.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-13 9:58 PM
Hello Mr. Pavel. A, I am trying Baremetal coding without any LL or HAL. I am not an expert in Software/Firmware. I am from Automative Electronics Hardware architect by profession. The code I have given will directly work in STM32Cube IDE without much dependencies. Also the code almost works. I must e missing a small link. I dont want to change my structure of coding to LL/HAL Anyhow thank you for your helping hands.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-14 10:18 AM
I have provided my code with formatting.
I have moved my function to transmit the frames outside of while(1) loop and I probed the TX pins and DE pin. I have attached photos of my oscilloscope plot too with my comments.
Please let me know my mistakes.
Here I have provided abstract of my code. Please let me know what I miss.
int main(void)
{
Config_System_Clock();
Init_GPIOs();
USART1_Init();
Init_Tim14_1ms();
for (uint8_t i = 0; i < 10; ++i) { TX_Array[i] = i; }
MB_RTU_Transmit(TX_Array, 10);
while(1)
{
for (uint16_t temp = 0; temp < 1000; ++temp);
}
}
void USART1_Init(void)
{
// 1. Enable clocks for GPIOB, USART1, and DMA1
RCC->AHBENR |= RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMA1EN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// 2. --- Configure PB5 as DE output ---
GPIOB->MODER &= ~(3 << (5 * 2));
GPIOB->MODER |= (1 << (5 * 2)); // Output mode
GPIOB->OTYPER &= ~(1 << 5); // Push-pull
GPIOB->OSPEEDR |= (3 << (5 * 2)); // High speed
GPIOB->PUPDR &= ~(3 << (5 * 2)); // No pull
GPIOB->ODR &= ~(1 << 5); // Set DE LOW (receive mode)
// 3. --- Configure PB6 as USART1_TX (AF0) ---
GPIOB->MODER &= ~(3 << (6 * 2));
GPIOB->MODER |= (2 << (6 * 2)); // Alternate function
GPIOB->OTYPER &= ~(1 << 6); // Push-pull
GPIOB->OSPEEDR |= (3 << (6 * 2)); // High speed
GPIOB->PUPDR &= ~(3 << (6 * 2));
GPIOB->PUPDR |= (1 << (6 * 2)); // Pull-up
GPIOB->AFR[0] &= ~(0xF << (6 * 4));
GPIOB->AFR[0] |= (0x0 << (6 * 4)); // AF0 = USART1
// 4. --- Configure USART1 ---
USART1->CR1 &= ~USART_CR1_UE; // Disable USART1
USART1->BRR = Set_USART1_BRR; // Set baud rate
switch (Set_Parity)
{
case 0: // 8N2 (No parity)
USART1->CR1 &= ~USART_CR1_PCE;
USART1->CR1 &= ~USART_CR1_M;
USART1->CR2 &= ~USART_CR2_STOP;
USART1->CR2 |= USART_CR2_STOP_1 | USART_CR2_STOP_0; // 2 stop bits
break;
case 1: // 8E1 (Even parity)
USART1->CR1 |= USART_CR1_PCE;
USART1->CR1 &= ~USART_CR1_PS;
USART1->CR1 |= USART_CR1_M;
USART1->CR2 &= ~USART_CR2_STOP;
break;
case 2: // 8O1 (Odd parity)
USART1->CR1 |= USART_CR1_PCE;
USART1->CR1 |= USART_CR1_PS;
USART1->CR1 |= USART_CR1_M;
USART1->CR2 &= ~USART_CR2_STOP;
break;
}
USART1->CR3 |= USART_CR3_DMAT; // Enable DMA TX
USART1->CR1 |= USART_CR1_TE; // Enable Transmit
USART1->CR1 |= USART_CR1_UE; // Enable USART1
// 5. --- Configure DMA1 Channel 2 for USART1_TX ---
DMA1_Channel2->CCR &= ~DMA_CCR_EN; // Step 0: Disable channel first
DMA1_Channel2->CPAR = (uint32_t)&USART1->TDR; // Step 1: Peripheral address
// Steps 2 & 3 (CMAR and CNDTR) will be configured during DMA transfer start
DMA1_Channel2->CCR = 0; // Clear all settings
// Step 4: Configure CCR
DMA1_Channel2->CCR |= DMA_CCR_MINC // Increment memory address
| DMA_CCR_DIR // Read from memory to peripheral
| DMA_CCR_TCIE // Transfer complete interrupt
| DMA_CCR_PL_1; // High priority (PL = 2)
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); // Step 5: Enable interrupt for DMA
}
void MB_RTU_Transmit(volatile uint8_t *data, uint16_t length)
{
SET_DE;
DMA1_Channel2->CCR &= ~DMA_CCR_EN; // Step 0: Disable DMA
DMA1->IFCR |= DMA_IFCR_CTCIF2; // Step 1: Clear any pending interrupt flag
DMA1_Channel2->CMAR = (uint32_t)data; // Step 2: Set memory address
DMA1_Channel2->CNDTR = length; // Step 3: Set data length
// Step 4 is implicit if DMA config is done in init
DMA1_Channel2->CCR |= DMA_CCR_EN; // Step 5: Enable DMA to start transfer
}
void DMA1_Channel2_3_IRQHandler(void)
{
if (DMA1->ISR & DMA_ISR_TCIF2) // Check if transfer is complete
{
DMA1->IFCR |= DMA_IFCR_CTCIF2; // Clear Transfer Complete flag
DMA1_Channel2->CCR &= ~DMA_CCR_EN; // Disable DMA after transmission
CLEAR_DE; // Optionally reset DE pin (back to receive mode)
Switch_Reset_LED_ON;
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-14 11:38 AM
I experienced this with DMA and USBC. Make sure your configuration is proper. Don't redo the configuration from an interrupt or allow it to be interrupted and reconfigured 1/2 way through.
Also your buffer used by DMA should be volatile.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-14 11:49 AM
Hello Carl_G, as per my understanding, I commented this DMA1_Channel2->CCR &= ~DMA_CCR_EN; inside ISR.
Even then, ISR seems not exceuted. The LED which must be turned inside ISR in not executed. I already declared the arrays as volatile. Please help me. .
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-14 12:13 PM
You want it to just keep transmitting on its own? Then you need circular mode. Also don't disable it in the IRQ.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
2025-05-14 12:37 PM
I dont want to transmit continuously. It must transmit only for the length I pass (DMA1_Channel2->CNDTR = length).
As I said in my previous replay I commented this linne DMA1_Channel2->CCR &= ~DMA_CCR_EN; Even then it seems that ISR is not called. I could confirm this sice the LED commanded to switch ON is not executed. I am sorry I may have not understood what you have told.
