标题:
Smart_car4红外避障+超声波跟随设计源代码
[打印本页]
作者:
QXDBOY
时间:
2018-6-1 17:05
标题:
Smart_car4红外避障+超声波跟随设计源代码
这是我上个月做的单片机智能小车,目前可实现红外避障和蓝牙遥控。保证原创,请多多指教。
0.png
(9.36 KB, 下载次数: 31)
下载附件
2018-6-2 01:40 上传
单片机源程序如下:
#include<reg52.h>
#include<C52_car.h>
#include <intrins.h>//包含_nop_指令的头文件
#define MAIN_Fosc 11059200UL //宏定义主时钟HZ
#define PCF8591_ADDR 0x90 //PCF8591地址
#define DACOUT_EN 0x40 //DAC输出使能
typedef unsigned char uchar;
typedef unsigned int uint;
unsigned char pwm_left_val = 0;//左电机占空比值 取值范围0-80,0最快
unsigned char pwm_right_val = 0;//右电机占空比值取值范围0-80 ,0最快
unsigned char pwm_t = 0;//周期
unsigned char control=0X01;//车运动控制全局变量,默认开机为停车状态
unsigned int time = 0;//传输时间
unsigned long S = 0;//距离
unsigned char count=0;
unsigned char SEH_count; //舵机朝向前面
#define LCD1602_DB P0 //LCD1602数据总线
uchar Control_mode = 0;
uchar AD_Value; //存储AD转换回的数字量
sbit LCD1602_RS = P3^5; //RS端
sbit LCD1602_RW = P3^6; //RW端
sbit LCD1602_EN = P3^4; //EN端
sbit DU = P2^6;//
sbit WE = P2^7;//数码管位选段选用于关闭数码管显示
void delay(unsigned int z)//毫秒级延时
{
unsigned int x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
void Delay10us(unsigned char i) //10us延时函数 启动超声波模块时使用
{
unsigned char j;
do{
j = 10;
do{
_nop_();
}while(--j);
}while(--i);
}
/*====================================
函数:void Delay5us()
描述:12T 51单片机5微秒延时函数自适应时钟(11.0592M,12M,22.1184M)
====================================*/
void Delay5us()
{
#if MAIN_Fosc == 11059200
_nop_();
#elif MAIN_Fosc == 12000000
_nop_();
#elif MAIN_Fosc == 22118400
_nop_(); _nop_(); _nop_();
#endif
}
/*小车前进*/
void forward()
{
left_motor_go; //左电机前进
right_motor_go; //右电机前进
}
/*小车左转*/
void left_run()
{
left_motor_stops; //左电机停止
right_motor_go; //右电机前进
}
/*小车右转*/
void right_run()
{
right_motor_stops;//右电机停止
left_motor_go; //左电机前进
}
/*PWM控制使能 小车后退*/
void backward()
{
left_motor_back; //左电机后退
right_motor_back; //右电机后退
}
void stop()
{
left_motor_stops; //左电机后退
right_motor_stops; //右电机后退
}
void Init(void)
{
EA = 1; //开总中断
PT0 = 1;
IT0 = 0; //边沿触发方式
IT1 = 0; //边沿触发方式
SCON |= 0x50; // SCON: 模式1, 8-bit UART, 使能接收
T2CON |= 0x34; //设置定时器2为串口波特率发生器并启动定时器2
TL2 = RCAP2L = (65536-(FOSC/32/BAUD)); //设置波特率
TH2 = RCAP2H = (65536-(FOSC/32/BAUD)) >> 8;
ES= 1; //打开串口中断
TMOD |= 0x01; //定时器0,工作模式1,16位定时模式
TH0 = 0;
TL0 = 0; //T0,16位定时计数用于记录ECHO高电平时间
TR0 = 1; //启动定时器0
ET0 = 1; //允许定时器0中断
TMOD |= 0x10; //定时器1,16位定时模式。
TH1 = 0xff; //配置定时器0初值,溢出时间为0.1ms
TL1 = 0xa3;
TR1 = 1; //启动定时器1
ET1 = 1; //允许T1中断
}
void StartModule() //启动超声波模块
{
TX=1; //启动一次模块
Delay10us(2);
TX=0;
}
/*计算超声波所测距离并显示*/
void Conut()
{
time=TH0*256+TL0;
TH0 = 0;
TL0 = 0;
S=(float)(time*1.085)*0.17; //算出来是MM
if(S>=7000) //超出测量范围
{
stop();
beep = 0;
delay(1000);
beep = 1;
}
}
/*====================================
函数:void SEH_count_0()
描述:舵机在左侧时指令
====================================*/
void SEH_count_5()
{
StartModule(); //启动模块测距
while(!RX); //当RX(ECHO信号回响)为零时等待
TR0 = 1; //开启计数
while(RX) ; //当RX为1计数并等待
TR0 = 0; //关闭计数
Conut(); //计算距离
if(S > 170 && S <850 )//设置随动距离(单位毫米)
{
left_run();
}
}
/*====================================
函数:void SEH_count_10()
描述:舵机在中间时指令
====================================*/
void SEH_count_10()
{
StartModule(); //启动模块测距
while(!RX); //当RX(ECHO信号回响)为零时等待
TR0 = 1; //开启计数
while(RX) ; //当RX为1计数并等待
TR0 = 0; //关闭计数
Conut(); //计算距离
if(S > 170 && S <850 )//设置随动距离(单位毫米)
{
forward();
}
}
/*====================================
函数:void SEH_count_20()
描述:舵机在右侧时指令
====================================*/
void SEH_count_15()
{
StartModule(); //启动模块测距
while(!RX); //当RX(ECHO信号回响)为零时等待
TR0 = 1; //开启计数
while(RX) ; //当RX为1计数并等待
TR0 = 0; //关闭计数
Conut(); //计算距离
if(S > 150 && S <850 )//设置随动距离(单位毫米)
{
right_run();
}
}
/*====================================
函数:I2C_init()
描述:I2C总线初始化
====================================*/
void I2C_init()
{
SDA = 1; //数据总线高
_nop_();
SCL = 1; //时钟总线高
_nop_();
}
/*====================================
函数:I2C_Start()
描述:I2C起始信号
====================================*/
void I2C_Start()
{
SCL = 1;
_nop_();
SDA = 1;
Delay5us();
SDA = 0;
Delay5us();
}
/*====================================
函数:I2C_Stop()
描述:I2C停止信号
====================================*/
void I2C_Stop()
{
SDA = 0;
_nop_();
SCL = 1;
Delay5us();
SDA = 1;
Delay5us();
}
/*====================================
函数:Master_ACK(bit i)
参数:i 为0时发送非应答 为1时发送应答
描述:I2C主机发送应答
====================================*/
void Master_ACK(bit i)
{
SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化
_nop_(); // 让总线稳定
if (i) //如果i = 1 那么拉低数据总线 表示主机应答
{
SDA = 0;
}
else
{
SDA = 1; //发送非应答
}
_nop_();//让总线稳定
SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号
_nop_();
SCL = 0;//拉低时钟总线, 占用总线继续通信
_nop_();
SDA = 1;//释放SDA数据总线。
_nop_();
}
/*====================================
函数:Test_ACK()
返回:0为非应答 1为应答
描述:I2C检测从机应答
====================================*/
bit Test_ACK() // 检测从机应答
{
SCL = 1;//时钟总线为高电平期间可以读取从机应答信号
Delay5us();
if (SDA)
{
SCL = 0;
I2C_Stop();
return(0);
}
else
{
SCL = 0;
return(1);
}
}
/*====================================
函数:I2C_send_byte(uchar byte)
参数:byte 要发送的字节
描述:I2C发送一个字节
====================================*/
void I2C_send_byte(uchar byte)
{
uchar i;
for(i = 0 ; i < 8 ; i++)
{
SCL = 0;
_nop_();
if (byte & 0x80) //
{
SDA = 1;
_nop_();
}
else
{
SDA = 0;
_nop_();
}
SCL = 1;
_nop_();
byte <<= 1;
}
SCL = 0;
_nop_();
SDA = 1;
_nop_();
}
/*====================================
函数:I2C_read_byte()
返回:读取的字节
描述:I2C读一个字节
====================================*/
uchar I2C_read_byte()
{
uchar i, dat;
SCL = 0 ;
_nop_();
SDA = 1;
_nop_();
for(i = 0 ; i < 8 ; i++)
{
SCL = 1;
_nop_();
dat <<= 1;
if (SDA)
{
dat |= 0x01;
}
_nop_();
SCL = 0;
_nop_();
}
return(dat);
}
/*读AD数据*/
bit ADC_Read(uchar CON)
{
I2C_Start();
I2C_send_byte(PCF8591_ADDR+0);
if (!Test_ACK())
{
return(0);
}
I2C_send_byte(CON);
Master_ACK(0);
I2C_Start();
I2C_send_byte(PCF8591_ADDR+1);
if (!Test_ACK())
{
return(0);
}
AD_Value = I2C_read_byte();
Master_ACK(0);
I2C_Stop();
return(1);
}
/*=================================================
*函数名称:Read_Busy
*函数功能:判断1602液晶忙,并等待
=================================================*/
void Read_Busy()
{
uchar busy;
LCD1602_DB = 0xff;//复位数据总线
LCD1602_RS = 0; //拉低RS
LCD1602_RW = 1; //拉高RW读
do
{
LCD1602_EN = 1;//使能EN
busy = LCD1602_DB;//读回数据
LCD1602_EN = 0; //拉低使能以便于下一次产生上升沿
}while(busy & 0x80); //判断状态字BIT7位是否为1,为1则表示忙,程序等待
}
/*=================================================
*函数名称:LCD1602_Write_Cmd
*函数功能:写LCD1602命令
*调用:Read_Busy();
*输入:cmd:要写的命令
=================================================*/
void LCD1602_Write_Cmd(uchar cmd)
{
Read_Busy(); //判断忙,忙则等待
LCD1602_RS = 0;
LCD1602_RW = 0; //拉低RS、RW操作时序情况1602课件下中文使用说明基本操作时序章节
LCD1602_DB = cmd;//写入命令
LCD1602_EN = 1; //拉高使能端 数据被传输到LCD1602内
LCD1602_EN = 0; //拉低使能以便于下一次产生上升沿
}
/*=================================================
*函数名称:LCD1602_Write_Dat
*函数功能:写LCD1602数据
*调用:Read_Busy();
*输入:dat:需要写入的数据
=================================================*/
void LCD1602_Write_Dat(uchar dat)
{
Read_Busy();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
/*=================================================
*函数名称:LCD1602_Dis_Str
*函数功能:在指定位置显示字符串
*调用:LCD1602_Write_Cmd(); LCD1602_Write_Dat();
*输入:x:要显示的横坐标取值0-40,y:要显示的行坐标取值0-1(0为第一行,1为第二行)
*str:需要显示的字符串
=================================================*/
void LCD1602_Dis_Str(uchar x, uchar y, uchar *str)
{
if(y) x |= 0x40;
x |= 0x80;
LCD1602_Write_Cmd(x);
while(*str != '\0')
{
LCD1602_Write_Dat(*str++);
}
}
/*=================================================
*函数名称:Init_LCD1602
*函数功能:1602初始化
*调用: LCD1602_Write_Cmd();
=================================================*/
void Init_LCD1602()
{
LCD1602_Write_Cmd(0x38); // 设置16*2显示,5*7点阵,8位数据接口
LCD1602_Write_Cmd(0x0c); //开显示
LCD1602_Write_Cmd(0x06); //读写一字节后地址指针加1
LCD1602_Write_Cmd(0x01); //清除显示
}
/*=================================================
*函数名称:Dispaly_LCD1602
*函数功能:1602显示字符
*调用: LCD1602_Write_Cmd();
=================================================*/
void Dispaly_LCD1602()
{
int LCD_CK=0;
uchar Str_1[] = {"Welcome to use"};
uchar Str_2[] = {"Pick a pattern"};
uchar Str_3[] = {"S2 -> automatic"};
uchar Str_4[] = {"S3 -> bluetooth"};
Init_LCD1602();//1602初始化
for(;;) //死循环
{
if(LCD_CK == 0)
{
LCD1602_Dis_Str(0, 0, &Str_1[0]); //显示字符串
LCD1602_Dis_Str(0, 1, &Str_2[0]); //显示字符串
delay(10);
}
if(LCD_CK == 20000)
{
LCD1602_Dis_Str(0, 0, &Str_3[0]); //显示字符串
LCD1602_Dis_Str(0, 1, &Str_4[0]); //显示字符串
delay(10);
}/**/
if(key_s2 == 0)// 实时检测S2按键是否被按下
{
delay(5); //软件消抖
if(key_s2 == 0)//再检测S2是否被按下
{
while(!key_s2);//松手检测
delay(50);//50毫秒延时
Control_mode = 0;
break; //退出FOR死循环
}
}
if(key_s3 == 0)// 实时检测S3按键是否被按下
{
delay(5); //软件消抖
if(key_s3 == 0)//再检测S3是否被按下
{
while(!key_s3);//松手检测
delay(50);//50毫秒延时
Control_mode = 1;
break; //退出FOR死循环
}
}
if(LCD_CK <= 40000) LCD_CK++;
if(LCD_CK >= 40000) LCD_CK = 0;
}
}
void main()
{
DU = 0;
WE = 0;
EN2 = EN1 =1;
SEH_count = 10;
Dispaly_LCD1602();
I2C_init();//I2C初始化
LCD1602_Write_Cmd(0x08); //关闭1602显示
Init();//定时器、串口初始化Init();//定时器、串口初始化
beep = 0;
delay(200);//延时1秒
beep = 1;
while(1)
{
if(Control_mode == 1)
{
EX0 = 0; //关闭外部中断1
EX1 = 0; //关闭外部中断2
while(1)
{
if(control>0X07)//如果成立,则表示接收的命令不在运行命令内
{
stop(); // 停车
}
switch(control)
{
case 0X02: forward(); break;//前进
case 0X03: backward(); break;//后退
case 0X04: left_run(); break;//左转
case 0X05: right_run(); break;//右转
case 0X01: stop(); break;//停车
case 0X08: beep = 0; break;//鸣笛
case 0X09: beep = 1; break;//停止鸣笛
case 0X0B: SEH_count = 10; break;//SEH_COUNT: 10:0.5+1(0.1*10T)=1.5ms->90°舵机角度
case 0X0A: Control_mode = 0;break;
}
break;
}
}
if(Control_mode == 0)
{
EX0 = 1; //打开外部中断1
EX1 = 1; //打开外部中断2
for (;;)
{
SEH_count = 12;
delay(300);
ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
if(AD_Value > 100) SEH_count_10();
SEH_count = 6;
delay(300);
ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
if(AD_Value > 100) SEH_count_5();//大于3.1V表示感应到了人体,小车行进
SEH_count = 12;
delay(300);
ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
if(AD_Value > 100) SEH_count_10();
SEH_count = 18;
delay(300);
ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
if(AD_Value > 150) SEH_count_15();
if(control == 0X0C)
{
Control_mode = 1;
break;
}
}
}
}
}
void int0() interrupt 0
{
EX0 = 0;
delay(10);
if(left_led2 == 0)
{
backward();
delay(500);
key_s3 = 1;
right_run();
delay(700);
forward();
delay(500);
stop();
}
EX0 = 1;
}
//定时器0中断
void timer0() interrupt 1
{
/* pwm_t++;//周期计时加
if(pwm_t == 120)
pwm_t = EN1 = EN2 = 0;
if(pwm_left_val == pwm_t)//左电机占空比
EN1 = 1;
if(pwm_right_val == pwm_t)//右电机占空比
EN2 = 1;
*/
}
void int1() interrupt 2
{
EX1 = 0;
delay(10);
if(right_led2 == 0)
{
backward();
delay(500);
key_s3 = 1;
left_run();
delay(700);
forward();
delay(500);
stop();
}
EX1 = 1;
}
//定时器1中断
void timer1() interrupt 3 //T1中断用来计数器溢出
{
TR1 = 0; //关闭定时器1
TH1 = 0xff; //重装初值0.1ms
TL1 = 0xa3;
//舵机1
if(count <= SEH_count) //控制占空比左右
{
//如果count的计数小于(5-25)也就是0.5ms-2.5ms则这段小t周期持续高电平。产生方波
Servo = 1;
}
else
{
Servo = 0;
}
count++;
if (count >= 200) //T = 20ms则定时器计数变量清0
{
count = 0;
}
TR1 = 1; //开启定时器1
}
/******************************************************************/
/* 串口中断程序*/
/******************************************************************/
void UART_SER () interrupt 4
{
unsigned char n; //定义临时变量
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
n=SBUF; //读入缓冲区的值
control=n;
/* if((n >= 51) && (n <= 150))//左电机调速0~100个档位 手机端软件进行调节
pwm_left_val = 0.8-((n-50)*0.8);
if((n >= 151) && (n <= 250)) //右电机调速0~100个档位 手机端软件进行调节
pwm_right_val = 0.8-((n-150)*0.8);*/
if((n >= 51) && (n <= 150))//左电机调速0~100个档位 手机端软件进行调节
pwm_left_val = 1.7-((n-50)*1.7);
if((n >= 151) && (n <= 250)) //右电机调速0~100个档位 手机端软件进行调节
pwm_right_val = 1.7-((n-150)*1.7);
}
}
复制代码
所有资料51hei提供下载:
Smart_car4 红外避障&超声波跟随.rar
(56.44 KB, 下载次数: 25)
2018-6-1 17:04 上传
点击文件名下载附件
下载积分: 黑币 -5
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1