Skip to main content
JCK.1
Associate
September 23, 2020
Question

Output PWM through TIM+DMA mode, the pulse number will be one more

  • September 23, 2020
  • 2 replies
  • 1977 views

I use the TIM3+DMA function of STM32F103VCT6 to generate PWM. I need to generate a series of adjustable frequencies, adjustable duty ratio and controllable pulse number.Such as 10KHz, 200KHz, 500KHz, 1MHz, 2MHz, 5MHz.I modify the VALUE of TIM's CCR by generating DMA requests through TIM comparison events. Given Pulse +1 as a DMA count value, the value is reduced to 0 to trigger a DMA interrupt to clear the value of TIM's CCR.At present, When I am generating 500 pulses with a duty cycle of 50% and a frequency of 500 KHZ, I am actually emitting 501 pulses.What's the reason for this?Listen for the reply.

This topic has been closed for replies.

2 replies

TDK
September 23, 2020

I think the answer will depend on a lot of the details you're not giving here. But if you're getting N+1 instead of N, why not just decrease the amount you set by 1?

"If you feel a post has answered your question, please click ""Accept as Solution""."
JCK.1
JCK.1Author
Associate
September 23, 2020

Yes, thank you for your answer.I didn't upload the more detailed implementation code.So I will upload part of the implementation code.I have considered your suggestion, but I think it is not for that reason. I am sorry that I did not make the situation clear.I'm going to output an indeterminate number of pulses in 1ms, so I'm going to select some pulses to verify that.When I output less than 500 pulses, the number of pulses is actually accurate.I get one more pulse when I send 500 or more pulses.And I catch the wave in photo,from the photo we can see the extra pluse.0693W000004GnOAQA0.png0693W000004GnNvQAK.png

#define PWM_CK_CNT1 42000000 //count clock(42M times per sec)
#define PWM_PRESCALER1 (SystemCoreClock/2/PWM_CK_CNT1) //SystemCoreClock=72M
#define PWM_TEST 			TIM3
 
oid TIMx_Dma_Pwm_Output_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	TIM_OCStructInit(&TIM_OCInitStructure);
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 , ENABLE);		
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1, ENABLE); 
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3);
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
 
	TIM_DeInit(PWM1_TIM);
	TIM_TimeBaseStructure.TIM_Period = 0xFFFF; 			
	TIM_TimeBaseStructure.TIM_Prescaler =PWM_PRESCALER1-1; 	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(PWM1_TIM, &TIM_TimeBaseStructure); 
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 				
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC1Init(PWM1_TIM, &TIM_OCInitStructure); 
	TIM_OC1PreloadConfig(PWM1_TIM, TIM_OCPreload_Enable);
 
	DMA_DeInit(DMA1_Stream4);														
	DMA_InitStructure.DMA_Channel = DMA_Channel_5;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&PWM1_TIM->CCR1; 
	DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&PWM_Duty1;					
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; 	
	DMA_InitStructure.DMA_BufferSize = 0; 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 	
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High; 		
	DMA_InitStructure.DMA_FIFOMode 			 = DMA_FIFOMode_Disable; 
	DMA_InitStructure.DMA_FIFOThreshold		 = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst		 = DMA_MemoryBurst_Single;
	DMA_InitStructure.DMA_PeripheralBurst	 = DMA_PeripheralBurst_Single;
 
	DMA_Init(DMA1_Stream4, &DMA_InitStructure); 			 	
	//DMA_Cmd(DMA1_Stream4, ENABLE); 		
	
	TIM_DMACmd(PWM1_TIM, TIM_DMA_CC1, ENABLE);
 
	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);		
	DMA_ClearFlag(DMA1_Stream4,DMA_FLAG_TCIF4); 
	DMA_ITConfig(DMA1_Stream4,DMA_IT_TC,ENABLE); 
	
	TIM_SetCompare1(PWM1_TIM,0);
	//TIM_Cmd(PWM1_TIM, ENABLE);
}
 
void TIMx_Dma_Pwm_Output_Test(void)
{
	int i = 0,j = 0;
	while(1)
	{
		for(i = 0;i < 6;i++)
		{
			TIMx_Dma_Pwm_Output(PWM_TEST,(u32)(pls_num1[i]*1000),50,pls_num1[i],1);
			delay_ms(300);
		}
	}
}
 
void TIMx_Dma_Pwm_Output(TIM_TypeDef* TIMx,u32 Frequency,u8 Duty,u16 PluseNum,bool DIR) 
{
	uint32_t pwm_period;
 
	if(TIMx == PWM1_TIM)
	{
		pwm_period = PWM_CK_CNT1/Frequency - 1; 
		PWM_Duty1=(pwm_period+1)*Duty/100; 
 
		TIM_Cmd(PWM1_TIM, DISABLE);
		
		TIM_SetCounter(PWM1_TIM, 0); 
		TIM_SetAutoreload(PWM1_TIM, pwm_period); 
		
		TIM_SetCompare1(PWM1_TIM,PWM_Duty1); 
 
		DMA_Cmd(DMA1_Stream4, DISABLE); 
		DMA_SetCurrDataCounter(DMA1_Stream4,PluseNum+1); 
		DMA_Cmd(DMA1_Stream4, ENABLE); 
		TIM_Cmd(PWM1_TIM, ENABLE); 
	}
}
 
void DMA1_Stream4_IRQHandler(void)
{
 if(DMA1->HISR & 0x00000020)
 {
	//PWM1_TIM->CCR1 = 0;//Zeroing in this way will generate one more pulse when output 500 pulses
	DMA1->HIFCR = (uint32_t)(DMA_FLAG_TCIF4|DMA_FLAG_HTIF4 & (uint32_t)0x0F7D0F7D );
	DMA_ClearFlag(DMA1_Stream4,DMA_FLAG_TCIF4|DMA_FLAG_HTIF4);
 }
}

waclawek.jan
Super User
September 23, 2020

It's not a good idea to try to stop the PWM output based on interrupts. You should use linked timers instead, count cycles of the PWM-generating timer by another linked timer, and then that another timer can drive DMA which changes the first timer's CCRx.

JW

JCK.1
JCK.1Author
Associate
September 24, 2020

Maybe I know what you said,thanks a lot!