01 前言
如果我們想對(duì)電機(jī)進(jìn)行速度或者轉(zhuǎn)角的精確控制,需要使用到很多算法,比如非常經(jīng)典的PID控制算法,或者一些只能算法,但這些算法都需要傳感器來(lái)提供轉(zhuǎn)速或轉(zhuǎn)角的反饋值,對(duì)于電機(jī)來(lái)說(shuō),編碼器是非常流行并且實(shí)用的電機(jī)配套傳感器,本文使用STM32F103C8T6+L298N+MG513P30電機(jī)進(jìn)行直流電機(jī)的編碼器測(cè)速。
02 編碼器原理
1.分類(lèi)
光電式編碼器的精準(zhǔn)度比霍爾式要高,但是由于它需要紅外線發(fā)生器和接收器,相對(duì)來(lái)說(shuō)造價(jià)要貴一些?,F(xiàn)在我們比較常用的是霍爾式增量編碼器,有很多電機(jī)都會(huì)自帶編碼器。
2.測(cè)速方法分類(lèi)
(1)M法測(cè)速
編碼器輸出的脈沖個(gè)數(shù)代表了位置,那么單位時(shí)間里的脈沖個(gè)數(shù)表示這段時(shí)間里的平均速度。因此,我們可以通過(guò)計(jì)量單位時(shí)間脈沖個(gè)數(shù)即可以估算出平均速度,稱(chēng)為M法測(cè)速(測(cè)脈沖個(gè)數(shù))測(cè)速原理如圖所示。
例如,若編碼器每轉(zhuǎn)產(chǎn)生N個(gè)脈沖,在T時(shí)間(單位s)產(chǎn)生m個(gè)脈沖,那么平均轉(zhuǎn)速如下式所示:
式中 n——平均轉(zhuǎn)速(r/min);
T——測(cè)速采樣時(shí)間(s);
m——T時(shí)間內(nèi)測(cè)得的編碼器脈沖個(gè)數(shù);
N——編碼器每轉(zhuǎn)脈沖數(shù)。
(2)T法測(cè)速
若用M法測(cè)速,在記錄時(shí)間短、速度低的時(shí)候,只能記錄幾個(gè)脈沖,則分辨率降低。針對(duì)該問(wèn)題,目前解決方法為:可以采用輸出碼盤(pán)脈沖為一個(gè)時(shí)間間隔,然后用計(jì)數(shù)器記錄在這段時(shí)間里高速脈沖源發(fā)出的脈沖數(shù)。即通過(guò)采集到脈沖源脈沖數(shù)來(lái)計(jì)量編碼器兩個(gè)脈沖時(shí)間間隔,從而估算出速度,稱(chēng)為T(mén)法測(cè)速(測(cè)脈沖周期),測(cè)速原理圖如圖所示。
T法測(cè)速,利用編碼器產(chǎn)生的脈沖用作門(mén)電路的觸發(fā)信號(hào);用已知頻率f的時(shí)鐘信號(hào)做輸入。若控制門(mén)電路在編碼器脈沖上升沿到來(lái)時(shí)開(kāi)始導(dǎo)通,再次上升沿到來(lái)時(shí)關(guān)閉,即計(jì)數(shù)器只記錄一個(gè)編碼器脈沖周期內(nèi)的時(shí)鐘脈沖個(gè)數(shù)。若在編碼器相鄰脈沖之間記錄的脈沖時(shí)鐘個(gè)數(shù)為m,那么,可以計(jì)算兩個(gè)編碼器脈沖的時(shí)間間隔為m /f;若編碼器每轉(zhuǎn)有N個(gè)線脈沖輸出,那么我們就知道編碼器轉(zhuǎn)過(guò)1/N轉(zhuǎn)時(shí)需要時(shí)間m /f。據(jù)此,可計(jì)算與編碼器同軸轉(zhuǎn)速為公式所示。
式中 n ——平均轉(zhuǎn)速(r/min);
f ——時(shí)鐘脈沖頻率(個(gè)/s);
m ——兩個(gè)編碼器脈沖之間的時(shí)鐘脈沖個(gè)數(shù);
N ——編碼器每轉(zhuǎn)脈沖數(shù)。
編碼器一般會(huì)輸出兩路信號(hào),分別稱(chēng)為A相和B相,它們相差90°,因此編碼器也稱(chēng)為十字碼盤(pán),通過(guò)捕獲兩路輸出信號(hào)可以測(cè)算出電機(jī)的轉(zhuǎn)速和轉(zhuǎn)向。
STM32使用編碼器的方法有兩種分別是外部中斷法和輸入捕獲法,這兩種方法都屬于M法測(cè)速,兩種方法比較來(lái)說(shuō)外部中斷法占用CPU資源較多,平時(shí)比較常用的是輸入捕獲法,但博主兩種方法都調(diào)試出來(lái)了,因此記錄下來(lái)跟大家分享一下。
03 外部中斷法測(cè)速
對(duì)于外部中斷的知識(shí),各個(gè)講STM32的教程都有,我就不過(guò)多贅述,外部中斷的初始化都一樣,主要是出發(fā)外部中斷時(shí)需要進(jìn)行的操作。
看這張圖,正轉(zhuǎn)方向是信號(hào)向右走,因?yàn)槲覀兪峭瑫r(shí)捕獲兩路信號(hào),有以下幾種情況:
然后我們?cè)O(shè)置兩個(gè)變量用來(lái)存儲(chǔ)捕獲的脈沖數(shù),每捕獲到一次脈沖信號(hào)根據(jù)上表進(jìn)行判斷,正轉(zhuǎn)時(shí)使其加1;反轉(zhuǎn)時(shí)使其減1;然后配置一個(gè)定時(shí)器,每隔一段時(shí)間反饋一次測(cè)速值。
1.外部中斷配置
先編寫(xiě)一個(gè)函數(shù)初始化外部中斷,使用PB12-15引腳復(fù)用為外部中斷輸入,外部中斷配置步驟如下:
1.端口初始化:RCC_APB2PeriphClockCmd()、GPIO_Init()
2.使能復(fù)用功能時(shí)鐘:RCC_APB2PeriphClockCmd()
3.設(shè)置IO口與中斷線的映射關(guān)系:GPIO_EXTILineConfig()
4.初始化線上中斷:EXTI_Init()
5.配置中斷分組:NVIC_Init()
完整函數(shù)如下:
/**************************************************************************
功能:應(yīng)用外部中斷方式采集編碼器數(shù)據(jù),使用M法測(cè)速反饋車(chē)輪實(shí)時(shí)速度
函數(shù):Encoder_EXTIX_Init(void) EXTI15_10_IRQHandler(void)
作者:K.Fire
日期:2022.01.30
引腳:PB12(左輪A相) PB13(左輪B相) PB14(右輪A相) PB15(右輪B相)
參數(shù):void
*************************************************************************/
int Encoder_L_EXTI=0;
int Encoder_R_EXTI=0;
void Encoder_EXTIX_Init(void)
{
//1.端口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 |GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//2.使能復(fù)用功能時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//3.設(shè)置IO口與中斷線的映射關(guān)系
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource12);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource13);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource15);
//4.初始化線上中斷
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line12 | EXTI_Line13 | EXTI_Line14 | EXTI_Line15;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//跳變沿觸發(fā)
EXTI_Init(&EXTI_InitStruct);
//5.配置中斷分組
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
}
2.外部中斷服務(wù)函數(shù)
函數(shù)的判斷邏輯與上表一致,外部中斷捕獲判斷函數(shù)如下:
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line12) != RESET)//左輪A相 PB12
{
EXTI_ClearITPendingBit(EXTI_Line12); //清除LINE上的中斷標(biāo)志位
if(PBin(12)==0) //這里判斷檢測(cè)到的是否是下降沿
{
if(PBin(13)==0) Encoder_L_EXTI++;//B相的電平如果是低,電機(jī)就是正轉(zhuǎn)加1
else Encoder_L_EXTI--;//否則就是反轉(zhuǎn)減1
}
else //上升沿
{
if(PBin(13)==1) Encoder_L_EXTI++; //B相電平如果為高,電機(jī)就是正轉(zhuǎn)加1
else Encoder_L_EXTI--;//否則就是反轉(zhuǎn)減1
}
}
if(EXTI_GetITStatus(EXTI_Line13) != RESET)//左輪B相 PB13
{
EXTI_ClearITPendingBit(EXTI_Line13); //清除LINE上的中斷標(biāo)志位
if(PBin(13)==0) //這里判斷檢測(cè)到的是否是下降沿
{
if(PBin(12)==1) Encoder_L_EXTI++;//B相的電平如果是高,電機(jī)就是正轉(zhuǎn)加1
else Encoder_L_EXTI--;//否則就是反轉(zhuǎn)減1
}
else //上升沿
{
if(PBin(12)==0) Encoder_L_EXTI++; //B相電平如果為高,電機(jī)就是正轉(zhuǎn)加1
else Encoder_L_EXTI--;//否則就是反轉(zhuǎn)減1
}
}
if(EXTI_GetITStatus(EXTI_Line14) != RESET)//右輪A相 PB14
{
EXTI_ClearITPendingBit(EXTI_Line14); //清除LINE上的中斷標(biāo)志位
if(PBin(14)==0) //這里判斷檢測(cè)到的是否是下降沿
{
if(PBin(15)==0) Encoder_R_EXTI++;//B相的電平如果是低,電機(jī)就是正轉(zhuǎn)加1
else Encoder_R_EXTI--;//否則就是反轉(zhuǎn)減1
}
else //上升沿
{
if(PBin(15)==1) Encoder_R_EXTI++; //B相電平如果為高,電機(jī)就是正轉(zhuǎn)加1
else Encoder_R_EXTI--;//否則就是反轉(zhuǎn)減1
}
}
if(EXTI_GetITStatus(EXTI_Line15) != RESET)//右輪B相 PB15
{
EXTI_ClearITPendingBit(EXTI_Line15); //清除LINE上的中斷標(biāo)志位
if(PBin(15)==0) //這里判斷檢測(cè)到的是否是下降沿
{
if(PBin(14)==1) Encoder_R_EXTI++;//A相的電平如果是高,電機(jī)就是正轉(zhuǎn)加1
else Encoder_R_EXTI--;//否則就是反轉(zhuǎn)減1
}
else //上升沿
{
if(PBin(14)==0) Encoder_R_EXTI++; //A相電平如果為低,電機(jī)就是正轉(zhuǎn)加1
else Encoder_R_EXTI--;//否則就是反轉(zhuǎn)減1
}
}
}