大家好,又见面了,我是你们的朋友全栈君。
文章目录- 一. 任务要求
- 二. 系统原理
- 三. 整体方案
- 1. 控制模块
- 最小系统
- 去除AD网格线
- 2. 电机驱动模块
- 3. 电源模块
- 4. 循迹模块
- 5. 超声波测距模块
- 四. 车体实物图
- 五. 软件实现
- 1. 控制模块
- 最小系统
- 去除AD网格线
- 2. 电机驱动模块
- 3. 电源模块
- 4. 循迹模块
- 5. 超声波测距模块
这学期开设的51单片机课程的课程设计即将验收,今天开始正式着手做循迹小车~
一. 任务要求
二. 系统原理
本系统以设计题目的要求为目的,采用STC89C52单片机为控制核心,利用红外传感器检测轨道,控制电动小汽车的自动循迹,快慢速行驶。
这里的轨道是指汽车沿着白色地板上的黑线行驶。由于黑线与白地板的反射系数不同,可以根据接收到的反射光的强度来判断“道路”。常用的方法是红外检测。
红外检测方法,即在汽车行驶过程中,利用红外光在具有不同颜色的物体表面的不同反射特性,不断向地面发射红外光。当红外光与白纸地板相遇时,会发生漫反射,反射光被安装在车内的接收器接收,如果遇到黑线,就会变成红色。外部光被吸收,车内的接收器不能接收红外线。单片机根据是否接收到反射的红外光来确定黑线的位置和汽车的行驶路线。
三. 整体方案
1. 控制模块
小车的核心控制部件采用STC公司生产的8位单片机STC89C52。它是一种低功耗、高性能CMOS8位微控制器,具有8K字节系统可编程Flash存储器。STC89C52使用经典的MCS-51内核,但是做了很多的改进使得芯片具有传统51单片机不具备的功能。STC89C52有3个16 位定时器/计数器,2个外部中断,1个串口中断。
最小系统
51单片机的具体引脚功能不细说,不了解的可自行百度。直接上最小系统电路图(自己画的,凑合着看)。顺便说一句,在我看来一个没有指示灯的电路是最烦的电路<(`^´)> 我的复位一定要有灯才行!
三部分: ①电源电路:给单片机提供5V的电源 ②时钟电路:外接11.0592M石英晶振。 ③复位电路:确保单片机是从一个确定的初始状态开始。
焊接时注意P0口要接上拉电阻,否则不能用,一般都用排阻做上拉电阻(当然如果能自己画板子就更好了)。
去除AD网格线
记一下怎么去除AD网格线,如图步骤,最后去掉Visible前面的对勾即可,别忘记最后点OK:
2. 电机驱动模块
我使用的是最经典的LM298N电机驱动:
我太喜欢用298了。不仅可以接至12v(即拥有更强的驱动能力),而且有过电流保护功能,当出现电机卡死时,可以保护电路和电机等。且自带7805降压模块可直接给单片机供电~~ 实物图:
驱动与各部分的连接简介: 输出A、B:分别接到左右马达上,通过使能A、使能B来控制电机正常工作(接高电平为正常工作); 单片机IO控制输入(即IN1-IN4):用来控制马达正反转。接单片机引脚,通过在程序中给引脚高低电平实现正转或反转(详见下边输入输出关系表); 12V输入:接电池盒正极; 5V输入:接单片机VCC,给单片机供电; 电源地(即GND):把电池盒负极和单片机的GND一起接入。
电路图:
输入输出关系(使能ENA、ENB均为1的状态下):
In1 | In2 | 运转状态 |
---|---|---|
1 | 0 | 正转 |
0 | 1 | 反转 |
1 | 1 | 刹停 |
0 | 0 | 停止 |
3. 电源模块
我自己使用的是两节18500锂电池供电。如果觉得两节电池不够,三节12V又太大,也可以在中间加一个7809稳压管把电压降到9V再接入驱动:
4. 循迹模块
光电循迹是由四对红外收发管组成,通过检测接收到的反射光强,判断黑白线。
当模块探测灯检测到黑线时指示灯熄灭,同时 OUT端口持续输出高电平信号,反之未检测到黑线的探测灯应常亮,OUT端口输出低电平。该模块检测距离 2~60cm,检测角度 35 ° ,检测距离可以通过电位器进行调节,顺时针调电位器,灵敏度增加;逆时针调电位器,灵敏度减少。
原理图由红外对管和电压比较器两部分组成,红外对管输出的模拟电压通过电压比较器转换成数字电平输出到单片机。
主控板的电路图:
小板电路图:
注意连接的时候:VCC-VCC、GND-GND、IN-OUT。
比较器LM339
5. 超声波测距模块
实物图:
电路图:
工作原理: (1)采用IO口TRIG触发测距,给至少10us的高电平信号; (2)模块自动发送8个40khz的方波,自动检测是否有信号返回; (3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。 测试距离=(高电平时间*声速(340M/S))/2;
四. 车体实物图
五. 软件实现
代码语言:javascript复制#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit ENA = P2^0; //右点机使能
sbit IN1 = P2^1; //为0右轮反转
sbit IN2 = P2^2; //为0右轮正转
sbit IN3 = P2^3; //为0左轮正转
sbit IN4 = P2^4; //为0左轮反转
sbit ENB = P2^5; //左电机使能
sbit left1 = P1^3;
sbit left2 = P1^2;
sbit right1 = P1^1;
sbit right2 = P1^0;
u8 PWMCnt1 = 0;
u8 PWMCnt2 = 0;
u8 cntPWM1 = 0;
u8 cntPWM2 = 0;
void Timer0Init();
void XunJi();
void main()
{
Timer0Init();
while(1)
{
XunJi();
}
}
// i = 1时, 大概延时10us
//void delay(u16 i)
//{
// while (i--);
//}
void Timer0Init()
{
TH0 = 0xFF;
TL0 = 0xA3;
TMOD &= 0xF0;
TMOD |= 0x01;
EA = 1;
ET0 = 1;
TR0 = 1;
}
void TurnRight1() //右转
{
IN1 = 0; //右轮反转
IN2 = 1;
IN3 = 0; //左轮正转
IN4 = 1;
cntPWM1 = 70;
cntPWM2 = 55;
}
void TurnRight2() //右转
{
IN1 = 0; //右轮反转
IN2 = 1;
IN3 = 0; //左轮正转
IN4 = 1;
cntPWM1 = 50;
cntPWM2 = 40;
}
void TurnLeft1() //左转
{
IN1 = 1;
IN2 = 0; //右轮正转
IN3 = 1;
IN4 = 0; //左轮反转
cntPWM1 = 55;
cntPWM2 = 70;
}
void TurnLeft2() //左转
{
IN1 = 1;
IN2 = 0; //右轮正转
IN3 = 1;
IN4 = 0; //左轮反转
cntPWM1 = 40;
cntPWM2 = 50;
}
void Forward() //前进
{
IN1 = 1;
IN2 = 0; //右轮正转
IN3 = 0; //左轮正转
IN4 = 1;
cntPWM1 = 40;
cntPWM2 = 40;
}
//void Backward() //后退
//{
// IN1 = 0; //右轮反转
// IN2 = 1;
//
// IN3 = 1;
// IN4 = 0; //左轮反转
// cntPWM1 = 30;
// cntPWM2 = 30;
//}
void Stop() //停止
{
IN1 = 0;
IN2 = 0;
IN3 = 0;
IN4 = 0;
}
void XunJi()
{
unsigned char flag = 0;
if((left1 == 0)&&(left2 == 0)&&(right1 == 0)&&(right2 == 0)) //0 0 0 0
flag = 0;
if((left1 == 0)&&(left2 == 0)&&(right1 == 0)&&(right2 == 1)) //0 0 0 1
flag = 1;
if((left1 == 0)&&(left2 == 0)&&(right1 == 1)&&(right2 == 0)) //0 0 1 0
flag = 0;
if((left1 == 0)&&(left2 == 0)&&(right1 == 1)&&(right2 == 1)) //0 0 1 1
flag = 1;
if((left1 == 0)&&(left2 == 1)&&(right1 == 0)&&(right2 == 0)) //0 1 0 0
flag = 0;
if((left1 == 0)&&(left2 == 1)&&(right1 == 0)&&(right2 == 1)) //0 1 0 1
flag = 4;
if((left1 == 0)&&(left2 == 1)&&(right1 == 1)&&(right2 == 0)) //0 1 1 0
flag = 0;
if((left1 == 0)&&(left2 == 1)&&(right1 == 1)&&(right2 == 1)) //0 1 1 1
flag = 1;
if((left1 == 1)&&(left2 == 0)&&(right1 == 0)&&(right2 == 0)) //1 0 0 0
flag = 3;
if((left1 == 1)&&(left2 == 0)&&(right1 == 0)&&(right2 == 1)) //1 0 0 1
flag = 0;
if((left1 == 1)&&(left2 == 0)&&(right1 == 1)&&(right2 == 0)) //1 0 1 0
flag = 2;
// ?
if((left1 == 1)&&(left2 == 0)&&(right1 == 1)&&(right2 == 1)) //1 0 1 1
flag = 0;
if((left1 == 1)&&(left2 == 1)&&(right1 == 0)&&(right2 == 0)) //1 1 0 0
flag = 3;
//?
if((left1 == 1)&&(left2 == 1)&&(right1 == 0)&&(right2 == 1)) //1 1 0 1
flag = 0;
if((left1 == 1)&&(left2 == 1)&&(right1 == 1)&&(right2 == 0)) //1 1 1 0
flag = 3;
if((left1 == 1)&&(left2 == 1)&&(right1 == 1)&&(right2 == 1)) //1 1 1 1
flag = 5;
switch(flag)
{
case 0:Forward();break;
case 1:TurnRight1();break;
case 2:TurnRight2();break;
case 3:TurnLeft1();break;
case 4:TurnLeft2();break;
default:Stop();break;
}
}
void InterruptTime0() interrupt 1
{
PWMCnt1 ;
PWMCnt2 ;
if(PWMCnt1 >= 200)
{
PWMCnt1 = 0;
}
if(PWMCnt1 <= cntPWM1)
{
ENA = 1;
}
else
{
ENA = 0;
}
if(PWMCnt2 >= 200)
{
PWMCnt2 = 0;
}
if(PWMCnt2 <= cntPWM2)
{
ENB = 1;
}
else
{
ENB = 0;
}
TH0 = (65536 - 50)/256;
TL0 = (65536 - 50)%6;
}
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152244.html原文链接:https://javaforall.cn