天天看點

STM32循迹PWM調速及阿克曼舵機轉向

1,準備

紅外循迹子產品,L298N電機驅動子產品,STM32C8T6最小系統,SG90舵機,KEIL5

2,涉及的理論知識和硬體參考

理論

阿克曼轉向是一種現代汽車的轉向方式,在汽車轉彎的時候,内外輪轉過的角度不一樣, 内側輪胎轉彎半徑小于外側輪胎。下圖就是理想的阿克曼轉向。
STM32循迹PWM調速及阿克曼舵機轉向
根據阿克曼轉向幾何設計轉向機構,在車輛沿着彎道轉彎時,利用四連杆的相等曲柄, 可以使内側輪的轉向角比外側輪大大約 2~4 度,使四個輪子路徑的圓心大緻上交會于後軸的 延長線上瞬時轉向中心,進而讓車輛可以順暢的轉彎。 
STM32循迹PWM調速及阿克曼舵機轉向
STM32循迹PWM調速及阿克曼舵機轉向
如果把每個輪子的運動半徑畫出來的話,就會得到圖上所示畫面。倒庫比正着往裡開更容易,因為後輪的運動半徑小于前輪的 運動半徑,是以相對于兩前輪而言,後輪的運動範圍更小,這也就意味着更容易将車“塞” 進停車位。
STM32循迹PWM調速及阿克曼舵機轉向
在車輛轉彎的時候,内側車輪的轉彎半徑明顯會短于外側車輪。這時,如果我們希望兩 個轉向輪可以正常轉彎,就需要讓内側車輪的轉角大于外側車輪。
STM32循迹PWM調速及阿克曼舵機轉向
STM32循迹PWM調速及阿克曼舵機轉向

小車硬體

(1)紅外循迹子產品

一般就是數字紅外輸出和模拟紅外輸出,兩種都可以,最終都是檢測紅外光傳回是否符合要求。比如數字紅外,當檢測到黑線時,大部分紅外光被吸收,紅外子產品引腳輸出高電平1,當無黑線時,紅外子產品引腳輸出低電平0。

(2)L298N驅動子產品

這WE裡采用六線接口L298N,即ENA,ENB,IN1,IN2,IN3,IN4.根據資料說明,由于本代碼采用PWM調速,是以ENA,ENB作為PWM接收端。

(3)SG90舵機

 SG90舵機的控制需要一個20ms時基脈沖,該脈沖的高電平部分一般為0.5ms~2.5ms範圍

PWM周期為20ms = (7200*200)/72000000=0.02是以TIM_Period = 199,TIM_Prescaler = 7199

(4)STM32C8T6最小系統

通用定時器2和4,GPIO,時鐘配置等。這裡有個細節就是定時器與管腳選擇有關,需看最小系統晶片原理圖為基礎配置定時器。

3,代碼實踐(以下代碼已完整,可自行調試)

(1)配置各個控制連接配接引腳及功能

void OUT_Init(void)//控制電機引腳配置
{
	     GPIO_InitTypeDef GPIO_InitStructure;
	     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

		 PIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_12|GPIO_Pin_13;
	     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	     GPIO_Init(GPIOB,&GPIO_InitStructure);

		 GPIO_ResetBits(GPIOB,GPIO_Pin_13);
		 GPIO_ResetBits(GPIOB,GPIO_Pin_12);
		 GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		 GPIO_ResetBits(GPIOB,GPIO_Pin_1);

}
    
           
void TIM2_PWM_Init(u16 arr,u16 psc)//PWM控制調速電機輸出配置
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//開啟定時器2時鐘
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);  
	GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);
                                                       	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; 
	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;
	TIM_TimeBaseStructure.TIM_Prescaler =psc;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse =0; 
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);  
    TIM_OCInitStructure.TIM_Pulse =0; 
	TIM_OC4Init(TIM2, &TIM_OCInitStructure); 

    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);   	
	TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);   
	TIM_ARRPreloadConfig(TIM2, ENABLE); 
    TIM_Cmd(TIM2, ENABLE);  

}
   
           
void TIM4_PWM_Init(u16 arr1,u16 psc1)//PWM輸出控制舵機角度引腳及功能配置
{ 
	 GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	

 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);                                        	

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //B_8引腳
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	TIM_TimeBaseStructure.TIM_Period = arr1; 
	TIM_TimeBaseStructure.TIM_Prescaler =psc1;  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; 
	TIM_OC3Init(TIM4, &TIM_OCInitStructure); 

	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);   
	TIM_ARRPreloadConfig(TIM4, ENABLE); 
	TIM_Cmd(TIM4, ENABLE);  
 
	}
               
           
void INFR_Init(void)//紅外引腳接收引腳配置
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOB時鐘
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//GPIOC時鐘//此處由于引腳數量限制開啟兩個GPIO始終端口
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//此處配置為浮空輸入模式,便于檢測紅外子產品高低電平狀态,以此判斷循迹狀态
 
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
  
 
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOC,GPIO_Pin_15);

	
		   
}
          
           

(2)電機運動模式配置

void forward(void)
{	
	TIM_SetCompare3(TIM4,187);//-------PB_8----定時器4通道3控制舵機轉向
	TIM_SetCompare3(TIM2,1000);//-------ENA--PB_10--定時器2通道3電機調速
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//-----IN1--PB_12
	GPIO_ResetBits(GPIOB,GPIO_Pin_13);//----IN2--PB_13
	
	TIM_SetCompare4(TIM2,1000);//-------ENB---PB_11-定時器2通道4電機調速
	GPIO_SetBits(GPIOB,GPIO_Pin_0);//----IN3---PB_0	
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);//-----IN4---PB_1
}

void turnleft(void)//
{		
	TIM_SetCompare3(TIM4,150);
	TIM_SetCompare3(TIM2,50);//-------ENA--PB10--
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//-----IN1
	GPIO_ResetBits(GPIOB,GPIO_Pin_13);//----IN2
	
	TIM_SetCompare4(TIM2,1000);//-------ENB---PB11-
	GPIO_SetBits(GPIOB,GPIO_Pin_0);//----IN3
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);//-----IN4
}
void turnright(void)//
{
  TIM_SetCompare3(TIM4,100);
	TIM_SetCompare3(TIM2,1000);//-------ENA--PB10
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//-----IN1
	GPIO_ResetBits(GPIOB,GPIO_Pin_13);//----IN2	
	
	TIM_SetCompare4(TIM2,50);//-------ENB---PB11
	GPIO_SetBits(GPIOB,GPIO_Pin_0);//----IN3
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);//-----IN4
}
void stop(void)
{
	  TIM_SetCompare3(TIM2,1500);//pwm
	  TIM_SetCompare4(TIM2,1500);
	  GPIO_ResetBits(GPIOB,GPIO_Pin_12);//·
	  GPIO_ResetBits(GPIOB,GPIO_Pin_13);
	
	  GPIO_ResetBits(GPIOB,GPIO_Pin_0);//
	  GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}



void turnleft2(void)//
{
  TIM_SetCompare3(TIM4,150);
	TIM_SetCompare3(TIM2,50);//-------ENA--PB10--
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//-----IN1
	GPIO_ResetBits(GPIOB,GPIO_Pin_13);//----IN2
	
	TIM_SetCompare4(TIM2,1000);//-------ENB---PB11--
	GPIO_SetBits(GPIOB,GPIO_Pin_0);//----IN3
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);//-----IN4
}
void turnright2(void)//
{
	TIM_SetCompare3(TIM4,100);
	TIM_SetCompare3(TIM2,1000);//-------ENA--PB10
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//-----IN1
	GPIO_ResetBits(GPIOB,GPIO_Pin_13);//----IN2	
	
	TIM_SetCompare4(TIM2,50);//-------ENB---PB11
	GPIO_SetBits(GPIOB,GPIO_Pin_0);//----IN3
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);//-----IN4
}
           

(3)實作主函數搭配功能融合

int main(void)
{
	int LXJ0,LXJ1,LXJ2,LXJ3,XJ4,RXJ5,RXJ6,RXJ7,RXJ15=0;
	
                                 INFR_Init();
                                 OUT_Init();
                                 delay_init();
                                 TIM4_PWM_Init(2000,719);
                                 TIM2_PWM_Init(2000,719);
                                 delay_ms(500);
                                 elay_ms(500);
                                 delay_ms(500);
                                 delay_ms(500);¯
	


while(1) 
{ 
	        LXJ0=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//讀取紅外引腳高低電平,
			LXJ1=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);//以此判斷是否在黑線上
		    LXJ2=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
			LXJ3=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);	
		    XJ4=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
		    RXJ5=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);
		    RXJ6=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
			RXJ7=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7);
	        RXJ15=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_15);
      		if((XJ4==1&&LXJ1==0&&LXJ2==0&&LXJ3==0&&RXJ5==0&&RXJ6==0&&RXJ7==0&&RXJ15==0&&LXJ0==0))
			 {		
				 forward();	 			 
			 }
			else if(LXJ3==1|XJ4==1&&LXJ0==0&&LXJ2==0&&LXJ3==0&&RXJ5==0&&RXJ6==0&&RXJ7==0&&RXJ15==0&&XJ4==0)
			 {		 
		    turnright();		 
			 }
			 else if(XJ4==1|RXJ5==1&&LXJ1==0&&LXJ0==0&&LXJ3==0&&RXJ5==0&&RXJ6==0&&RXJ7==0&&RXJ15==0&&XJ4==0)
			 {		
				  turnleft();						 	 
			 }
		   else if(LXJ0==1|LXJ1==1|LXJ2==1&&LXJ1==0&&LXJ2==0&&LXJ0==0&RXJ5==0&&RXJ6==0&&RXJ7==0&RXJ15==0&&XJ4==0)
			 {		
				  turnright2();	 
			 }
			
	  	 else if(RXJ6==1|RXJ7==1|RXJ15==1 &&LXJ1==0&&LXJ2==0&&LXJ3==0&&RXJ5==0&&RXJ6==0&&RXJ7==0&&RXJ15==0&&XJ4==0)
			 {		
				  turnleft2(); 	 
			 }
			
           

繼續閱讀