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