1.按键功能部分

	/*按键功能区*/
	switch(Key_down)
	{
		case 4:												//模式切换按键
			if(Dis_mode != 3)						//保证在触发状态下无效
				if(++Dis_mode == 3) Dis_mode = 0;	//模式循环切换
			Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位
			Dis_mode2 = 0;//历史子界面复位
		break;
		case 5:												//历史子界面切换按键
			if(Dis_mode == 2)						//保证仅在触发模式下生效
				if(++Dis_mode2 == 3) Dis_mode2 = 0;
		break;
		case 8:												//清空历史按键(历史界面有效)
			if(Dis_mode == 2)
			{
				Real_Save_flag = 0;//索引清空(实质上没有清空存储数组,但是显示效果和清空一样)
			}
		break;
	}

2.时间界面显示部分

case 0:																	//时间显示模式
			Seg_dis[0] = Time_ds1302[0] /16 %16;
			Seg_dis[1] = Time_ds1302[0]  %16;
			Seg_dis[3] = Time_ds1302[1] /16 %16;
			Seg_dis[4] = Time_ds1302[1]  %16;
			Seg_dis[6] = Time_ds1302[2] /16 %16;
			Seg_dis[7] = Time_ds1302[2]  %16;
			Seg_dis[2] = 11;Seg_dis[5] = 11;		//显示时间,11-“-”
		break;

3.电压数据显示部分

	/*数据采集区域*/
	Read_time(Time_ds1302);//读RTC
    /*读AD采样值(0~255)*/
	Right_V_data = AD_Read(0x43);//光敏电阻
	R_V_data = AD_Read(0x41);//电位器
case 1:																	//数据显示模式
			Seg_dis[0] = 12;Seg_dis[4] = 13;//0-P,4-U
			Seg_dian[1] = 1;Seg_dian[5] = 1;//小数点
            //将0~255乘100再除51,其转为0~5且扩大100倍,避免小数
            //注意要进行数据类型强转(500 > 255,用int)
			Seg_dis[1] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /100 %10);
			Seg_dis[2] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /10 %10);
			Seg_dis[3] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /1 %10);//光敏电压
			Seg_dis[5] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /100 %10);
			Seg_dis[6] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /10 %10);
			Seg_dis[7] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /1 %10);//电位器电压
		break;

4.历史查询界面及显示处理

void find_Save()	//判断是否有记录时间。有-读取显示,无-显示“-”
{
	if(Dis_mode2 < Real_Save_flag)
	{
		Seg_dis[2] = Save_time[Dis_mode2][0] /16 %16;//显示触发时间(用Dis_mode2作为索引)
		Seg_dis[3] = Save_time[Dis_mode2][0]  %16;
		Seg_dis[4] = Save_time[Dis_mode2][1] /16 %16;
		Seg_dis[5] = Save_time[Dis_mode2][1]  %16;
		Seg_dis[6] = Save_time[Dis_mode2][2] /16 %16;
		Seg_dis[7] = Save_time[Dis_mode2][2]  %16;
	}
	else
	{
		Seg_dis[2] = 11;Seg_dis[3] = 11;//“-”
		Seg_dis[4] = 11;Seg_dis[5] = 11;
		Seg_dis[6] = 11;Seg_dis[7] = 11;
	}
}
		case 2:										//历史显示模式
			Seg_dis[0] = 14;//A
			
			switch(Dis_mode2)		//子界面判断
			{
				case 0:
					Seg_dis[1] = 1;
					find_Save();
				break;
				case 1:
					Seg_dis[1] = 2;
					find_Save();
				break;
				case 2:
					Seg_dis[1] = 3;
					find_Save();
				break;
			}
		break;

 5.触发界面显示与触发逻辑、数据记录逻辑

/*触发逻辑及其处理操作*/
	if(Dis_mode != 3)								//保证非触发时可进行触发判断
	{
		if(Right_V_data < R_V_data)		//触发条件判断:光敏 < 电位器
			{
				Old_dis_mode = Dis_mode;	//保存当前模式
				Dis_mode = 3;							//切换为触发模式
				Save_time[2][0] = Save_time[1][0];
				Save_time[2][1] = Save_time[1][1];
				Save_time[2][2] = Save_time[1][2];
				Save_time[1][0] = Save_time[0][0];
				Save_time[1][1] = Save_time[0][1];
				Save_time[1][2] = Save_time[0][2];

				Save_time[0][0] = Time_ds1302[0];
				Save_time[0][1] = Time_ds1302[1];
				Save_time[0][2] = Time_ds1302[2];//时间记录数组传值(3-舍弃,2->3,1->2,Time_ds1302 -> 1)
				
				Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位(若不复位,数据模式时触发,小数点会一直保留下来)
				if(++Real_Save_flag == 3) Real_Save_flag = 3;//每次记录,历史最高记录次数加1,直到记满
			}
	}
case 3:									//触发显示模式
			Seg_dis[0] = 15;Seg_dis[1] = 15;//C

			Seg_dis[2] = Save_time[0][0] /16 %16;
			Seg_dis[3] = Save_time[0][0]  %16;
			Seg_dis[4] = Save_time[0][1] /16 %16;
			Seg_dis[5] = Save_time[0][1]  %16;
			Seg_dis[6] = Save_time[0][2] /16 %16;
			Seg_dis[7] = Save_time[0][2]  %16;
break;
if(Dis_mode == 3)					//触发模式持续时间计时(触发模式下有效)
	{
		if(++Time_dis_mode3 == 3000)//3s计时
		{
			Time_dis_mode3 = 0;
			Dis_mode = Old_dis_mode;	//回到原界面
		}
	}

6.LED显示

/*其他显示(LED、蜂鸣器、继电器)*/
void Other_Dis_Take()
{
	LED_dis[0] = 1 * (Dis_mode == 0);//时间界面LED1亮
	LED_dis[1] = 1 * (Dis_mode == 1);//数据界面LED2亮
	LED_dis[2] = 1 * (Dis_mode == 2);//历史界面LED3亮
	LED_dis[7] = 1 * (Dis_mode == 3);//触发界面LED8亮
}

完整程序如下:

main.c

/*头文件*/
#include <STC15F2K60S2.H>
#include <intrins.H>
#include <stdio.H>
#include <string.H>
#include <Init.H>
#include <LED.H>
#include <Key.H>
#include <Seg.H>
#include <DS1302.H>
#include <I2C.H>

/*全局变量声明区域*/
unsigned char Key_show_down;//按键减速变量
unsigned char Key_val,Key_old,Key_up,Key_down;//按键扫描变量,键值、前值、上升沿、下降沿
unsigned int Seg_show_down;//数码管减速变量
unsigned char Seg_pos;//数码管扫描位变量
unsigned char Seg_dis[] = {10,10,10,10,10,10,10,10};//数码管显示缓存数组
unsigned char Seg_dian[] = {0,0,0,0,0,0,0,0};//小数点显示缓存数组
unsigned char LED_dis[] = {0,0,0,0,0,0,0,0};//LED显示缓存数组

/*程序逻辑变量*/
unsigned char Dis_mode,Dis_mode2,Old_dis_mode;//模式选择,0-时间,1-数据,2-历史,3-触发。历史模式子界面。触发前模式
unsigned char Time_ds1302[] = {0x23,0x59,0x00};//RTC
unsigned char Save_time[3][3] = {{0x11,0x00,0x00},{0x22,0x00,0x00},{0x33,0x00,0x00}};//触发时间记录
unsigned char Right_V_data,R_V_data;//光敏原始值,电位器原始值
unsigned char Real_Save_flag;//历史记录次数标志,0~3,0-未记录。
unsigned int Time_dis_mode3;//触发模式持续时长

/*函数定义区域*/
/*按键功能函数*/
void Key_Take()
{
	if(Key_show_down) return;
	Key_show_down = 1;//按键减速
	
	Key_val = Key_jz();//读取按键键值(矩阵)
	Key_down = Key_val &(Key_old ^ Key_val);//读取按键下降沿
	Key_up = ~Key_val &(Key_old ^ Key_val);//读取按键上升沿
	Key_old = Key_val;//辅助扫描
	
	/*按键功能区*/
	switch(Key_down)
	{
		case 4:												//模式切换按键
			if(Dis_mode != 3)						//保证在触发状态下无效
				if(++Dis_mode == 3) Dis_mode = 0;	//模式循环切换
			Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位
			Dis_mode2 = 0;//历史子界面复位
		break;
		case 5:												//历史子界面切换按键
			if(Dis_mode == 2)						//保证仅在触发模式下生效
				if(++Dis_mode2 == 3) Dis_mode2 = 0;
		break;
		case 8:												//清空历史按键(历史界面有效)
			if(Dis_mode == 2)
			{
				Real_Save_flag = 0;//索引清空(实质上没有清空存储数组,但是显示效果和清空一样)
			}
		break;
	}
}

void find_Save()	//判断是否有记录时间。有-读取显示,无-显示“-”
{
	if(Dis_mode2 < Real_Save_flag)
	{
		Seg_dis[2] = Save_time[Dis_mode2][0] /16 %16;//显示触发时间(用Dis_mode2作为索引)
		Seg_dis[3] = Save_time[Dis_mode2][0]  %16;
		Seg_dis[4] = Save_time[Dis_mode2][1] /16 %16;
		Seg_dis[5] = Save_time[Dis_mode2][1]  %16;
		Seg_dis[6] = Save_time[Dis_mode2][2] /16 %16;
		Seg_dis[7] = Save_time[Dis_mode2][2]  %16;
	}
	else
	{
		Seg_dis[2] = 11;Seg_dis[3] = 11;//“-”
		Seg_dis[4] = 11;Seg_dis[5] = 11;
		Seg_dis[6] = 11;Seg_dis[7] = 11;
	}
}

/*数据显示功能函数*/
void Seg_Take()
{
	if(Seg_show_down) return;
	Seg_show_down = 1;//数码管减速
	
	/*数据采集区域*/
	Read_time(Time_ds1302);//读RTC
	Right_V_data = AD_Read(0x43);
	R_V_data = AD_Read(0x41);//读AD采样值(0~255)
	
	/*触发逻辑及其处理操作*/
	if(Dis_mode != 3)								//保证非触发时可进行触发判断
	{
		if(Right_V_data < R_V_data)		//触发条件判断:光敏 < 电位器
			{
				Old_dis_mode = Dis_mode;	//保存当前模式
				Dis_mode = 3;							//切换为触发模式
				Save_time[2][0] = Save_time[1][0];
				Save_time[2][1] = Save_time[1][1];
				Save_time[2][2] = Save_time[1][2];
				Save_time[1][0] = Save_time[0][0];
				Save_time[1][1] = Save_time[0][1];
				Save_time[1][2] = Save_time[0][2];

				Save_time[0][0] = Time_ds1302[0];
				Save_time[0][1] = Time_ds1302[1];
				Save_time[0][2] = Time_ds1302[2];//时间记录数组传值(3-舍弃,2->3,1->2,Time_ds1302 -> 1)
				
				Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位(若不复位,数据模式时触发,小数点会一直保留下来)
				if(++Real_Save_flag == 3) Real_Save_flag = 3;//每次记录,历史最高记录次数加1,直到记满
			}
	}
		
	/*数据显示区域*/
	switch(Dis_mode)
	{
		case 0:																	//时间显示模式
			Seg_dis[0] = Time_ds1302[0] /16 %16;
			Seg_dis[1] = Time_ds1302[0]  %16;
			Seg_dis[3] = Time_ds1302[1] /16 %16;
			Seg_dis[4] = Time_ds1302[1]  %16;
			Seg_dis[6] = Time_ds1302[2] /16 %16;
			Seg_dis[7] = Time_ds1302[2]  %16;
			Seg_dis[2] = 11;Seg_dis[5] = 11;		//显示时间,11-“-”
		break;
		case 1:																	//数据显示模式
			Seg_dis[0] = 12;Seg_dis[4] = 13;//0-P,4-U
			Seg_dian[1] = 1;Seg_dian[5] = 1;//小数点

			Seg_dis[1] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /100 %10);//将0~255乘100再除51,其转为0~5且扩大100倍,避免小数
			Seg_dis[2] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /10 %10);//注意要进行数据类型强转(500 > 255,用int)
			Seg_dis[3] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /1 %10);//光敏电压
			Seg_dis[5] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /100 %10);
			Seg_dis[6] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /10 %10);
			Seg_dis[7] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /1 %10);//电位器电压
		break;
		case 2:																	//历史显示模式
			Seg_dis[0] = 14;//A
			
			switch(Dis_mode2)		//子界面判断
			{
				case 0:
					Seg_dis[1] = 1;
					find_Save();
				break;
				case 1:
					Seg_dis[1] = 2;
					find_Save();
				break;
				case 2:
					Seg_dis[1] = 3;
					find_Save();
				break;
			}
		break;
		case 3:																	//触发显示模式
			Seg_dis[0] = 15;Seg_dis[1] = 15;//C

			Seg_dis[2] = Save_time[0][0] /16 %16;
			Seg_dis[3] = Save_time[0][0]  %16;
			Seg_dis[4] = Save_time[0][1] /16 %16;
			Seg_dis[5] = Save_time[0][1]  %16;
			Seg_dis[6] = Save_time[0][2] /16 %16;
			Seg_dis[7] = Save_time[0][2]  %16;
		break;
	}
}

/*其他显示(LED、蜂鸣器、继电器)*/
void Other_Dis_Take()
{
	LED_dis[0] = 1 * (Dis_mode == 0);//时间界面LED1亮
	LED_dis[1] = 1 * (Dis_mode == 1);//数据界面LED2亮
	LED_dis[2] = 1 * (Dis_mode == 2);//历史界面LED3亮
	LED_dis[7] = 1 * (Dis_mode == 3);//触发界面LED8亮
}

/*定时器1初始化(系统定时器)*/
void Timer1Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x18;		//设置定时初值
	TH1 = 0xFC;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1 = 1;
	EA = 1;
}

/*定时器1中断服务函数*/
void Timer1_Take() interrupt 3
{
	if(++Key_show_down == 10) Key_show_down = 0;//按键减速10ms
	if(++Seg_show_down == 200) Seg_show_down = 0;//数码管减速200ms
	if(++Seg_pos == 8) Seg_pos = 0;//数码管位扫描
	Display(Seg_pos,Seg_dis[Seg_pos],Seg_dian[Seg_pos]);//数码管显示函数
	LED(Seg_pos,LED_dis[Seg_pos]);//LED显示函数

	if(Dis_mode == 3)					//触发模式持续时间计时(触发模式下有效)
	{
		if(++Time_dis_mode3 == 3000)//3s计时
		{
			Time_dis_mode3 = 0;
			Dis_mode = Old_dis_mode;	//回到原界面
		}
	}
}

/*主程序*/
void main()
{
	Init();//系统初始化(关闭蜂鸣器、LED)
	Timer1Init();//定时器初始化
	Write_time(Time_ds1302);//初始时间写入
	while(1)
	{
		Key_Take();
		Seg_Take();
		Other_Dis_Take();
	}
}

Init.c

#include <STC15F2K60S2.H>

void Init()
{
	P0 = 0xff;//LED全灭
	P2 = P2 & 0x1f | 0x80;//打开LED通道
	P2 &= 0x1f;//关闭通道
	
	P0 = 0x00;//关闭蜂鸣器继电器(经过ULN2003芯片自带反相功能)
	P2 = P2 & 0x1f | 0xA0;//打开LED通道
	P2 &= 0x1f;//关闭通道
}

LED.c

#include <STC15F2K60S2.H>

/*
LED控制
参数:控制位,是否使能(点亮)
*/
void LED(unsigned char wei,unsigned char enable)
{
	static unsigned char temp = 0x00;
	static unsigned char temp_old = 0xff;
	
	if(enable)
		temp |= 0x01 << wei;//给点亮位传1
	else
		temp &= ~(0x01<<wei);//给点亮位传0
	
	if(temp != temp_old)	//避免译码器、锁存器频繁开关,仅在LED状态变化时打开通道
	{
		P0 = ~temp;//将点亮位传给P2(给0亮)
		P2 = P2 & 0x1f | 0x80;//打开LED通道
		P2 &= 0x1f;//关闭通道
		temp_old = temp;//新旧值交换
	}
}

Key.c

#include <STC15F2K60S2.H>

unsigned char Key_jz()
{
	unsigned char key = 0;
	/*
	使用串口时,用P30,P31,有硬件冲突。
	为防止对串口的影响,关闭轮询定时器(定时器1),结束按键扫描后再打开定时器。
	*/
	ET1 = 0;
	
	P3=0xff;P42=1;P44=0;//一列导通,扫描行号(0为通)
	if(P30==0) key=7;
	if(P31==0) key=6;
	if(P32==0) key=5;
	if(P33==0) key=4;
	
	P3=0xff;P42=0;P44=1;
	if(P30==0) key=11;
	if(P31==0) key=10;
	if(P32==0) key=9;
	if(P33==0) key=8;
	
	P3=0xff;P42=1;P44=1;P35=0;
	if(P30==0) key=15;
	if(P31==0) key=14;
	if(P32==0) key=13;
	if(P33==0) key=12;
	
//	P3=0xff;P42=1;P44=1;P34=0;
//	if(P30==0) key=19;
//	if(P31==0) key=18;
//	if(P32==0) key=17;
//	if(P33==0) key=16;
	
	P3=0xff;ET1 = 1;//重新开启轮询定时器(为何令P3=0xff待验证)·············
	return key;//返回键值
}

unsigned char Key_dl()
{
	unsigned char key = 0;
	if(P30 == 0) key = 7;
	if(P31 == 0) key = 6;
	if(P32 == 0) key = 5;
	if(P33 == 0) key = 4;
	return key;
}

Seg.c

#include <STC15F2K60S2.H>

code unsigned char DX[] = {0xc0,0xf9,0xa4,0xb0,0x99,
	0x92,0x82,0xf8,0x80,0x90,0xff,
	0xBF,0x8C,0xC1,0x88,0xC6
};//0~9,10-熄灭,11-“-”,12-P,13-U,14-A,15-C

void Display(unsigned char wei,unsigned char duan,unsigned char dian)
{
	P0 = 0xff;//段选消影
	P2 = P2 & 0x1f | 0xe0;//打开段选通道
	P2 &= 0x1f;//关闭通道
	
	P0 = 0x01 << wei;//不使用数组方式,直接使用位操作选择点亮位(类比LED)
	P2 = P2 & 0x1f | 0xc0;//打开位选通道
	P2 &= 0x1f;//关闭通道
	
	P0 = DX[duan];
	if(dian)
		P0 &= 0x7f;//有小数点,将其按位与0x7F,将最高位0传递到P0
	P2 = P2 & 0x1f | 0xe0;//打开段选通道
	P2 &= 0x1f;//关闭通道
}

DS1302.c (这里我添加了DS1302的12小时模式的显示,经测试是正常的)

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

sbit SCK = P1^7;
sbit RST = P1^3;
sbit SDA = P2^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);			
}

/*
写时间(24h)
参数:时、分、秒(数组)
*/
void Write_time(unsigned char time_ds1302[3])
{
	Write_Ds1302_Byte(0x8e,0x00);//去除写保护
	Write_Ds1302_Byte(0x84,time_ds1302[0]);//时
	Write_Ds1302_Byte(0x82,time_ds1302[1]);//分
	Write_Ds1302_Byte(0x80,time_ds1302[2]);//写入时间
	Write_Ds1302_Byte(0x8e,0x80);//打开写保护
}

/*
读时间(24h)
参数:时、分、秒(数组)
*/
void Read_time(unsigned char time_ds1302[3])
{
	time_ds1302[0] = Read_Ds1302_Byte(0x85);
	time_ds1302[1] = Read_Ds1302_Byte(0x83);
	time_ds1302[2] = Read_Ds1302_Byte(0x81);
}

/*
写时间(12h)
参数:时、分、秒(数组),AT:早晚,0早1晚
*/
void Write_time_12(unsigned char AT,unsigned char time_ds1302[3])
{
	Write_Ds1302_Byte(0x8e,0x00);//打开写保护
	if(AT)
		Write_Ds1302_Byte(0x84,time_ds1302[0] | 0xA0);//下午,bit5为1,12小时制,bit7为1
	else
		Write_Ds1302_Byte(0x84,time_ds1302[0] | 0x80);//上午,bit5为0,12小时制,bit7为1
	Write_Ds1302_Byte(0x82,time_ds1302[1]);
	Write_Ds1302_Byte(0x80,time_ds1302[2]);
	Write_Ds1302_Byte(0x8e,0x80);//关闭写保护
}

/*
读时间(12h)
参数:时、分、秒(数组)
返回值:AT:早晚,2早1晚
*/
unsigned char Read_time_12(unsigned char time_ds1302[3])
{
	unsigned char AT = 0;
	if((Read_Ds1302_Byte(0x85) & 0x20) == 0x20)
		AT = 1;
	else
		AT = 2;
	
	time_ds1302[0] = Read_Ds1302_Byte(0x85) & 0x1f;
	time_ds1302[1] = Read_Ds1302_Byte(0x83);
	time_ds1302[2] = Read_Ds1302_Byte(0x81);
	
	return AT;
}

IIC.c (在使用AD进行数据读取时,若读取到的数据闪烁的非常快,可以将这里的DELAY_TIME的值由10改为5)(官方提供的文件里DELAY_TIME的值为10,这里我已经进行了修改)

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


#define DELAY_TIME	5

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

//I2C等待
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--);      	
}

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

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

//I2C发送数据
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;  
}

//I2C接收数据
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;    
}

//I2C等待应带
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//I2C发送应答
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);
}

/*
读AD值
参数:通道(光敏0x41,变阻器0x43)
		(注意:同时读取两路信号时要用光敏电阻变量读取电位器值,电位器变量读取光敏电阻值)
返回值:AD值(0-255)

|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
|		PCF8591有一个模数转换值存储区,当主机未发送读取某一通道指令时,该区存储值默认128。				|
|有多路同时读取时,如光敏(AIN1)和电位器(AIN3),读光敏时调用读取函数将通道编码发送给芯片,	|
|该芯片返回的是存储区预存的值(128),同时将通道1的值读取出来,之后读取电位器时将1通道的值给	|
|电位器,同时将3通道值读出存储,以此循环																										|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
unsigned char AD_Read(unsigned char adder)
{
	unsigned char temp = 0;
	
	I2CStart();
	I2CSendByte(0x90);//发送写命令
	I2CWaitAck();//等待应答
	I2CSendByte(adder);//发送地址(通道,光敏0x41,变阻器0x43)
	I2CWaitAck();//等待应答
	
	I2CStart();
	I2CSendByte(0x91);//发送读命令
	I2CWaitAck();//等待应答
	temp = I2CReceiveByte();//接收数据
	I2CSendAck(1);//发送非应答信号
	I2CStop();//停止
	
	return temp;
}

/*
写入DA值(向PCF8591)
参数:数字量(0~255对应输出模拟量0~5V)
		(注意:使用时可用对应模拟量乘51,从J3的19号引脚读出)
*/
void DA_Write(unsigned char date)
{
	I2CStart();
	I2CSendByte(0x90);//发送写命令
	I2CWaitAck();//等待应答
	
	I2CSendByte(0x40);//使能DA转换(40~43均可,尽量与题目涉及模块保持一致,e.g.涉及光敏-41)
	I2CWaitAck();//等待应答
	I2CSendByte(date);//使能DA转换
	I2CWaitAck();//等待应答
	I2CStop();//停止
}

至此,十六届蓝桥杯单片机模拟Ⅱ试题的程序部分已经全部结束,经4T网站测评为满分,但是这里很多程序写的相对冗余,尤其是三次历史记录时间的传值部分。因为是在追求刷题速度,而未对其进行优化,希望看到这篇帖子的朋友有更好的写法能够分享出来共同交流。

 

更多推荐