cancel
Showing results for 
Search instead for 
Did you mean: 

D-shot with STM32F765: timer + pwm + dma

Pmica.1
Associate II

Hi all,

I have a STM32F765, which i want to use to generate 4 D-shot signals on 4 GPIO, in the specific 4 gpio connected to Timer 1 .

The timer is set as 4 channel pwm with DMA (memory to perpheral).

The issue is that i'm able to generate the desired signal only for 2 channels (CH1-CH4).

The CH2 and CH3 do not produce any output.

I'm quite sure the issue depends on the DMA.

Without DMA i can generate a PWM / Pulse on all the channels.

In addition if the each timer's channel has its dedicated dma, then all works fine.

Note that in the main loop a generic buffer of 5 entries is streamed on the dma (a Dshot has a dimension of 26)

So the issue is related to DMA2 stream 6 / Channel 0, where 3 timer channels are processed together:

0693W00000AMvOnQAL.png Solution:

Each timer channel needs a dedicated dma stream

int main(void) {
 
  HAL_Init();
 
  SystemClock_Config();
 
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
 
  uint32_t esc_dshot_buffer[5] = {25, 50, 0, 25, 0};
 
  uint8_t toggle = 0;
 
  while (1) {
 
    HAL_Delay(10);
 
    HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, esc_dshot_buffer, 5);
    HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_2, esc_dshot_buffer, 5);
    HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_3, esc_dshot_buffer, 5);
    HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_4, esc_dshot_buffer, 5);
 
 
    if (toggle) {
      toggle = 0;
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, RESET);
    } else {
      toggle = 1;
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, SET);
    }
  }
  /* USER CODE END 3 */
}
static void MX_TIM1_Init(void) {
 
  /* USER CODE BEGIN TIM1_Init 0 */
 
  /* USER CODE END TIM1_Init 0 */
 
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
 
  /* USER CODE BEGIN TIM1_Init 1 */
 
  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 3;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 71;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK) {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK) {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.BreakFilter = 0;
  sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
  sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
  sBreakDeadTimeConfig.Break2Filter = 0;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK) {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */
 
  /* USER CODE END TIM1_Init 2 */
  HAL_TIM_MspPostInit(&htim1);
}
 
/**
 * Enable DMA controller clock
 */
static void MX_DMA_Init(void) {
 
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA2_Stream4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspInit 0 */
 
  /* USER CODE END TIM1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();
 
    /* TIM1 DMA Init */
    /* TIM1_CH4_TRIG_COM Init */
    hdma_tim1_ch4_trig_com.Instance = DMA2_Stream4;
    hdma_tim1_ch4_trig_com.Init.Channel = DMA_CHANNEL_6;
    hdma_tim1_ch4_trig_com.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim1_ch4_trig_com.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim1_ch4_trig_com.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim1_ch4_trig_com.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_tim1_ch4_trig_com.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_tim1_ch4_trig_com.Init.Mode = DMA_NORMAL;
    hdma_tim1_ch4_trig_com.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_tim1_ch4_trig_com.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_tim1_ch4_trig_com) != HAL_OK)
    {
      Error_Handler();
    }
 
    /* Several peripheral DMA handle pointers point to the same DMA handle.
     Be aware that there is only one stream to perform all the requested DMAs. */
    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC4],hdma_tim1_ch4_trig_com);
    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_TRIGGER],hdma_tim1_ch4_trig_com);
    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_COMMUTATION],hdma_tim1_ch4_trig_com);
 
    /* TIM1_CH1_CH2_CH3 Init */
    hdma_tim1_ch1_ch2_ch3.Instance = DMA2_Stream6;
    hdma_tim1_ch1_ch2_ch3.Init.Channel = DMA_CHANNEL_0;
    hdma_tim1_ch1_ch2_ch3.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim1_ch1_ch2_ch3.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim1_ch1_ch2_ch3.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim1_ch1_ch2_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_tim1_ch1_ch2_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_tim1_ch1_ch2_ch3.Init.Mode = DMA_NORMAL;
    hdma_tim1_ch1_ch2_ch3.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_tim1_ch1_ch2_ch3.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_tim1_ch1_ch2_ch3) != HAL_OK)
    {
      Error_Handler();
    }
 
    /* Several peripheral DMA handle pointers point to the same DMA handle.
     Be aware that there is only one stream to perform all the requested DMAs. */
    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC1],hdma_tim1_ch1_ch2_ch3);
    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC2],hdma_tim1_ch1_ch2_ch3);
    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC3],hdma_tim1_ch1_ch2_ch3);
 
  /* USER CODE BEGIN TIM1_MspInit 1 */
 
  /* USER CODE END TIM1_MspInit 1 */
  }
 
}

static void MX_DMA_Init(void) {
 
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
 
  /* DMA2_Stream4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}

1 ACCEPTED SOLUTION

Accepted Solutions

For example stream 1 channel 6 means that compare/capture event on TIM1_CH1 generate DMA request and DMA moves single data element from source to destination (for example from memory to TIM CCR1).

In case Stream6 Channel0 you should enable only one of three DMA request (CH1 or CH2 or CH3) in timer. If you enable all three request, they will be logicaly ored (if i remember correctly) and that means any event (on CH1,CH2 or CH3) generate DMA request - your DMA move three data elements into single register in timer (!). You need use 4 DMA streams (1,2,4,6). Or if you want use only one DMA stream you have to use DMA Burst mode (timer feature).

View solution in original post

9 REPLIES 9
Pmica.1
Associate II

The issue has been temporary solved using different dma streams for each timer channel!

Not sure how it would write 4 sets of registers otherwise. Burst mode at Update?

Your code doesn't seem to be using or attempting to use that.

And yes, you can't share DMA resources between TIM, and the mapping limits the options.

HAL_TIM_DMABurst_WriteStart ?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Does it mean that with dma2 stream 6 channel 0, it is possible to use only one channel between CH1,CH2,CH3?

In that case i can consider this thread close 🙂

What is D-shot?

JW

A dshot is a digital protocol used to transmitt commands to motor’s ESC.

For more information, have a look to this link https://os.mbed.com/users/bwest32/notebook/dshot/

For example stream 1 channel 6 means that compare/capture event on TIM1_CH1 generate DMA request and DMA moves single data element from source to destination (for example from memory to TIM CCR1).

In case Stream6 Channel0 you should enable only one of three DMA request (CH1 or CH2 or CH3) in timer. If you enable all three request, they will be logicaly ored (if i remember correctly) and that means any event (on CH1,CH2 or CH3) generate DMA request - your DMA move three data elements into single register in timer (!). You need use 4 DMA streams (1,2,4,6). Or if you want use only one DMA stream you have to use DMA Burst mode (timer feature).

Thanks.

This is then similar to the protocol of those serially-controlled three-color LEDs. I believe there's loads of information on how to do that on the STM32s, out on the webs, so you perhaps can get inspired there.

JW

This answer my question. Thanks.

I used dma2 stream 1,2,4,6!

The problem was mostly related to a not deep understanding of the dma. About the D-Shot, it doesn’t really matter. I will change the title of the thread with generic buffer, instead of d-shot!