2017-06-27 06:02 AM
Hi.
I'm trying to use DMA to drive PWM output for led effects. MCU write seems to be OK, however there's few very weird consepts related to DMA configuration I don't seem to be able to wrap my head around.
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{ /*♯♯-1- Enable peripherals and GPIO Clocks ♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯*/ /* TIMx clock enable *//* Enable DMA2 clock */
DMAx_CLK_ENABLE /*♯♯-3- Configure the DMA stream ♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯*/ /* Set the parameters to be configured */ hdma_tim.Init.Channel = DMA_CHANNEL_CC3; hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tim.Init.MemInc = DMA_MINC_ENABLE; hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_tim.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdma_tim.Init.Mode = DMA_NORMAL; hdma_tim.Init.Priority = DMA_PRIORITY_HIGH;/* hdma_tim.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tim.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;*//* hdma_tim.Init.MemBurst = DMA_MBURST_SINGLE; hdma_tim.Init.PeriphBurst = DMA_PBURST_SINGLE; */ /* Set hdma_tim instance */ hdma_tim.Instance = TIMx_CC3_DMA_STREAM;/* Link hdma_tim to hdma[TIM_DMA_ID_UPDATE] update mode? */
__HAL_LINKDMA(htim, hdma[TIM_DMA_ID_UPDATE], hdma_tim); /* Initialize TIMx DMA handle */ HAL_DMA_Init(htim->hdma[TIM_DMA_ID_UPDATE]); /*♯♯-4- Configure the NVIC for DMA ♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯♯*/ /* NVIC configuration for DMA transfer complete interrupt */ HAL_NVIC_SetPriority(TIMx_DMA_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIMx_DMA_IRQn);}The link/initialize DMA uses the hdma array inside the TIM_HandleTypeDef. In the struct definition there's comment that array should be accessed via DMA_Handle_index. These are:
/** @defgroup DMA_Handle_index DMA Handle index
* @{ */&sharpdefine TIM_DMA_ID_UPDATE ((uint16_t) 0x0000U) /*!< Index of the DMA handle used for Update DMA requests */&sharpdefine TIM_DMA_ID_CC1 ((uint16_t) 0x0001U) /*!< Index of the DMA handle used for Capture/Compare 1 DMA requests */&sharpdefine TIM_DMA_ID_CC2 ((uint16_t) 0x0002U) /*!< Index of the DMA handle used for Capture/Compare 2 DMA requests */&sharpdefine TIM_DMA_ID_CC3 ((uint16_t) 0x0003U) /*!< Index of the DMA handle used for Capture/Compare 3 DMA requests */&sharpdefine TIM_DMA_ID_CC4 ((uint16_t) 0x0004U) /*!< Index of the DMA handle used for Capture/Compare 4 DMA requests */&sharpdefine TIM_DMA_ID_COMMUTATION ((uint16_t) 0x0005U) /*!< Index of the DMA handle used for Commutation DMA requests */&sharpdefine TIM_DMA_ID_TRIGGER ((uint16_t) 0x0006U) /*!< Index of the DMA handle used for Trigger DMA requests */I've tried to link this info somehow to registers in reference manual for both DMA and TIM2, however I cannot make any sense to this. What should I use? What are these?
I'm using TIM2 channel 2 so it should be DMA1 stream 6. The used GPIO pin is PA1.
#dma #pwm #stm32f469Solved! Go to Solution.
2017-06-28 08:07 AM
As I've said I don't and won't Cube, but I'd expect TIM_CHANNEL_2 means 'use TIMx_CH2', implying TIMx_CCR2 as the target register.
A textual search for 'HAL_TIM_PWM_Start_DMA' in CubeF4 returned me 10 projects (one per board, presumably all very similar), e.g. [STM32Cube_FW_F4_V1.15.0]\Projects\STM32F413ZH-Nucleo\Examples\TIM\TIM_DMA. The readme.txt says,
This example provides a description of how to use DMA with TIMER Update request
to transfer Data from memory to TIMER Capture Compare Register 3 (TIMx_CCR3).JW
2017-06-27 06:16 AM
I've tried to link this info somehow to registers in reference manual for both DMA and TIM2, however I cannot make any sense to this. What should I use? What are these?
Bit indexes in TIMx_DIER, offset by -8.
JW
2017-06-27 08:29 AM
,
,
Wow, what a fast reply Batman!
Does this have any dependency to used OC mode (TIM_OCMODE_PWM1)? , My code is based on TIM_PWMOutput example.
void TIM_TIMER_Init(void) {
/* Enable clock for TIM2 */
,
__HAL_RCC_TIM2_CLK_ENABLE(),,
/*,
TIM2 is connected to APB1 bus, which has on F469 device 16MHz clockRemember: Not each timer is connected to APB1, there are also timers connected
,
on APB2, which works at 16MHz by default, and internal PLL increase,
this to up to 32MHzPWM_frequency = timer_tick_frequency / (TIM_Period + 1)
TIM_Period = timer_tick_frequency / PWM_frequency - 1
In our case, for 10Khz PWM_frequency, set Period to
TIM_Period = 16000000 / 60 - 1 = 266665
*/
,
TimHandle.Instance = TIM2,TimHandle.Init.Period = 266666 - 1, /*60hz*/
,
TimHandle.Init.Prescaler = (uint32_t)(SystemCoreClock / 16000000) - 1,,
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP,,
TimHandle.Init.RepetitionCounter = 1,,
TimHandle.Init.ClockDivision = 0,uwPulse1 = (5 * TimHandle.Init.Period) / 10,
/* Initialize TIM2 */
,
if (HAL_TIM_Base_Init(&,TimHandle) != HAL_OK){,
printf('TIM2 init failed \r\n'),,
},
}void TIM_PWM_Init(void) {
,
TIM_OC_InitTypeDef sPWMConfig,,
/* ♯ ♯ -2- Configure the PWM channels ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ */,
/* Common configuration for all channels */,
sPWMConfig.OCMode = TIM_OCMODE_PWM1,,
sPWMConfig.OCFastMode = TIM_OCFAST_DISABLE,,
sPWMConfig.OCPolarity = TIM_OCPOLARITY_LOW,,
sPWMConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH,,
sPWMConfig.OCIdleState = TIM_OCIDLESTATE_RESET,,
sPWMConfig.OCNIdleState= TIM_OCNIDLESTATE_RESET,,
/* Set the pulse value for channel 1 */,
sPWMConfig.Pulse = uwPulse1,,
if (HAL_TIM_PWM_Init(&,TimHandle) != HAL_OK) {,
/* Configuration Error */,
printf('PWM init error \r\n'),,
}if(HAL_TIM_PWM_ConfigChannel(&,TimHandle, &,sPWMConfig, TIM_CHANNEL_2) != HAL_OK){
,
/* Configuration Error */,
printf('PWM configchannel error \r\n'),,
},
/* ♯ ♯ -3- Start PWM signals generation ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯ */,
/* Start channel 1 */,
if(HAL_TIM_PWM_Start(&,TimHandle, TIM_CHANNEL_2) != HAL_OK),
{,
/* Starting Error */,
printf('PWM start error \r\n'),,
},
TimHandle.Instance->,CCR1 = uwPulse1,}
2017-06-27 08:49 AM
Does this have any dependency to used OC mode (TIM_OCMODE_PWM1)?
Should not. Sorry, I don't speak the Cube gibberish.
JW
2017-06-28 02:25 AM
Ok. Any ideas which DMA should if use from that array? I don't quite get the modes described. With non-DMA solution I just wrote the 32 bit value (fraction of TimHandle.Init.Period as defined in TIM_TIMER_Init above) to CCR1 register.
2017-06-28 06:51 AM
Use UPDATE as the DMA trigger, and then DMA upon every timer overflow transfers a new value from memory to CCR1.
JW
2017-06-28 07:19 AM
Is there any examples related to that?
The current configuration (as seen above) is pretty much as you described, however scope shows constant 50:50 duty cycle and I don't get any EoD callbacks. Or am I missing something here? When triggering the DMA I just call
HAL_TIM_PWM_Start_DMA(&TimHandle, TIM_CHANNEL_2, from, amount);
I mean there's no configuration for the target register anywhere (the CCR1 that I used to write directly).
2017-06-28 08:07 AM
As I've said I don't and won't Cube, but I'd expect TIM_CHANNEL_2 means 'use TIMx_CH2', implying TIMx_CCR2 as the target register.
A textual search for 'HAL_TIM_PWM_Start_DMA' in CubeF4 returned me 10 projects (one per board, presumably all very similar), e.g. [STM32Cube_FW_F4_V1.15.0]\Projects\STM32F413ZH-Nucleo\Examples\TIM\TIM_DMA. The readme.txt says,
This example provides a description of how to use DMA with TIMER Update request
to transfer Data from memory to TIMER Capture Compare Register 3 (TIMx_CCR3).JW
2017-06-29 01:12 AM
Thanks again!
This was my bad. I searched my older fw yesterday, but apparently I made a typo as I didn't find then any references to _PWM_Start_DMA :/.
And man (and woman) definitely have to have principles!