其中,蓝牙控制功能要实现的包括1.驱动小车向前后左右运动;2.驱动小车在地面走出矩形的图案。
(1)超声波返回的很多不准确数据导致小车运动异常
超声波测距元件所测出来的数据不是精准数据,而且受到实际环境的影响很明显,所以在大量返回数据中有很多异常数据,这些数据会导致小车的运动异常。而我们的解决思路也很明确——过滤掉一些明显异常的数据。超声波模块(HC-SR04)测距范围为0~400cm,我们将模块返回的数据中大于500cm的数据过滤掉,并通过多次接收数据取平均值的方式提高测距的精确度,通过这种方法小车的运动更加稳定。
(2)对一些极端的情况缺少考虑
在设计小车的运动算法时,我们没有考虑到当小车斜对墙面运动时因为超声波模块自身原因导致数据异常的情况。因为这一次我们的设计方案为将三个超声波模块分别放于小车前方检测小车前方、左侧、右侧是否有障碍物。小车对于这一问题我们的解决思路为:当小车判断自己应该直行时,在执行的语句下同时判断左、右两侧超声波模块返回的数据,如果一侧距离小于5cm则小车向另一侧转向。
(3)对超声波避障和蓝牙控制之间的关系没有充分认识
在设计程序时我们把蓝牙控制和超声波避障视为两个互相独立的程序,设计思路为蓝牙控制与超声波避障不能同时执行。但事实是蓝牙控制由串口中断启动,即在超声波避障运行的同时蓝牙控制的功能不受影响。但蓝牙控制不能长时间地改变并保持小车的运动状态,因为串口中断服务函数执行完之后程序依然会继续在超声波避障中不断循环小车运动状态会不断被改变。为了在蓝牙控制时能够跳出避障功能我们在蓝牙控制的循环中加入了一句while循环,其跳出条件为接收数据寄存器中的值与57的差为零。所以当不满足跳出循环条件时,程序会一直卡在该while循环中,不会进入到避障的程序。
(4)之后就是临时加的一个需求——让小车自己跑出一个矩形图案,矩形的长和宽在每次都可以通过蓝牙串口自定义
小车的运动会受到很多外部环境的影响,比如地面的光滑程度、电池的剩余电量。对于这一功能算法的设计思路,我们先是测出小车的行驶速度,然后在算出小车的转向角速度。根据角速度算出小车转向90度所用的时间。之后不微断调小车的转向时间,以使小车转出90度的角度。
#ifndef __HCSR_H
#define __HCSR_H
#include "sys.h"
void TIM2_Cap_Init(u16 arr,u16 psc);
#endif
-----------------------------------------------------------------------
#include "sys.h"
void TIM2_Cap_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<0; //TIM2 时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
RCC->APB2ENR|=1<<3; //使能PORTB时钟
GPIOA->CRL&=0XFFFF000F; //PA0,PA1清除之前设置
GPIOA->CRL|=0X00008880; //PA0,PA1输入
GPIOA->ODR|=0<<1; //PA0,PA1下拉
GPIOA->ODR|=0<<2; //PA0,PA1下拉
GPIOA->ODR|=0<<3; //PA0,PA1下拉
GPIOB->CRL&=0X000FFFFF;//PB7清除之前设置
GPIOB->CRL|=0X33300000;//PB7推挽输出
GPIOB->ODR|=1<<7; //PB7 输出高
GPIOB->ODR|=1<<6;
GPIOB->ODR|=1<<5;
TIM2->ARR=arr; //设定计数器自动重装值
TIM2->PSC=psc; //预分频器
//CH2
TIM2->CCMR1|=1<<8; //CC2S=01 选择输入端 IC1映射到TI1上
TIM2->CCMR1|=1<<12; //IC2F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
TIM2->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<5; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中
//CH3
TIM2->CCMR2|=1<<0; //CC3S=01 选择输入端 IC1映射到TI1上
TIM2->CCMR2|=1<<4; //IC3F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
TIM2->CCMR2|=0<<2; //IC3PS=00 配置输入分频,不分频
TIM2->CCER|=0<<9; //CC3P=0 上升沿捕获
TIM2->CCER|=1<<8; //CC3E=1 允许捕获计数器的值到捕获寄存器中
//CH4
TIM2->CCMR2|=1<<8; //CC4S=01 选择输入端 IC1映射到TI1上
TIM2->CCMR2|=1<<12; //IC4F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
TIM2->CCMR2|=0<<10; //IC4PS=00 配置输入分频,不分频
TIM2->CCER|=0<<13; //CC4P=0 上升沿捕获
TIM2->CCER|=1<<12; //CC4E=1 允许捕获计数器的值到捕获寄存器中
//中断使能
TIM2->DIER|=1<<2; //允许捕获2中断
TIM2->DIER|=1<<3; //允许捕获3中断
TIM2->DIER|=1<<4; //允许捕获4中断
TIM2->DIER|=1<<0; //允许更新中断
TIM2->CR1|=0x01; //使能定时器2
MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2
}
#ifndef __SMG_H
#define __SMG_H
#include "sys.h"
#include "delay.h"
#define USART_REC_LEN 200 //定义最大接收字符数
#define EN_USART3_RX 1 //使能(1)/禁止(0)串口接收
extern u8 USART3_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节,末字节为换行符
extern u16 USART3_RX_STA; //接收状态标记
#endif
------------------------------------------------------------------------
#include "sys.h"
#include "lanya.h"
#include "delay.h"
#include "usart.h"
#include "dianji.h"
u8 BTFlag;
#if EN_USART1_RX
void Juxing()//小车的矩形运动函数
{
u8 chang,kuan,x,y;//chang、kuan分别是小车要走矩形的长和宽的值
delay_ms(200);
printf("Chang:\r\n");
while(1)
{
printf("Input Chang:\r\n");
delay_ms(200);
if(USART1->SR&(1<<5))//当串口接收到消息后跳出循环
{
chang=USART1->DR-'0';//将字符型的数据转换为整型数据
break;
}
}
printf("长:%d\r\n",chang);//打印串口接收到的数据
USART1->SR=0;//串口的接收标志位清零,为下一次接收宽度数据做准备
while(1)
{
printf("Input Kuan:\r\n");
delay_ms(200);
if(USART1->SR&(1<<5))
{
kuan=USART1->DR-'0';//当串口接收到数据后跳出循环
break;
}
}
printf("宽:%d\r\n",kuan);//打印宽度数据
//当前小车速度为0.25米每秒则小车每走1cm要用40ms所以以1cm为单位每走1cm耗时40ms用for函数驱动小车运动
for(x=0;xSR&(1<<5))
{
res=USART1->DR;// 令res等于串口接收到的数据
printf("\r\n%d",res);
//通过判断res的值改变小车运动状态并通过串口将运动状态发送给手机
if(res==50)//
{
GO(300,300);//小车前进
printf("\r\nGo Stright");
}
else if(res==56)
{
BACK(300,300);//小车后退
printf("\r\nGo Back");
}
else if(res== 52)
{
LEFT(300,300);//小车左转
printf("\r\nTurn Left");
}
else if(res==54)
{
RIGHT(300,300);//小车右转
printf("\r\nTurn Right");
}
else if(res==53)
{
STOP();//小车停止
printf("\r\nStop");
}
else if(res=='7')//小车走矩形
{
delay_ms(200);
Juxing();//小车的矩形运动函数
}
}
}
#endif
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "hcsr.h"
#include "lanya.h"
#include "dianji.h"
//定时器2通道1-4输入捕获配置
//arr:自动重装值
//psc:时钟预分频数
//捕获状态
//[7]:0,没有成功的捕获;1,成功捕获到一次.
//[6]:0,还没捕获到高电平;1,已经捕获到高电平了.
//[5:0]:捕获高电平后溢出的次数
u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH1_CAPTURE_Date2; //数据2
u16 TIM2CH1_CAPTURE_Date1; //数据1
//CH2
u8 TIM2CH2_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH2_CAPTURE_Date2; //数据2
u16 TIM2CH2_CAPTURE_Date1; //数据1
//CH3
u8 TIM2CH3_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH3_CAPTURE_Date2; //数据2
u16 TIM2CH3_CAPTURE_Date1; //数据1
//CH4
u8 TIM2CH4_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH4_CAPTURE_Date2; //数据2
u16 TIM2CH4_CAPTURE_Date1; //数据1
//定时器2中断服务程序
void TIM2_IRQHandler(void)
{
u16 tsr;
tsr=TIM2->SR;
//CH2中断处理
if((TIM2CH2_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH2_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH2_CAPTURE_Date2=0XFFFF;
}else TIM2CH2_CAPTURE_STA++;
}
}
if(tsr&0x04)//捕获2发生捕获事件
{
if(TIM2CH2_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH2_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH2_CAPTURE_Date2=TIM2->CCR2; //获取当前的捕获值.
TIM2->CCER&=~(1<<5); //CC2P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH2_CAPTURE_Date2=0;
TIM2CH2_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2CH2_CAPTURE_Date1=TIM2->CCR2;
TIM2->CCER|=1<<5; //CC2P=1 设置为下降沿捕获
}
}
}
//CH3中断处理
if((TIM2CH3_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH3_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH3_CAPTURE_Date2=0XFFFF;
}else TIM2CH3_CAPTURE_STA++;
}
}
if(tsr&0x08)//捕获3发生捕获事件
{
if(TIM2CH3_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH3_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH3_CAPTURE_Date2=TIM2->CCR3; //获取当前的捕获值.
TIM2->CCER&=~(1<<9); //CC3P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH3_CAPTURE_Date2=0;
TIM2CH3_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2CH3_CAPTURE_Date1=TIM2->CCR3;
TIM2->CCER|=1<<9; //CC3P=1 设置为下降沿捕获
}
}
}
//CH4中断处理
if((TIM2CH4_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH4_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH4_CAPTURE_Date2=0XFFFF;
}else TIM2CH4_CAPTURE_STA++;
}
}
if(tsr&0x10)//捕获4发生捕获事件
{
if(TIM2CH4_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH4_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH4_CAPTURE_Date2=TIM2->CCR4; //获取当前的捕获值.
TIM2->CCER&=~(1<<13); //CC4P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH4_CAPTURE_Date2=0;
TIM2CH4_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2CH4_CAPTURE_Date1=TIM2->CCR4;
TIM2->CCER|=1<<13; //CC4P=1 设置为下降沿捕获
}
}
}
TIM2->SR=0;//清除中断标志位
}
u32 CEJU2(void)
{
while(1)
{
u32 temp2=0;
PBout(7)=1; //打开TRIG
delay_us(20);
PBout(7)=0; //关闭TRIG
if(TIM2CH2_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp2=TIM2CH2_CAPTURE_STA&0X3F;
temp2*=65536; //溢出时间总和
temp2+=TIM2CH2_CAPTURE_Date2;
temp2-=TIM2CH2_CAPTURE_Date1; //得到总的高电平时间
temp2*=0.017; //得到距离
printf("TIM2_CH2_LENGTH:%d cm\r\n",temp2); //打印距离
TIM2CH2_CAPTURE_Date1=0;
TIM2CH2_CAPTURE_STA=0; //开启下一次捕获
}
if(temp2<500) return temp2; //返回距离值
else return 0;
}
}
u32 CEJU3(void)
{
while(1)
{
u32 temp2=0;
PBout(6)=1;
delay_us(20);
PBout(6)=0;
if(TIM2CH3_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp2=TIM2CH3_CAPTURE_STA&0X3F;
temp2*=65536; //溢出时间总和
temp2+=TIM2CH3_CAPTURE_Date2;
temp2-=TIM2CH3_CAPTURE_Date1; //得到总的高电平时间
temp2*=0.017;
printf("TIM2_CH3_LENGTH:%d cm\r\n",temp2);
TIM2CH3_CAPTURE_Date1=0;
TIM2CH3_CAPTURE_STA=0; //开启下一次捕获
}
if(temp2<500) return temp2;
else return 0;
}
}
u32 CEJU4(void)
{
while(1)
{
u32 temp2=0;
PBout(5)=1;
delay_us(20);
PBout(5)=0;
if(TIM2CH4_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp2=TIM2CH4_CAPTURE_STA&0X3F;
temp2*=65536; //溢出时间总和
temp2+=TIM2CH4_CAPTURE_Date2;
temp2-=TIM2CH4_CAPTURE_Date1; //得到总的高电平时间
temp2*=0.017;
printf("TIM2_CH4_LENGTH:%d cm\r\n",temp2);
TIM2CH4_CAPTURE_Date1=0;
TIM2CH4_CAPTURE_STA=0; //开启下一次捕获
}
if(temp2<500) return temp2;
else return 0;
}
}
int main(void)
{
u32 i=3,j;
u32 temp2=0,temp3=0,temp4=0;
extern u8 opq;
Stm32_Clock_Init(9); //初始化系统时钟
delay_init(72); //初始化延时
uart_init(72,9600); //串口初始化
TIM2_Cap_Init(0XFFFF,72-1); //超声波初始化,以2Mhz的频率计数
TIM_PWM1_Init(1000-1,36-1); //电机初始化,不分频。PWM频率=72000/(899+1)=80Khz
printf("Choose moudle:");
CEJU2(); //超声波预初始化
CEJU3();
CEJU4();
delay_ms(1000);
while(1)
{
while(USART1->DR-57) //当接收的数据为字符型9时,跳出循环
{
;
}
temp2=0; //每一次新循环,将temp2,temp3,temp4清零
temp3=0;
temp4=0;
while(i) //检测三次正超声波发回的值
{
j=CEJU4();
temp4+=j;
if(j!=0) i--;
}
temp4=temp4/3; //取三次超声波发回的值的平均数,以减小误差
i=3;
j=0;
if(temp4<40) //如果前方距离小于40cm,则进行判断
{
while(i) //检测三次左超声波发回的值
{
j=CEJU2();
temp2+=j;
if(j!=0) i--;
}
temp2=temp2/3;
i=3;
j=0;
if(temp2<40) //如果左侧距离小于40cm,则进行判断
{
while(i) //检测三次右超声波发回的值
{
j=CEJU3();
temp3+=j;
if(j!=0) i--;
}
temp3=temp3/3;
i=3;
j=0;
if(temp3<40) //如果右侧距离小于40cm,则进行后退,左转
{
BACK(300,300);
printf("back\r\n");
delay_ms(500);
LEFT(300,300);
delay_ms(500);
}
else //如果右侧距离大于40cm,则右转
{
RIGHT(300,300);
printf("right\r\n");
delay_ms(500);
}
}
else //如果右侧距离大于40cm,则左转
{
LEFT(300,300);
printf("left\r\n");
delay_ms(500);
}
}
else //如果前侧距离大于40cm,则判断
{
while(i) //检测三次左超声波发回的值
{
j=CEJU2();
temp2+=j;
if(j!=0) i--;
}
temp2=temp2/3;
i=3;
j=0;
if(temp2<=5) //如果左侧距离小于5cm,则右转
{
RIGHT(200,200);
delay_ms(500);
}
while(i) //检测三次右超声波发回的值
{
j=CEJU3();
temp3+=j;
if(j!=0) i--;
}
temp3=temp3/3;
i=3;
j=0;
if(temp3<=5) //如果右侧距离小于5cm,则左转
{
LEFT(200,200);
delay_ms(500);
}
GO(300,300); //直行
delay_ms(500);
printf("go\r\n");
}
TIM2->CNT=0; //每一次循环将CNT值清零
}
}