cancel
Showing results for 
Search instead for 
Did you mean: 

PWM, DMA, how to change the waveform.

Alejandro Bizzotto
Associate III
Posted on January 03, 2018 at 11:20

The original post was too long to process during our migration. Please click on the attachment to read the original post.
13 REPLIES 13
Posted on January 04, 2018 at 08:03

Wow, this is a surprise for me. I wouldn't expect writing M0AR would change anything. I wonder what's the exact mechanism behind this working.

JW

Posted on January 04, 2018 at 11:45

Well, sometimes I'm lucky

On this side of the word it was late on the night and I didn't wanted to read the entire chapter about DMA in the RM, so I tried something... and works.

But now I'm reading it and I don't have an explanation anyway. This is a STM32F401RE, and the page 148 of the manual give us the steps to configure the DMA, first step is to read the EN bit in the CR register to be sure it's disabled, if not disable it and wait until it is disabled, I followed the function HAL_TIM_PWM_Start_DMA() and it doesn't do it (to be sure I also looked into the CR and EN is high all the time, so I did it in my loop, this way:

while (1)

  {

      HAL_Delay(500);

      if (act_seg == 0) {

          act_seg = 1;

          HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, act_seg);

          HAL_TIM_PWM_Stop_DMA (&htim3, TIM_CHANNEL_1);

//          hdma_tim3_ch1_trig.Instance->M0AR = 0;

          while (hdma_tim3_ch1_trig.Instance->CR & (uint32_t)(DMA_SxCR_EN)) hdma_tim3_ch1_trig.Instance->CR &= (uint32_t)(~DMA_SxCR_EN);

          while (hdma_tim3_ch1_trig.Instance->CR & (uint32_t)(DMA_SxCR_DMEIE)) hdma_tim3_ch1_trig.Instance->CR &= (uint32_t)(~DMA_SxCR_DMEIE);

                HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *) seg0, 8192);

          hdma_tim3_ch1_trig.Instance->CR |= (uint32_t)(DMA_SxCR_EN);

          hdma_tim3_ch1_trig.Instance->CR |= (uint32_t)(DMA_SxCR_DMEIE);

      }

      else {

          act_seg = 0;

          HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, act_seg);

          HAL_TIM_PWM_Stop_DMA (&htim3, TIM_CHANNEL_1);

//          hdma_tim3_ch1_trig.Instance->M0AR = 0;

          while (hdma_tim3_ch1_trig.Instance->CR & (uint32_t)(DMA_SxCR_EN)) hdma_tim3_ch1_trig.Instance->CR &= (uint32_t)(~DMA_SxCR_EN);

          while (hdma_tim3_ch1_trig.Instance->CR & (uint32_t)(DMA_SxCR_DMEIE)) hdma_tim3_ch1_trig.Instance->CR &= (uint32_t)(~DMA_SxCR_DMEIE);

            HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *) seg1, 8192);

          hdma_tim3_ch1_trig.Instance->CR |= (uint32_t)(DMA_SxCR_EN);

          hdma_tim3_ch1_trig.Instance->CR |= (uint32_t)(DMA_SxCR_DMEIE);

      }

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }

This makes the DMA to stop, but don't effectively changes the source array for it (the waveform starts from the beginning of the first array every time), I still need to set the M0ARR to 0 before I can change it.

Posted on January 04, 2018 at 23:00

Digging a bit more on this:

If I run the loop like this:

 while (1)

  {

      HAL_Delay(500);

      if (act_seg == 0) {

          act_seg = 1;

          HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, act_seg);

          HAL_TIM_PWM_Stop_DMA (&htim3, TIM_CHANNEL_1);

                HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *) seg0, 8192);

      }

      else {

          act_seg = 0;

          HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, act_seg);

          HAL_TIM_PWM_Stop_DMA (&htim3, TIM_CHANNEL_1);

                  HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *) seg1, 8192);

      }

  }

Following it step by step, the function HAL_TIM_PWM_Start_DMA leads to >> HAL_DMA_Start_IT, who leads to>> DMA_SetConfig here the M0AR is written. And the first time it works, after the first time, in the funcion

HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)

{

  HAL_StatusTypeDef status = HAL_OK;

  /* calculate DMA base and stream number */

  DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;

 

  /* Check the parameters */

  assert_param(IS_DMA_BUFFER_SIZE(DataLength));

 

  /* Process locked */

  __HAL_LOCK(hdma);

 

  if(HAL_DMA_STATE_READY == hdma->State)

  {

    /* Change DMA peripheral state */

    hdma->State = HAL_DMA_STATE_BUSY;

    

    /* Initialize the error code */

    hdma->ErrorCode = HAL_DMA_ERROR_NONE;

    

    /* Configure the source, destination address and the data length */

    DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);

    

    /* Clear all interrupt flags at correct offset within the register */

    regs->IFCR = 0x3FU << hdma->StreamIndex;

    

    /* Enable Common interrupts*/

    hdma->Instance->CR  |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;

    hdma->Instance->FCR |= DMA_IT_FE;

    

    if(hdma->XferHalfCpltCallback != NULL)

    {

      hdma->Instance->CR  |= DMA_IT_HT;

    }

    

    /* Enable the Peripheral */

    __HAL_DMA_ENABLE(hdma);

  }

  else

  {

    /* Process unlocked */

    __HAL_UNLOCK(hdma);      

    

    /* Return error status */

    status = HAL_BUSY;

  }

 

  return status;

}

it always jumps over the bold section, unless I write something directly to the M0AR, I can write 0 or the direct address of the array I want to have, or the pointer to it, or a random number, doesn't matter it will work, but I don't see why.

And if I don't use the HAL_TIM_PWM_Stop_DMA() function, the result is the same, the writing action to M0AR stops the DMA then HAL_TIM_PWM_Start_DMA() can start it again with the correct address in the M0AR.

__HAL_DMA_ENABLE/DISABLE has no effect on the address neither.

Is this a bug or should I do something else aside of using HAL_TIM_PWM_Stop_DMA() prior to enable it again with a different address?

Posted on January 05, 2018 at 09:36

My guess is, that writing to M0AR results in some of the DMA errors, which in turn invokes the ISR which then sets the HAL status to READY.

JW