旋轉編碼器(rotary encoder)也稱為軸編碼器,是將旋轉的機械位移量轉換為電氣信號,對該信號進行處理後檢測位置速度等信號的感測器。檢測直線機械位移量的感測器稱為線性編碼器[1]。一般裝設在旋轉物體中垂直旋轉軸的一面。 ...
旋轉編碼器原理、選型及編碼
原理
旋轉編碼器(rotary encoder)也稱為軸編碼器,是將旋轉的機械位移量轉換為電氣信號,對該信號進行處理後檢測位置速度等信號的感測器。檢測直線機械位移量的感測器稱為線性編碼器[1]。一般裝設在旋轉物體中垂直旋轉軸的一面。
旋轉編碼器用在許多需要精確旋轉位置及速度的場合,如工業控制、機器人技術、專用鏡頭、電腦輸入裝置(如滑鼠及軌跡球)等。
旋轉編碼器可分為絕對型(absolute)編碼器及增量型(incremental)編碼器兩種。增量型編碼器也稱作相對型編碼器(relative encoder),利用檢測脈衝的方式來計算轉速及位置,可輸出有關旋轉軸運動的信號,一般會由其他設備或電路進一步轉換為速度、距離、每分鐘轉速或位置的信號。絕對型編碼器會輸出旋轉軸的位置,可視為一種角度感測器。
分類[2]:
- 以編碼器工作原理可分為:光電式、磁電式和觸點電刷式。
- 以碼盤刻孔方式不同分為:增量式和絕對式兩類。
二者的主要區別在於碼盤的結構和輸出信號的形式不同。增量型編碼器輸出的是脈衝信號,而絕對編碼器輸出的是二進位的數值[10]。
絕對值型編碼器
絕對型編碼器將轉軸的不同位置加以編號,再依目前轉軸位置輸出對應的編號,依構造主要可分為兩種:光學式及機械式[3]。
絕對型編碼器的特點是隨時可以知道轉軸的位置,有時也會將解角器視為是絕對型編碼器。
增量型編碼器
增量型編碼器和絕對型編碼器不同,當轉軸旋轉時,增量型編碼器輸出會隨之變化,根據輸出變化可以檢測轉軸的旋轉量。絕對型編碼器有針對轉軸旋轉的位置給予編號,轉軸不動時根據其輸出的信號可以求得其對應的位置,增量型編碼器無此功能,無法在轉軸不動時得到轉軸旋轉位置的信息。
增量型編碼器可用來感應轉軸旋轉量的信號,再由程式轉化旋轉方向、位置及角度等信息,增量型編碼器可以是線性的,也可以是旋轉型。增量型編碼器因為其低成本,以及其信號容易轉換為運動相關的信號(例如速度)等特性,是最廣為使用的編碼器。
增量型編碼器有機械式的及光學式的,機械式的編碼器需要對信號處理抖動,一般用在消費性產品上的旋鈕。例如大部分家用及車用的收音機就是用增量型編碼器作為音量控制的旋鈕,一般機械式編碼器只適用在轉速不高的應用場合。光學式的編碼器則用在高速或是需要高精準度的場合。
增量型編碼器有兩個主要輸出,分別稱為A和B,兩個輸出是正交輸出,相位差為90度。增量型編碼器的單圈脈衝數(PPR)為其旋轉一圈時會輸出的方波數,如PPR為600表示旋轉一圈時A和B都會輸出600個方波,但先後順序不同。光學式增量型編碼器可以有較高的單圈脈衝數,例如 2500 到 10000。
以下是順時針及逆時針旋轉時,編碼器輸出的變化:
逆時針旋轉的輸出 | 順時針旋轉的輸出 | ||||
---|---|---|---|---|---|
Phase | A | B | Phase | A | B |
1 | 0 | 0 | 1 | 1 | 0 |
2 | 0 | 1 | 2 | 1 | 1 |
3 | 1 | 1 | 3 | 0 | 1 |
4 | 1 | 0 | 4 | 0 | 0 |
二個信號有90度的相位差,在不同旋轉方向時,二個信號的相序也有所不同,可以利用程式將兩個信號進行解碼。根據其相序不同,在有方波時使一計數器上數或是下數,此計數器的值即可對應轉軸的旋轉量。
例如上一次的數值是00,目前的數值是01,表示轉軸已順時針旋轉了四分之一個單位(若單圈脈衝數為600,此處的單位即為六百分之一圈)。根據單位時間的旋轉量可以計算轉速,若是轉速很慢時可以直接根據方波的寬度計算轉速。若轉軸的旋轉速度太快,程式可能會跳過中間的狀態變化,出現無法識別轉軸的旋轉方向或是旋轉方向誤判的情形。
當順時針旋轉時A信號提前B信號90度相位,當逆時針旋轉時B信號提前A信號90度相位,電路接收到旋轉編碼器的A、B信號時,可以根據A、B的狀態組合判定編碼器的旋轉方向。
程式設計中我們可以對A、B信號檢測,檢測A信號的邊沿及B信號的狀態,
- 當A信號上升沿時B信號為低電平,或當A信號下降沿時B信號為高電平,證明當前編碼器為順時針轉動
- 當A信號上升沿時B信號為高電平,或當A信號下降沿時B信號為低電平,證明當前編碼器為逆時針轉動
有些旋轉編碼器除了A相及B相外還有一個輸出,一般稱為Z相,每旋轉一圈Z相信號會有一個方波輸出,可以用來判斷轉軸的絕對位置,例如用在位置控制的系統中。
若旋轉編碼器只有單獨一相的輸出,仍然可以判斷轉軸的轉速,只是不能判斷旋轉的方向。可以用在量測轉速的場合,有時也會以此量測運動的距離。
增量型編碼器輸出A相、B相和Z相分別代表的含義[4]:
編碼器軸每旋轉一圈,A相和B相都發出相同的脈衝個數,但是A相和B相之間存在一個90°(電氣角的一周期為360°)的電氣角相位差,可以根據這個相位差來判斷編碼器旋轉的方向是正轉還是反轉,正轉時,A相超前B相90°先進行相位輸出,反轉時,B相超前A相90°先進行相位輸出(如下圖所示)。編碼器每旋轉一圈,Z相只在一個固定的位置發一個脈衝,所以可以作為複位相或零位相來使用。
選型
在選型或採購旋轉編碼器的時候,需要從多方面進行考慮,特別是在技術參數上需要進行一個技術參數上的參考:包括編碼器的尺寸、類型、解析度、電氣介面等等,總的來說,第一步則是判斷應用需要的是增量編碼器、絕對編碼器還是換向編碼器。一經確定,就必須考慮解析度、安裝方式、電機軸尺寸等其他參數[5]。(對磁場環境有要求的務必不能選擇磁電技術的編碼器!!!)
外形尺寸
除了編碼器的定位止口,軸徑,安裝孔位;安裝空間體積等常規參數,還需考慮比如:安裝空間與選定軸的形態(中空軸、桿軸類):
以及連接線纜類型:
分解率精度
在考慮組裝機械裝置的要求精度和機械的成本的基礎上,選擇最適合的產品。一般選擇機械綜合精度的1/2~1/4精度的解析度。
輸出電路方式
對增量型編碼器而言,其輸出電路有很多類型,當使用高速計數器對編碼器的脈衝信號進行計數時,必須首先搞清楚該編碼器的輸出類型才能正確的接線並調試。
增量型編碼器的輸出電路包括集電極輸出(Collector Output)型、電壓輸出(Voltage Output)型、推輓輸出(Push-Pull Output)型及線驅動輸出(Line Driver Output)型。輸出電路的核心元器件是三極體。我們知道三極體有三個極:基極(Base)、發射極(Emitter)和集電極(Collector)。
最合適的輸出信號類型並不總是那麼明顯,而且往往受到忽視。最常見的三種類型是開集輸出(電壓輸出-E)、推輓輸出(F型HTL格式)和差分線路驅動器輸出。本文將分別介紹這三種輸出類型,幫助大家根據具體應用需求選擇合適的設備。
首要原則
無論是增量編碼器的正交輸出,換向編碼器的電機極輸出,還是使用特定協議的串列輸出,這些編碼器輸出都是數字信號。因此,5 V 編碼器的信號會一直在近似 0 V 與 5 V 之間切換,這兩個電壓分別對應邏輯 0 和 1。增量編碼器的輸出是基本方波,如下圖所示。
開集輸出(開路集電極輸出)
集電極輸出電路是以三極體的發射極為公共端,信號從集電極輸出的電路。由於三極體分為PNP和NPN兩種,相應的,編碼器的集電極輸出電路也分為PNP和NPN兩種。
下麵這張圖是歐姆龍E6B2-CWZ5B編碼器的集電極輸出電路(PNP型),可以看到編碼器的電源(+12V或+24V)通過一個3.3歐姆的電阻連接到三極體的發射極上,而脈衝信號(A/B/Z)則是從集電極進行輸出:
NPN型集電極輸出電路與之類似,只不過發射極(公共端)連接的是0V,如下麵的E6B2-CWZ6C輸出電路:
旋轉編碼器大多採用開集輸出,即輸入信號為高電平時,晶體管的集電極引腳保持開路或斷開。當輸出為低電平時,輸出直接接地。
NPN集電極開路輸出: 輸出電路採用NPN晶體管,發射極直接連接0V端,集電極作為輸出端,輸出端和+V端之間為開路(Open)狀態,這種輸出形態即為NPN集電極開路輸出。
集電極開路輸出是以輸出電路的晶體管發射極作為公共端,並且集電極懸空的輸出電路。一般分為NPN集電極開路輸出和PNP集電極開路輸出。
由於輸入信號為高電平時輸出斷開,需要使用外部“上拉”電阻,才能確保集電極電壓達到所需的電平,即邏輯 1。因此,工程師在連接不同電壓的系統時就更具靈活性:通過上拉電阻可將集電極電壓上拉至不同電壓,使之高於或低於編碼器工作電壓。
不過,這種介面也具有一些缺陷。許多現成的控制器都已內置了上拉電阻,而這些上拉電阻會消耗電流,即產生耗散功率。此外,當該電阻與寄生電容組成 RC 電路時,輸出在高電壓與低電壓之間的轉換速率將因此降低。轉換斜率即轉換速率。
通過降低轉換速率,上拉電阻會顯著降低編碼器運行速度,從而降低增量編碼器的解析度。減小電阻值可以提高轉換速率,但是當信號為低電平時,上拉電阻功耗的電流更大,耗散功率也更大。
當旋轉編碼器的電源電壓與控制部的電源電壓不一致時,此種輸出方式為最佳使用方案[6]。
電壓輸出
電壓輸出型是在NPN型三極體的集電極與電源之間連接一個上拉電阻,這樣集電極的輸出電壓會被鉗制在一個穩定的範圍。如下麵這張圖是歐姆龍E6B2-CWZ3E的電壓輸出電路:
以輸出電路的晶體管發射極為共通型,在集電極與電源間插入電阻,並輸出因電壓而變化的集電極的輸出電路[7]。得集電極和電源之間能有一個穩定的電壓狀態。
電壓輸出就是集電極開路輸出的反相增加一個電阻,構成一個極性是PNP或NPN,而另一個極性是電壓,實際上就是NPN+電壓或PNP+電壓,這是針對是PNP的或NPN的形式的接收設備的一種權宜,便於兩者都可以連接,但現在這種電壓介面往往已經做在了經濟型PLC上了,如果是那樣的PLC,還是應該直接選集電極開路輸出的,或電壓型的極性相當的編碼器,因為如果選電壓輸出型的編碼器PNP+電壓的,而連接的PLC是NPN+電壓的,就會有漏電流而產生錯誤[8]。
推輓輸出
推輓輸出(Push-Pull Output)電路由兩個三極體組成,比如下麵這張圖的T1和T2。
推輓輸出的兩個三極體分別接受輸入信號和該信號的反相信號,當輸入信號(Input Signal)為1時,T1導通,此時輸入信號的反相為0,因此T2截止;同樣的,當輸入信號為0時,T1截止,此時輸入信號的反相為1,因此T2導通;可見推輓式輸出電路可以輸出信號的正反兩相(比如A和A補),其抗干擾能力比較強,適合較遠距離的傳輸。
推輓式放大輸出,有的歐洲編碼器用HTL表示,其相當於NPN+PNP的推輓放大,而且大部分有標準的集成放大電路,根據供電,輸出10—30V,對於接收設備的相容性強,信號強而穩定,若果再有與差分長線驅動一樣有反相信號的話,因信號電壓高,傳遞最遠,差分傳遞及接受,抗干擾最好,工程項目或大型設備中,首選推輓式輸出。
推輓式含反相6通道輸出,也就是HTL-6。重型機械設備常常有較遠傳遞或大變頻電機工況下,就需要選推輓式並且含具有反相輸出的推輓式輸出編碼器(例如ABB變頻控制器,就有這樣的介面。
推輓輸出使用兩個晶體管,而不是一個,因此可以彌補上述開集輸出介面的缺陷。上部晶體管取代上拉電阻,導通時可將電壓上拉至電源電壓,由於電阻極小,因而轉換速率較快。而輸出信號為低電平時,晶體管關斷,因此相較於開集電路,該有源上拉電路的耗散功率也相對較小,從而使電池供電設備的運行時間相對較長。
在+V和0V之間由兩個晶體管迴路構成的輸出方式即為推拉輸出方式。當輸出信號為H時,上面的晶體管為ON,下麵的晶體管為OFF。當輸出信號為L時,則上面的晶體管為OFF,下麵的晶體管為ON。推拉輸出方式由於輸出電流為兩個方向(流入,流出)控制,輸出阻抗較低,波形不容易失真,也不易受到干擾的影響。
當編碼器的連接線較長時,可使用此輸出方式。
差分線路驅動器輸出(線路驅動器輸出)
線驅動輸出(Line Driver Output)電路是使用專用輸出晶元,輸出符合RS422標準的差分信號,抗干擾能力更強,適合用於傳輸速度較高、距離較遠的場合。下麵這張圖,是歐姆龍E6B2-CWZ1X的線驅動輸出電路:
線性驅動輸出是採用RS-422標準,用AM26LS31晶元應用於高速、長距離數據傳輸的輸出模式。信號以差分形式輸出,因此抗干擾能力更強。輸出信號需專門能接收線性驅動輸出的設備才能接收。輸出電路見下圖。
雖然使用推輓輸出的編碼器彌補了開集輸出的一些缺陷,但兩者都是單端輸出。在佈線距離較長的應用或存在電雜訊和干擾的環境中,使用單端輸出具有一定局限性。
佈線距離較長時,信號幅度衰減,電容效應將減慢轉換速率。由於單端信號的傳輸信號以地為參考,這類衰減就可能產生誤差,從而導致系統性能下降。
此外,在電雜訊環境中,不同幅度的干擾電壓都將耦合到電纜上,從而導致單端系統的接收器錯誤地解碼信號電壓。
電纜長度超過一米時,建議使用差分信號。使用差分線路驅動器的編碼器可產生兩個輸出信號:一個與原始信號相匹配,另一個與之完全相反,即互補信號。這兩個信號之間的幅度差是原始單端信號的兩倍,有助於剋服電壓降和電容引起的衰減問題。
此外,由於兩個信號均存在共模雜訊,可以相互抵消,因此接收系統可忽略其影響。由於雜訊抑制能力相當出色,差分線路驅動器介面廣泛用於工業和汽車應用。多種 CUI 編碼器都提供差分線路驅動器輸出選項,可用於要求嚴苛的應用。
本輸出方式採用高速、長距離輸送用的專用IC方式,該IC具有高速應答特性,適用於長距離傳送,不易受干擾影響,是依據RS422-A規格的數據傳送方式。信號以差動的2信號輸出,因此抗干擾能力強。接受線路驅動器輸出的信號時,可使用稱為線路接
該IC具有高速應答特性,適用於長距離傳送,不易受干擾影響。
補碼輸出(互補輸出)
補碼輸出是輸出上具備NPN和PNP 2種輸出晶體管的輸出電路。根據輸出信號的「H」、「L」,2個輸出晶體管交互進行「ON」、「OFF」動作。使用時,請在正極電源、OV上進行上拉、下降後再使用。補碼輸出,包括輸出電流的流出、流入兩個動作,其特征為信號的上、下降速度快,可延長代碼的長距離。比集電極開路輸出的電路傳輸距離能稍遠,可與開路集電極輸入機器(NPN、PNP)連接。
廠商
除了以上幾點外,價格也是十分關鍵的因素,購置到性價比高的產品當然是每個使用者最理想的想法,在選擇旋轉編碼器時要選擇價格與設備性能相符並且符合自己使用的產品,對於同類產品進行多方面的對比,在選擇編碼器廠家時也要儘量選擇大型公司,產品質量和服務質量更加有保障。
硬體電路及接線
電路
註意上拉電阻及信號線的104電容濾波,可以使信號更穩定[10]。
接線(PLC)
增量編碼器的連接,要清楚編碼器的信號輸出形式與接收設備的匹配問題,選編碼器或選接收設備一定要兩者信號形式的匹配。增量編碼器的信號輸出從波形上看,分正餘弦輸出(sin/cos)與方波輸出兩種。
①. 正餘弦輸出的信號是模擬量變化的信號周期,又分電壓輸出1Vpp和電流輸出1uApp,這兩種輸出一般PLC都沒有介面,大部分是連接專用的運動控制卡,其內部可做細分而獲得更高的解析度和動態特性,也有連接專用的細分盒再細分後輸出方波的,選型時搞清楚是電壓輸出還是電流輸出(現在大部分是電壓輸出了)。
②. 方波輸出的也有分集電極開路輸出(NPN或PNP)、電壓輸出、差分長線驅動、推輓式輸出等。
1、開路集電極輸出[9]
集電極開路輸出就是類晶體管放大電路,三極體放大集電極開路輸出,依據三極體的極性,分NPN與PNP,接收設備選型需要匹配不可選錯,這種輸出電路簡單經濟,但選型面窄,傳輸距離根據放大管有遠有近,但總體傳遞距離不遠,且保護不夠,較易損壞,大部分用在單機設備上而不是工程項目中。這種輸出的電壓依據供電,有5-12V輸出和12—24V輸出,這也要搞清楚才能確保信號的連接。
- NPN型集電極輸出
- PNP型集電極輸出
2、 線性驅動輸出(差分信號)
差分長線驅動是一種差分放大的電路,大部分是5V,提供A+、B+、Z+及其180度反相的A—、B—、Z—,讀取時,以A+與A-差分值讀取,對於共摸干擾有抑製作用,傳遞距離較遠,由於抗干擾能力較強,在運動控制(數控機床)中用得較多。
3、 電壓輸出型
電壓輸出就是集電極開路輸出的反相增加一個電阻,構成一個極性是PNP或NPN,而另一個極性是電壓,實際上就是NPN+電壓或PNP+電壓,這是針對是PNP的或NPN的形式的接收設備的一種權宜,便於兩者都可以連接,但現在這種電壓介面往往已經做在了經濟型PLC上了,如果是那樣的PLC,還是應該直接選集電極開路輸出的,或電壓型的極性相當的編碼器,因為如果選電壓輸出型的編碼器PNP+電壓的,而連接的PLC是NPN+電壓的,就會有漏電流而產生錯誤。
4、 互補輸出型
編碼
Raspberry Pi Pico
- C
#include <stdio.h>
#include "pico/stdlib.h"
#define PinA 27
#define PinB 26
#define PinSW 22
uint64_t time = 0;
int32_t count = 0;
int32_t num = 0;
void blinkA()
{
if ((time_us_64() - time) > 3000)
{
if (gpio_get(PinB))
{
count++;
}
else
{
count--;
}
}
time = time_us_64();
}
void init()
{
gpio_init(PinA);
gpio_init(PinB);
gpio_init(PinSW);
gpio_set_irq_enabled_with_callback(PinA, GPIO_IRQ_LEVEL_LOW, true, &blinkA);
gpio_set_dir(PinA, 0);
gpio_set_dir(PinB, 0);
gpio_set_dir(PinSW, 0);
}
int main()
{
stdio_init_all();
init();
time = time_us_64();
int i=0;
while (1)
{
if (num != count)
{
i = 1;
num = count;
printf("%ld\r\n", num);
}
if((!gpio_get(PinSW)) && 1 == i)
{
i = 0;
count = 0;
while(!gpio_get(PinSW));
}
}
return 0;
}
- Python
from machine import Pin,ADC
import utime
#Define pins and their initialization
right = machine.Pin(27, machine.Pin.IN)
left = machine.Pin(26, machine.Pin.IN)
down = machine.Pin(22, machine.Pin.IN)
right_assist = 0
count = 0 #Rotate the value of the encoder
def right_handler(pin):
global right_assist
right.irq(handler=None)
right_assist = 1
def down_handler(pin):
global count
down.irq(handler=None)
count = 0
print("down",count)
down.irq(trigger=machine.Pin.IRQ_FALLING, handler=down_handler)
right.irq(trigger=machine.Pin.IRQ_FALLING, handler=right_handler)
down.irq(trigger=machine.Pin.IRQ_FALLING, handler=down_handler)
while True :
if (right_assist == 1 ):
if (left.value() == 1 ):
count = count - 1
print("left", count)
elif (left.value() == 0 ):
count = count + 1
print("right", count)
while (left.value() == 0 ) | (right.value() == 0):
utime.sleep_ms(1)
right_assist = 0
right.irq(trigger=machine.Pin.IRQ_FALLING, handler=right_handler)
STM32
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t turn_flag;
int32_t count;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include "stdio.h"
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(turn_flag == 1)
{
if(HAL_GPIO_ReadPin(SIB_GPIO_Port, SIB_Pin ) == GPIO_PIN_RESET )
{
count --;
printf("Turn left!\r\n");
while(HAL_GPIO_ReadPin(SIA_GPIO_Port, SIA_Pin ) == GPIO_PIN_RESET);
}
else
{
count ++;
printf("Turn right!\r\n");
while(HAL_GPIO_ReadPin(SIA_GPIO_Port, SIA_Pin ) == GPIO_PIN_SET);
}
printf("count = %d \r\n",count);
turn_flag = 0;
}
if(HAL_GPIO_ReadPin(SW_GPIO_Port, SW_Pin ) == GPIO_PIN_RESET )
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(SW_GPIO_Port, SW_Pin ) == GPIO_PIN_RESET )
{
turn_flag = 0;
count = 0;
printf("Turn down!\r\n");
printf("count = %d \r\n",count);
while(HAL_GPIO_ReadPin(SW_GPIO_Port, SW_Pin ) == GPIO_PIN_RESET);
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/**
* @brief EXTI line detection callbacks
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if((GPIO_Pin == SIA_Pin) && (turn_flag == 0))
{
turn_flag = 1;
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
Arduino
int cnt = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(cnt);
Serial.println("Hello World\r\n");
// digitalWrite(3, HIGH);
// delay(1000);
// digitalWrite(3, LOW);
// delay(1000);
// digitalWrite(4, HIGH);
// delay(1000);
// digitalWrite(4, LOW);
// delay(1000);
digitalWrite(5, HIGH);
delay(1000);
digitalWrite(5, LOW);
delay(1000);
cnt++;
}
參考
- Omron:旋轉式編碼器
- 電子森林:基於STEP FPGA的旋轉編碼器電路驅動
- Wiki:旋轉編碼器
- Omron:增量型編碼器輸出A相、B相和Z相分別代表什麼含義?
- Digi-Key:瞭解編碼器輸出信號有助於選擇最佳設備
- Autonics:編碼器NPN集電極開路輸出、推拉輸出、線性驅動輸出的特點
- Oromn:編碼器的電壓輸出,集電極信號輸出,線性驅動輸出的區別?
- 工控網:增量編碼器的常用信號輸出有哪些?
- 編碼器的集電極輸出、電壓輸出、互補輸出和線性驅動輸出
- 微雪:Rotation Sensor
- 知乎:談談增量型編碼器的信號電路輸出方式