基於STM32 的帶AB相編碼器的直流電機底層驅動調試(上)-底層

以前在學校參加過飛思卡爾比賽,參加工作三年後才又有機會接觸電機控制,開始學習製作AGV小車.本以為有基礎會很容易上手,然而事實告訴自己:你想的太簡單了,以前瞭解的知識不過是九牛一毛!學會一個東西不難,精通一項技術是真不容易!

畢業一年後才正式從事STM32相關的開發,現在的能力也不過入門級別.只想把當前學到的一些東西和大家分享,自己也能鞏固下一下,希望能幫到某些需要的朋友.

學習STM32,發現它的定時器是一個難點,至今還有很多地方捉摸不透,當然,如果只是使用普通定時功能就不多說了.控制直流電機,PWM調速是必備,可是做小車速度一定要做到閉環控制,所以得有編碼器.查閱手冊,STM32的定時器還支持AB相正交編碼器功能,實在令人高興,無需再用定時器的輸入補獲功能來計算速度了.

作為AGV小車,最起碼的功能得是循跡吧!低端的就是學校用的光電循跡,白底黑線跑道.稍微高級點的是巡磁軌道,磁感應傳感器加磁條的方案,相對比較穩定,只是成本要高許多,更高端的應該就是SLAM激光導航模塊+慣性導航構建地圖來循跡,這種要複雜很多,目前也只是瞭解一些原理,而前兩種已經實踐過.就目前經驗而談,第一種適合學習(當初貼軌道可是花了我大把時間和體力),第二種適合初級應用,第三種就適合高級應用場合了. (以前飛思卡爾的攝像頭,光電,電磁循跡方案好像只是用於競賽,沒注意到有什麼實際應用) 唉!廢話說的有點多.......

電機選型:JGB37-545B帶AB相編碼器直流減速電機

驅動芯片:LMD18200T

MCU:STM32F103VET6

電機PWM接口使用定時器4的通道1和2,編碼器接口使用定時器1和定時器2的正交編碼器接口.電機初始化函數如下:

void Motor_Init(void)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TIM_OCInitStructure;

TIM_ICInitTypeDef TIM_ICInitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE) ;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 , ENABLE);

GPIO_PinRemapConfig(GPIO_Remap_TIM4, ENABLE);

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //方向控制引腳

GPIO_Init(GPIOD, &GPIO_InitStructure);

GPIO_ResetBits(GPIOD,GPIO_Pin_9|GPIO_Pin_10);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 ; //PWM輸出引腳

GPIO_Init(GPIOD, &GPIO_InitStructure);

TIM_DeInit(TIM4);

TIM_TimeBaseStructure.TIM_Period = 100-1;

TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //週期10K

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上計數模式

TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

TIM_ARRPreloadConfig(TIM4, ENABLE);

TIM_OCStructInit(&TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

TIM_OCInitStructure.TIM_Pulse = 0;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

TIM_OC1Init(TIM4, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);

TIM_OC2Init(TIM4, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); //定時器4的2路比較輸出功能使能

TIM_Cmd(TIM4, ENABLE);

// ******************************** 編碼器 1 *******************************//

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //使用IO重映射功能

GPIO_StructInit(&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11 ;

GPIO_Init(GPIOE, &GPIO_InitStructure);

TIM_DeInit(TIM1);

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = 0xFFFF;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

TIM_EncoderInterfaceConfig(TIM1, TIM_EncoderMode_TI1, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); //使用編碼器模式1,只能反映出速度 ,如果需要反映出方向,可以使用TIM_EncoderMode_TI12,雙向計數模式

TIM_ICStructInit(&TIM_ICInitStructure);

TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ;

TIM_ICInitStructure.TIM_ICFilter = 15; //

TIM_ICInit(TIM1, &TIM_ICInitStructure);

TIM_ICInitStructure.TIM_Channel = TIM_Channel_2 ;

TIM_ICInitStructure.TIM_ICFilter = 15;//選擇輸入比較濾波器

TIM_ICInit(TIM1, &TIM_ICInitStructure);

TIM_ClearFlag(TIM1, TIM_FLAG_Update);//清TIM更新標誌

TIM_SetCounter(TIM1,0);

TIM_Cmd(TIM1, ENABLE);

// *************************************** 編碼器2 *********************************//

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

GPIO_StructInit(&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 ;

GPIO_Init(GPIOA, &GPIO_InitStructure);

TIM_DeInit(TIM2);

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = 0xFFFF;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI1, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

//TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_BothEdge , TIM_ICPolarity_BothEdge);

TIM_ICStructInit(&TIM_ICInitStructure);

TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ;

TIM_ICInitStructure.TIM_ICFilter = 15;

TIM_ICInit(TIM2, &TIM_ICInitStructure);

TIM_ICInitStructure.TIM_Channel = TIM_Channel_2 ;

TIM_ICInit(TIM2, &TIM_ICInitStructure);

TIM_ClearFlag(TIM2, TIM_FLAG_Update);

TIM_SetCounter(TIM2,0);

TIM_Cmd(TIM2, ENABLE);

}

設置速度宏定義,這裡只是設定佔空比,如需設置具體速度(如cm/s)需要重新封裝

#define MOTOR_SET_SPEED_R(spd)TIM_SetCompare1(TIM4,spd)

#define MOTOR_SET_SPEED_L(spd)TIM_SetCompare2(TIM4,spd)

實際應用時需要知道一些固定參數,如輪子直徑,電機減速比,從而將輸出的脈衝(佔空比)對應到具體的速度值.

復位編碼器值:

#define MOTOR_RESET_ENCODER_R()TIM_SetCounter(TIM1,0)

#define MOTOR_RESET_ENCODER_L()TIM_SetCounter(TIM2,0)

讀編碼器值:

#define MOTOR_READ_ENCODER_R()-TIM_GetCounter(TIM1)

#define MOTOR_READ_ENCODER_L()TIM_GetCounter(TIM2)

就寫到這裡吧,寫的不是很清楚.底層驅動網上例程很多,很容易調試.應用中更注重方法,思路無非就是調節佔空比控制速度,讀編碼器單位時間輸入脈衝數獲得速度,然後加上PID調節使速度穩定.關於PID方面的個人理解下次再來分享!


分享到:


相關文章: