本项目是以AT89C51最小系统为核心,外接DS18B20温度传感器、OVEN加热棒、LCD1604显示屏、ADC0832模数转换器模拟浊度传感器、L298和MOTOR当电机模块。电机模块用于模拟洗碗机的进水、洗碗、出水状态,蜂鸣器模块用于提醒用户其洗碗机结束工作。
文章目录
1、仿真图
2、软件设计流程图
先把软件流程图画出来,有个思路也方便后面编写代码
3、keil4编写代码
我这里是写了一个主文件调用五个子文件,分别是:
main.c:主函数
ADC.c:ADC032模数转换器,获取电压大小,在主函数在将其转换为百分比
DS18B20.c: 获取温度传感器数据
KEY.c:按键模块
LCD1602.c: 对LCD1604显示屏进行初始化、写命令、写数据
STATUS.C:按钮功能、进水、出水、洗涤方式、定时器初始化函数以及一些公共定义
3.1 LCD1604模块
我是先写lcd显示屏,它是被调用,不需要调别的数据。LCD1604与LCD1602原理差不多,只是容量比LCD1602多一倍,我需要显示的数据比较多,所以本项目这里采用的LCD1604显示模块,主要显示当前选择模式、温度数据、浊度数据、定时时间、剩余时间、进度显示。RS引脚接单片机P2.0引脚,RW引脚接单片机P2.1引脚,E引脚接单片机P2.2,D0-D7引脚接单片机P0-P7引脚。这里我写了两种显示方式,一种是可根据我们的需要写进去,一种是获取数据再显示出来。
初始化状态
运行状态
#include "LCD1602.h"
#include <intrins.h>
#define uchar unsigned char
#define DataPort P0
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay(unsigned int n)//延时函数
{
unsigned int i=0,j=0;
for(i=0;i<n;i++)
{
for(j=0;j<120;j++);
}
}
// lcd 写入命令函数
void lcd_writer_com(uchar com)
{
RS=0; // RS:数据/命令选择端
RW=0; // R/W :读/写选择端
E=0; //使能端:下降沿有效
P0=com;
delay(5);
E=1;
E=0;
}
//写数据函数,
void lcd_write_data(uchar Data)
{
RS=1; // RS:数据/命令选择端
RW=0; // R/W :读/写选择端
E=0; // 使能端:下降沿有效
P0=Data;
delay(5);
E=1;
E=0;
}
//xy位置
void GotoXY(unsigned char x, unsigned char y)
{
if(x==0)
lcd_writer_com(0x80+y); //0x80是第一行的起始地址
if(x==1)
lcd_writer_com(0xc0+y); //0xc0是第二行的起始地址
if(x==2)
lcd_writer_com(0x90+y); //0x90是第三行的起始地址
if(x==3)
lcd_writer_com(0xd0+y); //0xd0是第四行的起始地址
}
void Print(unsigned char *str) //在LCD上显示数据,内部调用
{
while(*str!='\0')
{
lcd_write_data(*str);
str++;
}
}
void LCD_Print(unsigned char x, unsigned char y, unsigned char *str) //指定的位置打印指定数据
{
GotoXY(x,y);
Print(str);
}
void LCD_Print_num(unsigned char x, unsigned char y,long int temp_num) //指定的位置显示数值
{
uchar temp_tab[7]=0;
long int num=0;
uchar i=0;
num=temp_num;
GotoXY(x,y);
if(num<0)
{
num=0-num;
lcd_write_data('-');
}
temp_tab[0]=num/1000000%10+0x30;
temp_tab[1]=num/100000%10+0x30;
temp_tab[2]=num/10000%10+0x30;
temp_tab[3]=num/1000%10+0x30;
temp_tab[4]=num/100%10+0x30;
temp_tab[5]=num/10%10+0x30;
temp_tab[6]=num%10+0x30;
for(i=0;i<7;i++)
{
if(temp_tab[i]!=0x30)break;
}
if(i==7)
{
lcd_write_data(0x30);
}
else
{
while(i<7)
{
lcd_write_data(temp_tab[i]);
i++;
}
}
}
//lcd初始化
void lcd_init(void)
{
// RS =0 RW =0 写命令
// RS=1 RW=0 写数据
// RS =0 RW=1 读状态
lcd_writer_com(0x38); //0011 1000
lcd_writer_com(0x38);
lcd_writer_com(0x08); //0000 1000
lcd_writer_com(0x0c); //0000 0001 0x0C:设置开显示,不显示光标
lcd_writer_com(0x06); //0000 0110 0x06:写一个字符后地址指针加1
lcd_writer_com(0x01); //0000 1111 显示清0,数据指针清0
LCD_Print(0,0,"MODE:"); //显示屏显示当前模式
LCD_Print(1,0,"TIME:"); //显示屏显示剩余时间
LCD_Print(2,0,"DSHI:"); //显示屏显示定时
LCD_Print(2,8,"ZD:"); //显示屏显示浊度
LCD_Print(1,8,"WD:"); //显示屏显示温度
LCD_Print(3,0,"ZhunBei"); //显示屏显示准备
LCD_Print(2,14,"%" );
}
3.2 DS18B20模块
本项目这里采用的是DS18B20温度传感器模块,其VCC引脚接5V,GND引脚接地,DQ引脚接51单片机P3.3引脚,通过温度检测当前的水温来判断是否启动加热棒模块。
//测量温度
#include "DS18B20.h"
#include "lcd1602.h"
int readtemp =0; // 定义读出温度储存寄存器
unsigned char str[] ={"0123456789"}; //温度显示调用数组
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
sbit DQ =P3^3; //DS18B20 温度传感器信号端口
void delay_18B20(unsigned int i)//延时函数
{
for(;i>0;i--);
}
void Init_DS18B20(void) // DS18B20 温度传感器初始化子程序
{
unsigned char x=0;
DQ=1; //DQ拉高
delay_18B20(8);
DQ=0; //DQ拉低
delay_18B20(80); //延时大于480us
DQ=1; //拉高总线
delay_18B20(14);
x =DQ; //x =0 初始化成功,反之不成功
delay_18B20(20);
}
unsigned char ReadOneChar(void) //DS18B20温度传感器 读时序 子程序
{
unsigned char i=0;
unsigned char dat =0;
for(i=8;i>0;i--)
{
DQ=0; //拉低总线
dat>>=1; //每读取移位向右移位
DQ =1; //拉高总线
if(DQ)
dat |= 0x80;
delay_18B20(4);
}
return(dat);
}
void WriteOneChar(unsigned char dat) //DS18B20温度传感器 写时序 子程序
{
unsigned char i =0;
for(i=8;i>0;i--)
{
DQ =0;
DQ =dat & 0x01;
if(DQ)
{
delay_18B20(1);
DQ =1;
}
else
{
delay_18B20(5);
DQ =1;
}
dat>>=1;
}
}
unsigned char ReadTemperature(void) //DS18B20 温度传感器读出子程序
{
unsigned char a =0,b=0;
unsigned int temp =0;
Init_DS18B20(); // 初始化温度
WriteOneChar(0xcc); //跳过读序列号操作
WriteOneChar(0x44); //启动温度转换
delay_18B20(100);
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序列号操作
WriteOneChar(0xBE); //读取温度寄存器
delay_18B20(100);
a =ReadOneChar(); //读取温度低位
b =ReadOneChar(); //读取温度高位
temp=((b*256+a)>>4); // 当前采集温度除16得到实际温度
return(temp); //显示屏显示温度
}
void display() //显示函数
{
unsigned int temp1=0,temp2=0,i=0;
temp1=(readtemp%100)/10;
temp2=readtemp%10;
lcd_writer_com(0xc0+12); //0x80+0x40:LCD第2行的起始地址
lcd_write_data(str[temp1]);
lcd_write_data(str[temp2]);
lcd_write_data(0xDF);
lcd_write_data('C');
}
3.3 ADC0832模块
本项目这里采用的是ADC0832模数转换模块,通过电压大小来模拟浑浊程度,从而在选择智能洗的时候根据浑浊程度来进行相对应的洗涤时间。其VCC引脚接5V,GND引脚接地,CLK引脚接51单片机P1.1引脚,DI引脚接51单片机P1.2引脚,DO引脚接51单片机P1.3引脚,CS引脚接51单片机P1.4引脚,CH0引脚接滑动变阻器,
//浑浊度
#include "ADC.h"
sbit ADC_CS =P1^4; //片选端
sbit ADC_CLK= P1^1;
sbit ADC_DATI= P1^2;
sbit ADC_DATO =P1^3;
//adc转换
uchar adc0832(uchar temp_CH)
{
uchar temp_data =0x00; //ad值
uchar i,temp_ad_val1,temp_ad_val2;
temp_ad_val2 =0x00;
temp_ad_val1 =0x00;
ADC_CLK =0;//初始化
ADC_DATI =1;
_nop_();
ADC_CS =0;
_nop_();
ADC_CLK =1;
_nop_();
if (temp_CH ==0x00)
{
ADC_CLK =0;
ADC_DATI =1; //通道0的第一位
_nop_();
ADC_CLK =1;
_nop_();
ADC_CLK=0;
ADC_DATI =0; //通道0的第二位
_nop_();
ADC_CLK =1;
_nop_();
}
else
{
ADC_CLK =0;
ADC_DATI =1; //通道0的第一位
_nop_();
ADC_CLK =1;
_nop_();
ADC_CLK=0;
ADC_DATI =1; //通道0的第二位
_nop_();
ADC_CLK =1;
_nop_();
}
ADC_CLK =0;
ADC_DATI =1;
for(i =0;i<8;i++) //读取前八位
{
_nop_();
temp_ad_val2 <<=1;
ADC_CLK =1;
_nop_();
ADC_CLK=0;
if(ADC_DATO)
temp_ad_val2 |=0x01;
else
temp_ad_val2 |=0x00;
}
for(i =0;i<8;i++) //后读取8位的值
{
temp_ad_val1 >>= 1;
if (ADC_DATO)
temp_ad_val1 |= 0x80;
else
temp_ad_val1 |= 0x00;
_nop_();
ADC_CLK = 1;
_nop_();
ADC_CLK = 0;
}
if (temp_ad_val2 == temp_ad_val1) //比较前8位与后8位的值,如果不相同舍去。若一直出现显示为零,请将该行去掉
temp_data = temp_ad_val1;
_nop_();
ADC_CS = 1; //释放ADC0832
ADC_DATO = 1;
ADC_CLK = 1;
return temp_data;
}
3.4 STATUS模块
定时器初始化
void timer0_init(void)
{
TMOD =0X10; //定时器
TH1=(65536 -50000)/256;
TL1 =(65536-50000)%256;
TR1=1;
EA=1;
ET1=1;
TR0=0; //启动定时器为1
}
智能洗模块
uchar zhineng =0; // 智能洗的判断标志位 0 为不启动,1为启动
uchar zhuodu_ok =0; //智能洗时,根据浊度,确定洗涤时间
uchar zhineng_num =5; //智能洗时,循环检测5次,确保浊度稳定,之后根据浊度决定洗涤时间
void zhinengxi()
{ LCD_Print(0,5,"zhinengxi");
jinshui();
while(zhineng) //智能洗时
{
while(zhineng_num-- && !zhuodu_ok)
{
if(p_zhoudu <25) //浊度小于25%,定时10s
{
t_ding =10;
}
else if (p_zhoudu<60) //浊度小于60%,定时20s
{
t_ding =20;
}
else //浊度大于等于60,定时30s
{
t_ding =30;
}
if (zhineng_num ==0)
{
TR0 =1; //开启定时器,开始倒计时
}
}
zhuodu_ok =1;
motor(); //电机转动
adc0832(0); //ADC处理函数
LCD_Print_num(1,5,t_ding); //显示定时时间
if (t_ding ==0)
{
chushui();
}
}
}
电机模块
本项目这里采用的L298和MOTOR模块,其引脚IN1接单片机P1.5引脚,引脚IN2接单片机P1.6引脚,MOTOR步电机设置是5V。用于模拟进水状态、出水状态、洗涤状态。当处于进水状态、工作状态时,电机正转。出水状态时,电机反转。
void motor() //电机函数
{
LCD_Print(3,0,"xiwanzhong");
if(t_ding !=0)
{
m1=1;
m2=0;
}
if(t_ding ==0)
{
m1=0;
m2=0;
}
}
进水模块
出水模块电机与之相反即可。
void jinshui() //进水时m1 =1,m2=0;出水时 m1=0,m2=1 ,进水出水电机运行相反
{
LCD_Print(3,0,"jinshui");
if (zhineng)
{
m1 =1;
m2=0;
delay(10000); //进水10s
}
else if (jieneng)
{
m1 =1;
m2=0;
delay(8000); //进水8s
}
else if (chaokuai)
{
m1 =1;
m2=0;
delay(5000); //进水5s
}
else
{
m1 =1;
m2=0;
delay(3000);
}
m1=0;
m2=0;
c_num=0;
delay(2000); //进水结束,电机停止2s
}
3.5 KEY模块
这里我用switch语句来进行分别。按下按钮进行相对应的功能
void key_choose(void)
{ int button;
button =key_scan();
switch(button)
{
case 1:zhinengxi();zhineng =1;break;
case 2:jienengxi();jieneng=1;break;
case 3:chaokuaixi();chaokuai =1;break;
case 4:dingshixi();dingshi =1;break;
case 5:jia();break;
case 6:jian();break;
case 7:lcd_init();c_num=0;break;
}
}
3.6 main模块
在主函数这里,我给按钮模块加了一个if判断语句,当按下一个按键时,只能等那个按键实现完功能,才能继续按其他按钮。如果要强制中断其运行,可点击复位按钮,本人能力有限。
#include "ADC.h"
#include "LCD1602.h"
#include "DS18B20.h"
#include "STATUS.h"
#include "KEY.h"
int num =0;
void zhongduan() interrupt 3 //中断程序,每经过一次中断为50ms,20次就是一秒
{
TH1=(65536 -50000)/256;
TL1 =(65536-50000)%256;
num++;
if(num>=20)
{
t_ding--;
num=0;
if(t_ding<=0)
{
TR0 =0;
}
}
}
void main()
{
lcd_init(); //屏幕初始化
timer0_init(); //定时器初始化
m1 =0; //电机引脚m1,m2电平相同且为低,保证此时电机不转,因为两个引脚电压相同
m2 =0;
OVEN=1; //加热棒是低电平触发,因此给高电平,此时不加热
buzzer =1; //蜂鸣器是高电平触发,因此给低电平,此时不响
while(1)
{
v_zhoudu =adc0832(0); // 测浊度
p_zhoudu =v_zhoudu *100/255; //计算浊度百分比 因为是5v电压,放大20倍,方便转换
LCD_Print_num(2,12,p_zhoudu); //显示浊度
readtemp=ReadTemperature(); //获取温度
display(); //显示温度
if(c_num==0) //!c_num 用于来记录按键按下的次数。未按时为0,这个while循环成立,反之不成立
{
key_choose(); //模式按键函数 ,用以决定是何种洗涤模式
}
if(readtemp<30) //温度低于30时,启动加热
{
OVEN=0;
}
else{
OVEN=1;
}
while(zhineng) //智能洗时
{
zhinengxi();
}
while(jieneng) //节能洗时
{
jienengxi();
}
while(chaokuai) //超快洗
{
chaokuaixi();
}
while(dingshi) //定时洗
{
dingshixi();
}
}
}
写的不够好,但是我会继续努力的!!!