STM32输出差分PWM驱动桥式电路

(为免误导,特免责声明如下:本文所有内容,只是基于个人当前理解和实际做法,后面亦会有更正和修订,但任何版本都不免有个人能力有限、理解有误或者工作环境不同的状况,故文中内容仅供参考。任何人都可以借鉴或者直接使用代码片段,但对任何直接引用或者借鉴产生的技术问题等后果,作者不承担任何责任。)

1 目的

采用Stm32单片机产生脉宽可调的4路PWM信号驱动桥式电路。PWM周期4us,4路中1、3两路是同步的,2.4两路也是同步的,1,3与2,4之间错开。pwm的脉宽是一组设定的值,不停轮换发送。

2 技术方案

2.1 利用死区,错开两路(我忘了 不知道为啥后来没走这条路)

2.2 利用Stm32单片机的PWM的两种类型,一种是计数器大于比较值输出1,另一种是计数器小于比较值输出1,产生交错的PWM输出。

2.3利用DMA把预存在数组里的脉宽值传输到PWM设定中

3 关键点的实现

3.1 PWM 输出

  • 初始化
    时钟计数方式设置为中央对齐模式1,即从0—>TIM_Period ->0计数(注意周期是两倍的TIM_Period 计数时间)
    使用TIM3的4路PWM通道,
    通道2,4设置为PWM模式2:当计数值小于TIM_Pulse ,输出低,大于输出高,初始输出低,所以TIM_Pulse 初始值设为TIM_Period +1
    通道1,3设置为PWM模式1:当计数值小于TIM_Pulse ,输出高,大于输出低,初始输出低,所以TIM_Pulse 初始值设为0
	TIM_TimeBaseStructure.TIM_Period = 143;      	 			//定时器周期
	TIM_TimeBaseStructure.TIM_Prescaler=0;	   				 	//设置预分频:不预分频,即为72
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;				//设置时钟分频系数:不分频
	TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_CenterAligned1;//中央对齐模式1 
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
	/* PWM1 Mode configuration: 通道2,4 */
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;	    	//配置为PWM模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	
	TIM_OCInitStructure.TIM_Pulse = 144;//TIM_3_CH24_CCR;	   		//设置跳变值,当计数器计数到这个值时,电平发生跳变
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  	//当定时器计数值小于CCR1_Val时为高电平
	TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Set;
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);	 				//TIM3 PWM 通道2 PA7
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);					 //TIM3 PWM 通道4  PB1
	/* PWM1 Mode configuration: 通道1,3*/
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	    //配置为PWM模式1
	TIM_OCInitStructure.TIM_Pulse = 0;//TIM_34_CH2_CCR;	  //
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
	TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Set;	
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);	  //TIM3 PWM 通道1  PA6
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);	  //TIM3 PWM 通道3  PB0

	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);//TIM3 PWM 通道1 设置
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//TIM3 PWM 通道2 设置
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);//TIM3 PWM 通道3 设置
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);//TIM3 PWM 通道4 设置
  • 运行中改变/设置参数
//设置高频参数
/*输入参数: uKHz  0-250:代表100-350 kHz
   					uPercentage 0-45:占空比 0-45%
   参数不在范围内返回错误 
*/
u8 PWM_T3_SetPara(u8 uKHz,u8 uPercentage)
{
   u16 uFreq,uPeriod,uCCR1,uCCR2; 
   if((uKHz>250) || (uPercentage>45))return 1;
   uFreq=uKHz+100;   //将0-250映射到100-350KHz
   uPeriod=36000/uFreq;
   uCCR1=uPeriod*uPercentage/100;
   uCCR2=uPeriod-uCCR1;
   
   TIM3->CNT=uCCR1+1;   //把计时器当前计数值放在中间,时钟还没启用,计数值不会变,所以四路PWM都没有输出
   TIM3->ARR=uPeriod;
   TIM3->CCR1=uCCR1;//
   TIM3->CCR3=uCCR1;//
   TIM3->CCR2=uCCR2;//
   TIM3->CCR4=uCCR2;//
   
   return 0;
}
  • 启动PWM

//启动TIM3 的四路PWM
void ENPWM_TIM3(void)
{		
		TIM_Cmd(TIM4, ENABLE);
		TIM_ClearITPendingBit( TIM4, TIM_IT_Update);		
	    TIM_ITConfig( TIM4, TIM_IT_Update, ENABLE);	
		PWM_T3_SetPara(sysWorkPara[0],sysWorkPara[1]);//设置高频PWM参数	
		TIM_SelectOCxM(TIM3,TIM_Channel_1,TIM_OCMode_PWM1);
		TIM_CCxCmd(TIM3,TIM_Channel_1,TIM_CCx_Enable);
		TIM_SelectOCxM(TIM3,TIM_Channel_3,TIM_OCMode_PWM1);
		TIM_CCxCmd(TIM3,TIM_Channel_3,TIM_CCx_Enable);
		TIM_SelectOCxM(TIM3,TIM_Channel_2,TIM_OCMode_PWM2);
		TIM_CCxCmd(TIM3,TIM_Channel_2,TIM_CCx_Enable);
		TIM_SelectOCxM(TIM3,TIM_Channel_4,TIM_OCMode_PWM2);
		TIM_CCxCmd(TIM3,TIM_Channel_4,TIM_CCx_Enable);	
		TIM_CtrlPWMOutputs( TIM3, ENABLE);		
		TIM_Cmd(TIM3, ENABLE); 
}
  • 关闭PWM
//停止TIM3的四路PWM
void DENPWM_TIM3(void)
{		
		TIM_Cmd( TIM3, DISABLE);			// 关闭定时器3	
		TIM3->CNT=72;	
		TIM_SelectOCxM(TIM3,TIM_Channel_2,TIM_ForcedAction_InActive);
		TIM_SelectOCxM(TIM3,TIM_Channel_4,TIM_ForcedAction_InActive);
		TIM_SelectOCxM(TIM3,TIM_Channel_1,TIM_ForcedAction_InActive);
		TIM_SelectOCxM(TIM3,TIM_Channel_3,TIM_ForcedAction_InActive);
}

3.2 DMA方式修改脉宽

  • 准备数据,初始化DMA
u32 DMA_AddrofData[8]={0,(u32)DMA_Currency1_Data,(u32)DMA_Currency2_Data,(u32)DMA_Currency3_Data,(u32)DMA_Currency4_Data,(u32)DMA_Currency5_Data,(u32)DMA_Currency6_Data,(u32)DMA_Currency7_Data};
#define DMA_CHAN DMA1_Channel3    /*DMA 通道,根据激发条件*/
void DMA_PWM_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;  /*指定DMA通道DMA1_Channel3*/
	 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  /*开中断*/
	 NVIC_Init(&NVIC_InitStructure);	
}
  • 启动DMA
void DMA_PWM_Start(void)
{	
		if(!DMA_DataNumbers)return; 
		else DMA_SetCurrDataCounter(DMA_CHAN,DMA_DataNumbers); 	 /*再次重启需要重新设置数据量*/	
		DMA_Cmd(DMA_CHAN, ENABLE);/* DMA1 Channel3 enable */	
		TIM3->CNT=72;   //把计时器当前计数值放在中间,时钟还没启用,计数值不会变,所以四路PWM都没有输出
		TIM3->ARR=143;
		TIM3->CCR1=28;//
		TIM3->CCR3=28;//
		TIM3->CCR2=116;//
		TIM3->CCR4=116;//
	
		TIM_SelectOCxM(TIM3,TIM_Channel_1,TIM_OCMode_PWM1);
		TIM_CCxCmd(TIM3,TIM_Channel_1,TIM_CCx_Enable);
		TIM_SelectOCxM(TIM3,TIM_Channel_3,TIM_OCMode_PWM1);
		TIM_CCxCmd(TIM3,TIM_Channel_3,TIM_CCx_Enable);
		TIM_SelectOCxM(TIM3,TIM_Channel_2,TIM_OCMode_PWM2);
		TIM_CCxCmd(TIM3,TIM_Channel_2,TIM_CCx_Enable);
		TIM_SelectOCxM(TIM3,TIM_Channel_4,TIM_OCMode_PWM2);
		TIM_CCxCmd(TIM3,TIM_Channel_4,TIM_CCx_Enable);
		
		TIM_Cmd(TIM3, ENABLE); 
		TIM_CtrlPWMOutputs( TIM3, ENABLE);	

}
  • 根据参数,选用DMA数据源
    因为手册里只给了一个通道的DMA地址,所以采用BURST传输方式,一次传输4个连续地址(数据准备的时候也要注意匹配)
void DMA_PWM_SetWidth(u8 uWidId)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DataNumbers=DMA_DataNumofCurrency[uWidId];
	
	DMA_DeInit(DMA_CHAN);
			DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(TIM3->DMAR);  /*DMA外设地址,Burst 模式选择TIM3的DMAR*/
			DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMA_AddrofData[uWidId];     //src
			DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;   //内存到外设

			DMA_InitStructure.DMA_BufferSize = DMA_DataNumbers;
			DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
			DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;    //

			DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
			DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
			DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输完成结束,不做循环传输:DMA_Mode_Circular;
	
			DMA_InitStructure.DMA_Priority = DMA_Priority_High;
			DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA_CHAN, &DMA_InitStructure);

	TIM_DMAConfig(TIM3, TIM_DMABase_CCR1,TIM_DMABurstLength_4Transfers);//设置burst 方式DMA,一次传4个外设地址,CCR1 CCR2 CCR3 ,CCR4

//
	TIM_DMACmd(TIM3, TIM_DMA_CC4, ENABLE);  //采用 CH4的比较事件作为DMA请求信号,所以DMA通道是channel3
	DMA_ITConfig(DMA_CHAN, DMA_IT_TC, ENABLE); //开中断
}
  • DMA传输完成,调用中断
/**
	DMA 传输完成中断,
*/
void DMA1_Channel3_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC3))
  {
		TIM_Cmd( TIM3, DISABLE);			// 关闭定时器3	
		TIM3->CNT=72;	
		TIM_SelectOCxM(TIM3,TIM_Channel_2,TIM_ForcedAction_InActive);
		TIM_SelectOCxM(TIM3,TIM_Channel_4,TIM_ForcedAction_InActive);
		TIM_SelectOCxM(TIM3,TIM_Channel_1,TIM_ForcedAction_InActive);
		TIM_SelectOCxM(TIM3,TIM_Channel_3,TIM_ForcedAction_InActive);
 
		DMA_Cmd(DMA_CHAN, DISABLE);/* DMA1 Channel3 关闭 */
		DMA_ClearITPendingBit(DMA1_IT_TC3); //清除中断标志
 }
}

——————————
需要500关注,拜托点个关注,谢谢

你可能感兴趣的:(自编代码分享,项目记录,stm32,嵌入式硬件,单片机)