时长:三个半小时
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);//打开写保护
}