设计基于单片机开发的汽车尾灯控制器,要求实现以下功能:
(1)尾灯控制器由液晶屏、按键、蜂鸣器、LED灯、超声模块、光照传感器等组成;
(2)要求开机后先在液晶屏上显示课程名称、班级、学号、姓名等信息,要求显示时间为5秒;
(3)汽车车尾左右两侧各有四盏灯:黄灯、红灯、雾灯、倒车照明灯,前面有照明灯(远光、近光)、黄灯、雾灯;
(4)白天正常行驶时照明灯都不亮,夜间行驶时两个前照明灯都亮;
(5)左转弯时左侧黄灯闪亮(2Hz),右转弯时右侧黄灯闪亮;
(6)汽车刹车时,两个红灯都亮;
(7)能见度低或者夜间行驶时雾灯点亮;
(8)倒车时红灯、倒车照明灯亮。且通过超声传感器测量倒车过程中最近障碍物的距离,当安全距离小于5米、2米、1米和0.5米时分别以更为急促的蜂鸣器声音提示;
(9)所有的状态和测量参数均需在液晶屏上显示。
一、转向灯模块
转向灯模块很简单,就是AT89C51芯片引脚对应连接LED的引脚及按键,控制灯亮灯灭
#include <reg52.h>
#define unit unsigned int
#define uchar unsigned char
//按键接口
sbit k0=P1^0;//左转信号
sbit k1=P1^1;//右转信号
sbit k2=P1^2;//刹车信号
sbit k3=P1^3;//倒车信号
sbit k4=P1^4;//雾灯开关
//汽车前灯
sbit x0=P2^0;//
sbit x1=P2^1;//左转
sbit x2=P2^2;//
sbit x3=P2^3;//右转
sbit x4=P2^4;//
sbit x5=P2^5;//
sbit x6=P2^6;//光敏
//AOUT连接p口,DOUT连接流水灯
//汽车后灯
sbit y0=P3^0;// 右转
sbit y1=P3^1;// 刹车
sbit y2=P3^2;//
sbit y3=P3^3;// 倒车
sbit y4=P3^4;//倒车
sbit y5=P3^5;//
sbit y6=P3^6;//刹车
sbit y7=P3^7;//左转
bit flag=0 ;// 开始循环标志位
bit f1=0;
uchar num,miao;
void delay(unit ms)//ms级延时函数
{
uchar j;
for(ms;ms>0;ms--)
for(j=110;j>0;j--);
}
void kong_zhi()//控制信号函数
{
if(k2==0)//刹车信号
{
delay(10);//消抖
if(k2==0)
{
y1=y6=0;//
}
while(!k2);//刹车松开检测
P2=0xff;
P3=0xff; //刹车松开所有灯同时灭
}
else if(k3==0) //倒车信号
{
delay(10);//消抖
while(!k3)//停止松开检测
{
y3=y4=0;//倒车灯亮
}
P2=0xff;
P3=0xff; //倒车松开所有同时灭
}
else if(k0==0)//左转
{
delay(10);
TR0=1;//开启定时器
num=0;
miao=0;
while(!k0)
{
if(k0==0)
{
x1=y7=1;
while(!k0)
{
if(miao==0||miao==2) x1=y7=0;
else if(miao==1||miao==3) x1=y7=1;
}
}
if(miao==0||miao==2) x1=y7=0;
else if(miao==1||miao==3) x1=y7=1;
}
P2=0xff;
P3=0xff;
TR0=0;//关闭定时器
}
else if(k1==0)//右转
{
delay(10);
TR0=1;//开启定时器
num=0;
miao=0;
while(!k1)
{
if(k1==0)
{
x4=y0=1;
while(!k1)
{
if(miao==0||miao==2)x4=y0=0;
else if(miao==1||miao==3) x4=y0=1;
}
}
if(miao==0||miao==2) x4=y0=0;
else if(miao==1||miao==3) x4=y0=1;
}
P2=0xff;
P3=0xff;
TR0=0;//关闭定时器
}
else if(k4==0) //倒车信号
{
delay(10);//消抖
while(!k4)//停止松开检测
{
x0=x5=y2=y5=f1;//雾灯亮
}
f1=~f1;
}
}
void main()
{
TH0=(65536-50000)/256;//设置初值
TL0=(65536-50000)%256;
TMOD=0x01;//设置定时器模式
EA=1;//开启总中断
ET0=1;//开启定时器中断
TR0=0;//关闭定时器
P2=0xff;
P3=0xff;
x6=0;
while(1)
{
kong_zhi();//信号控制函数
}
}
void T0_init() interrupt 1
{
TH0=(65536-50000)/256;//设置初值
TL0=(65536-50000)%256;
num++;
if(num==5)
{
num=0;
miao++;
if(miao==4)
miao=0;
}
}
二、显示屏模块
显示屏使用的是12864液晶显示屏,12864点阵最多可显示32个中文字符。ST7920控制器带中文字库,免除了编制字库的麻烦
/*******************************************************
程序功能:LCD12864液晶测试
*******************************************************/
#include "STC12C5A60S2.h"
sbit LCD12864_RS = P3^4; //RS控制引脚
sbit LCD12864_RW = P3^5; //RW控制引脚
sbit LCD12864_EN = P3^6; //EN控制引脚
sbit LCD12864_PSB = P3^7; //CS1模式选择引脚,ST7920控制器,1为8位并行接口,0为串行接口
#define LCDPORT P1 //数据引脚
unsigned char code ucStr1[] = "汽车尾灯控制器"; //显示信息1
unsigned char code ucStr2[] = " 16计科一班"; //显示信息2
unsigned char code ucStr3[] = " 莫依 "; //显示信息3
unsigned char code ucStr4[] = " 1111111111 "; //显示信息4
void LCD12864_Init(void); //LCD12864初始化函数
void LCD12864_WriteInfomation(unsigned char ucData,bit bComOrData); //向LCD12864写入数据,bComOrData为1时写入的是数据,0时写入的是命令
void LCD12864_CheckBusy(void); //忙检测函数
void LCD12864_DisplayOneLine(unsigned char ucPos,unsigned char *ucStr); //向LCD12864写入一行文字
void Delay(unsigned int uiCount);
/******************************************************************************
函数名称:main
函数功能:主函数
*******************************************************************************/
void main(void)
{
LCD12864_Init(); //初始化液晶
LCD12864_DisplayOneLine(0x80,ucStr1); //显示信息1
LCD12864_DisplayOneLine(0x90,ucStr2); //显示信息2
LCD12864_DisplayOneLine(0x88,ucStr3); //显示信息3
LCD12864_DisplayOneLine(0x98,ucStr4); //显示信息4
while(1);
}
/******************************************************************************
函数名称:LCD12864_WriteInfomation
函数功能:向LCD12864写入命令或者数据
入口参数:ucData-要写入液晶的数据或者命令的内容
bComOrData-命令或者数据的标志位选择,0或者1,其中
1:写入的是数据
0:写入的是命令
*******************************************************************************/
void LCD12864_WriteInfomation(unsigned char ucData,bit bComOrData)
{
LCD12864_CheckBusy(); //忙检测
LCD12864_RW = 0; //拉低RW
LCD12864_RS = bComOrData; //根据标志位判断写入的是命令还是数据
Delay(150); //延时,等待操作
LCDPORT = ucData; //将数据送至数据端口
LCD12864_EN = 1; //使能信号
Delay(150); //延时
LCD12864_EN = 0; //按照时序来操作
Delay(150);
}
/******************************************************************************
函数名称:LCD12864_Init
函数功能:LCD12864液晶初始化
入口参数:无
返回值:无
备注:无
*******************************************************************************/
void LCD12864_Init(void)
{
Delay(400);//延时
LCD12864_PSB = 1; //8位并口工作模式
Delay(150);//延时
LCD12864_WriteInfomation(0x30,0); //基本指令集
Delay(150);
LCD12864_WriteInfomation(0x08,0); //显示设置
Delay(150);
LCD12864_WriteInfomation(0x10,0); //光标设置
Delay(150);
LCD12864_WriteInfomation(0x0c,0); //游标设置
Delay(150);
LCD12864_WriteInfomation(0x01,0); //清屏
Delay(150);
LCD12864_WriteInfomation(0x06,0); //进入点设定
Delay(150);
}
/******************************************************************************
函数名称:LCD12864_CheckBusy
函数功能:忙检测
入口参数:无
返回值:无
备注:使用变量i做计时,避免液晶在死循环处停滞。
*******************************************************************************/
void LCD12864_CheckBusy(void)
{
unsigned char i = 250; //局部变量
LCD12864_RS = 0; //拉低
LCD12864_RW = 1; //拉高
LCD12864_EN = 1; //使能
while((i > 0) && (P0 & 0x80))i--; //判断忙标志位
LCD12864_EN = 0; //释放
}
/******************************************************************************
函数名称:LCD12864_DisplayOneLine
函数功能:显示一行汉字(8个汉字或者16个英文字符)
入口参数:position-要显示的行的首地址,可选值0x80,0x88,0x90,0x98,其中:
0x80:液晶的第一行;
0x88:液晶的第三行;
0x90:液晶的第二行;
0x98:液晶的第四行。
p-要显示的内容的首地址。
*******************************************************************************/
void LCD12864_DisplayOneLine(unsigned char position,unsigned char *p)
{
unsigned char i;
LCD12864_WriteInfomation(position,0);//写入要显示文字的行的首地址
Delay(150);
for(i = 0;i<16;i++) //依次执行写入操作
{
LCD12864_WriteInfomation(*p,1);
p++;
}
}
/******************************************************************************
函数名称:Delay
函数功能:延时函数
入口参数:uiCount-延时参数
*******************************************************************************/
void Delay(unsigned int uiCount)
{
while(uiCount--);
}
三、超声波模块
使用的是HC_SR04超声波传感器
其工作原理如下:
1.给超声波模块接入电源和地。
2.给脉冲触发引脚(trig)输入一个长为20us的高电平方波
3.输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;(此时应该启动定时器计时)
4.当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;(此时应该停止定时器计数),定时器记下的这个时间即为超声波由发射到返回的总时长。
5.根据声音在空气中的速度为344米/秒,即可计算出所测的距离。
//超声波模块程序
//超声波模块程序
//Trig = P2^0
//Echo = P3^2
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit Trig = P2^0;
sbit Echo = P3^2;
sbit king=P1^1;
sbit test = P1^2;
sbit succeed_flag = P1^3;
uint time;
uint timeH;
uint timeL;
uint i;
//
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
//
void delay_20us()
{
uchar a ;
for(a=0;a<100;a++);
}
//***************************************************************
//显示数据转换程序
void display(uint temp)
{
if(temp<50)
{
king=0;
delay(500);
king=1;
delay(500);
}
}
//***************************************************************
void main()
{
uint distance;
test =0;
king=1;
Trig=0; //首先拉低脉冲输入引脚
EA=1; //打开总中断0
TMOD=0x10; //定时器1,16位工作方式
while(1)
{
EA=0; //关总中断
Trig=1; //超声波输入端
delay_20us(); //延时20us
Trig=0; //产生一个20us的脉冲
i=0;
while(Echo==0) {//等待Echo回波引脚变高电平
i++;
if(i==0xfffe) break;
}
if(i==0xffff) continue;
succeed_flag=0; //清测量成功标志
EA=1;
EX0=1; //打开外部中断0
TH1=0; //定时器1清零
TL1=0; //定时器1清零
TF1=0; //计数溢出标志
TR1=1; //启动定时器1
delay(20); //等待测量的结果
TR1=0; //关闭定时器1
EX0=0; //关闭外部中断0
if(succeed_flag==1)
{
time=timeH*256+timeL;
distance=time*0.172; //厘米
display(distance);
}
if(succeed_flag==0)
{
distance=0; //没有回波则清零
test = !test; //测试灯变化
}
}
}
//***************************************************************
//外部中断0,用做判断回波电平
void exter() interrupt 0 // 外部中断0是0号
{
timeH =TH1; //取出定时器的值
timeL =TL1; //取出定时器的值
succeed_flag=1;//至成功测量的标志
EX0=0; //关闭外部中断
}
//****************************************************************
//定时器1中断,用做超声波测距计时
void timer1() interrupt 3 //
{
TH1=0;
TL1=0;
}
四、AD/DA模块
结合光照强度和输出的模拟电压之间的关系,可以得到某一光强度下的对应的模拟电压。将模拟电压通过AD转换器转换为数字电压,然后再将数字电压转换成光照度。其中写数据读数据使用IIC协议模拟信号。
#include "reg52.h"
#include "intrins.h"
#define uint unsigned int
#define uchar unsigned char
#define PCF8591 0x90//PCF8591地址
/*********************************
*********************************/
//-----------定义变量--------------
sbit scl = P1^1; //串行数据输入端
sbit sda = P1^0; //串行时钟输入端
sbit DS_595 = P2^2;
sbit SHCP_595 = P2^1;
sbit STCP_595 = P2^0;
uchar code du[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};//段码
uchar code we[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码
void send_595(uint j)
{
uint num;
// STCP_595 = 0;
for( num = 0 ; num < 8 ; num++ )
{
SHCP_595 = 0;
DS_595 = j&0x80;
j = j<<1;
SHCP_595 = 1;
_nop_();
}
_nop_();
// STCP_595 = 1;
}
void Out_595()
{
STCP_595 = 0;
_nop_();
_nop_();
STCP_595 = 1;
}
//延时函数_1ms级别
void delay_1ms(uint z)
{
uint x,y;
for( x=z ; x>0 ; x-- )
for( y=114 ; y>0 ; y-- );
}
//延时函数_us级别
void delay_nop(void)//延时4-5个微秒
{
_nop_();_nop_();_nop_();_nop_();_nop_();
}
//初始化函数
void IIC_Init(void)
{
sda = 1;
delay_nop();
scl = 1;
delay_nop();
}
//起始信号函数
void IIC_Start(void)
{
sda = 1;
delay_nop();
scl = 1;
delay_nop();
sda = 0;
delay_nop();
}
//停止信号函数
void IIC_Stop(void)
{
sda = 0;
delay_nop();
scl = 1;
delay_nop();
sda = 1;
delay_nop();
}
//应答函数
void Ack(void)
{
uchar i;
scl = 1;
delay_nop();
while( (sda==1)&&(i<220) )
i++;
scl = 0;
delay_nop();
}
//非应答函数
void NoAck(void)
{
sda = 1;
delay_nop();
scl = 1;
delay_nop();
scl = 0;
delay_nop();
}
//写字节函数
void Write_byte(uchar dat)//单片机向里写
{
uchar i;
scl = 0;
for( i=0 ; i<8 ; i++ )
{
if( dat&0x80 )
{
sda = 1;
}
else
{
sda=0;
}
dat = dat<<1;
delay_nop();
scl = 1;//准备向里写数据
delay_nop();
scl = 0;
delay_nop();//数据写完毕
}
sda = 1;//数据线释放,不再占用,便于判断应答位
delay_nop();
}
//读字节函数
uchar Read_byte(void)
{
uchar i,byte;
scl = 0;
delay_nop();
sda = 1;
delay_nop();
for( i=0 ; i<8 ; i++ )
{
scl = 1;//准备读数据
delay_nop();//稳定下来
byte = (byte<<1)|sda;//先左移一位,再在最低位接受当前位
delay_nop();
scl = 0;//读完
delay_nop();
}
return byte;
}
//读数据函数
uchar Read_Data(uchar control)//任意一个地址读取字节函数
{
uchar IIC_Data;
IIC_Start();
Write_byte(PCF8591);//选择从机
Ack();//伪写
Write_byte(control);
Ack();
IIC_Start();
Write_byte(PCF8591+1);//读为1
Ack();
IIC_Data = Read_byte();
NoAck();
IIC_Stop();
return IIC_Data;
}
void display(uchar bai,uchar shi,uchar ge)
{
send_595(we[0]);
send_595(du[bai]);
Out_595();
delay_1ms(10);
send_595(we[1]);
send_595(du[shi]);
Out_595();
delay_1ms(10);
send_595(we[2]);
send_595(du[ge]);
Out_595();
delay_1ms(10);
}
//AD转换
void main()
{
uchar num,ge,shi,bai;
IIC_Init();
while(1)
{
num = Read_Data(0x40);
bai = num/100;
shi = num%100/10;
ge = num%10;
display(bai,shi,ge);
}
}