Showing posts with label PWM. Show all posts
Showing posts with label PWM. Show all posts

Saturday, December 16, 2006

Motor-controlling PWMs

一個脈寬調變(Pulse-width Modulation, PWM)訊號可控制一顆 DC motor 轉速,或決定一具 servomotor 的方向、位置或轉速。在複雜的機器人身上,常用上好幾顆馬達,因而能以一顆微控制器(microcontroller, uC)產生多組 PWM 訊號是非常實用的。

前陣子在 RobotFun.net 論壇看到一群機器人愛好者討論自製串列伺服控制器(serial servo controller, SSC)的討論。後來又在 CSZone 的 Robotics 版跟 happosaiMasterChang 討論了「以 uC 產生多組 PWM 訊號」的方法。這次就對這個議題作個整理:

Busy Waiting

我們先來看個最直接的作法:

 1: // List 1. PWM loop busy-waiting version (for servo)
 2: 
 3: int main()
 4: {
 5:     // Here put initial code
 6:     ...
 7: 
 8:     for (;;) {
 9:         for (i=0; i<NUM_OF_PWM; ++i)
10:             Pulse(&PWM[i], DutyCycle[i]);  // precision in us
11: 
12:         // calculates the delay of all pulses above
13:         pulse_delay = 0;
14:         for (i=0; i<NUM_OF_PWM; ++i)
15:             pulse_delay += DutyCycle[i];
16: 
17:         Pause(PERIOD - INNATE_DELAY - pulse_delay);  // precision in ms will do
18: 
19:         // Do other thing here
20:         ...
21:     }
22: } 
  • 上面虛擬碼的作法,限制條件是:
    • PERIOD ≥ INNATE_DELAY + pulse_delay
      • INNATE_DELAY 要視核心頻率(Fcore)及 code 最佳化情形來給定。
      • pulse_delay 則嚴重限制了此法能控制的 PWM 組數。
  • 這個方法可造出非常精準的 pulse width ,但產生的 PWM 不夠一般化:
    1. 它要求每個 PWM 的 period 都要一樣。
      • 這在通常的應用,每俱 servo 都一樣時,問題不大。
    2. 當 duty cycle 太大時,可控制的 PWM 組數減少。
      • 對控制 servo 動作的 PWM 而言,相對於 period ,其 pulse width 都不大,所以還可以接受。
  • 因此,此法較適用於控制 servos 。

Time Slicing

為了改進上述缺點,接下來就看看運用 timer interrupts 將時間切片的作法:

 1: // List 2. PWM Timer time-slicing version
 2: 
 3: void interrupt TimerISR_for_PWM1()
 4: {
 5:     ...
 6: 
 7:     for (i=0; i<NUM_OF_PWM; ++i) {
 8:         if (cycle[i] < DutyCycle[i])
 9:             PWM[i] = HI;
10:         else
11:             PWM[i] = LOW;
12: 
13:         ++cycle[i];
14:         cycle[i] %= Period[i];
15:     }
16: } 
  • 這個方法的限制條件為:
    • TICK_INTERVAL ≥ INNATE_DELAY
      • 這裡的 INNATE_DELAY 指的是 interrupt instruction cycles 佔的時間
      • INNATE_DELAY 也由核心頻率(Fcore)及 code 最佳化情形決定。
      • TICK_INTERVAL 是 timer ticks 的間隔時間;減短這個間隔,可造出更精準的 PWM 。
  • duty cycle 最小可為 0 ,最大可佔滿整個 PWM period;
  • 除了用於 servo 外,也非常適於 LED 亮度控制, DC motor 轉速控制等。
  • 只要設好 tick interval,注意要大於 interrupt instructions 執行的時間即可。
  • 相較於 List 1 中 busy waiting 的作法,此法較方便在不同 uC 間移植。
  • 此外,此法有動作冗餘(redundance),較 robust 。

我們可經由去除動作冗餘來換取較少的 interrupt instruction cycles:

 1: // List 3. Another PWM Timer time-slicing version
 2: 
 3: void interrupt TimerISR_for_PWM2()
 4: {
 5:     ...
 6: 
 7:     for (i=0; i<NUM_OF_PWM; ++i) {
 8:         if (cycle[i]==0  &&  DutyCycle[i]>0)
 9:             PWM[i] = HI;
10:         else if (cycle[i] == DutyCycle[i])
11:             PWM[i] = LOW;
12: 
13:         ++cycle[i];
14:         cycle[i] %= Period[i];
15:     }
16: } 
  • 此法比 List 2 作法佔用更少的 instruction cycles ,所以可控制更多 PWMs 。
  • 把 for loop 展開的話,程式雖變得累贅,但可進一步減少 instruction cycles 。
  • 容許的 instruction cycles 受限於 timer ticks 間隔可執行的 instruction 數。

顯然,有個副程式把 List 2 或 List 3 中的 duty cycles 清為 0 是很方便的:

1: // List 4. Initializes PWMs
2: 
3: void ResetPWM()
4: {
5:     for (i=0; i<NUM_OF_PWM; ++i)
6:         //PWM[i] = LOW;
7:         cycle[i] = 0;
8: } 

The Combination

List 2 或 List 3 的作法雖能產生夠一般化的 duty cycles 了,在核心頻率(Fcore)不太大提昇下,想造出高解析(resolution)的 pulse 時,還是得回到 List 1 那種純粹、直接的 loop busy-waiting。

以下就來看看綜合兩者優點(和缺點)的作法:

 1: // List 5. The combination: time-slicing with busy-wating
 2: 
 3: void interrupt TimerISR_for_PWMPeriod()
 4: {
 5:     ...
 6: 
 7:     for (i=0; i<NUM_OF_PWM; ++i) {
 8:         if (cycle[i] == 0)
 9:             bPulseNow[i] = true;
10: 
11:         ++cycle[i];
12:         cycle[i] %= Period[i];
13:     }
14: }
15: 
16: 
17: int main()
18: {
19:     // Here put initial code
20:     ...
21: 
22:     for (;;) {
23:         // for PWM pulse part
24:         for (i=0; i<NUM_OF_PWM; ++i) {
25:             if (bPulseNow[i]) {
26:                 Pulse(&PWM[i], DutyCycle[i]);  // precision in us
27:                 bPulseNow[i] = false;
28:             }
29:         }
30: 
31:         // Do other thing here
32:         ...
33:     }
34: } 
  • 這個綜合法一方面以 interrupt time-slicing 決定 PWM 週期,另一方面又以 loop busy-waiting 產生高解析的 duty cycles 。
  • 它綜合兩者的優點和缺點,非常適用在 servos 的高解析控制場合。

結論

  • 組數不多的 servos 控制訊號,可用 List 1 的 busy wating 產生。
  • 純粹的 interrupt time-slicing 較適用在 DC motors, LED flashing and brightness 及不需高解析控制 servos 的場合。
  • 在要控制盡量多的 servos 且又要求高解析的 pulses 時,也許可以試一下綜合法(List 5)。

補充說明

  • 為了有精準的 duty cycle ,產生 pulse 期間,不可讓其他中斷插進來。
  • 其他工作,例如 SSC 的 command 可在非 pulse 期間處理。

建議閱讀

Tags: [] [] [] [] []