单片机----第八届蓝桥杯国赛

第八届蓝桥杯国赛

我的解答

iic

iic.h
#ifndef __IIC_H__
#define __IIC_H__

void IIC_Start(void); 
void IIC_Stop(void);  
bit IIC_WaitAck(void);  
void IIC_SendAck(bit ackbit); 
void IIC_SendByte(unsigned char byt); 
unsigned char IIC_RecByte(void); 
void AT24C02_Write(unsigned char Address,Data);
unsigned char AT24C02_Read(unsigned char Address);
void PCF8591_Write(unsigned char Data);
void Delay(unsigned char x);
#endif
iic.c
/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "reg52.h"
#include "intrins.h"

#define DELAY_TIME 5

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}
//总线启动条件
void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}

//总线停止条件
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

//发送应答
void IIC_SendAck(bit ackbit)
{
    SCL = 0;
    SDA = ackbit;  					// 0:应答,1:非应答
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

//等待应答
bit IIC_WaitAck(void)
{
    bit ackbit;
	
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

    for(i=0; i<8; i++)
    {
        SCL  = 0;
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;
        byt <<= 1;
        IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;  
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)
    {   
    	SCL = 1;
	IIC_Delay(DELAY_TIME);
	da <<= 1;
	if(SDA) da |= 1;
	SCL = 0;
	IIC_Delay(DELAY_TIME);
    }
    return da;    
}
void AT24C02_Write(unsigned char Address,Data)
{
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(Address);
	IIC_WaitAck();
	IIC_SendByte(Data);
	IIC_WaitAck();
	IIC_Stop();
}
void PCF8591_Write(unsigned char Data)
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(0x40);
	IIC_WaitAck();
	IIC_SendByte(Data);
	IIC_WaitAck();
	IIC_Stop();
}
unsigned char AT24C02_Read(unsigned char Address)
{
	unsigned char Data;
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(Address);
	IIC_WaitAck();
	
	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	Data=IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	return Data;
}
void Delay(unsigned char x)		//@12.000MHz
{
	unsigned char i, j;

	while(x--)
	{
		i = 12;
		j = 169;
		do
		{
			while (--j);
		} while (--i);		
	}

}

main

main.h
#include <STC15F2K60S2.H>
#include "iic.h"
typedef unsigned char u8;
typedef unsigned int u16;
/*----------超声波----------*/
sbit A1=P1^0;
sbit B1=P1^1;
u16 time,wave_count,length;

void Start();
void Pros();
void delays();
void Wave_Rec();
/*----------矩阵键盘----------*/
sbit L1=P3^0;
sbit L2=P3^1;
sbit L3=P3^2;
sbit L4=P3^3;

sbit C1=P4^4;
sbit C2=P4^2;
sbit C3=P3^5;
sbit C4=P3^4;

u8 KN,key_count;
u8 GetState();
void Key_Loop();

/*----------数码管----------*/
u8 code ledguan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
u8 wei_xuan,disp[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};


void Timer0Init(void);
void Timer1Init(void);

u8 Mode=0;//0:测距 1:数据回显 2:参数设置
u8 last_length;//上一次测得的距离
bit ifirst;//判断是否刚上电,用来解决一个Bug:第一次上电的之后测距,测距完了之后,显示的前一次的测距数据一直是0(因为length=0)
bit Mode0;//Mode=0:0--误操作 1--加操作
bit ceju=0;//是否测距
u8 length_s[10];//十组测距数据
u8 i;//用来循环
u8 length_s_count;//测距的次数计数

bit an_5;//按下S5的次数,用来判断测距显示模式
bit an_9;//同理
bit shuaxin;//是否刷新
u8 sjbh;//数据编号
u8 S0=20,S0_Set;//阈值距离以及设定阈值距离

bit LED1,L7,L8;//LED1 LED7 LED8
bit shanshuo;//LED1是否该闪烁
u8 L1_shanshuo_count;//亮+灭次数计数
u8 L1_count;//定时器时间计时
void main()
{
	/*----------初始化----------*/
	P2=0x80;P0=0xff;P2=0x00;//LED灯熄灭
	ET0=0;//关闭定时器
	for(i=0;i<10;i++)
		length_s[i]=AT24C02_Read(i);//从EEPROM中读取十组测距数据
	length_s_count=AT24C02_Read(10);//读取测距次数
	if(length_s_count)last_length=length_s[length_s_count-1];//如果之前有数据,最后一个就是上一次测距的数据
	ET0=1;//开启定时器
	Timer0Init();Timer1Init();//定时器初始化
	//数码管初始化
	disp[0]=ledguan[0];
	disp[1]=0xff;
	disp[2]=ledguan[last_length/100%10];
	disp[3]=ledguan[last_length/10%10];
	disp[4]=ledguan[last_length%10];
	disp[5]=ledguan[length/100%10];
	disp[6]=ledguan[length/10%10];
	disp[7]=ledguan[length%10];
	while(1)
	{
		if(KN)
		{
			//按下S4
			if(KN==4)
			{
				L7=0;//LED7 8关闭
				L8=0;
				an_5=0;//按下5 9的次数归零
				an_9=0;
				Mode=0;//模式0
				Mode0=0;//模式0 的模式0
				ceju=1;//开始测距
				KN=0;//键值归零,实际上把我删掉的Key()加上去就不用KN归零了	
			}
			//按下S5
			if(KN==5)
			{
				L8=0;//LED 8关闭
				an_9=0;//按下9的次数归零
				KN=0;//键值归零,实际上把我删掉的Key()加上去就不用KN归零了	
				an_5=!an_5;//按下S5的次数取反,实际上只是用来判断状态
				//第一次按下 
				if(an_5)
				{
					L7=1;//LED7开启
					Mode=1;//模式1
					shuaxin=1;//刷新
				}
				//第二次按下 
				else
				{
					L7=0;//LED7开启
					Mode=0;//模式0
					shuaxin=1;//刷新
				}
			}
			//按下S9
			if(KN==9)
			{
				S0_Set=S0;//阈值设置更新,用于调整阈值,如果直接修改S0的话,会出现一个Bug:调整的时候会触发PCF8591的输出条件,所以应该是有一个变量用于调整,完成之后更新阈值
				an_5=0;//按下5的次数归零
				KN=0;
				shuaxin=1;//刷新
				an_9=!an_9;//按下S9的次数取反,实际上只是用来判断状态
				//第一次按下 
				if(an_9)
				{
					L8=1;//LED8开启
					L7=0;//LED7关闭
					Mode=2;//模式2
				}
				else
				{
					L7=0;//LED7关闭
					L8=0;//LED8关闭
					S0=S0_Set;//更新阈值
					AT24C02_Write(11,S0);//写入EEPROM
					Delay(5);
					Mode=0;//模式0
				}
			}
			//按下S8
			if(KN==8)
			{
				KN=0;
				//模式0下
				if(Mode==0)
				{
					//模式0的模式取反
					Mode0=!Mode0;
					//按下S8第一次 1 3 5 加操作
					if(Mode0)
					{
						disp[0]=ledguan[1];
						disp[2]=ledguan[(last_length+length)/100%10];
						disp[3]=ledguan[(last_length+length)/10%10];
						disp[4]=ledguan[(last_length+length)%10];
					}
					//按下S8第二次0 2 4 6 无操作
					else
					{
						disp[0]=ledguan[0];
						disp[2]=ledguan[last_length/100%10];
						disp[3]=ledguan[last_length/10%10];
						disp[4]=ledguan[last_length%10];
					}
				}
				//模式1下
				if(Mode==1)
				{
					sjbh++;//编号+1
					sjbh%=10;//0 1 2 3 4 5 6 7 8 9
					shuaxin=1;//刷新
				}
				//模式2下
				if(Mode==2)
				{
					S0_Set+=10;//刷新阈值
					if(S0_Set==100)S0_Set=0; //0 10 20 30 40 50 60 70 80 90
					shuaxin=1;//刷新
				}
			}
		}
		//LED1闪烁
		if(L1_count==200)
		{
			L1_count=0;
			LED1=!LED1;
			L1_shanshuo_count++;
			if(L1_shanshuo_count==20)
			{
				L1_shanshuo_count=0;
				shanshuo=0;
				L1_count=0;
				LED1=0;
			}
		}
		//模式0下
		if(!Mode)
		{
			//是否测距
			if(ceju)
			{
						disp[0]=ledguan[0];
						disp[1]=0xff;
						disp[2]=ledguan[last_length/100%10];
						disp[3]=ledguan[last_length/10%10];
						disp[4]=ledguan[last_length%10];
						disp[5]=ledguan[length/100%10];
						disp[6]=ledguan[length/10%10];
						disp[7]=ledguan[length%10];	
				//测距
					if(wave_count==350)
					{
						ceju=0;//避免一直测距
						wave_count=0;
						if(ifirst)last_length=length;//判断是不是刚上电
						else ifirst=1;
						Wave_Rec();
						shanshuo=1;//刚完成测距,闪烁
						if(length_s_count<10)//测距数据小于10时 往后排
						{
							length_s[length_s_count]=length;
							length_s_count++;
						}
						else//否则 循环取代
						{
							for(i=0;i<9;i++)
							{
								length_s[i]=length_s[i+1];
							}
							length_s[9]=length;
						}
						//更新测距数据
						for(i=0;i<10;i++)
						{
							AT24C02_Write(i,length_s[i]);
							Delay(5);
						}
							AT24C02_Write(10,length_s_count);
							Delay(5);
						
						disp[0]=ledguan[0];
						disp[1]=0xff;
						disp[2]=ledguan[last_length/100%10];
						disp[3]=ledguan[last_length/10%10];
						disp[4]=ledguan[last_length%10];
						disp[5]=ledguan[length/100%10];
						disp[6]=ledguan[length/10%10];
						disp[7]=ledguan[length%10];		
					}	
			}
				else if(shuaxin)
				{
					shuaxin=0;
					disp[0]=ledguan[0];
					disp[1]=0xff;
					disp[2]=ledguan[last_length/100%10];
					disp[3]=ledguan[last_length/10%10];
					disp[4]=ledguan[last_length%10];
					disp[5]=ledguan[length/100%10];
					disp[6]=ledguan[length/10%10];
					disp[7]=ledguan[length%10];		
				}
		}
		//模式1下
		if(Mode==1)
		{
			//如果刷新
			if(shuaxin)
			{
				shuaxin=0;
				disp[0]=ledguan[(sjbh+1)/10%10];
				disp[1]=ledguan[(sjbh+1)%10];
				disp[2]=0xff;
				disp[3]=0xff;
				disp[4]=0xff;
				disp[5]=ledguan[length_s[sjbh]/100%10];
				disp[6]=ledguan[length_s[sjbh]/10%10];
				disp[7]=ledguan[length_s[sjbh]%10];	
			}
		}
		//模式2下
		if(Mode==2)
		{
			//如果刷新
			if(shuaxin)
			{
				shuaxin=0;
				disp[0]=0x8e;
				disp[1]=0xff;
				disp[2]=0xff;
				disp[3]=0xff;
				disp[4]=0xff;
				disp[5]=0xff;
				disp[6]=ledguan[S0_Set/10%10];
				disp[7]=ledguan[S0_Set%10];
			}
		}
		/*----------矩阵键盘----------*/
		if(key_count==20)
		{
			key_count=0;
			Key_Loop();
		}
		/*----------PCF8591----------*/
		if(length<=S0)
			PCF8591_Write(0);
		else
			PCF8591_Write((u8)((length-S0)*1.02));
	}
}
void Start()
{
	u8 m;
	A1=0;
	for(m=0;m<10;m++)
	{
		A1^=1;
		delays();
	}
}
void Pros()
{
	if(TF1)
	{
		TF1=0;
		length=99;
	}
	else
	{
		time=(TH1<<8)|TL1;
		length=time*0.017;
		length/=12;
	}
	TH1=0;
	TL1=0;
}
void delays()
{
	u8 i;
	for(i=0;i<12;i++);
}
void Wave_Rec()
{
	TR1=0;
	Start();
	TR1=1;
	while(B1&&!TF1);
	TR1=0;
	Pros();
}
u8 GetState()
{
	u8 State=0;
	L1=1;L2=1;L3=0;L4=1;
	if(C1==0)State=5;
	if(C2==0)State=9;
	L1=1;L2=1;L3=1;L4=0;
	if(C1==0)State=4;
	if(C2==0)State=8;
	return State;
}
void Key_Loop()
{
	static u8 Now,Last;
	Last=Now;
	Now=GetState();
	if(!Now&&Last)
		KN=Last;
}
void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR = 0xc0;		//定时器时钟1T模式
	TMOD = 0x00;		//设置定时器模式
	TL0 = 0x20;		//设置定时初值
	TH0 = 0xD1;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	EA=1;
	ET0=1;
}
void Timer1Init(void)		//1毫秒@12.000MHz
{
	AUXR = 0xc0;		//定时器时钟1T模式
	TMOD = 0x00;		//设置定时器模式
	TL1 = 0x00;		//设置定时初值
	TH1 = 0x00;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 0;		//定时器1开始计时
	
}

void Timer0_R() interrupt 1
{
	static u8 smg_count,led_count;
	smg_count++;key_count++;led_count++;
	if(!Mode)
	{
		if(shanshuo)
			L1_count++;		
	}
	if(led_count==20)
	{
		led_count=0;
		P2=0x80;P0=~(((u8)L8<<7)|((u8)L7<<6)|(u8)(LED1))|0X3E;
	}
	if(ceju&&!Mode)wave_count++;
	else wave_count=0;
	if(smg_count==2)
	{
		smg_count=0;
		P2=0xc0;P0=1<<wei_xuan;P2=0x00;
		P2=0xe0;P0=0x00;P2=0x00;
		P2=0xe0;P0=disp[wei_xuan++];P2=0x00;
		wei_xuan%=8;
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值