Tuesday, 3 December 2019

stm32 - TIM2 DMA configuration for stm32h7


My problem: I can't configure DMA to working properly on Input Capture event. Data doesn't transfered and error occurs.


static void my_TIM2_initInputCaptureTimer(void) {

// enable clock source for timer
RCC->APB1LENR |= (0x1 << 0);

// set prescaler to 200
TIM2->PSC = 200 - 1;

// choose TIM2_CH1 input
TIM2->TISEL |= (0x0 << 0);
// set channel 1 as input mapped on TI1
TIM2->CCMR1 |= (0x1 << 0);
// digital filter length (0)
TIM2->CCMR1 |= (0x0 << 4);
// rising & falling edge

TIM2->CCER |= (0x1 << 1);
TIM2->CCER |= (0x1 << 3);
// prescaler to (0)
TIM2->CCMR1 |= (0x0 << 2);
// enable DMA interrupt & Capture/Compare interrupt
TIM2->DIER |= (0x1 << 9) | (0x1 << 1);
// capture enabled
TIM2->CCER |= (0x1 << 0);
// reset registers WARNING: need for preloading PSC
TIM2->EGR |= (0x1 << 0);


// enable TIM3 timer
TIM2->CR1 |= TIM_CR1_CEN;
// enable interrupt request
NVIC_EnableIRQ(TIM2_IRQn);
// set priority
NVIC_SetPriority(TIM2_IRQn, 1);

}


DMA configuration:


static void my_DMA_init(void) {
// enable DMA1 clocking
RCC->AHB1ENR |= (0x1 << 0);
// clear EN bit to 0
DMA1_Stream0->CR &= ~(0x1 << 0);
// safeguard EN bit reset
while (DMA1_Stream0->CR & 0x1);
// check LISR HISR registers
if ((DMA1->HISR == 0) && (DMA1->LISR == 0))

printf("status registers is clear\r\n");
else
printf("status register is not clear -- DMA wont start\r\n");
// set peripheral addres
DMA1_Stream0->PAR = TIM2_CCR1_Address;
// set memory addres
DMA1_Stream0->M0AR = (unsigned int)buffer;
// set total number of data items
DMA1_Stream0->NDTR = 10;


// NOTE: configurate TIM2_CH1 interrupt route
// set DMAMUX to route request (TIM2_CH1)
DMAMUX1_Channel0->CCR |= 18U;

// set DMA priority (very high)
DMA1_Stream0->CR |= (0x3 << 16);
// set memory data size (32)
DMA1_Stream0->CR |= (0x2 << 13);
// set peripheral data size (32)
DMA1_Stream0->CR |= (0x2 << 11);

// set memory addres increment (enable)
DMA1_Stream0->CR |= (0x1 << 10);
// set peripheral addres increment (disable)
DMA1_Stream0->CR |= (0x0 << 9);
// set circular buffer mode (enable)
DMA1_Stream0->CR |= (0x1 << 8);
// set data transfer direction (peripheral to memory)
DMA1_Stream0->CR |= (0x0 << 6);
// set transfer complete interrupt
DMA1_Stream0->CR |= (0x1 << 4);

// set transfer error interrupt
DMA1_Stream0->CR |= (0x1 << 2);
// enable DMA1
DMA1_Stream0->CR |= (0x1 << 0);
// enable IRQ
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
printf("DMA1_Stream0 %u \r\n", (DMA1_Stream0->CR & 0x1));
}

Interrupt routines:



void TIM2_IRQHandler(void) {

InCapTick = TIM2->CCR1;
// reset interrupt flag
TIM2->SR = 0;

tick = systick_ms;
flag++;
}


void DMA1_Stream0_IRQHandler(void) {

printf("within\r\n");
printf("LISR: %u \r\n", DMA1->LISR);
// clear interrupt flag
DMA1->LIFCR |= (0x1 << 3) | (0x1 << 5);

}

Full compiled project (gcc-arm\make) for thoose who interested: https://drive.google.com/open?id=1fwns5fKexGWJl64UDeYfDyFmSFA9BURZ



(stm32h743zi-nucleo board) expected behaviour: (when you press user button (the blue one) ->PG0 pin get SET (1) state -> so input capture pin (PA5, they should be connected via jumper) get low-high transition and capture first value (output it via com-port) when you free button, input capture pin get high-low transition and capture another value(output it via com-port), but none of this events do not kick start DMA request.


Data will be outputted via com port with 115200 speed.



Answer



DMA was initialized correctly. The problem was: buffer array wasn't initialized into any SRAM memory area, so when DMA trying to reach that memory via AHB (it's not possible) DMA crash with error and set TEI_flag (transfer error interrupt).


The easises way to make this work (and obviously it isn't correct way but for acquaintance purposes it works)


#define Destination_Address ((unsigned int)0x30000000)
DMA1_Stream0->M0AR = Destination_Address;

0x30000000 -> is boundary of SRAM1 memory


and you need first enable SRAM1\2\3:



static void my_SRAM_init(void) {
// activate SRAM1\2\3
RCC->AHB2ENR |= (0x7 << 29);
}

you can reach that area with:


  uint32_t *Ptr_Dest = (uint32_t *)Destination_Address;
printf("buffer: %lu %lu \r\n", *Ptr_Dest, *(Ptr_Dest + 1));

For those this won't work or some UB happens (corrupt data, strange placing) possible reasons:




  1. Cache (enabled cache could corrupt data)

  2. Your addresses should be aligned with PSIZE and MSIZE


Example: if your MSIZE and PSIZE is 32bit address must end on 0x4


I hope this will help someone, because this was huge pain in my ass for straight 3 days, I hate it.


No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...