STM32定时器编码器模式实现直流有刷电机测速(HAL库)
前言
最近在做一個單片機大作業(yè),要用到直流有刷,在這里把學習編碼器的知識記錄一下,學習參考資料:
正點原子DMF407電機控制專題教程_V1.0
編碼器測速原理
我所使用的編碼器是市面上常見的磁電增量式編碼器,其有AB兩相,用于輸出電機轉(zhuǎn)動時的脈沖數(shù),AB兩相的先后順序決定了電機的轉(zhuǎn)動方向
這其實就是單片機的外部計數(shù)器模式,51中也帶有同樣的功能
信號從通道被采樣后的處理過程如下
編碼器的計數(shù)接口是利用脈沖的邊沿來計數(shù)的,我們知道AB兩相都有脈沖且相位差為90度,那么一次檢測最多可以得到四個邊沿,此時我們可以通過配置計數(shù)的方式來實現(xiàn)不同的邊沿計數(shù)
由圖可以看出,總共有三種計數(shù)方式供我們選擇,不同的模式對應了不同的計數(shù)形式,這里我選用的是第三種,因此會產(chǎn)生四個邊沿,相當于對信號進行了四倍頻,即一個脈沖信號會計數(shù)4次,在計算時要注意
寄存器部分
在編碼過程中有幾個重要的寄存器要簡單介紹一下
TIMx_CR1控制寄存器
?我們要關注這里的DIR位,通過判斷DIR來判斷計數(shù)器所處的模式是遞增還是遞減,流程大致為:配置好定時器的裝載值為65536,不分頻,那么每一次接收到65536個脈沖后定時器就會溢出進入定時器中斷,此時通過判斷DIR是0還是1來判斷是正轉(zhuǎn)還是反轉(zhuǎn),進而將得到的脈沖數(shù)累積到總脈沖數(shù)中進行速度計算
同時還要注意 CEN位,該位用于使能計數(shù)器的工作,也就是相當于選擇模式,對應硬件就是內(nèi)部的開關,用于判斷是外部定時器模式還是內(nèi)部定時器模式,當該位置1為外部定時器模式
TIMx_CCMR1捕獲/比較模式寄存器1
在這里IC1F為濾波器,其實現(xiàn)方法為通過配置寄存器來設定濾波系數(shù),例如在一個采樣周期下,當N=2時代表要兩個相同的事件(比如兩個相同電位的高電平信號)才能視為一個有效邊沿,這個過程基于數(shù)字濾波器實現(xiàn),寄存器配置如下
IC1PSC是輸入捕獲預分頻器,和上述原理類似,這里我和教程一樣,選擇一個邊沿就觸發(fā)一次計數(shù)
CC1S用于配置定時器通道,這里不做多要求,IC1映射到TI1就可以了
TIMx_SMCR從模式控制器
?SMS代表的三位用于配置編碼器模式123,具體對應關系如下
?至此要用到的所有寄存器就介紹完了,接下來我們來進行CUBEMX工程的創(chuàng)建
硬件信息
主控芯片我選擇的是STM32F103C8T6,電機用的是常見的自帶編碼器的直流有刷減速電機,編碼器線數(shù)為11,電機驅(qū)動選用L298N,同時用OLED屏幕顯示電機轉(zhuǎn)速信息
工程創(chuàng)建
首先配置時鐘樹,直接拉滿,再初始化幾個IO口用于驅(qū)動電機
?剩下的流程就是選擇對應的IDE生成工程即可
代碼部分
初始化外設
/* USER CODE BEGIN 2 */run_left_motor(50,1); //左輪電機啟動OLED_Init(); //OLED初始化OLED_Clear();HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //開啟定時器編碼器通道HAL_TIM_Base_Start_IT(&htim4); //開啟定時中斷通道/* USER CODE END 2 */?初始化電機以及編碼器
#define ROTO_RATIO 44 //編碼器線數(shù)11*分頻系數(shù)4 #define REDUTATION_RATIO 30 //電機減速比30//編碼器結構體 typedef struct {int encode_old;int encode_new;float speed; }ENCODE_TypeDef; //電機結構體 typedef struct {float speed; }Motor_TypeDef;extern ENCODE_TypeDef g_encode_left; extern Motor_TypeDef g_motor_left;?電機轉(zhuǎn)動
void run_left_motor(uint8_t speed,unsigned char dir) {uint32_t arr;HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);if(dir == 1){HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);}HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);arr = __HAL_TIM_GET_AUTORELOAD(&htim2);__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,(1.0*speed/100)*arr); }電機速度計算
//encoder_now為定時器編碼器模式下的計數(shù)值,ms代表每隔多少ms進入一次計算公式 void left_speed_compute(int32_t encode_now,uint8_t ms) {uint8_t i=0,j=0;float temp = 0.0; //用于后續(xù)冒泡排序static uint8_t sp_count=0,k=0; //用于判斷隔多長時間進一次計算,靜態(tài)變量,不會隨著函數(shù)調(diào)用被刷新static float speed_array[10]={0.0}; //上面的k為數(shù)組的索引值if(sp_count == ms) //當調(diào)用函數(shù)次數(shù)超過50次即每間隔50ms進入一次計數(shù){g_encode_left.encode_new = encode_now; //編碼器值更新g_encode_left.speed = (g_encode_left.encode_new - g_encode_left.encode_old); //脈沖變化數(shù)計算//通過脈沖計算速度,公式:(50ms內(nèi)記錄的脈沖數(shù)/50ms*60)/減速比/編碼器線數(shù)/分頻系數(shù) = 每分鐘的脈沖數(shù)/已知常量speed_array[k++] = (float)(g_encode_left.speed*((1000/ms)*60.0)/(REDUTATION_RATIO*ROTO_RATIO));g_encode_left.encode_old = g_encode_left.encode_new; //更新編碼器計數(shù)值//冒泡排序法if(k==10){for(i=10;i>=1;i--){for(j=0;j<(i-1);j++) {if (speed_array[j] > speed_array[j + 1]) {temp = speed_array[j];speed_array[j] = speed_array[j + 1];speed_array[j + 1] = temp;}}}temp = 0.0; //初始化temp//去除兩邊高低數(shù)據(jù)for(i=2;i<8;i++){temp += speed_array[i];}temp = (float)(temp/6);/** 一階低通濾波 Y(n)= qX(n) + (1-q)Y(n-1)* 其中 X(n)為本次采樣值;Y(n-1)為上次濾波輸出值;Y(n)為本次濾波輸出值,* q 為濾波系數(shù)*/g_motor_left.speed = (float)((g_motor_left.speed)*(float)0.52)+((float)0.48*temp);//顯示當前速度unsigned char Tx_Buf[20];sprintf((char *)Tx_Buf,"Left: %.2f",g_motor_left.speed);OLED_ShowString(1,2,Tx_Buf,5);k = 0;}sp_count = 0;}sp_count++; }溢出計數(shù)統(tǒng)計以及調(diào)用計算
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {//判斷編碼器的溢出方向if(htim->Instance == TIM1){if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1)){g_tim1_encode_count--;}else{g_tim1_encode_count++;}}//每間隔1ms更新一次脈沖數(shù)if(htim->Instance == TIM4){int Left_encode_now = gtim_get_encode();left_speed_compute(Left_encode_now,50);} } //更新脈沖數(shù) int gtim_get_encode(void) {return ( int32_t )__HAL_TIM_GET_COUNTER(&htim1) + g_tim1_encode_count * 65536; /* 當前計數(shù)值+之前累計編碼器的值=總的編碼器值 */ }實驗現(xiàn)象驗證
轉(zhuǎn)動時轉(zhuǎn)速為20r/min?
不轉(zhuǎn)動時轉(zhuǎn)速為0
總結
以上是生活随笔為你收集整理的STM32定时器编码器模式实现直流有刷电机测速(HAL库)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端笔记-echarts加载geo地图
- 下一篇: 计算机中磁盘管理恢复分区,电脑磁盘修复