Interrupt not working - Controlling PWM using DMA with STM32 MCU

68 views Asked by At

I am working on controlling the output PWM signal to operate the SK6812RGBW LED on a STM32 MCU using the LL library.

In order to have the speed in operating the LEDs and free up the MCu from computation, I need to set up a DMA to provide the PWM values to the to the associated Compare output register.

To my understanding I would need to (in broad terms):

  1. Enable the timer (in this case TIM2) in the Compare output PWM mode
  2. Enable the DMA and have it interface with the timer using a channel
  3. Enable the GPIO to output the PWM signal (for Debugging)

Currently my main issue is that I am not able to detect the Half and Full transfer interrupts of the DMA, which is leading to believe that the DMA is not set up correctly. I have isolated this problem to the DMA is not properly being signaled or is not properly initialized. The PWM and Timer set up is verified.

Currently my method for updating the PWM signal is to wait for the auto reload of the timer, at which point the update event will signal to the peripheral to signal to the DMA for the new data.

I have shown my DMA, and TIM2 initialization code below.

Any guidance on what my potential issue for this matter would be helpful.

Below is what I think are the important pieces of code for this issue I am facing.

  1. DMA init funciton
  2. TIM2 init function
static void MX_DMA_Init(void)
{

  /* Init with LL driver */
  /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
  NVIC_EnableIRQ(DMA1_Channel1_IRQn);

  LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
  LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
  LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);

}

My code to Initialize TIM2 for the PWM and having the DMA related parameters is below:

static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */
  static int autoreloadVal = 17000;

  aCCValue[0] = (uint32_t)(((uint32_t) 75 * (autoreloadVal - 1)) / 100);
  /* Compute compare value to generate a duty cycle at 50% */
  aCCValue[1] = (uint32_t)(((uint32_t) 50 * (autoreloadVal - 1)) / 100);
  /* Compute compare value to generate a duty cycle at 25% */
  aCCValue[2] = (uint32_t)(((uint32_t) 25 * (autoreloadVal - 1)) / 100);

  /* USER CODE END TIM2_Init 0 */

  LL_TIM_InitTypeDef TIM_InitStruct = {0};
  LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

  /* TIM2 DMA Init */

  /* TIM2_CH3 Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_8);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH);

  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD); //different sizes from example but consistent sizing between mem and peripheral

  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);

  LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&aCCValue, (uint32_t)&TIM2->CCR3, LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1));

  LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, CC_VALUE_NB);

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  TIM_InitStruct.Prescaler = 0;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = autoreloadVal-LL_TIM_IC_FILTER_FDIV1_N2;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM2, &TIM_InitStruct);
  LL_TIM_EnableARRPreload(TIM2);
  LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
  LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH3);
  TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
  TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.CompareValue = 5;
  TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
  LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
  LL_TIM_OC_EnableFast(TIM2, LL_TIM_CHANNEL_CH3);
  LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_UPDATE);
  LL_TIM_DisableMasterSlaveMode(TIM2);

  /* USER CODE BEGIN TIM2_Init 2 */



  LL_TIM_EnableDMAReq_UPDATE(TIM2);
  LL_TIM_EnableDMAReq_CC3(TIM2);
  LL_TIM_EnableCounter(TIM2);
  LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3);

  NVIC_SetPriority(TIM2_IRQn, 0);
  NVIC_EnableIRQ(TIM2_IRQn);
  LL_TIM_EnableIT_UPDATE(TIM2);
  /* USER CODE END TIM2_Init 2 */

  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  /**TIM2 GPIO Configuration
  PA2   ------> TIM2_CH3
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM2_Init 3 */
  /* Force update generation */
   LL_TIM_GenerateEvent_UPDATE(TIM2);
  /* USER CODE END TIM2_Init 3 */

}
1

There are 1 answers

0
Harmandeep Dubb On

There were two issues with my code/assumptions:

  1. I was enabling the DMA in the DMA init while I had configuration code in the TIM2 init for DMA. To fix this I put the enable statement after all configurations for the registers was done. It is good practice to configure registers before enabling the peripheral.
  2. My intent to send the request was on the overflow of the Timer (TIM2), but to do this I needed to use a different channel. See below:

enter image description here

I was selecting channel 1 while channel 2 gives this behaviors since it holds the TIM2_UP request. The request in channel 1 is for the CH3 (where I was outputting the PWM wave). This channel gets a request if CH3 crosses the CH3 the compare value, which worked when I enabled the appropriate request through LL_TIM_EnableDMAReq_CC3 .

Hope this helps who ever runs into a similar problem.