stm32zet6巡线小车(7路数字循迹)

 网上好多小车循迹的程序,但讲的都不是很清楚,在这里我总结了我们校电赛的经验,将数字循迹小车的步骤+代码给大家讲一下,废话不多说,咱先来看看步骤:

1,先让小车能动起来,不管是L298n还是TB6612FNG的驱动,给他的输入端一个高电平一个低电平即可,硬件我就不多说讲了很简单,看着那个模块连接就可以。

2,用定时器的pwm输出,通过调节占空比让小车的速度可以改变。

3,获取红外开关or光电开关的数值(就是0或者1),很简单,一会儿直接代码。

4,写一些可以让小车动的程序(例如直行,停止啥的),一会代码就知道了。

5,写while()循环,根据不同情况改变轮子的速度来行走不同的路线。

接下来直接上代码:

注释掉的是我用的OLED屏幕来显示左右轮的占空比,文件里都有

第一步,让小车动

#include "motor.h"
  

//电机初始化    

void MOTOR_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟,我的左两轮
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);	 //使能PF端口时钟,右两轮
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 //使能PC端口时钟,TB6612FNG有个高电平输出端
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;	//电机方向控制端口选择
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOA, &GPIO_InitStructure);					 //GPIOA初始化
	
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15|GPIO_Pin_11;	//电机方向控制端口选择
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOF, &GPIO_InitStructure);					 //GPIOF初始化
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;	//两个TB6612FNG高电平输出端
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOC, &GPIO_InitStructure);					 //GPIOC初始化
		
	
 GPIO_SetBits(GPIOA, GPIO_Pin_0|GPIO_Pin_4);						 //PA0,PA4输出高
 GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_5); 						 //PA1,PA5输出低
 //PA0高,PA1低左前轮转(其他以此类推)
 
 //PA0,PA1的PWM为PA6       PA4,PA5的PWM为PA7
 
 GPIO_SetBits(GPIOF, GPIO_Pin_12|GPIO_Pin_14);						 //PF12,PF14输出高
 GPIO_ResetBits(GPIOF,GPIO_Pin_13|GPIO_Pin_15); 						 //PF13,PF15 输出低
 //PF12,13的PWM为PB0       PF14,15的PWM为PB1
 
  GPIO_SetBits(GPIOC, GPIO_Pin_4|GPIO_Pin_5);	//两个TB6612FNG的高电平输出端输出高
}

.h文件

#ifndef __MOTOR_H
#define __MOTOR_H	 
#include "sys.h"
//	 
// 
这里我用了几个宏定义,以便后边改变两个接口高低电平的输出,来改变轮子的转向
#define WHEELA_IN1 PAout(0)// 引脚PA0
#define WHEELA_IN2 PAout(1)// 引脚PA1	  左前

#define WHEELB_IN1 PAout(4)// 引脚PA4
#define WHEELB_IN2 PAout(5)// 引脚PA5	  左后

#define WHEELC_IN1 PFout(12)// 引脚PF12
#define WHEELC_IN2 PFout(13)// 引脚PF13   右前	

#define WHEELD_IN1 PFout(14)// 引脚PF14
#define WHEELD_IN2 PFout(15)// 引脚PF15   右后

void MOTOR_Init(void);//初始化

		 				    
#endif

第二步,加上pwm

#include "pwm.h"
#include "motor.h"
#include "usart.h"

//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数

//这个函数是初始化定时器3的pwm通道,和设置arr,psc.而占空比的调节要在TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)这个函数里来调节
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);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOA  |  RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO
 	 
   //设置引脚为复用输出功能,输出TIM3 CH的PWM脉冲波形	GPIOA.6  	GPIOA.7  	GPIOB.0  	GPIOB.1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //通道3 TIM_CH3   通道4 TIM_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //通道1 TIM_CH1   通道2 TIM_CH2
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
	
   //初始化TIM3定时器,我的4个pwm是用的定时器3的四个pwm通道
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	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 Channel 1 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC1
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器
	
		//初始化TIM3 Channel 2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高	
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
	
		//初始化TIM3 Channel 3 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC3
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR3上的预装载寄存器
	
		//初始化TIM3 Channel 4 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC4
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR4上的预装载寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	

}

.h

第三步,红外开关or光电开关

#include "stm32f10x.h"
#include "Kaiguan.h"
#include "sys.h" 

								    
//这里我用的是PF的4,5,6,7,8,9,10口来输入的
void Kaiguan_Init(void) //IO初始化
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能GPORTF时钟
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;    //设置成下拉
 	GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIOE4,3
	
	//这个是led灯的可加可不加(我懒得删除了)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPORTB时钟
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5;//
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB,5 


}

.h

#ifndef __KAIGUAN_H
#define __KAIGUAN_H	 
#include "sys.h"


#define L1 PFin(4)		//L1亮时为0
#define L2 PFin(5)		//L2亮时为0
#define L3 PFin(6)		//L3亮时为0
#define L4 PFin(7)		//L4亮时为0
#define L5 PFin(8)		//L5亮时为0
#define L6 PFin(9)		//L6亮时为0
#define L7 PFin(10)		//L7亮时为0


#define LED0 PBout(5)  //led灯可加可不加

void Kaiguan_Init(void);//IO初始化
#endif

第四步,

#include "stm32f10x.h"
#include "car_run.h"
#include "sys.h" 
#include "motor.h"

void car_foeward(void) //直行
{ 	
		  WHEELA_IN1=1;
			WHEELA_IN2=0;		//马达左前 
			
      WHEELB_IN1=1;
			WHEELB_IN2=0;		//马达左后 
			
		  WHEELC_IN1=1;
			WHEELC_IN2=0;		//马达右前 
			
      WHEELD_IN1=1;
			WHEELD_IN2=0;		//马达右后 
}

//转弯我是让一侧的轮子正转,另一侧的轮子反转,然后再用延时控制转动时间完成转弯
//下边两个轮子WHEEL_IN1=1,WHEEL_IN2=0,即一高电平,二低电平为正转,反过来就是反转如果你接好之后转向不对,把你的驱动模块上的这两个接口倒换一下即可
void CAR_right_w(void) //右转弯
{ 
		  WHEELA_IN1=1;
			WHEELA_IN2=0;		//马达左前 
			
      WHEELB_IN1=1;
			WHEELB_IN2=0;		//马达左后 
			
		  WHEELC_IN1=0;
			WHEELC_IN2=1;		//马达右前 
			
      WHEELD_IN1=0;
			WHEELD_IN2=1;		//马达右后 
		
}
void CAR_left_w(void) //左转弯
{ 
		  WHEELA_IN1=0;
			WHEELA_IN2=1;		//马达左前 
			
      WHEELB_IN1=0;
			WHEELB_IN2=1;		//马达左后 
			
		  WHEELC_IN1=1;
			WHEELC_IN2=0;		//马达右前 
			
      WHEELD_IN1=1;
			WHEELD_IN2=0;		//马达右后 
}

void CAR_stop(void) //停止
{ 
		  WHEELA_IN1=0;
			WHEELA_IN2=1;		//马达左前 
			
      WHEELB_IN1=0;
			WHEELB_IN2=1;		//马达左后 
			
		  WHEELC_IN1=0;
			WHEELC_IN2=1;		//马达右前 
			
      WHEELD_IN1=0;
			WHEELD_IN2=1;		//马达右后 
}

第五步,主函数

#include "motor.h"
#include "delay.h"
#include "Kaiguan.h"
#include "sys.h"
#include "usart.h"
#include "pwm.h"
#include "car_run.h"
#include "stdio.h"
//#include "oled.h"
 int main(void)
 {		
	 u16  arr=899,PSC=0;//定义了两个变量,让arr=899,psc=0.切记,ccr一定不能超过arr.
	 u16 ccr1=0,ccr2=0,ccr3=0,ccr4=0;//这里我定义了四个变量,分别来改变四个pwm通道的占空比
	
	MOTOR_Init();			     //MOTOR马达端口初始化
 	TIM3_PWM_Init(arr,PSC);	 //不分频。PWM频率=72000000/900=80Khz
	Kaiguan_Init();         //红外函数初始化
	delay_init();						//延时函数初始化
//	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
//	 OLED_Init();			//初始化OLED   

//	 OLED_ShowString(0,0,"ALIENTEK",16);  
//	 OLED_ShowString(0,24,"ccr1&ccr2:",16);  
//	 OLED_ShowString(0,48,"ccr3&ccr4:",16);  
//	 
//	 OLED_Refresh_Gram();		//更新显示到OLED 
//	 
	 
	 一定要看完下边这段文字
	 
	 //我的传感器是检测到黑色输出1,检测到白色输出0,根据检测情况的不同,改变轮子转速来使小车实现不同的效果
	 //我发现有好多人用数字量来做寻迹小车的时候,把每种情况的占空比只用了一个数值来表示
	 //即比如车往右偏了,不管偏多少,他的右两轮占空比都是400,左两轮占空比都是0.这样虽然没错,但实际运动起来必须要大大降低车速才能实现正常的循迹
	 //而我是将每每种不同情况都改变不同的占空比来实现的,比如我的车稍微往右偏了,我将右两轮占空比设为240,左两轮30
	 //如果我的车往右偏了很多我的右两轮占空比设为了800,左两轮占空比设为0,这样就能加快小车的速度
	 //而且这样的话,如果你的传感器越多什么九路,十一路了,他的调节就越灵敏,速度就可以提升的更高
	 
	while(1)
	{
			if(L1==0&&L2==0&&L3==0&&L4==1&&L5==0&&L6==0&&L7==0)
		{
			ccr1=800;	//左前轮的占空比
			ccr2=800;	//左后轮
			ccr3=800;	//右前轮
			ccr4=800;	//右后轮
//不同情况改变不同的占空比
			car_foeward();	//这个函数是来控制轮子的转向,具体在car_run.c里边很清楚
		}			
	else if((L4==1&&L5==1&&L6==1&&L7==1)||(L5==1&&L6==1&&L7==1)||(L4==1&&L5==1&&L6==1))
		{ 
			ccr1=200;	//左前轮的占空比
			ccr2=200;	//左后轮
			ccr3=200;	//右前轮
			ccr4=200;	//右后轮
			CAR_stop();
			
			ccr1=600;	//左前轮的占空比
			ccr2=600;	//左后轮
			ccr3=600;	//右前轮
			ccr4=600;	//右后轮
			CAR_left_w();
			delay_ms(300);	//延时控制转弯时间
		}
	else if((L1==1&&L2==1&&L3==1&&L4==1)||(L2==1&&L3==1&&L4==1)||(L1==1&&L2==1&&L3==1))
		{ 
			ccr1=200;	//左前轮的占空比
			ccr2=200;	//左后轮
			ccr3=200;	//右前轮
			ccr4=200;	//右后轮
			CAR_stop();
			
			ccr1=600;	//左前轮的占空比
			ccr2=600;	//左后轮
			ccr3=600;	//右前轮
			ccr4=600;	//右后轮
			CAR_right_w();
			delay_ms(300);			
		}
	else if(L1==1&&L2==1&&L3==1&&L4==1&&L5==1&&L6==1&&L7==1)
		{ 
			ccr1=200;	//左前轮的占空比
			ccr2=200;	//左后轮
			ccr3=200;	//右前轮
			ccr4=200;	//右后轮
			CAR_stop();
		}
	else if(L1==0&&L2==0&&L3==1&&L4==0&&L5==0&&L6==0&&L7==0)
		{
			ccr1=240;	//左前轮的占空比
			ccr2=240;	//左后轮
			ccr3=30;	//右前轮
			ccr4=30;	//右后轮
			car_foeward();										//右弯道
		}
			else if(L1==0&&L2==1&&L3==0&&L4==0&&L5==0&&L6==0&&L7==0)
		{
			ccr1=500;	//左前轮的占空比
			ccr2=500;	//左后轮
			ccr3=50;	//右前轮
			ccr4=50;	//右后轮
			car_foeward();										//右弯道
		}
			else if(L1==1&&L2==0&&L3==0&&L4==0&&L5==0&&L6==0&&L7==0)
		{
			ccr1=800;	//左前轮的占空比
			ccr2=800;	//左后轮
			ccr3=0;	//右前轮
			ccr4=0;	//右后轮
			car_foeward();										//右弯道
		}
	else if(L1==0&&L2==0&&L3==0&&L4==0&&L5==1&&L6==0&&L7==0)
		{
			ccr1=30;	//左前轮的占空比
			ccr2=30;	//左后轮
			ccr3=240;	//右前轮
			ccr4=240;	//右后轮
			car_foeward();																//左弯道
		}
			else if(L1==0&&L2==0&&L3==0&&L4==0&&L5==0&&L6==1&&L7==0)
		{
			ccr1=50;	//左前轮的占空比
			ccr2=50;	//左后轮
			ccr3=500;	//右前轮
			ccr4=500;	//右后轮
			car_foeward();																//左弯道
		}
			else if(L1==0&&L2==0&&L3==0&&L4==0&&L5==0&&L6==0&&L7==1)
		{
			ccr1=0;	//左前轮的占空比
			ccr2=0;	//左后轮
			ccr3=800;	//右前轮
			ccr4=800;	//右后轮
			car_foeward();																//左弯道
		}
		//上边判断完时候,将占空比输入进去,完成一次循环
			TIM_SetCompare1(TIM3,ccr1);	//D
			TIM_SetCompare2(TIM3,ccr2);	//B
			TIM_SetCompare3(TIM3,ccr3);	//A
			TIM_SetCompare4(TIM3,ccr4);	//C
		
//		 OLED_ShowNum(76,24,ccr1,3,16);
//	 	 OLED_ShowNum(76,48,ccr3,3,16);
//		 OLED_Refresh_Gram();		//更新显示到OLED 
		}
}

要我说,其实弄清步骤之后,这个循迹小车贼简单。

代码来了

https://download.csdn.net/download/qq_54652195/20496203

版权声明:本文为CSDN博主「..过云雨」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_54652195/article/details/119114896

生成海报
点赞 0

..过云雨

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

RT-Thread Studio移植LAN8720A驱动

RTT网络协议栈驱动移植(霸天虎) 1、新建工程 ​ 工程路径不含中文路径名,工程名用纯英文不含任何符号。 2、用CubeMx配置板子外设 2.1、配置时钟 ​ 按照自己板子配置相应时钟。

Lin总线通信在STM32作为主机代码以及从机程序

距离上次做资料准备已经过去六天了。最近在学车,上周末就没有开电脑。这周开始进行了Lin通信的代码整理,目前是可以正常通信的了,采用的是增强型校验方式。后期再进一步跟进研究。。。更新一博,留

4路红外循迹模块使用教程

4路红外循迹模块使用教程 个人原创博客:点击浏览模块详细信息: 工作电压:DC 3.3V~5V 工作电流:尽量选择1A以上电源供电 工作温度:-10℃~50℃ 安装孔

HAL库串口中断

一,配置串口初始化 void MX_USART1_UART_Init(void) {huart1.Instance USART1;huart1.Init.BaudRate 115200;huart1.Init.WordLen