1. 程式人生 > >STM32 PWM波驅動模擬舵機(庫函式版)

STM32 PWM波驅動模擬舵機(庫函式版)

數字舵機 vs 模擬舵機 “數字舵機區別於傳統的模擬舵機,模擬舵機需要給它不停的傳送PWM訊號,才能讓它保持在規定的位置或者讓它按照某個速度轉動,數字舵機則只需要傳送一次PWM訊號就能保持在規定的某個位置。”

"到底模擬舵機與數碼舵機在實際使用中有什麼區別呢?我自己總結大致有以下幾點:

1 數碼舵機在位置準確度方面要高於模擬舵機。
2 在同樣標稱1.6公斤的舵機面前數碼舵機在實際表現中會感覺更加“力氣大”而模擬舵機就會“肉”點。
3 模擬舵機由於控制晶片是類比電路,所以即便是相同型號的舵機會存在小小的效能差異,而數碼舵機在一致性方面就非常好。
4 數碼舵機一般均採用PID優化演算法,所以,線性要好過模擬舵機。
5 對於高靈敏度的控制,建議選擇數碼舵機,如直升機的控制,高速固定翼飛機,高速滑翔機,比賽用車膜型,雲臺的控制等
6 對於不是特別需要靈敏度的場合,如低速固定翼(二戰飛機,練習機,低速滑翔機等),船模,娛樂用車模等。可以考慮模擬舵機。

"(http://www.321mx.com/blog/548.html)

基本上參考《STM32不完全手冊——庫函式版本》

pwm.c 初始化函式

//PWM輸出初始化
//arr:自動重灌值
//psc:時鐘預分頻數

void TIM_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4, ENABLE);// 使能定時器時鐘TIM2/TIM3/TIM4
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB , ENABLE);  //使能GPIOA/GPIOB外設時鐘使能
	                                                                     	

   //設定該引腳為複用輸出功能,輸出TIM2 CH3(PA2複用輸出)、TIM2 CH4(PA3複用輸出)的PWM脈衝波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //TIM2_CH3(PA2複用輸出)、TIM2 CH4(PA3複用輸出)
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9; //TIM3的3、4通道,TIM4的3、4通道
  GPIO_Init(GPIOB,&GPIO_InitStructure);     //B口的0、1對應TIM3的3、4通道,B口的8、9對應TIM4的3、4通道,設定為複用推輓輸出

	
	TIM_TimeBaseStructure.TIM_Period = arr; //設定在下一個更新事件裝入活動的自動重灌載暫存器週期的值	 50HZ
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //設定用來作為TIMx時鐘頻率除數的預分頻值  不分頻
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設定時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位

 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈衝寬度調製模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //設定待裝入捕獲比較暫存器的脈衝值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高


  TIM_OC3Init(TIM2, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的引數初始化外設TIMx	
	TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);  //CH3預裝載使能	
  TIM_OC4Init(TIM2, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的引數初始化外設TIMx	
	TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);  //CH4預裝載使能
  TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的引數初始化外設TIMx	
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //CH3預裝載使能	
  TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的引數初始化外設TIMx	
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //CH4預裝載使能	
	TIM_OC3Init(TIM4, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的引數初始化外設TIMx	
	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);  //CH3預裝載使能	
  TIM_OC4Init(TIM4, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的引數初始化外設TIMx	
	TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);  //CH4預裝載使能
	
	
	TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIMx在ARR上的預裝載暫存器
	TIM_CtrlPWMOutputs(TIM2,ENABLE);	//MOE 主輸出使能
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2
	TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的預裝載暫存器
	TIM_CtrlPWMOutputs(TIM3,ENABLE);	//MOE 主輸出使能
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	TIM_ARRPreloadConfig(TIM4, ENABLE); //使能TIMx在ARR上的預裝載暫存器
	TIM_CtrlPWMOutputs(TIM4,ENABLE);	//MOE 主輸出使能
	TIM_Cmd(TIM4, ENABLE);  //使能TIM4
}

int main(void)
{
u16 pwmval1=749;//1.5ms->90度

delay_init(); //延時函式初始化

TIM_PWM_Init(9999,143);//不分頻。PWM 頻率=72*10^6/(9999+1)/(143+1)=50Hz

while(1)
{
//調節佔空比pwmval1/(9999+1)
TIM_SetCompare3(TIM2,pwmval1);TIM_SetCompare4(TIM2,pwmval1);
TIM_SetCompare3(TIM3,pwmval1);TIM_SetCompare4(TIM3,pwmval1);
TIM_SetCompare3(TIM4,pwmval1);TIM_SetCompare4(TIM4,pwmval1);
}

}

以下轉自:http://www.cnblogs.com/ghdnui/articles/3429732.html

void TIM_Configuration(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    GPIO_InitTypeDef    GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4 , ENABLE);
    
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //TIM3的1、2通道    ,產生PWM
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);       //PA的6,7口對應TIM3的1、2通道,設定為複用推輓輸出

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //TIM4的1、2、3、4通道
    GPIO_Init(GPIOB,&GPIO_InitStructure);     //B口的6,7,8,9對應TIM4的1、2、3、4通道,設定為複用推輓輸出

    TIM_TimeBaseStructure.TIM_Period =9999;           //自動過載週期值
    TIM_TimeBaseStructure.TIM_Prescaler =143;          //預分頻值 ,這裡是50HZ
    TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;        //時鐘分割
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;      //計數摸式為向上計數
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);         //TIM3,和TIM4用的相同配置,寫入配置  ,PWM頻率為50HZ
                                                                      
    //設定佔空比
    
    TIM_OCStructInit(& TIM_OCInitStructure);      //恢復初始
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  //定時器模式為pwm模式1
    TIM_OCInitStructure.TIM_Pulse =0;              //脈衝值,即輸出都是低電平
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;     //極性為高
    
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);     //將配置資料寫入TIM3的通道1
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);    //預裝載使能
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

    TIM_OC1Init(TIM4, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
    TIM_OC2Init(TIM4, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
    TIM_OC3Init(TIM4, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
    TIM_OC4Init(TIM4, &TIM_OCInitStructure);
    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);//TIM4的4個通道都用相同的配置
    TIM_Cmd(TIM3, ENABLE);
    TIM_CtrlPWMOutputs(TIM3, ENABLE);
    TIM_Cmd(TIM4, ENABLE);
    TIM_CtrlPWMOutputs(TIM4, ENABLE);          //使能PWM模式
}

void SetJointAngle(u8 ID, float angle)
{
    switch(ID)
    {
        case 0:                                      //-90°~90°   
            angle=angle+90.0;                      
            angle=(u16)(50.0*angle/9.0+249.0);     
            TIM_SetCompare1(TIM3,angle);        
            break;
                                                 //0°~180°
        case 1:
            angle=(u16)(4.175*angle+409.25);
            TIM_SetCompare2(TIM3,angle);          
            break;


        case 2:                                    //-150°~0°
            angle=-angle;
            angle=(u16)(4.175*angle+480.0);
            TIM_SetCompare1(TIM4,angle);
            break;

        case 3:
            angle=-180-angle;
            angle=-angle;
            angle=(u16)(4.175*angle+315.0);
        

            TIM_SetCompare2(TIM4,angle);
            break;
                                              //-90°~90°
        case 4:
            angle=90.0+angle;
            angle=(u16)(249.0+50.0*angle/9.0);
            TIM_SetCompare3(TIM4,angle);            
            break; 


        default: break;
    }    
}

舵機原理:http://blog.csdn.net/a2009374138/article/details/8772432

1、什麼是舵機

       舵機是一種位置(角度)伺服的驅動器,適用於那些需要角度不斷變化並可以保持的控制系統。目前在高檔遙控玩具,如航模,包括飛機模型,潛艇模型;遙控機器人中已經使用得比較普遍。舵機是一種俗稱,其實是一種伺服馬達。

        

2、工作原理

       控制訊號由接收機的通道進入訊號調製晶片,獲得直流偏置電壓。它內部有一個基準電路,產生週期為20ms,寬度為1.5ms的基準訊號,將獲得的直流偏置電壓與電位器的電壓比較,獲得電壓差輸出。最後,電壓差的正負輸出到電機驅動晶片決定電機的正反轉。當電機轉速一定時,通過級聯減速齒輪帶動電位器旋轉,使得電壓差為0,電機停止轉動。當然我們可以不用去了解它的具體工作原理,知道它的控制原理就夠了。就象我們使用電晶體一樣,知道可以拿它來做開關管或放大管就行了,至於管內的電子具體怎麼流動是可以完全不用去考慮的。

3、舵機的控制

        舵機的控制一般需要一個20ms左右的時基脈衝,該脈衝的高電平部分一般為0.5ms~2.5ms範圍內的角度控制脈衝部分。以180度角度伺服為例,那麼對應的控制關係是這樣的:
      0.5ms-----------    0度
      1.0ms-----------  45度
      1.5ms-----------  90度
      2.0ms-----------135度
      2.5ms-----------180度

      小型舵機的工作電壓一般為4.8V或6V,轉速也不是很快,一般為0.22/60度或0.18/60度,所以假如你更改角度控制脈衝的寬度太快時,舵機可能反應不過來。如果需要更快速的反應,就需要更高的轉速了。

      舵機上有三根線,分別為VCC、GND、訊號線。而不需要另外接驅動模組,直接用微控制器的管腳控制就行了。控制訊號一般要求週期為20ms的PWM訊號。

      如果要更為精確的控制舵機(轉動角度差<=1度),則需要控制輸出PWM訊號的佔空比,

      例如:我可以把0~180分為1024份(可以任取,決定與定時器的時鐘頻率),範圍為0.5ms~2.5ms

                  則可以得到0.09度/us,因此可以由 PWM=0.5+N*0.09(N是角度)控制舵機轉動0~180度間的任意角度。

轉自:http://blog.csdn.net/gtkknd/article/details/39296151

首先,本人雖然初學STM32但極力反對一種誤人子弟的觀點:“對於STM32這樣級別的MCU,有庫函式就不用去看暫存器怎麼操作的了!”

好了,言歸正傳,最近總看到很多朋友對於PWM這個實驗有很多的疑惑,看到原子也在極力的回覆也挺累的(體諒一下幸苦的原子大神,(*^__^*) ),所以我打算寫這麼一篇文字來闡述一下我個人對STM32的PWM的理解。

首先來說,你要使用PWM模式你得先選擇用那個定時器來輸出PWM吧!除了TIM6、TIM7這兩個普通的定時器無法輸出PWM外,其餘的定時器都可以輸出PWM,每個通用定時器可以輸出4路PWM,高階定時器TIM1、TIM8每個可輸出7路PWM,這裡為了方便起見,我們選擇與實驗相同的TIM3的通道2來說明。選好定時器及通道後,下一步就是要使能定時器的時鐘,根據需要看看是否需要重對映IO,然後就是配置輸出PWM的IO及定時器,到這裡原子的視訊及例程都有詳細的介紹,這裡只需要提一點有些網友疑惑的TIM_TimeBaseStructure.TIM_ClockDivision = 0;這句話是什麼作用?其實仔細看過技術手冊後發現這句話與PWM輸出實驗其實是沒關係的,這句話是設定定時器時鐘(CK_INT)頻率與數字濾波器(ETR,TIx)使用的取樣頻率之間的分頻比例的(與輸入捕獲相關),0表示濾波器的頻率和定時器的頻率是一樣的。至於其餘部分,我就不再贅述。做完這些準備工作後,我就針對大多數朋友疑惑的地方——PWM模式的初始化設定做一個詳細的闡述:先貼程式碼

     1       TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈衝寬度調製模式2
     2       TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
     3       TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
     4       TIM_OC2Init(TIM3, &TIM_OCInitStructure);   //根據T指定的引數初始化外設TIM3 OC2
     5       TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的預裝載暫存器,即TIM3_CCR2的預裝載值在更新事件到來時才能被傳送至當前暫存器中。
     6       TIM_Cmd(TIM3, ENABLE);  //使能TIM3
這6句話就把PWM的通道配置好了,一句句來解釋:
這裡原子選擇的PWM2模式,為什麼選擇的是PWM2模式呢?為什麼不選擇PWM1模式呢?兩者又有什麼區別呢?下面我們就一探究竟,PWM1和PWM2模式是由CCMR1的OC1M和OC2M來決定的,因為我們選擇的是是通道2,所以設定的是OC2M,再看相關介紹
OC1M[2:0]:輸出比較1模式(Output compare 1 enable)

110:PWM模式1- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1為有效電平,否則為
無效電平;在向下計數時,一旦TIMx_CNT>TIMx_CCR1時通道1為無效電平(OC1REF=0),否
則為有效電平(OC1REF=1)。
111:PWM模式2- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1為無效電平,否則為
有效電平;在向下計數時,一旦TIMx_CNT>TIMx_CCR1時通道1為有效電平,否則為無效電
平。

 看到紅色的“有效電平”了吧,那麼這又是誰定義的呢?別急,再看手冊,可知它是由CCER這個暫存器的CCxP來決定的這裡是通道2,所以是CC2P,繼續看介紹

CC1P:輸入/捕獲1輸出極性(Capture/Compare 1 output polarity)  位1 
CC1通道配置為輸出:
0:OC1高電平有效
1:OC1低電平有效
現在很清楚了吧,又因為第3句,TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高,所以這裡我們設定的CC2P是0,也就是預設的OC2高電平有效。這樣第3句話也捎帶著解釋了,哈哈!由於我們的戰艦板的LED是低電平亮,而剛開始的給CC2P用來設定佔空比的led0pwmval為0它是小於等於TIM3_CNT的,也就符合TIMx_CNT>=TIMx_CCR1時通道2輸出有效電平,也就是高電平,所以你把原子的例程原封不動的Down到板子裡,會看到剛上電,LED燈是不亮的。現在這塊明白了吧!若你覺得還是不爽,我就非得用PWM1模式,那也可以,就像有個網友說“我拿原子的PWM Code就改了一個PWM1模式,按原子講的PWM1和PWM2的輸出是相反的啊,可是我上電發現LED是常亮的啊?怎麼回事啊,求解釋啊。。。”我們來分析一下這位朋友的程式碼,他把PWM2改成了PWM1,別的什麼都沒動,那麼現在符合“PWM模式1- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1為有效電平”
,否則為無效電平。“結果必然是就是LED長亮嘍,要想得到跟原代媽一樣的效果,那就把CC2P設定成1,OC2低電平有效,這樣就可以了,有興趣的朋友可以動手試試!(實踐出真知嗎!)
好了,廢了這麼多話,也不早了 洗洗睡吧!希望這篇文字對PWM有疑惑的朋友有所幫助!希望大家共同進步!分享是一種快樂,歡迎批評指正!

 遺漏了一點,第5句還沒解釋呢,

5       TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的預裝載暫存器,即TIM3_CCR2的預裝載值在更新事件到來時才能被傳送至當前暫存器中。
這句話是說,CCR2中的預裝載值何時被傳送到當前的CNT暫存器中,這裡我們選擇的是當更新事件到來的時候才裝載,追蹤暫存器的設定可知,原來設定的是CCMR1的OC2PE,其實還有一種方式是立即裝載看手冊:
OC1PE:輸出比較1預裝載使能(Output compare 1 preload enable)  位3 
0:禁止TIMx_CCR1暫存器的預裝載功能,可隨時寫入TIMx_CCR1暫存器,並且新寫入的數
值立即起作用。
1:開啟TIMx_CCR1暫存器的預裝載功能,讀寫操作僅對預裝載暫存器操作,TIMx_CCR1的
預裝載值在更新事件到來時被傳送至當前暫存器中。


注意:

舵機供電得特別注意;供電電流必須能夠驅動舵機呢。 否則 結果不如人意。舵機的電一般是單獨提供的呢。