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