1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ @brhans DMAMUX1_Channel0->CCR |= 18U; \$\endgroup\$ – stronk_kisik Aug 7 '18 at 14:59
  • \$\begingroup\$ nvm - right in the middle... \$\endgroup\$ – brhans Aug 7 '18 at 15:00
  • \$\begingroup\$ Are you certain that all of the other bits you're not using in DMAMUX1_Channel0->CCR are cleared? \$\endgroup\$ – brhans Aug 7 '18 at 15:07
  • 1
    \$\begingroup\$ Ummm - hold on a second - why are you trying to printf() from within an IRQ handler?!? \$\endgroup\$ – brhans Aug 7 '18 at 15:12
  • 1
    \$\begingroup\$ @user9403409 DMA1_Stream0->CR |= (0x2 << 11); what is consistent. Respect our time. Do not use magic numbers. Use symbols provided by the manufacturer. \$\endgroup\$ – P__J__ Aug 7 '18 at 18:12
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.