寒假无聊,总结下目前所学到,做过的项目。这是第一篇博客,就从一个简单的项目(大二做的课程设计)51单片机寻迹避障运输小车开始说起。。。。。好!转回正题。
小车主要实现的功能:(1)当有物品放上小车上时,小车开动掉头进行循迹,去到目的地停下;(2)当拿走物体时,小车自动掉头循迹回到初始位置。(3)在途中如果遇到障碍物时,会停止前进等待障碍物撤除后再继续前进。
视频演示1 视频演示2
因为当时是课程设计,用的时间比较短,也比较紧凑。程序编写方面还是会有一些BUG,所以这篇博客仅用作给需要的朋友参考。。谢谢
①首先列举下本项目使用到的模块以及传感器:
所使用到的传感器都是从某宝买回来的,比较廉价,对于我们初学者来说也算是比较实用。说一些比较主要的模块。
循迹功能需要使用到的传感器主要是红外传感器。传感器原理很简单,每个探头都有一个发送红外线端,一节接收端,上电后,白色探头会不停发送红外光,当没遇到黑线时,红外光会反射回来给接收探头接收;当遇到黑线时,黑线会将红外线吸收,就不会有反射光反射回来给接收端接收,相应地与单片机接收端也会有相应的高低电平返回。利用这些高低电平就可以判断小车当前的位置。以便给单片机作处理。
避障功能需要用到超声波模块。
基本工作原理:
(1)采用 IO 口 TRIG 触发测距,给最少 10us 的高电平信呈。
(2)模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
(3)有信号返回,通过 IO 口 ECHO 输出一个高电平,高电平持续的时间就是超声 波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;
如右图接线,VCC 供 5V 电源,GND 为地线, TRIG 触发控制信号输 入,ECHO 回响信号输出等四个接口端的。
车上是否有物品判别:压力传感器 (电阻式薄膜+转换模块) 根据压力大小输出模拟值(电压值)或数字值(0或1)
电机驱动模块、电源稳压模块这些都是小车的驱动必不可少的拉
②再看看这里面的小算法(调速算法)。当然你也可以尝试使用光码盘测速,再去用pid算法进行调速。。。
当你着手去做这个项目时,你会发现单单几个传感器和单片机自身,他们的处理能力和精度是很有限的,而直接驱动小车电机会使得车速过快,单片机处理不过来而做不到循迹这个功能。
这里就要引入PWM波来对电机的转速进行调速了。
PWM(脉宽调制)基本原理:控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期中产生多个脉冲,使各脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。按一定的规则对各脉冲的宽度进行调制,即可改变逆变电路输出电压的大小,也可改变输出频率。
然而在单片机怎样实现呢??单片机虽然比较低端,但是还是会有定时器这些基本功能。我们可以通过设定两个比较值,当push_value(设定值)>pwm_value(定时器计数值)则将io口置1否则置0。当计数值>设定值再重新置0重新计数。就可达到pwm的可调输出了。PWM生成具体实现代码如下:
//左轮PWM函数
void pwm_left(void){
if(push_value_left!=0){
if(pwm_value_left20)
pwm_value_left=0;
}
}
//右轮PWM函数
void pwm_right(void){
if(push_value_right!=0){
if(pwm_value_right20)
pwm_value_right=0;
}
}
//实现PWM的中断函数
void timer0() interrupt 1 using 2{
TH0=0XFC; //重新赋初值
TL0=0X66;
pwm_value_left++;
pwm_value_right++;
pwm_left();
pwm_right();
}
③说得差不多了。。。搭建小车
④最后,贴下完整代码:
#include
#include
#define unint unsigned int
unsigned int time=0; //定时器时间
unsigned long distance=0; //计算距离
bit flag =0; //忙标记位
int S;//距离计算变量
//超声波和蜂鸣器引脚
sbit Trig = P1^6;
sbit Echo = P1^7;
sbit led=P0^0;
sbit kaiguan=P3^7;
sbit yali=P1^4;
//传感器引脚
sbit left = P2^0;
sbit middle = P2^1;
sbit right = P2^2;
//电机驱动模块引脚定义,zh:正,fu:负
sbit leftzh = P1^0;
sbit leftfu = P1^1;
sbit rightzh = P1^2;
sbit rightfu = P1^3;
//PWM和push值以及临时变量定义
unint pwm_forward_left = 0;
unint pwm_forward_right = 0;
unint push_forward_left = 0;
unint push_forward_right = 0;
unint pwm_back_left = 0;
unint pwm_back_right = 0;
unint push_back_left = 0;
unint push_back_right = 0;
unint num,num1= 0;
unint start = 0;
//延时x毫秒
void delay(unint xms){
unint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
//正常前进函数,普通速度
void run(void){
rightfu=0;
leftfu=0;
push_forward_left=7;
push_forward_right=7;
push_back_left=0;
push_back_right=0;
}
//停止函数
void stop(void){
push_forward_left=0;
push_forward_right=0;
push_back_left=0;
push_back_right=0;
leftzh=1;
leftfu=1;
rightzh=1;
rightfu=1;
}
//右直角转弯函数
void right_90(void){
push_forward_right=0;
push_back_left=0;
push_forward_left=8;
push_back_right=8;
leftfu=0;
rightzh=0;
}
//前左转函数
void turn_left(void){
rightfu=0;
push_forward_left=0;
push_forward_right=8;
push_back_left=0;
push_back_right=0;
leftzh=0;
leftfu=0;
}
//前右转函数
void turn_right(void){
leftfu=0;
push_back_left=0;
push_back_right=0;
push_forward_right=0;
push_forward_left=8;
rightzh=0;
rightfu=0;
}
void back_right()
{
push_back_left=0;
push_back_right=0;
push_forward_right=0;
push_forward_left=0;
leftzh=0;
leftfu=0;
rightzh=0;
rightfu=1;
}
void back_left()
{
push_back_left=0;
push_back_right=0;
push_forward_right=0;
push_forward_left=0;
leftzh=0;
leftfu=1;
rightzh=0;
rightfu=0;
}
//左轮前进PWM函数 PWM一个周期为20,push值是用来决定占空比
void pwm_left_forward(void){
if(push_forward_left!=0){
if(pwm_forward_left20)
pwm_forward_left=0;
}
}
//右轮前进PWM函数
void pwm_right_forward(void){
if(push_forward_right!=0){
if(pwm_forward_right20)
pwm_forward_right=0;
}
}
//左轮后PWM
void pwm_left_back(void){
if(push_back_left!=0){
if(pwm_back_left20)
pwm_back_left=0;
}
}
//右轮后PWM函数
void pwm_right_back(void){
if(push_back_right!=0){
if(pwm_back_right20)
pwm_back_right=0;
}
}
//中断产生PWM
void timer0() interrupt 1 using 2{
TH0=0XFC;
TL0=0X66;
pwm_forward_left++;
pwm_forward_right++;
pwm_left_forward();
pwm_right_forward();
pwm_back_left++;
pwm_back_right++;
pwm_left_back();
pwm_right_back();
}
//计算距离
int Conut(void)
{
time=TH1*256+TL1;
TH1=0;
TL1=0;
distance=(time*1.7)/100;
return distance;
}
//启动模块
void StartModule()
{
Trig=1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
Trig=0;
}
//超声波中断
void timer1() interrupt 3
{
flag=1; //中断溢出标志
}
//循迹函数
void xunji()
{
//为单片机引脚赋变量
unint l,m,r;
l=left;
m=middle;
r=right;
//传感器检测到黑线就会给单片机返回高电平,1
if(l==0&&m==1&&r==0){
run();
num=0;
}
else if(l==1&&m==0&&r==0){
turn_left();
}
else if(l==0&&m==0&&r==1){
turn_right();
}
else if(l==0&&m==1&&r==1){
turn_right();
num=0;
}
else if(l==1&&m==1&&r==0){
turn_left();
num=0;
}
else if(l==0&&m==0&&r==0)
{
turn_left();
}
else if(l==1&&m==1&&r==1){
stop();
num=0;
}
}
void CSB()
{
StartModule();
while(!Echo); //当RX为零时等待
TR1=1; //开启计数
while(Echo); //当RX为1计数并等待
TR1=0; //关闭计数
S=Conut();
}
//主函数
void main(void){
yali=1; //无源蜂鸣器pwm
leftzh = 0;
leftfu = 0;
rightzh = 0;
rightfu = 0;
TMOD=0X11; //两个定时器
TH0= 0XFC;
TL0= 0X66;
TR0= 1;
ET0= 1;
TH1=0;
TL1=0;
ET1=1;
EA=1;
while(1){
if(kaiguan==0) //模式一
{
xunji();
if(yali==0)
{ right_90();
delay(500);
while(!(left==0&&middle==1&&right==0));
while(!yali)
{
CSB();
if(S<=20)
{
led=0;
stop();//如果距离小于xxcm ,电机停止
}
else if(S>20||flag==1)
{
flag=0;
led=1;
xunji();
}
}
}
else if(yali==1)
{
right_90();
delay(500);
while(!(left==0&&middle==1&&right==0));
while(yali)
{
CSB();
if(S<=20)
{
led=0;
stop();//如果距离小于xxcm ,电机停止
}
else if(S>20||flag==1)
{
flag=0;
led=1;
xunji();
}
}
}
}
else //模式2
{
CSB();
if(S<=35)
{
led=0;
num1++;
if(num==1)
{
back_right();
delay(20);
}
else
{
back_left();
delay(5);
num=0;
}
}
else if(S>35||flag==1)
{
flag=0;
led=1;
run();
delay(20);
}
}
}
}