2015-01-12 56 views
0

我正在處理基於arduino mega的quadcopter,並試圖爲4個電機設置PWM頻率 - 每個400hz。
我發現了一個有趣的解決方案,其中4個ATmega2560 16位定時器用於控制4個帶PWM的ESC,因此可以達到400Hz的頻率。 700到2000μs是ESC正在處理的正常脈衝寬度。
1sec/REFRESH_INTERVAL = 1/0.0025 = 400hz。使用4個16位定時器400hz PWM

this is servo.h lib: 
#define MIN_PULSE_WIDTH  700  // the shortest pulse sent to a servo 
#define MAX_PULSE_WIDTH  2000  // the longest pulse sent to a servo 
#define DEFAULT_PULSE_WIDTH 1000  // default pulse width when servo is attached 
#define REFRESH_INTERVAL  2500  // minimum time to refresh servos in microseconds 

#define SERVOS_PER_TIMER  1  // the maximum number of servos controlled by one timer 
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER) 

問題是要使它工作,每個PWM應該用1個16位定時器控制。另外,例如,1個定時器上的2個esc將會給200hz。所以所有的16位定時器都忙於控制4個ESC,但我仍然需要從接收器讀取輸入PPM。要做到這一點,我需要至少一個更多的16位計時器,我不再擁有這個計時器。
它仍然是一個8位定時器免費位它只能讀取0..255數字,而正常的數字操作是1000..2000和東西。

那麼如果我將使用相同的16位定時器同時讀取pwm和ppm,會發生什麼?它會起作用嗎?它會大幅降低速度嗎?
我有arduino與Raspberry Pi配對,它可以控制數據過濾,調試和其他東西,將ppm讀數轉移到Raspberry更好嗎?

+0

你如何做到「ppm讀數」?你使用定時器的輸入捕捉模式嗎?或引腳更改中斷? - 一個8位計數器可以通過軟件擴展到任意寬度;如果仔細完成(ISR延遲!),這也適用於輸入捕獲。 – JimmyB

+0

我正在使用引腳更改中斷。謝謝,軟件8位計數器擴展可能是一個解決方案,感謝新的空間谷歌。 – user3081123

+0

你需要什麼樣的PWM信號分辨率?也就是說,在停止和全速之間你需要多少步驟?這個數字乘以那些400Hz,就可以指出PWM的速度有多快,並且可以判斷軟件PWM是否是一種選擇。 – JimmyB

回答

2

爲了回答您的問題之一:

那麼會發生什麼,如果我將使用相同的16位定時器,PWM和PPM 閱讀?它會起作用嗎?

是的。當您的引腳更改中斷觸發時,您可能只需讀取當前的TCNT值,以查明自從最後一個TCNT值以來的時間。這不會以任何方式干擾定時器的硬件PWM操作。

它會大幅降低速度嗎?

號PWM由專用硬件實現,軟件業務在同一時間運行後會不會影響它的速度,所以並不會有任何的ISR你可能已經激活相應的計時器。因此,您可以讓定時器根據需要生成PWM,並仍然使用它來a)從中讀取當前計數器值,並b)將輸出比較和/或溢出ISR掛接到它以創建軟件擴展定時器。

編輯迴應您的評論:

注意的是,在TCNT寄存器中的實際值是當前定時器(刻度)計數在任何時候,不管PWM是否處於活動狀態。此外,定時器OVerflow中斷(TOV)可用於任何模式。這兩個特性允許通過以下步驟進行任意其他時間測量任務的軟件擴展定時器:

  1. 安裝並激活一個定時器中斷服務程序爲您要使用的定時器/計數器。在ISR中,您基本上只是增加一個(volatile!)全局變量(例如timer1OvfCount),該變量有效計數定時器溢出並因此延長實際的定時器範圍。當前絕對滴答計數可以計算爲timer1OvfCount * topTimerValue + TCNTx。當處理例程(例如引腳變化ISR)發生事件(例如,一個引腳上的上升沿)時,讀取當前定時器/計數器(TCNT)值timer1OvfCount,並將這些值存儲在另一個全局變量(如startTimestamp),有效地開始您的時間測量。
  2. 當第二事件發生時,例如在處理程序中的一個引腳上有一個下降沿(例如引腳改變ISR),你讀取當前定時器/計數器(TCNT)值timer1OvfCount。現在您有startTimestamp中信號開始的時間戳和另一個變量中信號結束的時間戳。這兩個時間戳之間的差異正是您所處的脈衝持續時間。

兩點雖然考慮:

  1. 當使用相位修正PWM模式計時器將計數上下依次之間交替。這使得查找自上次TOV中斷以來傳遞的實際滴答數量稍微複雜一些。
  2. 一段代碼首先讀取TCNT,然後讀取timer1OvfCount和TOV ISR,可能會出現競爭狀態。這可以通過禁止中斷,然後讀取TCNT,然後讀取timer1OvfCount,然後檢查TOV中斷標誌來抵消;如果該標誌置位,則有一個待處理,未處理的溢出中斷 - >啓用中斷並重復。

但是,我很確定有一些庫函數可以維護軟件擴展的定時器/計數器,它們爲您執行所有定時器處理。

+0

謝謝你,你的回答對我來說非常有用。我也不知道16位定時器有3個比較器。現在是時候讓我研究一下,如果一個定時器控制3個pwms,伺服lib會同時使用它們全部3個。如果沒有,我會寫我自己的程序。如果ppm讀數不會影響pwm頻率,則可能不需要製造一個。 * BB – user3081123

+0

嘿,飯能。我已經從假期回來,開始挖掘你的答案,很少有東西對我來說不夠清楚。設想使用標準伺服庫,意味着每個定時器只使用一個比較器(OCRnA)。所以我們需要爲每個伺服設置700,800,700和1000的寬度並讀取ppm。 TCNT1開始計數到OCR1A = 700,TCNT3到OCR3A = 800,TCNT4到OCR4A = 700,TCNT5到OCR5A = 1000。所以所有TCNT寄存器都忙於計數。然後我們得到一個ppm高信號,表示開始讀取數值,所以TCNT應該開始計時,但是它們都忙於計算PWM寬度。不明白。 – user3081123

+0

再次感謝您。你真的幫了我很多。 – user3081123

0

什麼是700和2000的單位?我猜是usec.You沒有在你的問題中提出太多的問題,但是我發現你需要25ms的持續時間,其中700次使用的時間可能是0度,2000年可能是180次現在每個伺服脈衝輸入可以與AVR的任何GPIO相連接,並且這個GPIO向伺服提供PWM信號。所以我想你甚至可以用一個定時器來控制所有的電機。使用這種編碼:

假設你有一個計時器,在每50次使用時產生中斷。 現在如果你想700微秒爲motor1,800微秒電機2900微秒電機3 & 1000微秒電機4則只是這樣做:

#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500 

unsigned short motor1=14; // 700usec = 50x14 
unsigned short motor2=16; // 800usec 
unsigned short motor3=18; // 900usec 
unsigned short motor4=20; // 1000usec 

unsigned char motor1_high_flag=1; 
unsigned char motor2_high_flag=1; 
unsigned char motor3_high_flag=1; 
unsigned char motor4_high_flag=1; 

PA.0 = 1; // IO for motor1 
PA.1 = 1; // IO for motor2 
PA.2 = 1; // IO for motor3 
PA.3 = 1; // IO for motor4 

void timer_inturrupt_at_50usec() 
{ 
    motor1--;motor2--;motor3--;motor4--; 
    if(!motor1) 
    { 
     if(motor1_high_flag) 
     { 
      motor1_high_flag = 0; 
      PA.0 = 0; 
      motor1 = CYCLE_PERIOD - motor1; 
     } 
     if(!motor1_high_flag) 
     { 
      motor1_high_flag = 1; 
      PA.0 = 1; 
      motor1 = 14; // this one is dummy;if you want to change duty time update this in main 
     } 
    } 

    if(!motor2) 
    { 
     if(motor2_high_flag) 
     { 
      motor2_high_flag = 0; 
      PA.1 = 0; 
      motor2 = CYCLE_PERIOD - motor2; 
     } 
     if(!motor2_high_flag) 
     { 
      motor2_high_flag = 1; 
      PA.1 = 1; 
      motor2 = 16; 
     } 
    } 


    if(!motor3) 
    { 
     if(motor3_high_flag) 
     { 
      motor3_high_flag = 0; 
      PA.2 = 0; 
      motor3 = CYCLE_PERIOD - motor3; 
     } 
     if(!motor3_high_flag) 
     { 
      motor3_high_flag = 1; 
      PA.2 = 1; 
      motor3 = 18; 
     } 
    } 

    if(!motor4) 
    { 
     if(motor4_high_flag) 
     { 
      motor4_high_flag = 0; 
      PA.3 = 0; 
      motor4 = CYCLE_PERIOD - motor4; 
     } 
     if(!motor4_high_flag) 
     { 
      motor4_high_flag = 1; 
      PA.3 = 1; 
      motor4 = 19; 
     } 
    } 
} 

&告訴我什麼是ESC?

+1

我想我已經很好地解釋了我的問題。我用的不是伺服電機,而是無刷電機。在ESC上使用伺服庫(電子速度控制器)可以改變RPM每分鐘的轉速,其中ESC最小脈衝長度爲700us,代表電機在全速運行時不移動到脈衝長度2000us。所以重點是讓4個速度控制器接收400hz的PWM信號,這隻有在每個ESC使用1個定時器的情況下才有可能。如果每個定時器使用2個ESC,則會給出2倍的處理速度,即200hz。使用GPIO而不是PWM看起來也會降低頻率。 – user3081123

+0

好吧,但是爲什麼不嘗試上面的程序,它將很容易在四個不同的頻道上使用一個16位定時器產生400赫茲的信號。 –