我一直都觉得自动跟随小车很酷,所以我的单片机课设就决定做这个了。其实现在网上有这个的代码不多,反正我怎么找也找不到,当时还没学完STM32的时候觉得好难实现,接近期末的时候终于搞出来了,虽然不是很理想,但还是不错滴,看一下效果。
这个是只有超声波传感器的,看上去有点简约,可以实现基本的自动跟随功能了
这个是接了火焰传感器、烟雾传感器、蜂鸣器、红外避障模块等等,看上去酷多了。
可能因为板子的端口有冲突,没有把三个超声波的引脚定义到同一组GPIO口上,但是这并不影响。
void CH_SR04_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructer;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructer;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*TRIG触发信号*/
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_8 |GPIO_Pin_6;
GPIO_Init(GPIOB, &GPIO_InitStructer);
/*ECOH回响信号*/
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_7;
GPIO_Init(GPIOB, &GPIO_InitStructer);
/*定时器TIM2初始化*/
TIM_DeInit(TIM4);
TIM_TimeBaseInitStructer.TIM_Period=999;//定时周期为1000
TIM_TimeBaseInitStructer.TIM_Prescaler=71; //分频系数72
TIM_TimeBaseInitStructer.TIM_ClockDivision=TIM_CKD_DIV1;//不分频
TIM_TimeBaseInitStructer.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructer);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);//开启更新中断
NVIC_Config();
TIM_Cmd(TIM4,DISABLE);//关闭定时器使能
}
我把右边的超声波分开定义了,你也可以自己修改。
void right(void){ //右边超声波的引脚声明
GPIO_InitTypeDef GPIO_InitStructer;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*TRIG触发信号*/
GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructer);
/*ECOH回响信号*/
GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructer.GPIO_Pin=GPIO_Pin_4;
GPIO_Init(GPIOA, &GPIO_InitStructer);
}
下面这部分就是超声波工作的过程了,也是核心代码。注释也解释得很清楚了,需要注意的是这里的超声波是测量了五次才返回一个值的,这样做的好处是起到一个滤波的作用,使得超声波的值更精确、稳定。但是他也有不好的地方,在小车前进过程可能会识别很慢或者转向失败,因为三个超声波是使用同一个定时器的,分时复用的,也就是同一时刻只能有一个超声波工作,所以如果处理器不够快的话可以把次数降低一点,我就是值检测一次的,效果也还行。左右超声波的函数也是一样的,把函数名改一下就好了。
//中间超声波的工作过程
float Senor_Using(void)
{
float length=0,sum=0;
u16 tim;
uint i=0;
/*测5次数据计算一次平均值*/
while(i!=5)
{
PBout(8)=1; //拉高信号,作为触发信号
delay_us(25); //高电平信号超过10us
PBout(8)=0;
/*等待回响信号*/
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==RESET);
TIM_Cmd(TIM4,ENABLE);//回响信号到来,开启定时器计数
i+=5; //每收到一次回响信号+1,收到5次就计算均值
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==SET);//回响信号消失
TIM_Cmd(TIM4,DISABLE);//关闭定时器
tim=TIM_GetCounter(TIM4);//获取计TIM4数寄存器中的计数值,一边计算回响信号时间
length=(tim+overcount*1000)/58.0;//通过回响信号计算距离
sum=length+sum;
TIM4->CNT=0; //将TIM4计数寄存器的计数值清零
overcount=0; //中断溢出次数清零
delay_ms(10);
}
length=sum/5;
return length;//距离作为函数返回值
}
这个是定时器的中断服务函数,避免因为距离过长,定时器中的值溢出。
void TIM4_IRQHandler(void) //中断,当回响信号很长是,计数值溢出后重复计数,用中断来保存溢出次数
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
{
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清除中断标志
overcount++;
}
}
#include "moter.h"
void CarGo(void)
{
TIM_SetCompare1(TIM3 , 300); //数值越大速度越慢
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare3(TIM3 , 300);
TIM_SetCompare4(TIM3 , 900);
}
void CarStop(void)
{
TIM_SetCompare1(TIM3 , 900);
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare3(TIM3 , 900);
TIM_SetCompare4(TIM3 , 900);
}
void CarBack(void)
{
TIM_SetCompare1(TIM3 , 900);
TIM_SetCompare2(TIM3 , 400);
TIM_SetCompare3(TIM3 , 900);
TIM_SetCompare4(TIM3 , 400);
}
void CarLeft(void)
{
TIM_SetCompare1(TIM3 , 900);
TIM_SetCompare2(TIM3 , 200);
TIM_SetCompare3(TIM3 , 200);
TIM_SetCompare4(TIM3 , 900);
}
void CarBigLeft(void)
{
TIM_SetCompare1(TIM3 , 900);
TIM_SetCompare2(TIM3 , 100);
TIM_SetCompare3(TIM3 , 100);
TIM_SetCompare4(TIM3 , 900);
}
void CarRight(void)
{
TIM_SetCompare1(TIM3 , 200);
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare3(TIM3 , 900);
TIM_SetCompare4(TIM3 , 200);
}
void CarBigRight(void)
{
TIM_SetCompare1(TIM3 , 100);
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare3(TIM3 , 900);
TIM_SetCompare4(TIM3 , 100);
}
void TIM3_PWM_Init(void)
{
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 | RCC_APB2Periph_AFIO, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 899;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM3 , &TIM_TimeBaseStructure);
//端口复用
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//PWM通道1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC1Init(TIM3 , &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3 , TIM_OCPreload_Enable);
//PWM通道2
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC2Init(TIM3 , &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3 , TIM_OCPreload_Enable);
//PWM通道3
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC3Init(TIM3 , &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3 , TIM_OCPreload_Enable);
//PWM通道4
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC4Init(TIM3 , &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3 , TIM_OCPreload_Enable);
TIM_Cmd(TIM3 , ENABLE);
}
while(1)
{
length_res[0]=Senor_Using();
delay_ms(100);
if(length_res[0]>100.00)
{
CarGo();
}
if(length_res[0]<=40.00)
{
CarBack();//先后退一段距离
delay_ms(200);
CarBack();//先后退一段距离
delay_ms(200);
length_res[0] =Senor_Using();//再次测量前方距离
delay_ms(50);
length_res[1]=Senor_Using_left();
delay_ms(10);
if(length_res[1]<=110.00&&length_res[0]>150.00)
{
CarLeft();
delay_ms(10);
}
length_res[2]=Senor_right();
if(length_res[2]<=110.00&&length_res[0]>150.00)
{
CarRight();
delay_ms(10);
}
}
length_res[1]=Senor_Using_left();
delay_ms(10);
if(length_res[1]<=110.00&&length_res[0]>150.00)
{
CarLeft();
delay_ms(50);
CarLeft();
delay_ms(50);
if(length_res[1]<=30){
CarRight();
delay_ms(50);
}
}
length_res[2]=Senor_right();
if(length_res[2]<=110.00&&length_res[0]>150.00)
{
CarRight();
delay_ms(50);
CarRight();
delay_ms(50);
if(length_res[2]<=30){
CarLeft();
delay_ms(50);
}
}
}
完成的工程代码已上传,有积分的土豪可以直接下载,链接:工程代码点这里
没有积分的话也可以关注我的公众号【物联网技术猿】,后台回复“自动跟随”即可领取。