【微控制器入門】(四)應用層軟體開發的微控制器學習之路-----ESP32開發板PWM控制電機以及中斷的使用

2022-11-01 06:00:24

引言

各位大佬,晚上好啊,在上一篇部落格中,我們講了什麼是UART串列埠通訊,以及使用USB轉TTL使得微控制器可以和c#上位機做一個串列埠通訊,接下來,為大家帶來PWM的概念原理,以及實際案例,使用PWM對電機進行速度調變,因為本課程的最後是做一個紅外遙控的智慧小車,所以是需要電機四個,驅動四個,輪胎四個,所以PWM對於最後的成果也是極為重要,並且在實際開發中,PWM也是比較常用的調速方式。

概念

PWM全稱Pulse width modulation,中文翻譯為脈衝寬度調變,其基本原理為控制方式就是對逆變電路開關器件的通斷進行控制,使輸出端得到一系列幅值相等但寬度不一致的脈衝,用這些脈衝來代替正弦波或所需要的波形。也就是在輸出波形的半個週期中產生多個脈衝,使各脈衝的等值電壓為正弦波形,所獲得的輸出平滑且低次諧波少。按一定的規則對各脈衝的寬度進行調變,既可改變逆變電路輸出電壓的大小,也可改變輸出頻率。

可能上面對於原理的解釋過於官方,大家可能看不懂,通俗易懂的來說,就是通過對電子元器件的電路進行高低電平進行控制,在一段時間內,高低電平在輸出會形成一段波動,這個波動可以成為PWM波形,而我們需要使用程式碼去控制PWM的輸出波形,高電平在這一段波動中,通電時間即高電平時間是佔了總時間多少,同時在這一段PWM波形中,高低電平來回切換的頻率又是多少,形成了這麼一段波形,這就引入了兩個概念,佔空比(Duty Ratio)和頻率,佔空比代表著,高電平通電總時和總時的一個佔比(這段波形中,高低電平的總共佔用時間),而頻率則是高低電平在這段波形中,來回切換的一個頻率。

如下圖,下方在Arduino串列埠繪圖器中,展示了一段鋸齒波形,看下方的GIF我們可以看到對應的電機運動也是有快到慢的一個運動狀態。

程式碼解析



void setup() {
  Serial.begin(9600);
  ledcSetup(0, 5000, 8);
  ledcAttachPin(12, 0);
}

// the loop function runs over and over again forever
void loop() {
   for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
    ledcWrite(0, dutyCycle);
    delay(7);
    Serial.println(dutyCycle);
  }
}

在Arduino中我們可以使用LEDC來實現對PWM的控制,而在純c樂鑫的開發板中,是可以使用MCPWM進行控制,但是由於Arduino在此處不能使用MCPWM,則就有了LEDC作為替代品,ESP32帶有一個16通道的一個LED PWM控制器,對應使用的是樂鑫的LED PWM控制,ESP32 LED PWM,分為8路高速通道和8路低速通道,然後我們使用不同的頻率,和佔空比來實現控制電機轉速的控制。

在上面的程式碼中,我們先設定了ledc的通道為0,頻率為5000,第八個低速LED控制器,即程式碼為 ledcSetup(0, 5000, 8);然後需要將通道和引腳進行管理使用ledcAttachPin(12, 0);將引腳12和第0個通道關聯起來,在loop程式碼中,可以看到,我們寫入的最大的佔空比為255,而0-255總數為256,那是因為,佔空比是和通道是有關係的,上文提到,LED的PWM控制器一共有16個,此處我們使用8,而256則為2的8次方的值,所以佔空比最大為256,如果取值為10,佔空比的最大值則為1024-1;ledcwrite(0,dutyCycle);則是將佔空比寫入對應的通道,便完成了PWM對電機進行調速設定。

Arduino針對ESP32 樂鑫PWM的封裝,目前已知的有LEDC,不需要安裝,預設就可以使用,而其他的也有對於PWM的封裝,個人測試了一兩個倒也沒有這個好用,後續各位朋友也可以繼續探索其他好用的PWM庫進行開發。

中斷

在上面講完PWM之後,我們再來講一下中斷,以及中斷的一個實際案例。中斷,顧名思義,是在程式執行期間,遇到某一個事件的時候,將暫停手上的工作先去執行某一件事情,這個事情則是我們中斷當下工作,去執行的事情,這個動作,稱之為中斷。雖然在程式碼中,可以註冊一個後臺任務(在純c中),進行不停的while,但是這樣在效能上還是無法發揮微控制器的功效,所以這種場景下我們便需要使用中斷,來實現我們的某種功能,例如使用按鈕,來判斷是否需要開啟LED,或者是其他的行為。

在Arduino中,我們可以使用attachInterrupt函數來進行對引腳增加中斷以及使用detachInterrupt來移除中斷,

attachInterrupt函數需要三個引數,第一個為中斷需要使用的引腳pin,第二個為中斷觸發的函數,第三個為中斷的型別,對於ESP32的中斷,在Arduino中,其方法名前面必須加一個IRAM_ATTR標記其為中斷函數,第一個函數中的digitalPinToInterrupt為將27和中斷進行一個繫結,同時還有其他方法,但是官方均不推薦,

在下方的程式碼中,我們定義了一個change的函數用來處理ESP32 27引腳的中斷,用27引腳的電平控制LED引腳2的電平,以此來控制是否點亮LED燈,先設定引腳2為輸出模式,27引腳為上拉輸入模式,可以理解為上拉電阻的一般都需要用到這種模式,然後我們將引腳27和中斷進行關聯,設定中斷函數為change,模式為CHANGE。然後在LOOP函數中,我們給引腳2寫入state的值,當進入change中斷函數中,會將state取反,然後進入loop寫入值。以此實現控制LED的顯示和不顯示,在這裡,提醒一下,由於在微控制器中,中斷以及定時器都是非阻塞模式,而Serial.println函數是阻塞寫入緩衝區,會導致中斷函數會不斷的輸出錯誤,錯誤:Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1).

這是因為println函數阻塞導致定時器無法繼續執行,所過非要使用此函數,可以嘗試設定中間變數,然後在loop函數中判斷是否改變值,然後進行輸出資訊到串列埠。

可以在下方GIF看到,我們使用按鈕進行控制LED的顯示和不顯示。

volatile byte state = LOW;

void IRAM_ATTR change()
{
   state=!state;
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  pinMode(27, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(27), change, CHANGE);
}

void loop() {
  digitalWrite(2, state);// put your main code here, to run repeatedly:
}

可以看到第二個方法是傳入一個interrupt的中斷編號,但是ESP32上面的中斷編號,不在官方資料中,所以我們只有需要第一個方法來進行引腳和中斷函數的關聯,當然了可能最後一個也是可以,只是此處我沒有嘗試,感興趣的可以進行嘗試,

在mode中,Arduino是支援五種模式,第一種為LOW,,看翻譯我們知道,這個是在電平處於低電平時會觸發中斷函數,

第二個CHANGE是不管是高到低,還是低到高,都會觸發

第三種是引腳在由低電平到高電平時觸發,而不是已經到了高電平觸發,

第四種是下降,當電平由高到低時會觸發中斷函數,

第五種是電平處於高電平時會觸發中斷函數。

結語

今天講了PWM還有中斷的使用,可能一次性講的有點多,有點難以消化,有什麼不懂的可以及時問我,以及後面我更新的時間會稍微慢一點,防止講的過快,一時間不明白,後面還會有對於IIC,SPI的一個案例講解,在這些講完後,我會開始準備最終極的目標,做一個智慧小車,其中會需要的配件,這兩天我會總結好發到群裡,以及購買連結。有感興趣的同學可以加QQ群,一起學習,一起討論,博主也是一個剛開始玩微控制器的學徒,後面也會研究stm32系列微控制器,歡迎大家加入討論,學習。