上一篇文章已經能夠把從mpu6050的數據通過stm32上傳到匿名上位機進行實時波形顯示,這一篇就來談談怎麼讓一個小車按照我們想要的狀態動起來,這一篇也算是我弄了好幾天才明白的東西做一個大概的介紹,算是來自一個小白的“吐槽”吧,哈哈哈,不多說,進入主題!(本文不介紹每一個模塊的具體原理,只是介紹一下怎麼用! )
首先我們先來了解一下我們要使用的電機:編碼器電機,如圖:
這是一款增量式輸出的霍爾編碼器,好用但有點貴,哈哈。電機線+電機線-是PWM的控制接口,來決定輸出的速度是多少。其實如果不要使用編碼器的話,完全可以當成一個普通的直流電機來使用。所以我們就用定時器3來控制PWM輸出,代碼如下:
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE); //使能GPIO外設時鐘使能
//設置該引腳爲複用輸出功能,輸出TIM1 CH1的PWM脈衝波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7 ; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1 ; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作爲TIMx時鐘頻率除數的預分頻值 不分頻
TIM_TimeBaseStructure.TIM_ClockDivision =0; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
//TIM3通道一
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈衝寬度調製模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = 0; //設置待裝入捕獲比較寄存器的脈衝值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根據TIM_OCInitStruct中指定的參數初始化外設TIMx
//TIM3通道二
TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
//TIM3通道三
TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
//TIM3通道四
TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_CtrlPWMOutputs(TIM3,ENABLE); //MOE 主輸出使能
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的預裝載寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM1
TIM3_PWM_Init(7199,0);//10KHZ //主函數的初始化。使用時這段話不要放在一起,我這是爲了介紹。
TIM_SetCompare1(TIM3,x) //使用方法,注意X不要大於7199,畢竟7199已經是滿轉狀態
TIM_SetCompare2(TIM3,x)
TIM_SetCompare3(TIM3,x)
TIM_SetCompare4(TIM3,x)
我用的是L298N,這個用來驅動這個電機不是很好,發熱很大,但是手頭上沒有tb6612,只能勉強的用一下。這個L298N我是接的外電源,就是想跟stm32的電源分開,但是記住,一定要共地,圖片如下:
通過上面的已經能夠讓車輪動起來了,但是僅僅是作爲一個普通的直流電機,還不是我們選擇編碼器電機想要的效果,所以我們就需要使用我們剛纔沒有用到的編碼器。編碼器是通過AB相來對外輸出它檢測到的數值。stm32有專門的編碼器接口,所以我們無需額外配置,直接來用便可。代碼如下:
void Encoder_Init_TIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定時器4的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &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(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);
TIM_SetCounter(TIM2,0);
TIM_Cmd(TIM2, ENABLE);
}
因爲一個定時器只能用計數一個編碼器,所以還有一個TIM4,這裏篇幅不夠,可以自行加上。
/**************************************************************************
函數功能:單位時間讀取編碼器計數
入口參數:定時器
返回 值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch(TIMX)
{
case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0;break;
case 4: Encoder_TIM= (short)TIM4 -> CNT; TIM4 -> CNT=0;break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}
/**************************************************************************
函數功能:TIM2中斷服務函數
入口參數:無
返回 值:無
**************************************************************************/
void TIM2_IRQHandler(void)
{
if(TIM2->SR&0X0001)//溢出中斷
{
}
TIM2->SR&=~(1<<0);//清除中斷標誌位
}
通過上面的介紹以及代碼配置,我們就能真正的使用這一款編碼器電機!
這裏有一篇文章介紹編碼器的,我覺得挺好https://blog.csdn.net/weixin_44430455/article/details/97790029?ops_request_misc={%22request_id%22:%22158174719519724847018744%22,%22scm%22:%2220140713.130056874…%22}
介紹到這裏,平衡車可以組裝起來了。接下來就是PID的調試了。先上兩篇我覺得比較好的文章https://blog.csdn.net/zhaoyuaiweide/article/details/54572483
https://blog.csdn.net/ReCclay/article/details/84789455
下一篇文章講講平衡車的pid如何使用調試,僅僅是我的一些些想法。