蓝桥杯单片机国赛12届-超声波物位计

时长:三个半小时

main.c 

#include <STC15F2K60S2.H>
#include <intrins.h>
#include "iic.h"
#include "ds1302.h"

//BCD和十进制转换
#define BCDToint(bcd)  (bcd/16*10)+(bcd%16)
#define intToBCD(in)  (in/10*16)+(in%10)


//LED共用体定义
typedef struct 
{
	unsigned char b1:1;
	unsigned char b2:1;
	unsigned char b3:1;
	unsigned char b4:1;
	unsigned char b5:1;
	unsigned char b6:1;
	unsigned char b7:1;
	unsigned char b8:1;
}Bit;

typedef union
{
	unsigned char Hex;
	Bit B;
}HexToBit;

HexToBit led_control;


//数码管
code unsigned char Seg_Table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char smg[8];

//时间
unsigned char time[3]={0x01,0x20,0x21};  //BCD码 秒分时

//采集时间参数  (2,3,5,7,9)
unsigned char timemode_dat[5]={2,3,5,7,9}; //五个值
unsigned char now_tm=0; //当前采集时间参数 索引值

//距离参数设置
unsigned char set_dist=20; //初始化20

//测量距离最大值 最小值 均值
unsigned int Da,Jun;
unsigned int Xiao=9999; //最小值要初始化为最大


//基础设备操作
void vDevice(unsigned char p2dat,unsigned char p0dat)
{
	P0=p0dat;
	P2=(P2&0x1f)|p2dat;
	P2=P2&0x1f;
}

//----------------定时器----------------------
//定时器1
void Timer1Init(void)		//65535微秒@12.000MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x00;		//设置定时初值
	TH1 = 0x00;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 0;		//定时器1开始计时
}

//定时器2
void Timer2Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x04;		//定时器时钟1T模式
	T2L = 0x20;		//设置定时初值
	T2H = 0xD1;		//设置定时初值
	AUXR |= 0x10;		//定时器2开始计时
	
	EA=1;
	IE2|=0x04;
}


//-----------------读取时间-------------------
unsigned char sec,min,hour;
unsigned char cnt_time; 
void vRead_time()
{
	if(cnt_time>=100)
	{
		cnt_time=0;
		sec = BCDToint(Read_Ds1302_Byte(0x81));
		min = BCDToint(Read_Ds1302_Byte(0x83));
		hour = BCDToint(Read_Ds1302_Byte(0x85));
	}
}


//-------------超声波-----------------------
//发送方波
void vSend_wave()
{
	unsigned char i,j;
	for(i=0;i<8;i++)
	{
		P10=1;
		for(j=0;j<25;j++)
		_nop_();
		P10=0;
		for(j=0;j<25;j++)
		_nop_();
	}
}

//接收方波
unsigned char vReceive_wave()
{
	unsigned int dat;
	vSend_wave();
	TH1=0;TL1=0;
	TR1=1;
	while((P11==1)&&(TF1==0));
	TR1=0; //停止计时
	if(TF1==1)//超出测距范围
	{
		TF1=0;
		dat=999;
	}
	else
	{
		dat = ((TH1<<8)|TL1)*0.017;
	}
	return dat;
}

//测距
unsigned int dist;
unsigned int cnt_dist;  
unsigned int cnt_num=0;//num计算测量次数
unsigned char alarm_num=0;//测量数据在距离参数附件的次数
bit flag_L5=0; //LED6的标志位
void vGet_dist()
{
	if(cnt_dist>=100)
	{
		cnt_dist=0;
		dist = vReceive_wave();	
	}
	if(Da<dist) Da=dist;
	if(Xiao>dist) Xiao=dist;
	Jun = ((dist*10+Jun*cnt_num))/(cnt_num+1); //乘10倍,小数点后一位
	
	if((dist<(set_dist+5))&&(dist>(set_dist-5))) //测量数据在距离参数“附近”
	{
		alarm_num++;
		if(alarm_num>=3) flag_L5=1;
	}
	else  //不在参数附近了 就把统计次数清零,且把标志位置0关闭led5
	{
		alarm_num=0;
		flag_L5 = 0; 
	}
}

unsigned char mode_dist=1;  //测距模式 0定时  1触发
unsigned int cnt_1s;  //防止在1s内多次测量
bit flag_dist=0; //采集电压标志位,触发模式
void Distance()
{
	if(mode_dist==0) //定时模式
	{
		if(sec%timemode_dat[now_tm]==0)  //秒能够整除时间参数时 更新距离
		{
			flag_dist=1;
			if(cnt_1s>=1000) 
			{
				cnt_1s=0;
				flag_dist=0;
				vGet_dist();	
			}
		}
		
	}
	else if(mode_dist==1)  //触发模式
	{
		if(flag_dist==1)  //触发了就采集一次距离
		{
			flag_dist=0;  
			vGet_dist();	
			cnt_num++;
		}
	}
}


//---------------RD1光敏电阻------------------
unsigned char rd1_dat, pre_rd1;
unsigned char cnt_rd1;
void vRead_rd1()
{
	if(cnt_rd1>=100)
	{
		cnt_rd1=0;
		rd1_dat = ADC_Get();  //读取电压
	}
	if((pre_rd1>150)&&(rd1_dat<100))  //之前的大于150为环境光,现在小于100为遮挡
	{
		flag_dist=1; //下降沿打开测距标志
		pre_rd1=rd1_dat;
	}
	if((pre_rd1<100)&&(rd1_dat>150))  //变回环境光了 要更改pre,以便下次判断电压下降沿
	{
		pre_rd1=rd1_dat;
	}
}



//---------------数码管---------------------
//数码管操作函数
unsigned char mode_1=1;   //数据显示和参数显示界面
unsigned char mode_2=1;  //子模式  数据显示:3个  参数设置:2个
unsigned char mode_3=2;  //数据显示->数据记录->显示最大值最小值还是均值
void vSMG_Work()
{
	if(mode_1==0) //数据显示
	{
		if(mode_2==0)//时间数据
		{
			smg[0]=Seg_Table[hour/10];
			smg[1]=Seg_Table[hour%10];
			smg[2]=0xbf;
			smg[3]=Seg_Table[min/10];
			smg[4]=Seg_Table[min%10];
			smg[5]=0xbf;
			smg[6]=Seg_Table[sec/10];
			smg[7]=Seg_Table[sec%10];
		}		
		else if(mode_2==1) //距离数据
		{
			smg[0]=0xc7;
			if(mode_dist==0) smg[1]=0x8e;  //定时模式为F 触发模式为C
			if(mode_dist==1) smg[1]=0xc6;
			smg[2]=0xff;
			smg[3]=0xff;
			smg[4]=0xff;
			if(dist<100)
			{
				smg[5]=0xff;
			}
			else
			{
				smg[5]=Seg_Table[dist/100];
			}
			smg[6]=Seg_Table[dist/10%10];
			smg[7]=Seg_Table[dist%10];
		}
		else if(mode_2==2) //数据记录
		{
			if(mode_3==0) //最大值
			{
				smg[0]=0x89;
				smg[1]=0xfe;
				smg[2]=0xff;
				smg[3]=0xff;
				if(Da<1000) smg[4]=0xff;
				else smg[4]=Seg_Table[Da/1000];
				
				if(Da<100) smg[5]=0xff;
				else smg[5]=Seg_Table[Da/100%10];
				
				if(Da<10) smg[6]=0xff;
				else smg[6]=Seg_Table[Da/10%10];
				
				smg[7]=Seg_Table[Da%10];
			}
			
			else if(mode_3==1)//平均值
			{
				smg[0]=0x89;
				smg[1]=0xbf;
				smg[2]=0xff;
				smg[3]=0xff;
				if(Jun<1000) smg[4]=0xff;
				else smg[4]=Seg_Table[Jun/1000];
				
				if(Jun<100) smg[5]=0xff;
				else smg[5]=Seg_Table[Jun/100%10];
				
				if(Jun<10) smg[6]=0xff;
				else smg[6]=Seg_Table[Jun/10%10]&0x7f;
				
				smg[7]=Seg_Table[Jun%10];
			}		
			
			else if(mode_3==2)//最小值
			{
				smg[0]=0x89;
				smg[1]=0xf7;
				smg[2]=0xff;
				smg[3]=0xff;
				if(Xiao<1000) smg[4]=0xff;
				else smg[4]=Seg_Table[Xiao/1000];
				
				if(Xiao<100) smg[5]=0xff;
				else smg[5]=Seg_Table[Xiao/100%10];
				
				if(Xiao<10) smg[6]=0xff;
				else smg[6]=Seg_Table[Xiao/10%10];
				
				smg[7]=Seg_Table[Xiao%10];
			}		
		}
	}
	
	if(mode_1==1)// 参数设置
	{
		if(mode_2==0) //采集时间设置
		{
			smg[0]=0x8c;
			smg[1]=Seg_Table[1];
			smg[2]=0xff;
			smg[3]=0xff;
			smg[4]=0xff;
			smg[5]=0xff;
			smg[6]=Seg_Table[timemode_dat[now_tm]/10];
			smg[7]=Seg_Table[timemode_dat[now_tm]%10];
		}		
		else if(mode_2==1) //距离参数设置
		{
			smg[0]=0x8c;
			smg[1]=Seg_Table[2];
			smg[2]=0xff;
			smg[3]=0xff;
			smg[4]=0xff;
			smg[5]=0xff;
			smg[6]=Seg_Table[set_dist/10];
			smg[7]=Seg_Table[set_dist%10];
		}
	}
	
	
}

//数码管显示
void vSMG_Show()
{
	static unsigned char i;
	vDevice(0xc0,0x00);
	vDevice(0xe0,smg[i]);
	vDevice(0xc0,0x01<<i);
	i=(i+1)%8;
}


//-----------------------KBD按键-----------------------
//KBD按键扫描
unsigned char KBD_Scan()
{
	unsigned char key_io=0xff;
	P32=0;P33=0;
	P42=1;P44=1;
	if(P44==0) key_io=0x70;  //按下的是低电平
	if(P42==0) key_io=0xb0;
	
	P32=1;P33=1;
	P42=0;P44=0;
	if(P33==0) key_io=key_io|0x07;
	if(P32==0) key_io=key_io|0x0b;
	
	return key_io;
}

//三行按键
unsigned char Trg,Cont;
void Three_Key()
{
	unsigned char kio;
	unsigned char ReadData;
	kio = KBD_Scan();
	
	ReadData = kio^0xff;  //相当于取反。变成了按下的是高电平
	Trg = ReadData&(ReadData^Cont);
	Cont = ReadData;	
}

//按键处理
unsigned char cnt_key;
void Key_Process()
{
	if(cnt_key>=20)
	{
		cnt_key=0;
		Three_Key();
		
		if(Trg == 0x88)      //S4
		{
			mode_1 = (mode_1+1)%2;
			mode_2 = 0;  //满足界面切换模式要求1和2
		}
		if(Trg == 0x84)       //S5
		{
			if(mode_1==0)
			{
				mode_2 = (mode_2+1)%3;
				if(mode_2 == 2) mode_3=0; //进入数据记录界面,默认显示测距最大值  满足界面切换模式要求3
			}
			else if(mode_1==1)
			{
				mode_2 = (mode_2+1)%2;
			}
		}
		if(Trg == 0x48)       //S8
		{
			if((mode_1 == 0)&&(mode_2 == 1)) //数据显示-测距数据 模式
			{
				mode_dist = (mode_dist+1)%2;
			}
			else if((mode_1 == 0)&&(mode_2 == 2)) //数据显示 - 数据记录
			{
				mode_3 = (mode_3+1)%3;
			}
		}
		if(Trg == 0x44)       //S9
		{
			if((mode_1==1)&&(mode_2==0))//采集时间参数模式
			{
				now_tm = (now_tm+1)%5;
			}
			else if((mode_1==1)&&(mode_2==1)) //距离参数
			{
				set_dist = set_dist+10;
				if(set_dist == 90) set_dist=10;
			}
			
		}
	}
}

//------------------DAC输出--------------------------

void vDAC_Process()
{
	if(dist<10) DAC_out(51);  //输出1V
	
	else if(dist<80) 
	{
		DAC_out((51*10+2040/70)/10);
	}
		
	if(dist>80) DAC_out(255); //输出5V
	
	
}

//---------------LED指示灯功能------------------
void LED_Process()
{
	if(mode_1==0)
	{
		led_control.Hex = led_control.Hex | 0x07; //低三位置1,熄灭
		switch(mode_2)
		{
			case 0:
				led_control.B.b1=0;
			break;
			case 1:
				led_control.B.b2=0;
			break;
			case 2:
				led_control.B.b3=0;
			break;
		}
	}
	
	if(mode_dist==1) led_control.B.b4=0; //触发模式 L4点亮
	else led_control.B.b4=1;
	
	if(mode_dist==0)
	{
		if(flag_L5==1) 
		{
			led_control.B.b5=0; //报警效果 L5点亮
		}
	}
	
	if(rd1_dat>150) led_control.B.b6=0; //报警效果 L5点亮
	else led_control.B.b6=1;
	
	
	vDevice(0x80,led_control.Hex); //输入IO口P0
}




//---------------系统初始化-------------------
void System_init()
{
	led_control.Hex = 0xff;
	Write_time(time[0],time[1],time[2]);
	vDevice(0x80,led_control.Hex);
	vDevice(0xa0,0x00);
}

void main()
{
	Timer1Init();
	Timer2Init();
	System_init();
	while(1)
	{
		vSMG_Work();
		vRead_time();
		Distance();
		vRead_rd1();
		Key_Process();
		vDAC_Process();
	}
}

void Timer2() interrupt 12
{
	cnt_time++;cnt_dist++;cnt_rd1++;cnt_key++;
	if((mode_dist==0)&&(flag_dist==1)) //定时模式。且整除为0时计时1s 防止1s内多次测量
	{
		cnt_1s++;
	}
	vSMG_Show();
	LED_Process();
}

iic.c

/*	#   I2C代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/

#include <reg52.h>
#include <intrins.h>

#define DELAY_TIME	5

//
sbit sda=P2^1;
sbit scl=P2^0;

static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();		
    }
    while(n--);      	
}

//
void I2CStart(void)
{
    sda = 1;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 0;
	I2C_Delay(DELAY_TIME);
    scl = 0;    
}

//
void I2CStop(void)
{
    sda = 0;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 1;
	I2C_Delay(DELAY_TIME);
}

//
void I2CSendByte(unsigned char byt)
{
    unsigned char i;
	
    for(i=0; i<8; i++){
        scl = 0;
		I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
		I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
		I2C_Delay(DELAY_TIME);
    }
	
    scl = 0;  
}

//
unsigned char I2CReceiveByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++){   
		scl = 1;
		I2C_Delay(DELAY_TIME);
		da <<= 1;
		if(sda) 
			da |= 0x01;
		scl = 0;
		I2C_Delay(DELAY_TIME);
	}
	return da;    
}

//
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//
void I2CSendAck(unsigned char ackbit)
{
    scl = 0;
    sda = ackbit; 
	I2C_Delay(DELAY_TIME);
    scl = 1;
	I2C_Delay(DELAY_TIME);
    scl = 0; 
	sda = 1;
	I2C_Delay(DELAY_TIME);
}

//DAC输出电压
void DAC_out(unsigned char dat)
{
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(0x40);
	I2CWaitAck();
	I2CSendByte(dat);
	I2CWaitAck();
	I2CStop();
}

//ADC采集 RD1光敏电阻
unsigned char ADC_Get()
{
	unsigned char dac;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(0x41);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	dac = I2CReceiveByte();
	I2CSendAck(1);
	return dac;
}

ds1302.c 

/*	# 	DS1302代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/								

//
#include <reg52.h>
#include <intrins.h>

sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST=P1^3;

void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK = 0;
		SDA = temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1; 	_nop_();  
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);		
 	RST=0; 
}

//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
 	Write_Ds1302(address);
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0;
		temp>>=1;	
 		if(SDA)
 		temp|=0x80;	
 		SCK=1;
	} 
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
	SCK=1;	_nop_();
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	return (temp);			
}

//写入时间 初始化
void Write_time(unsigned char sec,unsigned char min,unsigned char hour)
{
	Write_Ds1302_Byte(0x8e,0x00);//关闭写保护
	Write_Ds1302_Byte(0x80,sec);
	Write_Ds1302_Byte(0x82,min);
	Write_Ds1302_Byte(0x84,hour);
	Write_Ds1302_Byte(0x8e,0x80);//打开写保护
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值