目录
一、主要功能
开机即两个直流电机运转,然后三个气体传感器,如果超过阈值,即蜂鸣器报警;
超声波传感器,如果检测到障碍,电机停止;
温度传感器,超过阈值,电机停止,蜂鸣器报警,点亮一个灯;
循迹模拟,与电机联动;
仿真图:
编辑
二、硬件资源
基于KEIL5编写C++代码,PROTEUS8.15进行仿真,全部资源在页尾,提供安装包。
1、51单片机
2、超声波模块
3、烟雾传感器
4、一氧化碳传感器
5、二氧化碳传感器
6、DS18B20温度传感器
7、灯光报警模块
8、PCF8591电机模块
9、LCD1602显示模块
10、L298N电机模块
三、程序编程
/*全部代码资源在页尾*/
#include <REGX52.H>
#include<intrins.h>
#include<stdio.h>
#include "Delay.h"
#include "LCD1602.h"
#define uchar unsigned char
#define uint unsigned int
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
sbit led = P3^5; //LED灯引脚
sbit BEEP = P3^6; //蜂鸣器引脚
sbit DS=P3^7; //DS18B20温度传感器
sbit Motor1_IN1 = P3^3; //电机IN1口
sbit Motor1_IN2 = P3^4;
sbit Motor1_EN = P3^2; //电机使能端1
sbit Motor2_IN1 = P2^4; //电机IN2口
sbit Motor2_IN2 = P2^5;
sbit Motor2_EN = P2^7; //电机使能端2
sbit CS=P1^0; //51单片机引脚设置
sbit CLK=P1^1;
sbit DIO=P1^2;
sbit CS1=P1^3; //51单片机引脚设置
sbit CLK1=P1^4;
sbit DIO1=P1^2;
sbit CS2=P1^5; //51单片机引脚设置
sbit CLK2=P1^6;
sbit DIO2=P1^2;
sbit Tr=P3^0;//触发信号
sbit Ec=P3^1;//回响信号
sbit I2C_SCL=P2^0;//pcf8591
sbit I2C_SDA=P2^1;
uchar Recv_Buffer\[4\];
uint Voltage\[\]={'0','0','0','0'};
bit bdata IIC_ERROR;
unsigned char count;
unsigned int distance;
static uint temp;
static float ftemp = 0.0f;//温度转变
uint temp;
static unsigned char num;
static int maxnumber=100,difference;
unsigned int Read_value(void);//读值函数
uint8 AD_value = 0;//AD值
uint8 AD_value1 = 0;//AD值
void tmpchange();
uint tmp();
void beep_warning(uint);
void Delay10us(void);//10us延时函数
uint8 get\_ADC\_vaule(uint8 chn);
uint8 get\_ADC\_vaule1(uint8 chn);
void Time0_Init() //定时器初始化
{
TMOD = 0x01;
TH0 = 0x00;
TL0 = 0x00;
TR0 = 0;//先关闭定时器0
}
void Time0_Int() interrupt 1 //中断程序
{
TH0 = 0xfe; //重新赋值
TL0 = 0x33;
}
unsigned int Read_value()
{
uint result;
Tr=1;//触发引脚发出11us的触发信号(至少10us)
Delay10us();
Tr=0;
while(!Ec);//度过回响信号的低电平
TR0=1;//开启定时器0
while(Ec);//度过回响信号高电平
TR0=0;//关闭定时器0
result=((TH0\*256+TL0)\*0.034)/2; // 距离cm=(时间us * 速度cm/us)/2
return result + 2; //+2修正补偿
}
uchar get\_AD\_Res() //ADC0832启动读取函数
{
uchar i, data1=0, data2=0;
CS=0;
CLK=0;DIO=1;\_nop\_();
CLK=1;\_nop\_();
CLK=0;DIO=1;\_nop\_();
CLK=1;\_nop\_();
CLK=0;DIO=0;\_nop\_();
CLK=1;\_nop\_();
CLK=0;DIO=1;\_nop\_();
for(i=0; i<8; i++)
{
CLK=1;\_nop\_();
CLK=0;\_nop\_();
data1=(data1<<1)|(uchar)DIO;
}
for(i=0; i<8; i++)
{
data2=data2|(uchar)DIO<<i;
CLK=1;\_nop\_();
CLK=0;\_nop\_();
}
CS=1;
return(data1 == data2)?data1:0;
}
uchar get\_AD\_Res1() //ADC0832启动读取函数
{
uchar i, data1=0, data2=0;
CS1=0;
CLK1=0;DIO1=1;\_nop\_();
CLK1=1;\_nop\_();
CLK1=0;DIO1=1;\_nop\_();
CLK1=1;\_nop\_();
CLK1=0;DIO1=0;\_nop\_();
CLK1=1;\_nop\_();
CLK1=0;DIO1=1;\_nop\_();
for(i=0; i<8; i++)
{
CLK1=1;\_nop\_();
CLK1=0;\_nop\_();
data1=(data1<<1)|(uchar)DIO1;
}
for(i=0; i<8; i++)
{
data2=data2|(uchar)DIO1<<i;
CLK1=1;\_nop\_();
CLK1=0;\_nop\_();
}
CS1=1;
return(data1 == data2)?data1:0;
}
uchar get\_AD\_Res2() //ADC0832启动读取函数
{
uchar i, data1=0, data2=0;
CS2=0;
CLK2=0;DIO2=1;\_nop\_();
CLK2=1;\_nop\_();
CLK2=0;DIO2=1;\_nop\_();
CLK2=1;\_nop\_();
CLK2=0;DIO2=0;\_nop\_();
CLK2=1;\_nop\_();
CLK2=0;DIO2=1;\_nop\_();
for(i=0; i<8; i++)
{
CLK2=1;\_nop\_();
CLK2=0;\_nop\_();
data1=(data1<<1)|(uchar)DIO2;
}
for(i=0; i<8; i++)
{
data2=data2|(uchar)DIO2<<i;
CLK2=1;\_nop\_();
CLK2=0;\_nop\_();
}
CS2=1;
return(data1 == data2)?data1:0;
}
void dsreset(void) //发出命令
{
uint i;
DS=0;
i=103; //将总线拉低480us~960us
while(i>0)i--;
DS=1; //然后拉高总线,若DS18B20做出反应会将在15us~60us后将总线拉低
i=4; //15us~60us等待
while(i>0)i--;
//while(DS);
}
bit tmpreadbit(void) //读取数据
{
uint i;
bit dat;
DS=0;i++; //i++ for delay
DS=1;i++;i++;
dat=DS;
i=8;while(i>0)i--;
return (dat);
}
uchar tmpread(void) //读取数据
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tmpreadbit();
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里
}
return(dat);
}
void tmpwritebyte(uchar dat) //传输数据给DS18B20
{
uint i;
uchar j;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) //write 1
{
DS=0;
i++;i++;
DS=1;
i=8;while(i>0)i--;
}
else
{
DS=0; //write 0
i=8;while(i>0)i--;
DS=1;
i++;i++;
}
}
}
void tmpchange(void) //DS18B20开始工作
{
dsreset();
Delay(1);
tmpwritebyte(0xcc);
tmpwritebyte(0x44);
}
uint tmp() //获得温度
{
float tt;
uchar a,b;
dsreset();
Delay(1);
tmpwritebyte(0xcc);
tmpwritebyte(0xbe);
a=tmpread();//低八位
b=tmpread();//高八位
temp=b;
temp<<=8; //two byte compose a int variable
temp=temp|a;
tt=temp*0.0625; //算出来的是测到的温度,数值可到小数点后两位
temp=tt*10+0.5; //为了显示温度后的小数点后一位并作出四舍五入,因为取值运算不能取小数点后的数
return temp;
}
void beep_warning(uint ftemp) //温度传感器蜂鸣器警报并且电机转动
{
if(ftemp>40)
{
Beep(); //蜂鸣器报警
led=1;
Motor1_EN = 0; //关闭电机1
Motor2_EN = 0; //关闭电机2
}
else
{
led=0;
BEEP=0;
Motor1_EN = 1; //关闭电机1
Motor2_EN = 1; //关闭电机2
}
}
void I2C_delay()//I2C延时函数
{
\_nop\_();
\_nop\_();
\_nop\_();
\_nop\_();
}
void I2C_start()//I2C起始信号
{
I2C_SDA = 1;
I2C_SCL = 1;
I2C_delay();
I2C_SDA = 0;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
}
void I2C_stop()//I2C停止信号
{
I2C_SDA = 0;
I2C_SCL = 0;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
I2C_SDA = 1;
I2C_delay();
}
bit I2C_write(uint8 dat)//I2C写一个字节
{
bit ack = 0;
uint8 mask = 0;
for(mask=0x80;mask!=0;mask>>=1)
{
if((mask&dat) == 0)
I2C_SDA = 0;
else
I2C_SDA = 1;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
}
I2C_SDA = 1;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
ack = I2C_SDA;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
return (~ack);
}
uint8 I2C\_read\_ACK()//I2C读一个字节,并发送应答位
{
uint8 dat = 0;
uint8 mask = 0;
I2C_SDA = 1;
for(mask=0x80;mask!=0;mask>>=1)
{
if(I2C_SDA == 0)
dat = dat & (~mask);
else
dat = dat | mask;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
}
I2C_SDA = 0;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
return dat;
}
uint8 I2C\_read\_NACK()//I2C读一个字节,并发送非应答位
{
uint8 dat = 0;
uint8 mask = 0;
I2C_SDA = 1;
for(mask=0x80;mask!=0;mask>>=1)
{
if(I2C_SDA == 0)
dat = dat & (~mask);
else
dat = dat | mask;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
}
I2C_SDA = 1;
I2C_delay();
I2C_SCL = 1;
I2C_delay();
I2C_SCL = 0;
I2C_delay();
return dat;
}
uint8 get\_ADC\_vaule(uint8 chn)//获取AD值
{
uint8 value = 0;
I2C_start();//I2C起始信号
if(!I2C_write(0X90))//写入PCF8591地址及读写选择位为写
{
I2C_stop();
return 0;
}
// I2C_write(0X40 | chn);//写入PCF8591通道0
I2C_write(0x00 | chn);//写入PCF8591通道0
I2C_start();//I2C起始信号
I2C_write(0x48<<1 | 0x01);
I2C\_read\_ACK();//提供转换所需的时钟信号
value = I2C\_read\_NACK();//读取上一次转换的结果
I2C_stop();//I2C结束信号
return value;
}
uint8 get\_ADC\_vaule1(uint8 chn)//获取AD值
{
uint8 value = 0;
I2C_start();//I2C起始信号
if(!I2C_write(0X90))//写入PCF8591地址及读写选择位为写
{
I2C_stop();
return 0;
}
// I2C_write(0X40 | chn);//写入PCF8591通道0
I2C_write(0x01 | chn);//写入PCF8591通道0
I2C_start();//I2C起始信号
I2C_write(0x48<<1 | 0x01);
I2C\_read\_ACK();//提供转换所需的时钟信号
value = I2C\_read\_NACK();//读取上一次转换的结果
I2C_stop();//I2C结束信号
return value;
}
void main() //主函数
{
uchar u,U,R,u1,U1,R1,u2,U2,R2;
Tr=0;//出发引脚首先拉低
// led=0; //灯关掉
// BEEP=0; //蜂鸣器关掉
LCD_Init(); //显示屏初始化
Time0_Init();
// Motor1_EN = 1;
// Motor1_IN1 = 1; //电机IN口
// Motor1_IN2 = 0;
// Motor2_EN = 1;
// Motor2_IN1 = 1; //电机IN口
// Motor2_IN2 = 0;
while(1)
{
// AD\_value = get\_ADC_vaule(0);//读取通道0的AD值 左电机
// AD\_value1 = get\_ADC_vaule1(0);//读取通道0的AD值 右电机
// difference=AD\_value-AD\_value1; //差值等于左电机减去右电机
// if(difference>50)
// {
// Motor1_EN = 0;//左电机停止
// }
// else if(difference<(-50))
// {
// Motor2_EN = 0;//右电机停止
// }
// LCD\_ShowNum(2,10,AD\_value,3); //第一行显示温度
// LCD\_ShowNum(2,14,AD\_value1,3); //第一行显示温度
// u=get\_AD\_Res();
// U=(250*u)/128; //二氧化碳
// R=200*U/250;
// u1=get\_AD\_Res1();
// U1=(250*u1)/128; //烟雾
// R1=200*U1/250;
// u2=get\_AD\_Res2();
// U2=(250*u2)/128; //一氧化碳
// R2=200*U2/250;
// tmpchange(); //让18b20开始转换温度
// temp = tmp(); //读取温度
// ftemp = temp/10.0f; //转换温度
distance = Read_value();//读值
distance-=1;
LCD_ShowNum(2,5,distance,3); //第一行显示温度
// if(R>maxnumber||R1>maxnumber||R2>maxnumber)
// {
// Beep();
// }
// else
// {
// BEEP=0;
// }
// if(distance<50)
// {
// Motor1_EN = 0;
// Motor2_EN = 0;
// Motor1_IN1 = 0; //电机IN口
// Motor1_IN2 = 0;
// Motor2_IN1 = 0; //电机IN口
// Motor2_IN2 = 0;
// }
// else if(ftemp<=40)
// {
// Motor1_EN = 1;
// Motor2_EN = 1;
// Motor1_IN1 = 1; //电机IN口
// Motor1_IN2 = 0;
// Motor2_IN1 = 1; //电机IN口
// Motor2_IN2 = 0;
// }
// LCD_ShowNum(1,1,R,3); //第一行显示温度
// LCD_ShowNum(1,5,R1,3); //第一行显示温度
// LCD_ShowNum(1,9,R2,3); //第一行显示温度
// LCD_ShowNum(2,1,ftemp,3); //第一行显示温度
// LCD_ShowNum(2,5,distance,3); //第一行显示温度
// beep_warning(ftemp); //温度超出报警,舵机转动
}
}
void Delay10us()
{
TL0=0xF5;
TH0=0xFF;
TR0=1;
while (TF0==0);
TR0=0;
TF0=0;
}
四、实现现象
具体动态效果看B站演示视频:
基于51单片机的循迹小车避障转弯加气体传感器_哔哩哔哩_bilibili
全部资料(源程序、仿真文件、安装包、演示视频):
链接:https://pan.baidu.com/s/1B1k9zD7gyX8sjBVFUNlCxg
提取码:r7wo
–来自百度网盘超级会员V4的分享