cancel
Showing results for 
Search instead for 
Did you mean: 

Inexplicable extra pulses when using PWM + DMA

Beartama
Associate II

I'm try to generate some pulses to PB0 using STM32F103C8T6 with the following code

#include <stdint.h>
// STM32F103C8 is a medium density device, hence we use the xB header
#include <stm32f103xb.h>

#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning                                                                       \
    "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif

void enable_clock() {
  RCC->CR |= RCC_CR_HSION;
  while ((RCC->CR & RCC_CR_HSIRDY) == 0);
}

// @formatter:off
#define HI 6
#define LO 3
uint16_t buffer[] = {
  0, LO, HI, LO
};
// @formatter:on

void blink_dma_to_gpio() {
  enable_clock();

  // GPIO B configuration
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Enable IO port B

  GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0); // GPIO B: Clear pin 6 configuration
  GPIOB->CRL |= GPIO_CRL_CNF0_1 | GPIO_CRL_MODE0_0; // GPIO B: Set pin 6 to alternate function push-pull 10MHz output

  // TIM3 configuration
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Enable TIM3
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Enable alternate function

  TIM3->PSC = 1 - 1;
  TIM3->ARR = 10 - 1;

  TIM3->CR1 |= TIM_CR1_ARPE;
  TIM3->BDTR |= TIM_BDTR_MOE;

  TIM3->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 // PWM mode 1
      | TIM_CCMR2_OC3PE // Enable preload
  ;
  TIM3->CCER |= TIM_CCER_CC3E; // Enable capture compare 3

  TIM3->CCMR2 |= TIM_CCMR2_OC4M_0; // Active on match
  TIM3->CCER |= TIM_CCER_CC4E; // Enable capture compare 4
  TIM3->DIER |= TIM_DIER_CC4DE; // Enable DMA1 request
  TIM3->CCR4 = 7;

  // DMA1 configuration
  RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable DMA1

  DMA1_Channel3->CPAR = (uint32_t) &TIM3->CCR3;
  DMA1_Channel3->CMAR = (uint32_t) &buffer;
  DMA1_Channel3->CNDTR = sizeof(buffer) / sizeof(buffer[0]);

  DMA1_Channel3->CCR = DMA_CCR_PL // Very high priority
  | DMA_CCR_MSIZE_0 // 16 bit peripheral
      | DMA_CCR_PSIZE_0 // 16 bit peripheral
      | DMA_CCR_MINC // Increment memory
      | DMA_CCR_CIRC // Circular memory
      | DMA_CCR_DIR  // Memory to peripheral
      | DMA_CCR_HTIE // Half transfer interrupt
      | DMA_CCR_TCIE // Transfer complete interrupt
  ;

  NVIC_EnableIRQ(DMA1_Channel3_IRQn);

  // Enable peripherals
  DMA1_Channel3->CCR |= DMA_CCR_EN;
  TIM3->CR1 |= TIM_CR1_CEN;
}

int main() {
  blink_dma_to_gpio();
  for (;;);
}

void DMA1_Channel3_IRQHandler() {
  if (DMA1->ISR & DMA_ISR_HTIF3) {
    DMA1->IFCR = DMA_IFCR_CHTIF3;
  }
  if (DMA1->ISR & DMA_ISR_TCIF3) {
    DMA1->IFCR = DMA_IFCR_CTCIF3;
  }
}

I expect there to be only 3 pulses from the beginning to the end of the DMA buffer. However, when hooking to an oscilloscope, the pulses output are inconsistent.

pic_130_6.gif 

Some pulses are right with short, long, and short pulses, but some pulses just have random long or short pulse in them. I can't see what's wrong with my code. Please let me know if there is anything potentially wrong with my code.

1 ACCEPTED SOLUTION

Accepted Solutions
bmckenney
Associate III

The DMA is pretty fast, but it isn't instantaneous. If it can't transfer the new compare value within a single TIM cycle (10 clocks in your case) the next cycle will re-use the stale compare value, resulting in a duplicate pulse.

I don't recall seeing a specification (for any STM32) for how many bus clocks a DMA transfer takes, but 6-10 is not unusual for other MCUs. Judging by the pattern in your trace I'm guessing it's about 12.

A quick experiment would be to slow down the timer by e.g. doubling the PSC (->(2-1)) to see if the symptom still appears.

View solution in original post

4 REPLIES 4
bmckenney
Associate III

The DMA is pretty fast, but it isn't instantaneous. If it can't transfer the new compare value within a single TIM cycle (10 clocks in your case) the next cycle will re-use the stale compare value, resulting in a duplicate pulse.

I don't recall seeing a specification (for any STM32) for how many bus clocks a DMA transfer takes, but 6-10 is not unusual for other MCUs. Judging by the pattern in your trace I'm guessing it's about 12.

A quick experiment would be to slow down the timer by e.g. doubling the PSC (->(2-1)) to see if the symptom still appears.

Beartama
Associate II

Yes, putting (12 -1) to the auto reload register actually makes it much more stable and fixes the issue. Thank you very much.

Beartama
Associate II
bmckenney
Associate III

I hadn't seen this before. Thanks!