D-shot with STM32F765: timer + pwm + dma
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:
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);
}