Skip to main content
Visitor II
November 12, 2024
Solved

DMA on STM32L011F4

  • November 12, 2024
  • 2 replies
  • 1149 views

I'm trying to program an STM32L011F4 to transmit data through USART2 by way of the DMA controller.  My code compiles but the IRQ handler for DMA is never called.  Either I have not correctly initialized the DMA controller or there is another problem I am overlooking.  Can anyone provide suggestions?

 


//-----------------------------------------------------------------------------
#include <stdio.h>
#include "stm32l0xx.h"


//-----------------------------------------------------------------------------
#define USART_TX_PIN GPIO_PIN_2
#define DMA_BUFFER_SIZE 64
uint8_t dma_buffer[ DMA_BUFFER_SIZE ] = "String to send through USART2 by DMA!\r\n";

//-----------------------------------------------------------------------------
void SystemClock_Config(void);
void USART2_Init(void);
void DMA_Init(void);
void Start_DMA_Transmission(void);


//-----------------------------------------------------------------------------
int main( void ) {

 SystemClock_Config(); // Configure system clock
 USART2_Init(); // Initialize USART2
 DMA_Init(); // Initialize DMA

 Start_DMA_Transmission(); // Start data transmission over DMA

 while (1) { __asm__( "NOP" ); };

}; // end main


//-----------------------------------------------------------------------------
void SystemClock_Config( void ) {

 // Configure the system clock to use HSI at 16 MHz
 RCC->CR |= RCC_CR_HSION; // Enable HSI
 while ( !( RCC->CR & RCC_CR_HSIRDY ) ){ __asm__( "NOP" ); }; // Wait for HSI

 RCC->CFGR = 0; // System clock source is HSI
 SystemCoreClockUpdate(); // Update SystemCoreClock variable

}; // end SystemClock_Config


//-----------------------------------------------------------------------------
void USART2_Init( void ) {

 // Enable GPIOA and USART2 clocks
 RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
 RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

 // Configure PA2 as USART2 TX
 GPIOA->MODER &= ~GPIO_MODER_MODE2; // Clear mode bits
 GPIOA->MODER |= GPIO_MODER_MODE2_1; // Set PA2 to alternate function mode
 GPIOA->AFR[0] |= ( 4 << GPIO_AFRL_AFSEL2_Pos ); // Set PA2 to AF4 (USART2)

 // Configure USART2 baud rate, enable TX, enable USART
 USART2->BRR = SystemCoreClock / 9600; // Set baud rate to 9600 (assuming 16 MHz clock)
 USART2->CR1 = USART_CR1_TE | USART_CR1_UE; // Enable TX and USART

}; // end USART2_Init

//-----------------------------------------------------------------------------
void DMA_Init(void) {

 RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable DMA clock

 DMA1_Channel2->CCR &= ~DMA_CCR_EN; // Disable channel before configuration

 DMA1_Channel2->CCR &= ~( DMA_CCR_MSIZE_0 | DMA_CCR_MSIZE_1 ); // Set size to 8 bits
 DMA1_Channel2->CCR &= ~( DMA_CCR_PSIZE_0 | DMA_CCR_PSIZE_1 ); // Set USART to 8 bits

 DMA1_Channel2->CCR =
 DMA_CCR_MINC | // Enable memory increment
 DMA_CCR_DIR | // Read from memory
 DMA_CCR_TEIE | // Transfer error flag
 DMA_CCR_HTIE | // Half transfer flag
 DMA_CCR_TCIE; // Enable transfer complete interrupt

 // Configure DMA for USART2 TX (Channel 2)
 DMA1_Channel2->CPAR = (uint32_t)(&(USART2->TDR)); // Set peripheral address to USART2 TDR
 DMA1_Channel2->CMAR = (uint32_t)dma_buffer; // Set memory address to dma_buffer
 DMA1_Channel2->CNDTR = sizeof(dma_buffer) - 1; // Set number of data items to transfer

 DMA1_Channel2->CCR &= ~( DMA_CCR_PL_0 | DMA_CCR_PL_1 ); // Set priority to low

 // Enable DMA interrupt in NVIC
 NVIC_SetPriority( DMA1_Channel2_3_IRQn, 0x0 );
 NVIC_EnableIRQ( DMA1_Channel2_3_IRQn );

}; // DMA_Init


//-----------------------------------------------------------------------------
void Start_DMA_Transmission( void ) {

 USART2->CR3 |= USART_CR3_DMAT; // Enable DMA for USART2 TX

 while ( ( USART2->ISR & USART_ISR_TC ) != USART_ISR_TC ) { __asm__( "NOP" ); };
 USART2->ICR = USART_ICR_TCCF; // Clear TC Flag

 DMA1_Channel2->CCR |= DMA_CCR_EN; // Enable DMA Channel 2

}; // end Start_DMA_Transmission


//-----------------------------------------------------------------------------
void DMA1_Channel2_3_IRQHandler( void ) {

 if ( DMA1->ISR & DMA_ISR_TCIF2 ) { // Check for transfer complete interrupt flag

 DMA1->IFCR = DMA_IFCR_CTCIF2; // Clear transfer complete interrupt flag
 DMA1_Channel2->CCR &= ~DMA_CCR_EN; // Disable DMA Channel 4 after transfer

 return;

 }; // end TCIF2

}; // end DMA1_Channel2_3_IRQHandler

 

    This topic has been closed for replies.
    Best answer by waclawek.jan

    You have to set DMA_CSELR.C2S = 0b0100 to map the USART2_TX request to DMA1_Channel2 (your STM32 model is Cat.1, right?)

    waclawekjan_0-1731436880271.png

    JW

    2 replies

    Super User
    November 12, 2024

    > IRQ handler for DMA is never called.

    How do you know?

    Where does the PC end up, isn't it some default handler?

    Read out and check/post content of the DMA registers.

    Check the interrupt vector table in disasm.

    Generic "interrupt does not fire" checklist here.

    JW

    Super User
    November 12, 2024

    You have to set DMA_CSELR.C2S = 0b0100 to map the USART2_TX request to DMA1_Channel2 (your STM32 model is Cat.1, right?)

    waclawekjan_0-1731436880271.png

    JW

    Visitor II
    November 12, 2024

    That was it, the code now works correctly.  Thanks for pointing out what I had overlooked.

    I had read that section of the manual several times but thought it wasn't required because I was transmitting and not receiving data on the usart. 

    -Archadious