基於STM32F103的紅外循跡避障小車設計(含Proteus仿真)

基於STM32F103的紅外循跡避障小車設計

紅外循跡及紅外避障實現較簡單,無論是51單片機還是STM32單片機,其例程隨處可見。但是完全可以運行的Proteus仿真,開源的並不多,更不要說基於STM32單片機的仿真。
下面跟大家聊聊基於STM32F103的紅外循跡避障小車的Proteus仿真。

首先,我們來驗證一下,Proteus軟件能否對STM32進行仿真。

我所使用的是Proteus8.6版本,我們可以看到STM32芯片有以下幾款:

在這裏插入圖片描述

我選用STM32F103R6芯片,進行點燈仿真,從而證明Proteus可以對STM32進行仿真實驗。

在這裏插入圖片描述

可以看到運行之後,LED燈被點亮。

在這裏插入圖片描述

此部分程序(即STM32點亮LED)如下:
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h"



static void Delay(__IO uint32_t nCount)	 //簡單的延時函數
{
	for(; nCount != 0; nCount--);
}



/**
  * @brief  主函數
  * @param  無  
  * @retval 無
  */
int main ( void )
{	
	LED_Init ();	//初始化 LED
	Key_GPIO_Config();
  macLED1_OFF ();
  macLED2_OFF ();
	macLED3_OFF ();
	
	while ( 1 )
	{
		if( Key_Scan(macKEY1_GPIO_PORT,macKEY1_GPIO_PIN) == KEY_ON  )
		
		{
			/*LED1反轉*/
			  macLED1_TOGGLE() ;
				macLED2_TOGGLE() ;
				macLED3_TOGGLE() ;
				 //return main();
		}   
		
			
	/*	macLED1_ON ();			  // 亮 此處爲流水燈程序
		Delay ( 22100 );
		macLED1_OFF ();		  // 滅

		macLED2_ON ();			  // 亮
		Delay ( 22100 );
		macLED2_OFF ();		  // 滅

		macLED3_ON ();			  // 亮
		Delay ( 22100 );
		macLED3_OFF ();		  // 滅	   
		*/
	}

}

下面開始紅外循跡及避障的Proteus仿真:

紅外循跡模塊,選用此款,使用簡單,價格便宜

在這裏插入圖片描述

其工作原理我不再贅述,其原理圖如下圖:

在這裏插入圖片描述

紅外避障模塊,對比之後,發現紅外避障效果最好的,就是這款紅外模塊。

在這裏插入圖片描述

電機驅動模塊,選擇L298電機驅動,此驅動較常用,使用也較簡單。

在這裏插入圖片描述

其原理圖如下:

在這裏插入圖片描述

STM32單片機選擇STM32F103C8T6,只需要買此小核心板即可,經濟實惠。

在這裏插入圖片描述

單片機最小系統原理圖:

在這裏插入圖片描述

電源部分原理圖如下:

在這裏插入圖片描述

Proteus紅外模塊的仿真,只能用按鍵模擬,即單片機引腳檢測0、1信號。

完整Proteus仿真圖如下:

在這裏插入圖片描述

點擊運行

在這裏插入圖片描述

大家可以看到,程序跑不動,顯示在運行,但是程序無法正常循環

在這裏插入圖片描述

實踐證明,Proteus仿真STM32只能進行簡單的仿真,無法進行復雜仿真。不過仿真51單片機是完全沒有問題的。
所以,我的建議,直接做實物,最終實物調試成功,沒有任何問題

來張實物圖片

在這裏插入圖片描述

部分程序如下,可參考

主程序

#include "stm32f10x.h"
#include "bsp_SysTick.h"
#include "bsp_led.h"
#include <string.h>
#include <stdlib.h>
#include "bsp_pwm_output.h" 
#include "infrared.h" 
#include "delay.h"
//全局變量
unsigned int Task_Delay[NumOfTask];

/**
  * @brief  主函數
  * @param  無
  * @retval 無
  */
int main(void)
{	
	
	  //初始化systick
	SysTick_Init();											//用於延時等操作
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
	TIMx_PWM_Init();			//PWM波初始化
  infrared_Initial();		//紅外初始化
	while(1)
	{
		if((infrared_Scan(infrared_5_GPIO_PORT,infrared_5_GPIO_PIN)==INFRARED_ON)&&\
			(infrared_Scan(infrared_6_GPIO_PORT,infrared_6_GPIO_PIN)==INFRARED_ON))//前方有障礙,必須兩邊同時檢測到才能觸發,否則均認爲是誤觸
		{		
			DelayS(3);		//延遲3秒
			if((infrared_Scan(infrared_5_GPIO_PORT,infrared_5_GPIO_PIN)==INFRARED_ON)&&\
			(infrared_Scan(infrared_6_GPIO_PORT,infrared_6_GPIO_PIN)==INFRARED_ON))//仍舊有障礙
			{
					TIMx_Mode_Config(500,0,0,500);//右轉500ms
		  		DelayMs(500);
				
			}
			else
			{
			     TIMx_Mode_Config(500,0,0,500);//直行500ms
					 DelayMs(500);
			}
			
		}
		else
		{
			if((infrared_Scan(infrared_3_GPIO_PORT,infrared_3_GPIO_PIN)==INFRARED_ON))//循跡,右側內部傳感器見到黑線,說明車身方向偏右
			    TIMx_Mode_Config(0,0,600,0);//輕微左轉
			else if((infrared_Scan(infrared_2_GPIO_PORT,infrared_2_GPIO_PIN)==INFRARED_ON))//循跡,左側內部傳感器見到黑線,說明車身方向偏左
			    TIMx_Mode_Config(600,0,0,0);//輕微右轉
			else if((infrared_Scan(infrared_4_GPIO_PORT,infrared_4_GPIO_PIN)==INFRARED_ON))//循跡,右側外部傳感器見到黑線,說明車身方向偏左
			    TIMx_Mode_Config(0,0,900,0);//劇烈左轉
			else if((infrared_Scan(infrared_1_GPIO_PORT,infrared_1_GPIO_PIN)==INFRARED_ON))//循跡,左側外部傳感器見到黑線,說明車身方向偏左
			    TIMx_Mode_Config(900,0,0,0);//劇烈右轉
			else
				 TIMx_Mode_Config(500,0,500,0);//直行
		}
	}

}

PWM波程序

#include "bsp_pwm_output.h" 

 /**
  * @brief  配置TIM3複用輸出PWM時用到的I/O
  * @param  無
  * @retval 無
  */
static void TIMx_GPIO_Config(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;

	/* 設置TIM3CLK 爲 72MHZ */
//  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 
	macTIM_APBxClock_FUN (macTIM_CLK, ENABLE);

  /* GPIOA and GPIOB clock enable */
  macTIM_GPIO_APBxClock_FUN (macTIM_GPIO_CLK, ENABLE); 

  /*GPIOA Configuration: TIM3 channel 1 as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin =  macTIM_CH1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		    // 複用推輓輸出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(macTIM_CH1_PORT, &GPIO_InitStructure);

  /*GPIOB Configuration: TIM3 channel 2 as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin =  macTIM_CH2_PIN;
  GPIO_Init(macTIM_CH2_PORT, &GPIO_InitStructure);

  /*GPIOB Configuration: TIM3 channel 3 as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin =  macTIM_CH3_PIN;
  GPIO_Init(macTIM_CH3_PORT, &GPIO_InitStructure);
	
  /*GPIOB Configuration: TIM3 channel 4 as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin =  macTIM_CH4_PIN;
  GPIO_Init(macTIM_CH4_PORT, &GPIO_InitStructure);
	
}

/**
  * @brief  配置TIM3輸出的PWM信號的模式,如週期、極性、佔空比
  * @param  無
  * @retval 無
  */
/*
 * TIMxCLK/CK_PSC --> TIMxCNT --> TIMx_ARR --> TIMxCNT 重新計數
 *                    TIMx_CCR(電平發生變化)
 * 信號週期=(TIMx_ARR +1 ) * 時鐘週期
 * 佔空比=TIMx_CCR/(TIMx_ARR +1)
 */
void TIMx_Mode_Config(u16 CCR1_Val,u16 CCR2_Val,u16 CCR3_Val, u16 CCR4_Val)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;


/* ----------------------------------------------------------------------- 
    macTIMx Channel1 duty cycle = (macTIMx_CCR1/ macTIMx_ARR+1)* 100% = 50%
    macTIMx Channel2 duty cycle = (macTIMx_CCR2/ macTIMx_ARR+1)* 100% = 37.5%
    macTIMx Channel3 duty cycle = (macTIMx_CCR3/ macTIMx_ARR+1)* 100% = 25%
    macTIMx Channel4 duty cycle = (macTIMx_CCR4/ macTIMx_ARR+1)* 100% = 12.5%
  ----------------------------------------------------------------------- */

  /* Time base configuration */		 
  TIM_TimeBaseStructure.TIM_Period = 999;       //當定時器從0計數到999,即爲1000次,爲一個定時週期
  TIM_TimeBaseStructure.TIM_Prescaler = 0;	    //設置預分頻:不預分頻,即爲72MHz
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;	//設置時鐘分頻係數:不分頻(這裏用不到)
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上計數模式
  TIM_TimeBaseInit(macTIMx, &TIM_TimeBaseStructure);

  /* PWM1 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	    //配置爲PWM模式1
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;	   //設置跳變值,當計數器計數到這個值時,電平發生跳變
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //當定時器計數值小於CCR1_Val時爲高電平
  TIM_OC1Init(macTIMx, &TIM_OCInitStructure);	 //使能通道1
  TIM_OC1PreloadConfig(macTIMx, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;	  //設置通道2的電平跳變值,輸出另外一個佔空比的PWM
  TIM_OC2Init(macTIMx, &TIM_OCInitStructure);	  //使能通道2
  TIM_OC2PreloadConfig(macTIMx, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;	//設置通道3的電平跳變值,輸出另外一個佔空比的PWM
  TIM_OC3Init(macTIMx, &TIM_OCInitStructure);	 //使能通道3
  TIM_OC3PreloadConfig(macTIMx, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel4 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;	//設置通道4的電平跳變值,輸出另外一個佔空比的PWM
  TIM_OC4Init(macTIMx, &TIM_OCInitStructure);	//使能通道4
  TIM_OC4PreloadConfig(macTIMx, TIM_OCPreload_Enable);
  TIM_ARRPreloadConfig(macTIMx, ENABLE);			 // 使能TIM3重載寄存器ARR

  /* TIM3 enable counter */
  TIM_Cmd(macTIMx, ENABLE);                   //使能定時器3	
}

/**
  * @brief  TIM3 輸出PWM信號初始化,只要調用這個函數
  *         TIM3的四個通道就會有PWM信號輸出
  * @param  無
  * @retval 無
  */
void TIMx_PWM_Init(void)
{
	TIMx_GPIO_Config();
	TIMx_Mode_Config(0,0,0,0);	
}

延時程序

#include "stm32f10x.h"
#include "delay.h"

static u8  fac_us=0;//us延時倍乘數
static u16 fac_ms=0;//ms延時倍乘數

//初始化延遲函數
//當使用ucos的時候,此函數會初始化ucos的時鐘節拍
//SYSTICK的時鐘固定爲HCLK時鐘的1/8
//SYSCLK:系統時鐘
void DelayInit()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//選擇外部時鐘  HCLK/8
	fac_us=SystemCoreClock/8000000;	//爲系統時鐘的1/8  
	fac_ms=(u16)fac_us*1000;//非ucos下,代表每個ms需要的systick時鐘數   
}

//延時nus
//nus爲要延時的us數.		    								   
void DelayUs(unsigned long nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; //時間加載	  		 
	SysTick->VAL=0x00;        //清空計數器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //開始倒數	 
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//等待時間到達   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //關閉計數器
	SysTick->VAL =0X00;       //清空計數器	 
}
//延時nms
//注意nms的範圍
//SysTick->LOAD爲24位寄存器,所以,最大延時爲:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK單位爲Hz,nms單位爲ms
//對72M條件下,nms<=1864 
void DelayMs(unsigned int nms)
{
	u32 temp;
	SysTick->LOAD=(u32)nms*fac_ms;//時間加載(SysTick->LOAD爲24bit)
	SysTick->VAL =0x00;           //清空計數器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //開始倒數  
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//等待時間到達
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //關閉計數器
	SysTick->VAL =0X00;       //清空計數器
}

void DelayS(unsigned int ns)//延時秒
{
	unsigned char i;
	for(i=0;i<ns;i++)
	{
		DelayMs(1000);
	}
}

涉及到STM32的設計,Proteus軟件暫時無法進行完全的仿真,建議直接設計實物,調試實物代替仿真

分享決定高度,學習拉開差距

作爲學習者給大家分享自己完成的此作品,希望對大家有幫助,當然上文若有不妥之處,歡迎指正。

歡迎大家留言,批評指正!

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