Skip to main content
Visitor II
July 18, 2024
Question

STM32L011 DMA read from const variable (flash)

  • July 18, 2024
  • 3 replies
  • 2010 views

Controller: STM32L011G4U
Libraries: CMSIS
Compiler: arm-none-eabi-gcc version 13.2.1

In order to change Capture Compare Register of TIM2 automatically every time when TIM2 updates, I want to use DMA to get the current CCR values from a const array.

 

const uint16_t source[5] = { 1, 2, 3, 4, 5 };

RCC->AHBENR |= RCC_AHBENR_DMA1EN; // enable DMA1 clock

// DMA TIM2_UP
DMA1_CSELR->CSELR |= (0b1000 << DMA_CSELR_C2S_Pos); // put TIM2_UP on DMA1 channel 2
DMA1_Channel2->CCR = DMA_CCR_MINC; // memory address pointer is incremented
DMA1_Channel2->CCR |= DMA_CCR_DIR; // direction is memory to peripheral
DMA1_Channel2->CCR |= DMA_CCR_CIRC; // circular mode
DMA1_Channel2->CCR |= (0b01 << DMA_CCR_MSIZE_Pos); // size of memory data is 16 Bit
DMA1_Channel2->CCR |= (0b01 << DMA_CCR_PSIZE_Pos); // size of peripheral data is 16 Bit
DMA1_Channel2->CPAR = (uint32_t) &TIM2->CCR4; // peripheral address
DMA1_Channel2->CMAR = (uint32_t) &source; // memory address
DMA1_Channel2->CNDTR = 5; // number of data to transfer

// TIM2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable clock

TIM2->PSC = 0;
TIM2->ARR = 0xFFFF;
TIM2->CR1 = TIM_CR1_ARPE; // ARR register is buffered
TIM2->DIER = TIM_DIER_UDE; // enable DMA for timer update
TIM2->CCMR2 |= (0b110 << TIM_CCMR2_OC4M_Pos); // PWM mode 1
TIM2->CCMR2 |= TIM_CCMR2_OC4PE; // output compare preload enable
TIM2->CCR4 = 0; // reset output compare register
TIM2->CCER |= TIM_CCER_CC4E; // enable output
TIM2->EGR |= TIM_EGR_UG;
TIM2->CR1 |= TIM_CR1_CEN; // enable counter

DMA1_Channel2->CCR |= DMA_CCR_EN; // enable DMA

 

This code works as long as source is not defined as const variable. If source is defined as const variable, DMA sends random values to CCR4.

Is there anything to consider if DMA source data is located in flash memory?

    This topic has been closed for replies.

    3 replies

    Super User
    July 21, 2024

    I don't think there should be any problem with DMA reading from FLASH.

    At which address is the source[] array? Is it in FLASH indeed?

    JW

    Marc1Author
    Visitor II
    July 21, 2024

    Address is 0x08003064.

    Do FLASH registers need specific configuration?

    Super User
    July 21, 2024

    > Do FLASH registers need specific configuration?

    I don't think so.

    I don't know where's the problem.

    What are those "random" values?

    Can you read values from source[] array using the processor? For example, as an experiment, instead of DMA, mimic it with the processor: in a polling loop, check the TIM2_SR.UIE flag, and if set, clear it, read the next value from the source[] array and write it to TIM2_CCR4.

    JW

    Marc1Author
    Visitor II
    July 23, 2024
    uint16_t source[3] = { 200, 400, 600 };
    
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; // enable DMA1 clock
    
    // DMA TIM2_UP
    DMA1_CSELR->CSELR |= (0b1000 << DMA_CSELR_C2S_Pos); // put TIM2_UP on DMA1 channel 2
    DMA1_Channel2->CCR = DMA_CCR_MINC; // memory address pointer is incremented
    DMA1_Channel2->CCR |= DMA_CCR_DIR; // direction is memory to peripheral
    DMA1_Channel2->CCR |= DMA_CCR_CIRC; // circular mode
    DMA1_Channel2->CCR |= (0b01 << DMA_CCR_MSIZE_Pos); // size of memory data is 16 Bit
    DMA1_Channel2->CCR |= (0b01 << DMA_CCR_PSIZE_Pos); // size of peripheral data is 16 Bit
    DMA1_Channel2->CPAR = (uint32_t) &TIM2->CCR4; // peripheral address
    DMA1_Channel2->CMAR = (uint32_t) &source; // memory address
    DMA1_Channel2->CNDTR = 3; // number of data to transfer

     

    Read from RAM:

    dma_test_ram.png

    The PWM output Pin of CCR4 looks correct this if DMA source is defined: uint16_t source[3] = { 200, 400, 600 };

     

    Read from flash:

    dma_test_flash.png

    The PWM output Pin of CCR4 looks like this if DMA source is defined: const uint16_t source[3] = { 200, 400, 600 };

    TIM2->ARR = 2499;
    TIM2->PSC = 0;

    If I read TIM2->CCR4 or TIM2->ARR randomly via UART, values are shown correctly. But PWM output appears randomly.

    This routine works well if I use timer update interrupt instead of DMA for changing CCR4. But CPU load is high. In this case source is read by processor inside the interrupt routine.

    Marc1Author
    Visitor II
    July 23, 2024

    New finding:

    During main while loop the CPU goes into low-power mode frequently. And wakes up every interrupt.

    PWR->CR = PWR_CR_CWUF | PWR_CR_LPSDSR;
    WFI();

    If I comment out these lines, DMA access to flash works.

    New questions:
    How can I get DMA access to flash working during low-power mode?
    Why does DMA access to RAM work at all while low-power mode is activated?

    Technical Moderator
    July 24, 2024

    Hi @Marc1 ,

    These two instructions : 

    PWR->CR = PWR_CR_CWUF | PWR_CR_LPSDSR;
    WFI();

    to have function like   Lowpowerentry();  Link it to be executed from RAM Area at linker file or using pragma to force it.  The goal is to make a trial and see what happens ?

     

    Thank you 

    STOne-32.