使用 BLDC 霍爾感測器當作位置編碼器 - 第 3 篇

使用 Teensy 3.5 微控制器計算位置、方向和距離

以下資訊旨在協助解讀霍爾效應感測器的邏輯輸出,用以確定位置、方向與速度。雖然此輸出可用於馬達換向,但這個 BLDC 馬達操作層面並非本文探討範圍。

請參見本部落格系列的第 1 篇第 2 篇,回顧此專案到目前為止的內容。

概覽

當 BLDC 中的三個霍爾效應感測器輸出饋送到微控制器時,訊號可以像三通道編碼器一樣接受處理。資料可進行顯示,或用於確定脈衝數、轉動方向和平均每分鐘轉數 (RPM)。對 RPM 進行平均是為了讓顯示的值平順地遞進。

PJRC Teensy 3.5 開發板

SparkFun 的 PJRC Teensy 3.5 開發板 (1568-1464-ND) 具有足夠的數位中斷,能處理來自霍爾感測器的三個訊號輸入,同時還配有焊入式排針座。由於具有足夠的額外 I/O 通道,Teensy 3.5 功能十分強大,能夠執行許多額外的任務,並可搭配內建的 SD 卡,實現資料記錄功能。

圖 1:SparkFun Electronics 的 Teensy 3.5 評估板。(圖片來源:SparkFun Electronics)

使用試驗電路板測試感測器輸出和 PJRC Teensy 3.5

使用試驗電路板 (438-1045-ND 或類似產品),將 Teensy 3.5 的 USB 連接器放在右側,將上方排針座引腳插入隔板上方的第一排試驗電路板孔洞 (圖 2)。這樣就可以留出空間,將感測器輸出連接到 Teensy I/O。

圖 2:連有 Teensy 3.5 開發板及跳接線的試驗電路板。(圖片來源:DigiKey)

使用實心跳接線 (BKWK-3-ND) 完成所有的試驗電路板連接。將 5 V、1 A 電源供應器的正極 (+) 引線連接到試驗電路板的上方或下方正電軌,隨後將負極 (-) 電源引線連接到上方或下方負電軌。將霍爾感測器連接器的正極 (紅色) 和負極 (黑色) 引線,分別連接到試驗電路板的正負軌,然後以任意順序將連接器的三條感測器引線,連接到 Teensy 3.5 的引腳 2、3 和 4。

感測器輸出為低態動作,因此觸發時,此輸出會連接到負電軌。未觸發時,需要將感測器輸出向上拉至正電軌,以建立兩個定義的邏輯狀態。在試驗電路板中插入三個 4 KΩ 至 8 KΩ 的電阻,即可用作感測器輸出的上拉電阻 (圖 2)。

使用 micro B 轉標準 A 型 USB 纜線,將 Teensy 3.5 連接至電腦。

軟體

Teensy 3.5 與 Arduino 整合式開發環境 (IDE) 相容,可實現編程用途。IDE 和 Teensyduino 附加組件均可從網路上取得。請遵循 https://www.pjrc.com/teensy/td_download.html 的安裝步驟繼續操作。

下面提供的編程範例程式碼使用三個硬體中斷,來監測霍爾感測器輸出的任何變化 (上升緣和下降緣)。一旦發生中斷,即會讀取 Teensy 3.5 的歷時時脈,以及三個輸入引腳中的兩個引腳。接著比較感測器的值以確定轉動方向,然後進行其他計算,以確定脈衝數和平均 RPM。透過將當前時脈值與先前儲存的前一個中斷時脈值進行比較,計算出兩個中斷之間的時間。

在 void loop 中,有四個值可用於進行序列列印。註解或取消註解程式碼行,可停用或啟用序列列印功能,然後將程式碼下載到 Teensy 並啟動序列監視器,可查看即時資料。旋轉 BLDC 馬達,在列印監視器中觀察數值的變化。

註:序列列印功能會降低微控制器的速度。I/O 中斷會導致顯示值暫停和跳轉,因為根據定義,每當輸入引腳改變狀態時,序列列印過程就會中斷。如果不使用顯示功能,請確保從程式碼中註解排除掉所有序列列印功能。

複製/* BLDC Hall Sensor read and calculation program for Teensy 3.5 in the Arduino IDE (Ver.1).DigiKey*/

/***************************** Variables *********************************/

#define CW             1			// Assign a value to represent clock wise rotation
#define CCW           -1			// Assign a value to represent counter-clock wise rotation

bool HSU_Val = digitalRead(2);		// Set the U sensor value as boolean and read initial state
bool HSV_Val = digitalRead(3);		// Set the V sensor value as boolean and read initial state 
bool HSW_Val = digitalRead(4);		// Set the W sensor value as boolean and read initial state 

int direct = 1;				// Integer variable to store BLDC rotation direction
int pulseCount;				// Integer variable to store the pulse count

float startTime;				// Float variable to store the start time of the current interrupt 
float prevTime; 				// Float variable to store the start time of the previous interrupt 
float pulseTimeW; 			// Float variable to store the elapsed time between interrupts for hall sensor W 
float pulseTimeU; 			// Float variable to store the elapsed time between interrupts for hall sensor U 
float pulseTimeV; 			// Float variable to store the elapsed time between interrupts for hall sensor V 
float AvPulseTime; 			// Float variable to store the average elapsed time between all interrupts 

float PPM;				// Float variable to store calculated pulses per minute
float RPM; 				// Float variable to store calculated revolutions per minute

/***************************** Setup *********************************/

void setup()
{
// Set digital pins 2, 3 and 4 as inputs
  pinMode(2, INPUT);			
  pinMode(3, INPUT);			
  pinMode(4, INPUT);

// Set digital pins 2, 3 and 4 as interrupts that trigger on rising and falling edge changes.Call a function (i.e.HallSensorU) on change
  attachInterrupt(digitalPinToInterrupt(2), HallSensorU, CHANGE);      
  attachInterrupt(digitalPinToInterrupt(3), HallSensorV, CHANGE);
  attachInterrupt(digitalPinToInterrupt(4), HallSensorW, CHANGE);

// Initialize the print monitor and set baud rate to 9600 
  Serial.begin(9600);
}

/*************************** Main Loop ******************************/

void loop()
{
  if ((millis() - prevTime) > 600) RPM = 0;                                     			// Zero out RPM variable if wheel is stopped

  //Serial.print(HSU_Val); Serial.print(HSV_Val); Serial.println(HSW_Val);      	// Display Hall Sensor Values
  //Serial.println(direct);                                                    				// Display direction of rotation
  //Serial.println(pulseCount);                                                 			// Display the pulse count
  Serial.println(RPM);                            		      			// Display revolutions per minute
}

/************************ Interrupt Functions ***************************/

void HallSensorW()
{
  startTime = millis();						// Set startTime to current microcontroller elapsed time value
  HSW_Val = digitalRead(4);					// Read the current W hall sensor value
  HSV_Val = digitalRead(3);						// Read the current V (or U) hall sensor value 
  direct = (HSW_Val == HSV_Val) ?CW : CCW;			// Determine rotation direction (ternary if statement)
  pulseCount = pulseCount + (1 * direct);				// Add 1 to the pulse count
  pulseTimeW = startTime - prevTime;				// Calculate the current time between pulses
  AvPulseTime = ((pulseTimeW + pulseTimeU + pulseTimeV)/3);	// Calculate the average time time between pulses
  PPM = (1000 / AvPulseTime) * 60;					// Calculate the pulses per min (1000 millis in 1 second)
  RPM = PPM / 90;						// Calculate revs per minute based on 90 pulses per rev
  prevTime = startTime;						// Remember the start time for the next interrupt
}

void HallSensorV()
{
  startTime = millis();
  HSV_Val = digitalRead(3);
  HSU_Val = digitalRead(2);					// Read the current U (or W) hall sensor value 
  direct = (HSV_Val == HSU_Val) ?CW : CCW;
  pulseCount = pulseCount + (1 * direct);
  pulseTimeV = startTime - prevTime;				
  AvPulseTime = ((pulseTimeW + pulseTimeU + pulseTimeV)/3);		
  PPM = (1000 / AvPulseTime) * 60;					
  RPM = PPM / 90;
  prevTime = startTime;
}

void HallSensorU()
{
  startTime = millis();
  HSU_Val = digitalRead(2);
  HSW_Val = digitalRead(4);					// Read the current W (or V) hall sensor value		
  direct = (HSU_Val == HSW_Val) ?CW : CCW;
  pulseCount = pulseCount + (1 * direct);
  pulseTimeU = startTime - prevTime;				
  AvPulseTime = ((pulseTimeW + pulseTimeU + pulseTimeV)/3);		
  PPM = (1000 / AvPulseTime) * 60;					
  RPM = PPM / 90;
  prevTime = startTime;
}

註:編程人員也許想將重複的中斷函數程式碼分解成額外的函數,以簡化整個編程。但這麼做可能會導致額外的函數中斷,並導致數值在不同的計算之間發生更改,進而產生資料錯誤。如試驗電路板測試步驟和程式碼中所述,感測器輸入的順序只會影響對轉動方向的確定。取消註解與變數「direct」有關的序列列印行,即可在顯示監視器中查看值。確認這個值是否保持為 1 或 -1,這取決於您轉動轉輪的方式。如果偏離,在相應的中斷函數中,反轉三進制程式碼的「CW」和「CCW」,以修正輸出。

總結

BLDC 霍爾感測器現設定為低解析度的三通道編碼器,能夠提供準確的資料來輔助導航及轉輪位置感測,而不會妨礙其主要的馬達控制功能。有些 BLDC 控制器則只使用反電動勢來確定線圈和磁鐵的位置,造成霍爾感測器輸出僅用於導航和位置感測。但無論是哪種方式,感測器給使用者帶來的價值都遠不止是馬達控制。

其他資源:

Arduino IDE:http://www.arduino.cc/en/Main/Software

Teensyduino:https://www.pjrc.com/teensy/td_145/TeensyduinoInstall.exe

關於作者

Image of Don Johanneck

Don Johanneck 是 DigiKey 的技術內容開發人員,自 2014 年加入本公司。他於近期轉任此職位,負責編撰影片說明與產品內容。Don 透過 DigiKey 獎學金計畫,自北地社區暨技術學院取得電子技術和自動化系統領域的應用科學副學士學位。他喜歡無線電控制模型、古董型機器修復與工藝修繕。

More posts by Don Johanneck
 TechForum

Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.

Visit TechForum