原文:編碼器速度和方向檢測,371電機方向與速度檢測,stm32編碼器接口模式
轉載鏈接:http://www.yfrobot.com/thread-2411-1-1.html
出處:yfrobot論壇
作者:aosini
編碼器是什麼玩意呢,它可是一個好玩的東西,做小車測速必不可少的玩意,下面,我將從編碼器的原理講起,一直到用stm32的編碼器接口模式,測出電機轉速與方向。
1.編碼器
圖1 編碼器示意圖
圖1爲編碼器的示意圖,中間是一個帶光柵的碼盤,光通過光柵,接收管接收到高電平,沒通過,接收到低電平。電機旋轉一圈,碼盤上有多少光柵,接受管就會接收多少個高電平。371電機中的碼盤就是這樣的,他是334線碼盤,具有較高的測速精度,也就是電機轉一圈輸出334個脈衝,芯片上已集成了脈衝整形觸發電路,輸出的是矩形波,直接接單片機IO就OK。
增量式旋轉編碼器通過內部兩個光敏接受管轉化其角度碼盤的時序和相位關係,得到其角度碼盤角度位移量增加(正方向)或減少(負方向)。下圖爲編碼器的原理圖:
圖2 增量式旋轉編碼器
A,B兩點對應兩個光敏接受管,A,B兩點間距爲 S2 ,碼盤的光柵間距分別爲S0和S1。S0+S1的距離是S2的四倍。這樣保證了A,B波形相位相差90度。旋轉的反向不同,鋸齒波A,B先到達高電平的順序就會不同,如上圖左側所示,順序的不同,就可以得到旋轉的方向。
2.stm32編碼器接口模式(寄存器)
stm32的編碼器接口模式在STM32中文參考手冊中有詳細的說明,在手冊273頁,14.3.12節。程序是完全按照 下圖方式,設置寄存器的。
圖3
從圖3中可以看出,TI1波形先於TI2波形90°時,每遇到一個邊沿變化是,計數器加1(可以通過寄存器設置加減),可以看出一個光柵,被計數了4次。TI1波形後於TI2波形90°時 ,每遇到一次邊沿變化,計數器減1。
[color=rgb(51,102,153) !important]
- //TIM2_Encoder_Init,Tim2_CH1(PA0);Tim2_CH2(PA1)
- //arr:自動重裝值 0XFFFF
- //psc:時鐘預分頻數 ,不分頻
- void TIM2_Encoder_Init(u16 arr,u16 psc)
- {
- RCC->APB1ENR|=1<<0; //TIM2時鐘使能
- RCC->APB2ENR|=1<<2; //使能PORTA時鐘
- GPIOA->CRL&=0XFFFFFF00; //PA0、PA1 清除之前設置
- GPIOA->CRL|=0X00000044; //PA0、PA1 浮空輸入
- TIM2->ARR=arr; //設定計數器自動重裝值
- TIM2->PSC=psc; //預分頻器
- TIM2->CCMR1 |= 1<<0; //輸入模式,IC1FP1映射到TI1上
- TIM2->CCMR1 |= 1<<8; //輸入模式,IC2FP2映射到TI2上
- TIM2->CCER |= 0<<1; //IC1不反向
- TIM2->CCER |= 0<<5; //IC2不反向
- TIM2->SMCR |= 3<<0; //所用輸入均在上升沿或下降沿有效
- TIM2->CR1 |= 1<<0; //使能計數器
- }
複製代碼
3 硬件
用到的模塊有STM32核心板、L298電機驅動、371帶編碼器電機(1:34)。這裏主要介紹一下電機,1:34指的是電機軸轉動34圈,電機輸出1圈。1:X,X值越小,電機的輸出轉速也就越快,扭矩也就越小;反之,X值越大,電機的輸出轉速越慢,扭矩也越大。
圖4 電機實物圖
左邊兩根黃線是電機兩極。綠線和白線是脈衝輸出線,分別接編碼器的接收管A、B,用一根可以測得速度,兩根同時用可測出電機速度與轉向。紅線和黑線是編碼器電源接線,紅正黑負,電壓3.3V-5V,不不可接反。
4 控制代碼
工作指示燈、電機方向與速度控制代碼。
- //LED IO 初始化 端口PD.2 運行指示燈
- void LED_Init(void)
- {
- RCC->APB2ENR|=1<<5; //使能PORTD時鐘
- GPIOD->CRL&=0XFFFFF0FF;
- GPIOD->CRL|=0X00000300; //PD.2推輓輸出
- GPIOD->ODR|=1<<2; //PD.2輸出高
- }
- //電機旋轉方向控制信號端口初始化
- //PC1~0推輓輸出,輸出高
- void M_Init(void)
- {
- RCC->APB2ENR|=1<<4; //使能PORTC時鐘
- GPIOC->CRL&=0XFFFFFF00;
- GPIOC->CRL|=0X00000033; //PC1~0推輓輸出
- GPIOC->ODR|=0XF<<0; //PC1~0輸出高電平
- }
- //定時器TIM3,PWM輸出初始化,CH1(PA6)
- //arr:自動重裝值
- //psc:時鐘預分頻數
- //設置自動重裝值爲900,那麼PWM頻率=72000/900=8Khz
- ////見STM32參考手冊,14.3.9PWM模式。
- void TIM3_PWM_Init(u16 arr,u16 psc) //arr設定計數器自動重裝值
- //psc預分頻器不分頻,psc=0
- {
- RCC->APB1ENR|=1<<1; //TIM3時鐘使能
- GPIOA->CRL&=0XF0FFFFFF;//PA6輸出
- GPIOA->CRL|=0X0B000000;//複用功能輸出
- GPIOA->ODR|=1<<6;//PA6上拉
- TIM3->ARR=arr;//設定計數器自動重裝值
- TIM3->PSC=psc;//預分頻器不分頻
- TIM3->CCMR1|=6<<4; //CH1 PWM1模式 高電平有效
- TIM3->CCMR1|=1<<3; //CH1預裝載使能
- TIM3->CCER|=1<<0; //OC1 輸出使能
- TIM3->CR1=0x0080; //ARPE使能
- TIM3->CR1|=0x01; //使能定時器3
- }
- //電機方向與速度控制,速度調節範圍爲-100~+100
- //大於0時,正轉,小於0時,反轉
- // 佔空比低於0.4時電機不轉
- //(佔空比是指高電平在一個週期之內所佔的時間比率)
- //TIM3->CCR1的設定範圍爲0~900(因爲arr=900)
- //見STM32參考手冊,14.3.9PWM模式。
- void Motor_Speed_Control(s16 motorSpeed)
- {
- s16 speed = 0 ;
- if(motorSpeed>100) speed = 100;
- else if (motorSpeed<-100) speed = -100;
- else speed = motorSpeed;
- if(speed == 0)
- {
- M_1 = 0;
- M_2 = 0;
- }
- else if(speed > 0)
- {
- M_1 = 0;
- M_2 = 1;
- TIM3->CCR1 = speed * 9;
- }
- else
- {
- M_1 = 1;
- M_2 = 0;
- TIM3->CCR1 = -speed * 9;
- }
- }
複製代碼
電機速度與方向檢測代碼
- //TIM2_Encoder_Init,Tim2_CH1(PA0);Tim2_CH2(PA1)
- //arr:自動重裝值 0XFFFF
- //psc:時鐘預分頻數 ,不分頻
- //見STM32中文手冊 14.3.12編碼器接口模式
- void TIM2_Encoder_Init(u16 arr,u16 psc)
- {
- RCC->APB1ENR|=1<<0; //TIM2時鐘使能
- RCC->APB2ENR|=1<<2; //使能PORTA時鐘
- GPIOA->CRL&=0XFFFFFF00; //PA0、PA1 清除之前設置
- GPIOA->CRL|=0X00000044; //PA0、PA1 浮空輸入
- TIM2->ARR=arr; //設定計數器自動重裝值
- TIM2->PSC=psc; //預分頻器
- TIM2->CCMR1 |= 1<<0; //輸入模式,IC1FP1映射到TI1上
- TIM2->CCMR1 |= 1<<8; //輸入模式,IC2FP2映射到TI2上
- TIM2->CCER |= 0<<1; //IC1不反向
- TIM2->CCER |= 0<<5; //IC2不反向
- TIM2->SMCR |= 3<<0; //所用輸入均在上升沿或下降沿有效
- TIM2->CR1 |= 1<<0; //使能計數器
- }
- //計數寄存器賦值
- void TIM2_Encoder_Write(int data)
- {
- TIM2->CNT = data;
- }
- //讀計數個數
- int TIM2_Encoder_Read(void)
- {
- TIM2_Encoder_Write(0); //計數器清0
- delay_ms(10); //檢測時間,可調節
- return (int)((s16)(TIM2->CNT)); //數據類型轉換
- //記錄邊沿變化次數(一個柵格被記錄4次)
- }
複製代碼
這裏我們只顯示邊沿變化次數,沒有具體的算出速度。
主函數
- int main(void)
- {
- // motorSpeed的範圍爲-100 ~ +100;
- s16 motorSpeed = 100;
- Stm32_Clock_Init(9); //系統時鐘設置
- delay_init(72); //延時初始化
- uart_init(72,9600); //串口1初始化
- LED_Init(); //初始化與LED連接的硬件接口
- M_Init(); ////初始化電機運行方向控制端口
- TIM3_PWM_Init(900,0); //不分頻。PWM頻率=72000/900=8Khz
- TIM2_Encoder_Init(0xffff, 0); //計數器自動重裝值爲最大
- while(1)
- {
- LED =! LED;
- Motor_Speed_Control(motorSpeed);
- printf("編碼器值:%d\n ",TIM2_Encoder_Read());
- }
- }
複製代碼
5 估算驗證
這裏我們只是大概的估算驗證測量值是否正確,不具有完全正確性。
我們設置motorSpeed = 100 ,得到測量值如下圖:
圖5 motorSpeed = 100
因爲誤差是不可避免的,所以看到每次檢測的值都是不一樣的。我們取462,因爲一個光柵被記錄了4次,所以在10ms內一共檢測到了462/4=115.5,那麼得到11.55個/ms,每ms內檢測到11.55個光柵。
通過碼錶,記錄電機輸出50圈,用時50.2s,那麼這時應該檢測到的光柵個數爲50*34(電機轉34圈,輸出1圈)*334(每圈有334個光柵)=567800,除以時間,得到估算值11.31個/ms。可以看出估算值與測量值是相近的,認爲測量是準確的。
設置motorSpeed = -50 ,得到測量值如下圖 :
圖6 motorSpeed=-50
可以看到測量值是負值,說明電機是反轉,與實際設置相符。
附件
stm32 編碼器接口.rar (76.87 KB, 下載次數: 39)
我們讀的是計數器TIM2->CNT中的值,此值爲什麼會是負的,這裏爲什麼這樣用? 編碼器模式中使用上下計數,假設我們初始化TIM2_Encoder_Init_1(0xff, 0);自動裝載值爲0xFF,這時,計數器中的值,就會在0x00與0xFF之間循環變化,由0x01減爲0x00,再減1時,計數器中的值爲0xFF,我們將此數做爲有符型整數處理,當然,計數的前提是每個週期的計數個數不能超過0x7F,超過,計數將不準確。
符號強制轉換,return (int)((s16)(TIM2->CNT));裏面有個類型轉換,強制轉換返回有符型數據。數值都是以補碼錶示的,正整數補碼是源碼,負整數補碼是絕對值取反加1。向下計數時減1,爲0時,就需要向高位借位減“1”,可以這樣理解,一個8位數00000000B-00000001B,但0不夠減1的,就向不存在的第9位借1,100000000B-00000001B=11111111B,數是以補碼形式表示的,這樣11111111B就爲-1了。
在例程中,初始化自動重裝值爲0xFFFF可以做個實驗,直接輸出TIM2->CNT的值看一下: printf("編碼器值1:%x \r\n",TIM2->CNT)。