上期內(nèi)容我們介紹了定時器的PWM輸出功能,本期內(nèi)容來介紹一下定時器的正交譯碼器功能(編碼器接口)。正交譯碼器是和正交編碼器外設(shè)配合使用的,可對編碼器輸入的脈沖進行計數(shù)進而實現(xiàn)速度測量,本期內(nèi)容我們通過一個使用旋轉(zhuǎn)編碼器的計數(shù)小實驗,來初步了解它的應(yīng)用方法。
系統(tǒng)環(huán)境
Windows 10-64bit
軟件平臺
NucleiStudio IDE 202102版
硬件需求
RV-STAR開發(fā)板
旋轉(zhuǎn)編碼器
正交編碼器
正交編碼器(Quadrature Encoder)是一種用于測量旋轉(zhuǎn)速度和方向的傳感器。常見的正交編碼器有兩個輸出信號:A信道和B信道。每個信道可以對運動進行測量并產(chǎn)生數(shù)字脈沖,這兩個脈沖的相位相差90度(因此稱為“正交”),這使得你可以根據(jù)它們判斷運動的方向,通過積分(累加)運算后,還可以用來測算距離。
上圖中,A和B分別連接到兩個傳感器單元上,黑白相間的圓環(huán)稱之為「柵格」。傳感器單元和柵格的實現(xiàn)方式有很多種,包括「反射式傳感器+反光率不同的柵格」「對射式傳感器+鏤空光柵」「霍爾傳感器+磁極圓環(huán)」「觸點+導軌」等。
本次實驗中,我們使用的是下圖所示的市面上常見的旋轉(zhuǎn)編碼器(數(shù)字電位器):
GD32VF103的正交譯碼器
正交譯碼器功能使用TIMERx_CH0和TIMERx_CH1引腳生成的CI0和CI1正交信號各自相互作用產(chǎn)生計數(shù)值。通過設(shè)置SMC=0x01、0x02或0x03來選擇是僅由CI0、僅由CI1、或者由CI0和CI1來決定定時器的計數(shù)方向。在每個方向選擇源的電平改變期間,DIR位是由硬件自動改變的。計數(shù)器計數(shù)方向改變的機制如下方的圖表所示。
正交譯碼器可以當作一個帶有方向選擇的外部時鐘,這意味著計數(shù)器會在0和自動加載值之間連續(xù)地計數(shù)。因此,用戶必須在計數(shù)器開始計數(shù)前配置TIMERx_CAR寄存器。
實驗部分
首先需要參照如下的示意圖,對RV-STAR開發(fā)板和旋轉(zhuǎn)編碼器進行連線:
然后在集成開發(fā)環(huán)境中創(chuàng)建一個新工程,開始編寫代碼。
首先需要對定時器的編碼器接口進行配置,我們使用的是TIMER2的編碼器接口,對應(yīng)的是PA6和PA7引腳,首先要使能它們的外設(shè)時鐘和復(fù)用時鐘,然后配置為浮空輸入模式。
接著需要創(chuàng)建定時器初始化參數(shù)結(jié)構(gòu)體,對定時器的功能進行配置,其中需要注意的是要將結(jié)構(gòu)體參數(shù)的預(yù)分頻系數(shù)設(shè)為0,周期設(shè)為10000(即定時器的自動加載值,也可以設(shè)為其他值),然后需要將定時器的模式設(shè)置為TIMER_ENCODER_MODE2(編碼器模式,使用CI0和CI1計數(shù)),然后將定時器的計數(shù)值配置為5000(這樣讀取定時器計數(shù)的初值就是5000),最后使能TIMER2。
相關(guān)代碼實現(xiàn)如下:
void encoder_init()
{
/* TIMER2_CH0 - PA6, TIMER2_CH1 - PA7 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
rcu_periph_clock_enable(RCU_TIMER2);
timer_deinit(TIMER2);
/* initialize TIMER init parameter struct */
timer_parameter_struct timer_initpara;
timer_struct_para_init(&timer_initpara);
/* TIMER2 configuration */
timer_initpara.prescaler = 0;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 10000; /* set auto-reload value */
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER2, &timer_initpara);
/* select the encoder mode */
timer_slave_mode_select(TIMER2, TIMER_ENCODER_MODE2);
timer_counter_value_config(TIMER2, 5000); /* config the initial value */
timer_enable(TIMER2);
}
本次實驗,我們通過串口的打印輸出來查看編碼器的計數(shù)值,因此在主程序中需要對串口進行初始化,然后在循環(huán)體中,每次讀取依次定時器的計數(shù)值,再打印輸出到串口。
int main()
{
encoder_init();
gd_com_init(GD32_COM0);
int counter = 0;
while (1) {
counter = timer_counter_read(TIMER2);
printf("Counter: %dn", counter);
delay_1ms(500);
}
}
代碼編寫完成后,進行編譯和上傳,然后打開串口終端(波特率115200),可以查看到在串口終端中輸出定時器的初值為5000:
然后順時針旋轉(zhuǎn)編碼器的旋鈕,觀察到計數(shù)值增加,并且每轉(zhuǎn)動一個單位計數(shù)值增加4,符合芯片數(shù)據(jù)手冊中的功能描述:
逆時針旋轉(zhuǎn),計數(shù)值減少:
“RVMCU課堂”專欄已在社區(qū)開通如需獲取實驗源碼或有實驗相關(guān)問題掃碼進入即可
更多實驗例程
盡在RVMCU社區(qū)
www.rvmcu.com