cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L011 DMA read from const variable (flash)

Marc1
Associate III

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?

8 REPLIES 8

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

Address is 0x08003064.

Do FLASH registers need specific configuration?

> 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

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.

Marc1
Associate III

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?

Dear @Marc1 ,

Can you try to execute these two instructions from RAM instead ? And let us know - you can put them in a C function that you call from RAM .  I see we have an errata on early silicon revisions and seems an extra time is required for Flash to be ready after wake-up 

IMG_9046.jpeg

 let us know .

 

STOne-32

Hi @STOne-32,

Thank you for replying. What two instructions do you exactly mean?

Your article describes a procedure during power-down and during wake-up. In my case there is no wake-up event before using DMA.

STOne-32
ST Employee

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.