cancel
Showing results for 
Search instead for 
Did you mean: 

DMA with UART

Anesh
Associate III

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.

 
#include <stdint.h>
 
#include "stm32f030x8.h"
 
#define Set_USART1_BRR (40000000/9600) // 9600 Baudrate
#define Set_Parity 0x01 // EVen
 
#define Switch_Reset_LED_ON (GPIOB->BSRR = GPIO_BSRR_BS_10) // PB10 (Set pin)
#define Switch_Status_LED_ON (GPIOB->BSRR = GPIO_BSRR_BS_4) // PB4 (Set pin)
#define Switch_RXD_LED_ON (GPIOB->BSRR = GPIO_BSRR_BS_3) // PB3 (Set pin)
#define Switch_TXD_LED_ON (GPIOA->BSRR = GPIO_BSRR_BS_15) // PA15 (Set pin)
 
#define Switch_Reset_LED_OFF (GPIOB->BSRR = GPIO_BSRR_BR_10) // PB10 (Set pin)
#define Switch_Status_LED_OFF (GPIOB->BSRR = GPIO_BSRR_BR_4) // PB4 (Set pin)
#define Switch_RXD_LED_OFF (GPIOB->BSRR = GPIO_BSRR_BR_3) // PB3 (Set pin)
#define Switch_TXD_LED_OFF (GPIOA->BSRR = GPIO_BSRR_BR_15) // PA15 (Set pin)
 
#define CLEAR_DE (GPIOB->ODR &= ~(1 << 5));
#define SET_DE (GPIOB->ODR |= (1 << 5));
 
void Config_System_Clock(void);
void Init_GPIOs(void);
void USART1_Init(void);
void MB_RTU_Transmit(volatile uint8_t *data, uint16_t length);
void Init_Tim14_1ms(void);
void delay_ms(uint32_t ms);
 
volatile uint32_t ms_Ticks;
volatile uint8_t Tick_1ms;
volatile uint8_t TX_Array[10]={0}, RX_Array[10]={0};
 
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; }
 
while(1)
{
//Switch_Reset_LED_ON; delay_ms(500);
//Switch_Reset_LED_OFF; delay_ms(500);
 
//SET_DE; delay_ms(10);
MB_RTU_Transmit(TX_Array, 10);
//delay_ms(100);
//CLEAR_DE; delay_ms(10);
}
}
 
void Init_GPIOs(void)
{
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // Enable GPIOA clock
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // Enable GPIOB clock
RCC->AHBENR |= RCC_AHBENR_GPIOCEN; // Enable GPIOC clock
 
/* Settings for ONB_Reset_LED: PB10 */
GPIOB->MODER &= ~GPIO_MODER_MODER10;                // Clear mode for PB10
GPIOB->MODER |= GPIO_MODER_MODER10_0;              // Set as Output (01)
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_10;                // Push-pull (0)
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR10;          // Clear speed for PB10
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR10_0;        // Medium speed (01)
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR10;                // No pull-up, no pull-down (00)
 
/* Settings for ONB_Status_LED: PB4 */
GPIOB->MODER &= ~GPIO_MODER_MODER4;                // Clear mode for PB4
GPIOB->MODER |= GPIO_MODER_MODER4_0;              // Set as Output (01)
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_4;                // Push-pull (0)
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR4;          // Clear speed for PB4
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR4_0;        // Medium speed (01)
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR4;              // No pull-up, no pull-down (00)
 
/* Settings for ONB_RXD_LED: PB3 */
GPIOB->MODER &= ~GPIO_MODER_MODER3;                // Clear mode for PB3
GPIOB->MODER |= GPIO_MODER_MODER3_0;              // Set as Output (01)
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_3;                // Push-pull (0)
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR3;          // Clear speed for PB3
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR3_0;        // Medium speed (01)
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR3;                // No pull-up, no pull-down (00)
 
/* Settings for ONB_TXD_LED: PA15 */
GPIOA->MODER &= ~GPIO_MODER_MODER15;                // Clear mode for PA15
GPIOA->MODER |= GPIO_MODER_MODER15_0;              // Set as Output (01)
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_15;                // Push-pull (0)
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEEDR15;          // Clear speed for PA15
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR15_0;        // Medium speed (01)
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR15;                // No pull-up, no pull-down (00)
}
 
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
 
    DMA1_Channel2->CMAR  = (uint32_t)data;     // Step 2: Memory address
    DMA1_Channel2->CNDTR = length;             // Step 3: Data length
 
    DMA1_Channel2->CCR |= DMA_CCR_EN;          // Step 5: Enable DMA channel
}
 
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)
    }
}
 
void Init_Tim14_1ms(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM14EN;
 
    TIM14->PSC = 39;
    TIM14->ARR = 999;
    TIM14->CNT = 0;
 
    TIM14->SR &= ~TIM_SR_UIF;           // Clear any pending flag
    TIM14->DIER |= TIM_DIER_UIE;        // Update interrupt enable
    TIM14->CR1 |= TIM_CR1_CEN;          // Counter enable
 
    NVIC_EnableIRQ(TIM14_IRQn);         // Enable IRQ in NVIC
}
 
 
void TIM14_IRQHandler(void)
{
    if (TIM14->SR & TIM_SR_UIF)
    {
    //Switch_Status_LED_ON;
    TIM14->SR &= ~TIM_SR_UIF;
        ms_Ticks++;
        Tick_1ms=1;
        //Switch_Status_LED_OFF;
    }
}
 
void delay_ms(uint32_t ms)
{
    uint32_t start = ms_Ticks;
    while ((ms_Ticks - start) < ms);
}
 
void Config_System_Clock(void)
{
    // 1. Enable HSE (High Speed External oscillator)
    RCC->CR |= RCC_CR_HSEON;
    while ((RCC->CR & RCC_CR_HSERDY) == 0); // Wait for HSE ready
 
    // 2. Configure PLL
    // PLL source = HSE, PLL multiplier = 5
    RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL); // Clear PLL config
    RCC->CFGR |= RCC_CFGR_PLLSRC_HSE_PREDIV | RCC_CFGR_PLLMUL5; // HSE / 1 * 5
 
    // 3. Enable PLL
    RCC->CR |= RCC_CR_PLLON;
    while ((RCC->CR & RCC_CR_PLLRDY) == 0); // Wait for PLL ready
 
    // 4. Select PLL as system clock
    RCC->CFGR &= ~RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL used as system clock
}

 

4 REPLIES 4
Karl Yamashita
Principal

 

Use the </> when posting code so it's properly formatted

 

Code Snippet.jpg

Don't worry, I won't byte.
TimerCallback tutorial! | UART and DMA Idle tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.
Pavel A.
Evangelist III

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.

 

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.

 

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.