BLDC開發筆記2.六步PWM輸出


6步PWM互補輸出

6步PWM輸出是對 F103 的 TIM1 進行配置成PWM輸出模式,帶剎車和死區功能。按照模塊化進行初始化配置。勾選keil中的C99標准(支持任意地方定義變量)。

GPIO初始化

打開相應功能模塊時鍾,將TIM1 的TIx引腳配置為復用推挽輸出模式,BKIN(剎車)引腳配置為浮空輸入模式。

通過在頭文件進行宏定義配置,在硬件改變的時候方便修改

#define BLDC_TIMx                       TIM1
#define BLDC_TIM_APBxClock_FUN          RCC_APB2PeriphClockCmd
#define BLDC_TIM_CLK                    RCC_APB2Periph_TIM1

#define BLDC_TIM_GPIO_APBxClock_FUN     RCC_APB2PeriphClockCmd
#define BLDC_TIM_GPIO_CLK               (RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB)
#define BLDC_TIM_CH1_PORT               GPIOA
#define BLDC_TIM_CH1_PIN                GPIO_Pin_8   //通道1
#define BLDC_TIM_CH2_PORT               GPIOA      
#define BLDC_TIM_CH2_PIN                GPIO_Pin_9   //通道2
#define BLDC_TIM_CH3_PORT               GPIOA
#define BLDC_TIM_CH3_PIN                GPIO_Pin_10  //通道3

#define BLDC_TIM_CH1N_PORT              GPIOB
#define BLDC_TIM_CH1N_PIN               GPIO_Pin_13  //互補通道1
#define BLDC_TIM_CH2N_PORT              GPIOB
#define BLDC_TIM_CH2N_PIN               GPIO_Pin_14  //互補通道2
#define BLDC_TIM_CH3N_PORT              GPIOB
#define BLDC_TIM_CH3N_PIN               GPIO_Pin_15  //互補通道3

#define BLDC_TIM_BKIN_PORT              GPIOB
#define BLDC_TIM_BKIN_PIN               GPIO_Pin_12  //剎車輸入

GPIO初始化函數

static void BLDC_TIMx_GPIO_Config(void) 
{
        //GPIO初始化結構體
        GPIO_InitTypeDef GPIO_InitStruct;
    
        //打開GPIOA和GPIOB的時鍾和復用功能時鍾
        BLDC_TIM_GPIO_APBxClock_FUN(BLDC_TIM_GPIO_CLK|RCC_APB2Periph_AFIO, ENABLE); 

	//通道1引腳配置
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_CH1_PIN;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;	
	GPIO_Init(BLDC_TIM_CH1_PORT,&GPIO_InitStruct);
	
	//通道2引腳配置
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_CH2_PIN;
	GPIO_Init(BLDC_TIM_CH2_PORT,&GPIO_InitStruct);
	
        //通道3引腳配置
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_CH3_PIN;
	GPIO_Init(BLDC_TIM_CH3_PORT,&GPIO_InitStruct);
	
	//互補通道1引腳配置
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_CH1N_PIN;
	GPIO_Init(BLDC_TIM_CH1N_PORT,&GPIO_InitStruct);
	
        //互補通道2引腳配置
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_CH2N_PIN;
	GPIO_Init(BLDC_TIM_CH2N_PORT ,&GPIO_InitStruct);
	
	//互補通道3引腳配置
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_CH3N_PIN;;
	GPIO_Init(BLDC_TIM_CH3N_PORT,&GPIO_InitStruct);
	
        //BKIN pin 引腳配置
	GPIO_InitStruct.GPIO_Pin=BLDC_TIM_BKIN_PIN;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(BLDC_TIM_BKIN_PORT ,&GPIO_InitStruct);
}

時基初始化

時基配置

APB2時鍾為72Mhz,預分頻系數PSC為0,(PSC決定記一次的時間),方便計算

將PWM頻率為 f=20khz,故定時器計數周期ARR=72M/(PSC+1)/ f,因為我們預分頻系數為0,故 ARR=72M/(0+1)/20k=3600。

通過在h文件宏定義配置這些參數:

//PWM頻率
#define BLDC_TIM_PWM_FREQ   20000 
// 定時器預分頻系數
#define BLDC_TIM_PRESCALER  0
//定時器計數周期
#define BLDC_TIM_PERIOD    (uint16_t)(SystemCoreClock/(BLDC_TIM_PRESCALER+1)/BLDC_TIM_PWM_FREQ) 
//定時器重復寄存器數值
#define BLDC_TIM_REPETITIONCOUNTER   0

時基初始化函數

這里需要說明的是 TIM_TimeBaseInitStruct.TIM_ClockDivision ,時鍾分割系數,她實際上配置的是定時器控制寄存器1的 CKD [1:0]。

她的作用主要在兩個方面:

  1. 死區時間配置,死區時間發生器需要一個死區時鍾來計數,她通過內部時鍾 CK_INT 分頻得來,后面詳細講計算。這里我將CKD配置為00,即不分頻,還是72Mhz。
  2. 當使用外部時鍾模式(ETR/TIx)/輸入捕獲功能時(TIx),如果頻率太高需要降頻,或者濾波時,需要一個時鍾對這些信號進行采樣,采樣時鍾 \(F_{DTS}=CK_{INT}/CKD/N\),N是數字濾波器濾波長度
  3. 當CKD[1:0]=00,不對CK_INT分頻,當CKD[1:0]=01,對CK_INT進行2分頻,當CKD[1:0]=10,對CK_INT進行4分頻。
static void BLDC_TIMx_TimeBaseInit(void)
{  	
	//打開TIM1時鍾
	BLDC_TIM_APBxClock_FUN(BLDC_TIM_CLK,ENABLE); 
    
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;  //時基初始化結構體
  
        /*時基初始化*/
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;  //時鍾分割為1
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;  //向上計數
	TIM_TimeBaseInitStruct.TIM_Period=BLDC_TIM_PERIOD;  //計數周期
	TIM_TimeBaseInitStruct.TIM_Prescaler=BLDC_TIM_PRESCALER;  //預分頻
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=BLDC_TIM_REPETITIONCOUNTER;  //重復計數器值為0,不重復
	TIM_TimeBaseInit(BLDC_TIMx,&TIM_TimeBaseInitStruct);
    
        //定時器使能預裝載功能
        TIM_ARRPreloadConfig(BLDC_TIMx,ENABLE);  //ARR預裝載
}

輸出比較模式初始化

輸出比較模塊的功能框圖:

  1. 將定時器配置為輸出比較模式——PWM1模式。將定時器配置為向上計數模式,當計數器數值小於輸出比較寄存器的值時,即TIMx_CNT<TIMx_CCR1,輸出有效電平。有效電平的選擇通過TIM_OCInitStruct.TIM_OCPolarity這個成員配置,我將其配置為高電平有效。

  2. 需要注意的是,TIM_OCInitStruct.TIM_OCIdleStateTIM_OCInitStruct.TIM_OCNIdleState這兩個成員是配置引腳空閑狀態的,就是關閉定時器時的輸出電平,所以在剎車功能有效時(關閉定時器輸出),我們不能將同一橋的兩個輸入都配置為高電平,否則就燒mos了。這里我將兩個空閑狀態都配置為低電平。

  3. 占空比=CCR/ARR

static void BLDC_TIMx_OCInit(void)
{
  TIM_OCInitTypeDef TIM_OCInitStruct;  //輸出比較初始化結構體

  //輸出比較通道1模式配置
  TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;  //TIMx_CNT<TIMx_CCR1,輸出有效電平
  TIM_OCInitStruct.TIM_OCIdleState=TIM_OCIdleState_Reset;  //關閉定時器時空閑狀態為高電平
  TIM_OCInitStruct.TIM_OCNIdleState=TIM_OCIdleState_Reset;
  TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;   //輸出有效電平為高電平
  TIM_OCInitStruct.TIM_OCNPolarity=TIM_OCNPolarity_High;
  TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;     //輸出使能
  TIM_OCInitStruct.TIM_OutputNState=TIM_OutputNState_Enable;   //互補通道輸出使能
  TIM_OCInitStruct.TIM_Pulse=0;  //設置占空比,即CCR值,這里不使用,后面用庫函數配置
  TIM_OC1Init(BLDC_TIMx ,&TIM_OCInitStruct);

  //輸出比較通道2模式配置
  TIM_OCInitStruct.TIM_Pulse=0;
  TIM_OC2Init(BLDC_TIMx, &TIM_OCInitStruct);

  //輸出比較通道3模式配置
  TIM_OCInitStruct.TIM_Pulse=0;
  TIM_OC3Init(BLDC_TIMx, &TIM_OCInitStruct);

  //輸出比較使能預裝載功能
  TIM_OC1PreloadConfig(BLDC_TIMx,TIM_OCPreload_Enable);  //CCR1預裝載
  TIM_OC2PreloadConfig(BLDC_TIMx,TIM_OCPreload_Enable);  //CCR2預裝載
  TIM_OC3PreloadConfig(BLDC_TIMx,TIM_OCPreload_Enable);  //CCR3預裝載
}

死區和剎車功能初始化

static void BLDC_TIMx_BDTRInit(void)
{
  TIM_BDTRInitTypeDef TIM_BDTRInitStructure;  //剎車與死區初始化結構體
  
  //剎車功能初始化,配置斷路時通道輸出狀態,以及死區時間
  /* Automatic Output enable, Break, dead time and lock configuration*/
   
  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;//運行模式下“關閉狀態”選擇
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;//空閑模式下“關閉狀態”選擇
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; //鎖定設置,防止軟件出錯,提供寫保護
  TIM_BDTRInitStructure.TIM_DeadTime = 10;  //死區時間
  TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;  //使能剎車功能
  TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;  //剎車輸入極性,高電平有效
  TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;
  TIM_BDTRConfig(BLDC_TIMx, &TIM_BDTRInitStructure);
}

死區時間的計算

關於死區時間的計算,她是在剎車和死區寄存器(TIMx_BDTR)中的UTG[7:0]: 死區發生器設置 (Dead-time generator setup)中進行配置。

舉例說明,假設將成員配置為TIM_BDTRInitStructure.TIM_DeadTime = 10;這個成員實際上配置的就是UTG[7:0]的值。

  1. 十進制10的二進制表示為00001010,可得DTG[7:5]=000,故死區持續時間DT=DTG[7:0]*Tdtg=10*Tdtg=10*Tdts

  2. Tdts由控制寄存器CR1中的CKD決定,前面我們已經分析過,我們將CKD[1:0]=00(即不分頻),故Tdts=1/72M

  3. 綜上,我們可以算出,死區持續時間DT=10/72M≈138.9ns

PWM輸出總初始化

static void BLDC_TIMx_PWM_Init(void)
{
  //GPIO初始化  
  BLDC_TIMx_GPIO_Config();  
  //時基初始化
  BLDC_TIMx_TimeBaseInit(); 
  //輸出比較模式初始化
  BLDC_TIMx_OCInit();  
  //死區和剎車功能初始化
  BLDC_TIMx_BDTRInit();  

  //定時器使能
  TIM_Cmd(BLDC_TIMx, ENABLE);

  //PWM輸出使能,配置的是BDTR寄存器的MOE位,高級定時器獨有。
  TIM_CtrlPWMOutputs(BLDC_TIMx, ENABLE);

  //關閉定時器輸出比較
  TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Disable);
  TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Disable);
  TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Disable);
  TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Disable);
  TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Disable);
  TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Disable);
		
}

這樣就初始化完畢了。

換相函數

換相函數是要根據霍爾換相時序表編寫,按照順序對給定MOS管PWM信號,這里采用的是H-PWM-L-ON驅動方式,所以上橋CCR按照占空比給定,下橋CCR給定ARR值,讓她一直高電平

在頭文件中宏定義占空比

//PWM占空比
#define speed_duty 15  //占空比為15/100,注意這里沒有除以100,只是定義數值

函數TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t CompareX),配置的就是CCR的值,我們知道占空比=CCR/ARR,所以CCR=ARR*(speed_duty/100)

void BLDC_PHASE_CHANGE(uint8_t uwstep)
{
  switch(uwstep)
	{
	  case 6: //B+ C-
			
	        //輸出比較通道1配置
    	        TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Disable);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Disable);
		
		//輸出比較通道2配置
		TIM_SetCompare2(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/100);
    		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Enable);
		
	        //輸出比較通道3配置
                TIM_SetCompare3(BLDC_TIMx,BLDC_TIM_PERIOD);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Enable);
							
	  break;
		
	 case 2: //B+ A-
			
		//輸出比較通道3配置
                TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Disable);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Disable);
		
	        //輸出比較通道1配置
		TIM_SetCompare1(BLDC_TIMx,BLDC_TIM_PERIOD);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Enable);
		
		//輸出比較通道2配置
	        TIM_SetCompare2(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/100);
    		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Enable);
		
	  break;
		
	 case 3: //C+ A-
			
		//輸出比較通道2配置
                TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Disable);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Disable);	
		
	        //輸出比較通道1配置
		TIM_SetCompare1(BLDC_TIMx,BLDC_TIM_PERIOD);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Enable);
		
	        //輸出比較通道3配置
                TIM_SetCompare3(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/100);
	        TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Enable);
		
	  break;
		
	 case 1: //C+ B-
			
		//輸出比較通道1配置
    		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Disable);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Disable);
		
	        //輸出比較通道2配置
		TIM_SetCompare2(BLDC_TIMx,BLDC_TIM_PERIOD);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Enable);
		
	        //輸出比較通道3配置
                TIM_SetCompare3(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/100);
	        TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Enable);

	  break;
		
	 case 5: //A+ B-
					
		//輸出比較通道3配置
                TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Disable);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Disable);
						
		//輸出比較通道1配置
		TIM_SetCompare1(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/100);
    		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Enable);
		
	        //輸出比較通道2配置
                TIM_SetCompare2(BLDC_TIMx,BLDC_TIM_PERIOD);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Enable);
    
           break;
		
	 case 4: //A+ C-
				
		//輸出比較通道2配置
                TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Disable);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Disable);
		
		//輸出比較通道1配置
		TIM_SetCompare1(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/100);
    		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Enable);

		//輸出比較通道3配置
                TIM_SetCompare3(BLDC_TIMx,BLDC_TIM_PERIOD);
	        TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Enable);
		
	break;		
		
     default:  //關閉輸出
			
		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Disable);
		TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Disable);
		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Disable);
		TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Disable);
		TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Disable);
		TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Disable);
		
        break;		
	}
}

波形測試

通過邏輯分析儀看MCU輸出的六路PWM信號,判斷輸出邏輯正不正確。我用的是Saleae logic 16.

在下橋高電平期間,上橋是占空比為20%的矩形波。

其中發現下橋高電平期間會出現低電平問題,是因為邏輯分析儀采樣問題,在設置為500KS/S時候,低電平時間正好是2us。

在在設置為1MS/S時候,低電平時間正好是1us.

而用示波器(只有兩路)看的波形則沒有這種問題,在此留個小坑,待深入了解一下邏輯分析儀為什么會出現這種問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM