摘要:編碼器是一種將角位移或者角速度轉換成一串電數字脈沖的旋轉式傳感器。編碼器又分為光電編碼器和霍爾編碼器。
霍爾編碼器是有霍爾碼盤和霍爾元件組成。霍爾碼盤是在一定直徑的圓板上等分的布置有不同的磁極。霍爾碼盤與電動機同軸,電動機旋轉時,霍爾元件檢測輸出若干脈沖信號,為判斷轉向,一般輸出兩組存在一定相位差的方波信號。
采集數據方式:
第一種軟件技術直接采用外部中斷進行采集,根據AB相位差的不同可以判斷正負。
第二種硬件技術直接使用定時器的編碼器模式。
這里采用第二種。也是大家常說的四倍頻,提高測量精度的方法。其實就是把AB相的上升沿和下降沿都采集而已,所以1變4。自己使用外部中斷方式實現就比較占用資源了,所以不建議使用。
速度計算方法:
真實的物理轉速:
電機轉動一圈的脈沖數:num1 單位:個
單位時間:t 單位:秒
單位時間內捕獲的脈沖變化數:num2 單位:個 (反應電機正反轉)
電機輪子半徑:r 單位:m
圓周率:pi 單位:無
速度:speed 單位: mm/s
因為半徑用的是m為單位,速度為mm所以需要乘以1000。
代碼:(使用TIM2和TIM4兩個定時器來測兩個輪子的速度)
將編碼器AB相使用的引腳設置成定時器的編碼器模式,我們根據TIMx->CNT寄存器數據的變化,計算出單位時間內,脈沖的變化值。
然后在定時器中斷服務函數中進行速度計算。
#include "encoder.h"
/**************************************************************************
函數功能:把TIM2初始化為編碼器接口模式
入口參數:無
返回 值:無
**************************************************************************/
void Encoder_Init_TIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定時器2的時鍾
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口時鍾
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根據設定參數初始化GPIOA
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //預分頻器
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //設定計數器自動重裝值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //選擇時鍾分頻:不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上計數
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用編碼器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除TIM的更新標志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM2,0);
//===============================================
TIM2->CNT = 0x7fff;
//===============================================
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************
函數功能:把TIM4初始化為編碼器接口模式
入口參數:無
返回 值:無
**************************************************************************/
void Encoder_Init_TIM4(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定時器4的時鍾
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口時鍾
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOB, &GPIO_InitStructure); //根據設定參數初始化GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 預分頻器
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //設定計數器自動重裝值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //選擇時鍾分頻:不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上計數
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用編碼器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除TIM的更新標志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM4,0);
//===============================================
TIM4->CNT = 0x7fff;
//===============================================
TIM_Cmd(TIM4, ENABLE);
}
/**************************************************************************
函數功能:讀取編碼器脈沖差值,讀取單位時間內的脈沖變化值
入口參數:TIM_TypeDef * TIMx
返回 值:無
**************************************************************************/
s16 getTIMx_DetaCnt(TIM_TypeDef * TIMx)
{
s16 cnt;
cnt = TIMx->CNT-0x7fff;
TIMx->CNT = 0x7fff;
return cnt;
}
/**************************************************************************
函數功能:計算左右輪速
入口參數:int *leftSpeed,int *rightSpeed
返回 值:無
//計算左右車輪線速度,正向速度為正值 ,反向速度為負值,速度為乘以1000之后的速度 mm/s
//一定時間內的編碼器變化值*轉化率(轉化為直線上的距離m)*200s(5ms計算一次) 得到 m/s *1000轉化為int數據
一圈的脈沖數:
左:1560
右:1560
輪子半徑:0.03m
輪子周長:2*pi*r
一個脈沖的距離:
左:0.000120830m
右:0.000120830m
速度分辨率:
左: 0.0240m/s
右: 0.0240m/s
200 5ms的倒數
1000 擴大分辨率
**************************************************************************/
void Get_Motor_Speed(int *leftSpeed,int *rightSpeed)
{
//5ms測速 5ms即這里說的單位時間
*leftSpeed = getTIMx_DetaCnt(TIM4)*1000*200*0.000120830;
*rightSpeed = getTIMx_DetaCnt(TIM2)*1000*200*0.000120830;
}
main.c
#include "sys.h"
//====================自己加入的頭文件===============================
#include "delay.h"
#include "led.h"
#include "encoder.h"
#include "usart3.h"
#include "timer.h"
#include <stdio.h>
//===================================================================
int leftSpeedNow =0;
int rightSpeedNow =0;
int main(void)
{
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 啟用 SWD
MY_NVIC_PriorityGroupConfig(2); //=====設置中斷分組
delay_init(); //=====延時函數初始化
LED_Init(); //=====LED初始化 程序燈
usart3_init(9600); //=====串口3初始化 藍牙 發送調試信息
Encoder_Init_TIM2(); //=====初始化編碼器1接口
Encoder_Init_TIM4(); //=====初始化編碼器2接口
TIM3_Int_Init(50-1,7200-1); //=====定時器初始化 5ms一次中斷
while(1)
{
printf("L=%d,R=%d\r\n",leftSpeedNow,rightSpeedNow);
delay_ms(15);
}
}
//5ms 定時器中斷服務函數
void TIM3_IRQHandler(void) //TIM3中斷
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查指定的TIM中斷發生與否:TIM 中斷源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx的中斷待處理位:TIM 中斷源
Get_Motor_Speed(&leftSpeedNow,&rightSpeedNow);
Led_Flash(100);
}
}