平衡車(二)——讓小車能夠動起來

上一篇文章已經能夠把從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如何使用調試,僅僅是我的一些些想法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章