51单片机课设02

自动喂食机的课设

  1. 构思原理图的预前准备:
    ①24c02的资料:I2C的驱动分析;挂载的静态内存-EEPROM24C02
    ②8位7段的数码显示关的处理(管脚太多的处理):MAX7221的经典电路相关资料MAX7221的中文说明MAX7221驱动说明
    ③DS1302的相关:DS1302的典型电路及应用代码
    ④串口的应用:我可不可以理解成PC和MCU交流(加一个仿真接收和发送端)
    ⑤ 7个按键处理:按键消抖,矩阵开关
    ⑥继电器模块的处理:继电器经典电路
    ⑦蜂鸣器和LED提示灯的处理:蜂鸣器的了解更详细的蜂鸣器资料
    ⑧下载器:下载器的各种芯片;其中CH340芯片的下载器相关资料下载器和串口收发器的相关资料;我采用方法二;

  2. 原理图形成:
    单片机和外设电路:
    在这里插入图片描述

    下载器的原理图:
    

下载器原理图
可能少一部分MAX232的部分;
3. 写代码实现功能:

头文件

#ifndef __MAIN_H_
#define __MAIN_H_

#include <reg51.h>
#include <intrins.h>
#define NOP4() _nop_(),_nop_(),_nop_(),_nop_()

//AT24C02的addr
#define  Second      0x00      //秒
#define  Minute		 0x01      //分
#define  Hour        0x02      //时
#define  FEED_SUM_H  0x03      //喂食总量高数据  因为一个字节最多可以存255,故变化增加字节
#define  FEED_SUM_L  0x04      //喂食总量低数据
#define  ONE_FEED_H  0x05	   //一次喂食量高数据
#define  ONE_FEED_L  0x06	   //一次喂食量高数据

typedef  unsigned char u8;
typedef	 unsigned int  u16;
//MAX7221的PIN
sbit DIN = P1^0; //数据串出引脚
sbit CS  = P1^1; //片选端
sbit CLK = P1^2; //移位时钟端
 //DS1302的PIN
sbit RST  =P1^3;//RET,使能输入引脚,当读写时,置高位
sbit SCLK =P1^4;//SCLK,时钟信号
sbit IO   =P1^5;//IO     ,双向通信引脚,读写数据都是通过这个完成
//24C02B的PIN
sbit SCL  =P1^6;
sbit SDA  =P1^7;
//蜂鸣器的PIN
sbit Beep =P3^3;
//LED0的指示等,等效继电器  
sbit Relay=P3^4;
//74LS148的PIN
sbit A0   =P3^5;
sbit A1   =P3^6;
sbit A2   =P3^7;

//延时函数
void DelayMS(u16);
//DS1302的函数
void Ds1302_Init(u8*);  //初始化时间
void Get_ds1302_time(u8*); //获取时间函数
//MAX7221的函数
void Init_max7221();    //初始化MAX7221
void MAX7221_Run(u8 *a,u8 *format);   //运行数组,并选择显示格式
//AT24C02的函数
void I2cinit();  //I2C的初始化
void Write_addr_dat(u8 add,u8 dat);    //AT24C02写数据
u8 Read_add(u8 addr);      //AT24C02读数据
//74LS148的按键扫描
u8 Key_scan();
//蜂鸣器
void BEEP();
//写入AT24C02中
void Note_value(u8 a[7]);  //记录数据写入AT24C02中
//发送函数
void Trasmit_data(u8 *a);    //串口发送函数
#endif

main文件(main.c)

#include "main.h"

u8 data_arry[7]={0,59,6,3,135,1,45};    //初始化的时间  900和300默认值
u8 data_str[]="12:30-300\r\n";
volatile u8 LED_mode=0;

Timer0初始化
void Timer0_init()														
{
	//方式一
	TMOD = 0x01;														
	TH0 = (65536 - 2000) / 256;											
	TL0 = (65536 - 2000) % 256;
	TR0 = 1;															  
	ET0 = 1;	                                                          								   
	EA  = 1;	
}

//装换成data_str格式,并发送
void Conver_data_str(u16 one)
{
	Get_ds1302_time(data_arry);  //获取最新时间
	data_str[0]=data_arry[2]/10+'0';
	data_str[1]=data_arry[2]%10+'0';
	data_str[3]=data_arry[1]/10+'0';
	data_str[4]=data_arry[1]%10+'0';
	data_str[6]=one/100 +'0';
	data_str[7]=one/10%10 +'0';
	data_str[8]=one%10 +'0';
	
	Trasmit_data(data_str);  //发送PC或者移动服务端
}

void main(void)
{
	//变量设置
	u8 key_value=0;
	u16 one_feed=0,feed_sum=0;
	
	//初始化外设设备
	I2cinit();
	Init_max7221();	
	Ds1302_Init(data_arry);
	
	//继电器和蜂鸣器初始化
	Relay=0,Beep=0;
	
	//写入初始化数据、定时器初始化、发送串口示范语句
	Note_value(data_arry);  
	Timer0_init();  
	Trasmit_data(data_str);

	//缓冲0.2秒
	DelayMS(200);
	while(1)	
	{		
		key_value=Key_scan();
		if(key_value !=0)
		{
			TR0=0;   //关掉定时器
			switch(key_value){
				case 1:{      //手动喂食
						data_arry[5]=Read_add(ONE_FEED_H);
						data_arry[6]=Read_add(ONE_FEED_L);
						one_feed=data_arry[5]*255+data_arry[6];
						//粮食总量不足的处理
						if(feed_sum < one_feed)
						{
							one_feed=feed_sum;   //剩下的总量(真实减去的量)
							data_arry[3]=0/255;
							data_arry[4]=0%255;
							data_arry[5]=one_feed/255;
							data_arry[6]=one_feed%255;
						}
						Conver_data_str(one_feed);  //转换格式并发送
						LED_mode=1;  
						}break;
				case 2:{  //喂食量设置键
						data_arry[5]=Read_add(ONE_FEED_H);  
						data_arry[6]=Read_add(ONE_FEED_L);
						one_feed=data_arry[5]*255+data_arry[6];   //读出数据
						LED_mode=2;
						}break;
				case 3:{  //切换显示系统时间 确定键
						Write_addr_dat(ONE_FEED_H,one_feed/255),DelayMS(10);
						Write_addr_dat(ONE_FEED_L,one_feed%255),DelayMS(10);
						one_feed=0;
						LED_mode=0; 
						}break;
				case 4:{    //查看喂食总量
						data_arry[3]=Read_add(FEED_SUM_H);
						data_arry[4]=Read_add(FEED_SUM_L);
						LED_mode=3;
						}break;
				case 5:{   //加
						 one_feed=one_feed+50;
						 data_arry[5]=one_feed/255;  
						 data_arry[6]=one_feed%255;
						}break;
				case 6:{  //减
						 one_feed=one_feed-50;
						 data_arry[5]=one_feed/255;
						 data_arry[6]=one_feed%255;
						}break;
				case 7:{   //补粮键恢复到初始值  初始量
						data_arry[3]=900/255;
						data_arry[4]=900%255;
						LED_mode=3;
						}break;
			}
			TR0=1;
		}
		
		//补粮提示
		feed_sum=data_arry[3]*255+data_arry[4];
		if(feed_sum <= 100)	 BEEP();   //报警
		
		//每时每刻记录着
		Note_value(data_arry);
		
		//缓冲时间
		DelayMS(500);  
	}
}

void Time0_inter() interrupt 1{
	static u16 time;
	TH0 = (65536 - 2000)/256;											  
	TL0 = (65536 - 2000)%256;											  
	time++;
	if(time >= 500)		//1秒运行一次
	{
		time = 0;
		MAX7221_Run(data_arry,&LED_mode);  //数码显示
	}
}

相关的驱动函数

#include "main.h"

void DelayMS(u16 x){
	u8 i;
	while(x--)	for(i = 0;i < 120;i++);
}
/************DS1302的相关驱动函数**********************************/
static void Write_DS1302_onebyte(u8 dat){
	u8 i;
	for(i=0;i<8;i++)
	{
		IO =(bit)(dat&0x01);    //取最低位的数据
		SCLK=0,_nop_(),_nop_(),SCLK=1;    //SCLK上升沿写入数据
		dat=dat >>1;   //为下一次做准备
	}
}
static u8 Read_DS1302_onebyte(){
	u8 i,dat=0;
	for(i=0;i<8;i++)
	{
		SCLK=1,_nop_(),_nop_(),SCLK=0;  //下降沿读出数据
		dat=dat >>1;             //由低读到高
		if(IO)	dat=dat |0x80;  //如果读出的数据为1,将1取出,放在高位
	}
	return dat;
}
void Write_DS1302_add_dat(u8 addr,u8 dat){
	//DS1302初始化管脚
	RST=0,SCLK=0,RST=1;  //SCLK为0时,RST才可以写成1   ----启动
	Write_DS1302_onebyte(addr);   //先写地址 再写数据
	Write_DS1302_onebyte(dat);
	SCLK=1,RST=1;    //将时钟放置为已知状态,静止数据传输
}
static u8 Read_DS1302_addr(u8 addr){
	u8 dat;
	RST=0,SCLK=0,RST=1;  //SCLK为0时,RST才可以写成1   ----启动
	Write_DS1302_onebyte(addr);   //写地址
	dat=Read_DS1302_onebyte();    //读数据
	SCLK=1,RST=1;    //将时钟放置为已知状态,静止数据传输
	return dat;
}
static u8 DEC_BCD_conv(u8 x){  //十进制转换成BCD8421数据
	u8 bcd=x%10;  //取余,低四位
	x =x /10;     //取整
	x =x <<4;	  //低四位变高四位
	bcd=bcd | x;
	return bcd;
}
static u8 BCD_DEC_conv(u8 x){
	u8 dec=0x0f&x; //保留低四位
	x=x>>4;        //高位变低四位
	dec=dec + x*10;
	return dec;
}

//获得时间信息函数
void Get_ds1302_time(u8 arry[3]){    //放在DataTime数组里
	u8 i,temp;
	for(i=0;i<3;i++)   //只读时分秒
	{
		temp=Read_DS1302_addr(0x81+i*2);  //读到数据
		arry[i]=BCD_DEC_conv(temp); //BCD转换DEC数据,
	}
}

//DS1302初始化设置时间
void Ds1302_Init(u8 time[3])
{
    u8 i,temp;
    Write_DS1302_add_dat(0x8e,0x00);      //禁止写保护,就是关闭写保护功能
    for (i=0; i<3; i++)//写入7个字节的时钟信号:分秒时日月周年
    {
		temp=DEC_BCD_conv(time[i]);
        Write_DS1302_add_dat(0x80+i*2,temp); //只写时分秒
    }
     Write_DS1302_add_dat(0x8e,0x80);      //打开写保护功能
}

/***************MAX7221的相关驱动*******************************/
static void write_data(u8 addr,u8 dat){
	u8 i;
	CS = 0;       
	for(i = 0;i < 8;i++){
		  CLK = 0,addr <<= 1,DIN = CY,CLK = 1;      
		  _nop_(),_nop_(),CLK = 0;      
     }
    for(i = 0;i < 8;i++){
		  CLK = 0,dat <<= 1,DIN = CY,CLK = 1;      
		  _nop_(),_nop_(),CLK = 0;      
     }
	 CS = 1;      //CS上升沿,数据锁存
}

//初始化函数
void Init_max7221(){
	 write_data(0x09,0xff);    
	 write_data(0x0a,0x07);    
	 write_data(0x0b,0x07);    
	 write_data(0x0c,0x01);    
	 write_data(0x0f,0x00);    
}
//MAX7221的运行
void MAX7221_Run(u8 *a,u8 *format){
	u8 i,DSY[8];
	static u16 t1,t2;
	
	if(*format == 0)
	{
		Get_ds1302_time(a);   //获取最新时间
		DSY[0]=a[2]/10,DSY[1]=a[2]%10;  //时
		DSY[5]=10;   // '-'
		DSY[3]=a[1]/10,DSY[4]=a[1]%10;  //分
		DSY[2]=10;     
		DSY[6]=a[0]/10,DSY[7]=a[0]%10;  //秒
	}
	else if(*format == 1)   //手动喂食
	{
		if(Relay == 0)  //第一次进入手动喂食
		{
			t1=a[5]*255 + a[6];   //保存上一次的喂食量
			t2=a[5]*255 + a[6];  //第一次一样
			Relay=1;  //开继电器
		}
		DSY[0]=t1/100,DSY[1]=t1/10%10,DSY[2]=t1%10;     //预设定的值  
		DSY[3]=15,DSY[4]=15;	// 全灭 
		DSY[5]=t2/100,DSY[6]=t2/10%10,DSY[7]=t2%10;     //等待变化值
		if(t2 != 0)   //动态变化值
		{
			t2 = t2 - 50;
		}
		else
		{
			t2=a[3]*255+a[4]-t1;   //总量减去预设量
			
			//写入数据数组-回到while(1)中写入EEPROM中
			a[3]=t2/255;  
			a[4]=t2%255;
			
			//关掉继电器
			Relay=0; 
			t1=0;
			t2=0;
			
			//切换到系统时间界面
			*format=0;   
		}
	}
	else if(*format == 2)  ///预设定的一次喂食量
	{
		t1=a[5]*255 + a[6];
		DSY[0]=t1/100,DSY[1]=t1/10%10,DSY[2]=t1%10;     //预设定的显示 
		DSY[3]=15,DSY[4]=15,DSY[5]=15,DSY[6]=15,DSY[7]=15;	// 全灭		
	}
	else if(*format == 3) //查看喂食总量
	{
		t1=a[3]*255 + a[4];
		DSY[0]=t1/100,DSY[1]=t1/10%10,DSY[2]=t1%10;     //预设定的显示 
		DSY[3]=10,DSY[4]=10,DSY[5]=10,DSY[6]=10,DSY[7]=10;	// 全灭	
	}
	else
	{
		for(i=0;i<8;i++)	DSY[i]=8;    //参数不对,全部是8
	}
	//MAX7221显示
	for(i=0;i<8;i++)	write_data(i+1,DSY[i]); 
}

/************************AT24C02的I2C协议***********************/
void I2cinit()  //I2C的初始化
{
	SDA = 1,NOP4();SCL = 1,NOP4();
}
static void Start()    //I2C启动
{
 	SDA=1;SCL=1;NOP4();SDA=0;NOP4();SCL=0;
}

static void Stop()    //I2C停止
{
 	SDA=0;SCL=0;NOP4();SCL=1;NOP4();SDA=1;
}
static void RACK()      //应答
{
 	SDA=1;NOP4();SCL=1;NOP4();SCL=0;
}
static void NO_ACK()    //不应答
{
 	SDA=1;SCL=1;NOP4();SCL=0;SDA=0;
}

static void Write_A_Byte(u8 b)     //写一个字节
{
 	u8 i;
	for(i=0;i<8;i++)
	{
	 	b<<=1;SDA=CY;_nop_();SCL=1;NOP4();SCL=0;
	}
	RACK();   //应答
}

static u8 Receive_A_Byte()     //读一个字节
{
 	u8 i,d;
	for(i=0;i<8;i++)
	{
	 	SCL=1;d<<=1;d|=SDA;SCL=0;
	}
	return d;
}

void Write_addr_dat(u8 add,u8 dat)  
{
 	Start();
	Write_A_Byte(0xa0);      
	Write_A_Byte(add);       
	Write_A_Byte(dat);       
	Stop();
	DelayMS(10);
}

u8 Read_add(u8 addr)    
{
	u8 d=0;
 	Start();
	Write_A_Byte(0xa0); 
	Write_A_Byte(addr); 
	Stop();
	//读数据
	Start();
	Write_A_Byte(0xa1);      
	d=Receive_A_Byte();      
	NO_ACK();     
	Stop();
	return d;		
}
u8 Key_scan(void){
	u8 ret=0;
	if(A0 == 0 || A1 == 0 || A2 == 0)   //有按键变动
	{
		DelayMS(5);  //消抖完后取出稳定且正确的值
		if(!A0|!A1|!A2)   //和上面等效的写法
		{
			ret+= A2==0?4:0;
			ret+= A1==0?2:0;
			ret+= A0==0?1:0;
			while(!(A0==0 || A1 == 0 || A2 == 0));  //等待按键松开 防止重复的按
		}
	}
	return ret;
}
void BEEP()   //放屁声
{
	u8 i,j;
	Beep=0;
	for(i=0;i<120;i++)
	{
		for(j=0;j<10;j++)	Beep=~Beep,DelayMS(1);
		DelayMS(10);
	}
}
void Note_value(u8 a[7])  //记录数据写入AT24C02中
{
	u8 i;
	for(i=0;i<7;i++)	Write_addr_dat(i,a[i]),DelayMS(10); 
}

void Trasmit_data(u8 *a){   //串口发送
	u8 *str=a;
	while(*str)
	{
		SBUF=*str;
		while(TI==0);  //等待发送完成
		TI=0;  //软件清除
		str++;  //字符偏移
		DelayMS(5); //缓冲5ms
	}
}

原理图有些改动(只要的主干),电阻、电容、都要和上面一样典型的接法;这个里面还有MAX212需要接上,因为和PC或者其他焦虑可能需要
在这里插入图片描述
搞出的现象一部分展示
在这里插入图片描述
在这里插入图片描述
4、干完了才发现,已经忘记了那么多!
51中断的相关信息:中断函数和51寄存器的总结
可能有用:在这里插入图片描述

最后有一个问题是Pertues仿真时,虚拟终端显示有问题,不知道咋弄!完整的代码下载链接:单片机相关课设

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值