蓝桥杯第七届国赛试题--“电压、频率采集设备”

题目要求

设计任务及要求
1. 频率信号测量
使用竞赛板 NE555 方波信号发生器产生用于频率测量功能测试的方波信号,信号频率范围为 500Hz ~ 20KHz,电位器 RB3 调节信号频率, 可使用“跳线帽” 将单片机P34 引脚与方波信号发生器输出引脚 NET_SIG 短接。
2. 电压信号测量
使用竞赛板上 RB2 电位器输出测试电压信号,信号输出范围为 0V-5V。
3. 实时时钟功能
读取 DS1302 实时时钟芯片,并通过数码管显示时间数据, 设备上电后初始时间配置为 23 时 59 分 55 秒。
4. 记录与输出功能
根据给定硬件的性能参数,设计合理的采样周期, 当电压低于电压下限 VL 或高于电压上限 VH时, 将事件类型和发生时间保存到 E2PROM 中。 设备中只需保存最近一次发生的电压波动情况, 数据存储格式可自行定义。
在这里插入图片描述
上图例, 电压连续变化过程中, 在 T1和 T2时刻会触发事件记录功能。
5. 显示与按键控制
5.1 4x4 矩阵键盘按键功能定义如图 2 所示:
在这里插入图片描述
5.2 按键 S7 定义为“时钟”按键,按下后, 数码管显示当前时钟信息, 显示界面如图 3 所示:
在这里插入图片描述
在时钟显示界面下, S4 功能按键为时钟调整按键, 按下 S4,循环切换选择时、分、秒, 对应的显示单元 1 秒间隔亮灭, 通过“加、减” 按键调整当前选择的时间单位,再次按下按键 S7 返回到时钟显示界面,完成时钟配置功能。
在这里插入图片描述
时钟调整状态下,“加、减”按键可令当前选择调整的时间增加或减少 1 个单位。
5.3 按键 S6 定义为“电压测量”按键,按下后,启动电压测量功能,数码管显示格式如图 4 所示:
在这里插入图片描述
电压测量界面下, S4 功能按键为电压阈值调整按键,按下 S4,循环选择电压上限、下限,对应的显示单元 1 秒间隔亮灭,通过“加、减” 按键调整电压阈值, 再次按下按键 S6 返回到电压测量界面,完成电压阈值配置功能并将新的参数写入E2RPOM。
在这里插入图片描述
电压阈值调整状态下,“加、减”按键可令当前选择调整的阈值增加或减少 500mV。
5.4 按键 S5 定义为“频率测量”按键,按下后,启动频率测量功能,设备采集输入 P34 引脚的信号频率,数码管切换到“频率测量”显示界面,“频率测量”功能下,按键 S4 定义为“周期/频率”按键,可以进行频率和周期数据的切换显示,显示界面如图 6、 7 所示:
在这里插入图片描述在这里插入图片描述
5.5 按键 S9 定义为“查询按键”,按下后,显示最近一次电压波动发生的时间和波动类型, 显示格式如图 8、 9 所示:
在这里插入图片描述
在事件查询界面下,功能按键 S4 可切换显示事件时间和事件类型,“00”表示低于下限事件,“01”表示超出上限事件。
在这里插入图片描述
6. 说明
6.1 最近一次电压波动发生时间和类型、电压上限 VH、电压下限 VL 保存在
E2PROM 中,设备重新上电后,能够从存储器中获取参数和数据。
6.2 建立一个准考证号命名的 txt 文档, 写出作品设定的单片机内部振荡器频率,保存在考生文件夹中。

程序代码

主函数

#include "stc15f2k60s2.h"
#include "key.h"
#include "display.h"
#include "ds1302.h"
#include "iic.h"

extern uchar set_time_display[8];
extern unsigned char code smg_tab[10];

extern uchar shi,fen,miao;				//设置时间界面下,时分秒
extern uchar ask_shi,ask_fen,ask_miao;	//查询界面下,事件发生时,时分秒
extern uchar kinds_of_things;  			//事件类型
extern uint ad_value;					// A/D值
extern uchar mode_display;				//显示模式

uchar key_value;	  //按键值
uchar S4_count_time;	  //时钟设置界面下S4按键次数计数
uchar S4_count_ad;		  //电压界面下S4按键次数计数
uchar S4_count_fre;		 //频率界面下S4按键次数计数
uchar S4_count_ack;		 //查询界面下S4按键次数计数
uchar test_eeprom;		   //测试eeprom标志,确保程序第一次下载时,是预设初始值
uchar hour,minute,second;	   //时分秒

uchar VH_temp,VL_temp;
uint VH_limit=2000,VL_limit=1000;	//电压上、下限值
uint over_count1,over_count2;		//超过电压极限值计数

bit set_time_flag;		   //设置时间标志
bit set_ad_flag;		   //设置A/D标志
bit set_fre_flag;		   //设置频率标志
bit blink_flag;			   //数码管闪烁标志
bit low_flag,high_flag;	   //低于电压下限、上限标志

void S4_set();		   //S4功能按键函数
void smg_blink();	   //数码管闪烁函数
void add_set();		   //“加” 函数
void sub_set();		   //“减”函数
void eeprom_init();	   //eeprom初始化函数
void voltage_ask();	   //S9按键查询函数

void All_init()
{
    
    
  P2=(P2&0X1F)|0XA0;
  P0=0X00;
  P2=P2&0X1F;

  P2=(P2&0X1F)|0X80;
  P0=0XFF;
  P2=P2&0X1F;
}

void main()
{
    
    
  All_init();
  Timer0Init();
  Timer1Init();
  ds1302_init();
  eeprom_init();
  
  while(1)
  {
    
    
	time_display_init();	//时间显示函数
	AD_dispaly_init();		//电压显示函数
	fre_display_init();		//频率显示函数
	smg_blink();			//数码管闪烁显示函数
	set_ad_init();			//设置电压上下限显示函数
	period_display_init();	//周期显示函数
	voltage_ask();			//电压上下限处理函数
	ask_display_init();		//S9按键查询显示函数

	key_value=key_init();
	switch(key_value)
	{
    
    
	  case 7:
	  mode_display=0;		 //时钟显示	  
	  if(set_time_flag)
	  {
    
    
		set_time_flag=0;
		hour=(shi/10*16)+shi%10;
		minute=(fen/10*16)+fen%10;
		second=(miao/10*16)+miao%10;
		Ds1302_Single_Byte_Write(ds1302_control_addr,0x00);
		Ds1302_Single_Byte_Write(ds1302_sec_addr,second);
		Ds1302_Single_Byte_Write(ds1302_min_addr,minute);
		Ds1302_Single_Byte_Write(ds1302_hr_addr,hour);
		Ds1302_Single_Byte_Write(ds1302_control_addr,0x80);		
	  }
	  break;

	  case 6:
	  mode_display=1;	   //电压显示
	  if(set_ad_flag)
	  {
    
    
	    set_ad_flag=0;
		VH_temp=VH_limit/100;
		VL_temp=VL_limit/100;
		Write_eeprom(0x01,VH_temp);
		Delay10ms();
		Write_eeprom(0x02,VL_temp);
		Delay10ms();

		Write_eeprom(0x00,100);		//程序下载后,将选择的固定位设置为想要的数值
        Delay10ms();
	  }
	  break;

	  case 5:
	  mode_display=2;	  //频率显示
	  if(set_fre_flag)
	  {
    
    
	    set_fre_flag=0;
	  }
	  break;

	  case 4:
	  if(mode_display==0)
	  {
    
    
	    set_time_flag=1;	    
	  }

	  if(mode_display==1)
	  {
    
    
	    set_ad_flag=1;	    
	  }

	  if(mode_display==2)
	  {
    
    
	    set_fre_flag=1;	    
	  }
	  S4_set();	      
	  break;

	  case 11:
	  add_set();
	  break;

	  case 10:
	  sub_set();
	  break;

	  case 9:
	  mode_display=6;
	  break;
	}
  }
}

void eeprom_init()
{
    
    
  test_eeprom=Read_eeprom(0x00);   //读取选择的固定位,看是不是想要的数值
  if(test_eeprom==100)			   //是,正常读取电压上下限
  {
    
    
    VH_limit=Read_eeprom(0x01)*100;
    Delay10ms();
    VL_limit=Read_eeprom(0x02)*100;
    Delay10ms();
  }
  else							  //不是,将电压上下限设置为初始值
  {
    
    
    VH_limit=2000;
    VL_limit=1000;
  }

  kinds_of_things=Read_eeprom(0x10);
  Delay10ms();
  ask_shi=Read_eeprom(0x11);
  Delay10ms();
  ask_fen=Read_eeprom(0x12);
  Delay10ms();
  ask_miao=Read_eeprom(0x13);
  Delay10ms();
}

void voltage_ask()
{
    
    
  if(ad_value<=VL_limit)		//低于电压下限
  {
    
    
	kinds_of_things=0;
	over_count1++;
	if(over_count1<10)		   //如果电压一直低于下限,确保记录的时间是事件发生时刻的时间
	low_flag=1;	
  }
  else
  over_count1=0;
  if(low_flag)
  {
    
    
    low_flag=0;
	ask_shi=(hour/16)*10+hour%16;
	ask_fen=(minute/16)*10+minute%16;
	ask_miao=(second/16)*10+second%16;

  }

  if(ad_value>=VH_limit)
  {
    
    
	kinds_of_things=1;
	over_count2++;
	if(over_count2<10)
	high_flag=1;
  }
  else
  over_count2=0;

  if(high_flag)
  {
    
    
    high_flag=0;
	ask_shi=(hour/16)*10+hour%16;
	ask_fen=(minute/16)*10+minute%16;
	ask_miao=(second/16)*10+second%16;
  }

  Write_eeprom(0x10,kinds_of_things);
  Delay10ms();
  Write_eeprom(0x11,ask_shi);
  Delay10ms();
  Write_eeprom(0x12,ask_fen);
  Delay10ms();
  Write_eeprom(0x13,ask_miao);
  Delay10ms();
}

void S4_set()
{
    
    
  if(mode_display==0)
  {
    
    
    mode_display=3;		//设置时间界面
  }
  if(mode_display==3)
  {
    
    
	S4_count_time++;
	if(S4_count_time==4)
	S4_count_time=1;
  }

  if(S4_count_time==1)
  {
    
    
	shi=(hour/16)*10+hour%16;
	fen=(minute/16)*10+minute%16;
	miao=(second/16)*10+second%16;
  }

  if(mode_display==1)
  {
    
    
	mode_display=4;		  //设置电压上下限界面
  }

  if(mode_display==4)
  {
    
    
	S4_count_ad++;
	if(S4_count_ad==3)
	S4_count_ad=1;
  }

  if(mode_display==2)
  {
    
    
	mode_display=5;		 //周期显示界面
  }

  if(mode_display==5)
  {
    
    
	S4_count_fre++;
	if(S4_count_fre==3)
	S4_count_fre=1;

	if(S4_count_fre==2)
    mode_display=2;
  }
  

  if(mode_display==6)
  {
    
    
	mode_display=7;		 //查询事件发生时间界面
  }

  if(mode_display==7)
  {
    
    
	S4_count_ack++;
	if(S4_count_ack==3)
	S4_count_ack=1;

	if(S4_count_ack==2)
    mode_display=6;
  }
}

void add_set()
{
    
    
  if(mode_display==3)
  {
    
    
    if(S4_count_time==1)
	{
    
    
	  shi++;
	  if(shi==24)
	  shi=0;
	}
	if(S4_count_time==2)
	{
    
    
	  fen++;
	  if(fen==60)
	  fen=0;
	}
	if(S4_count_time==3)
	{
    
    
	  miao++;
	  if(miao==60)
	  miao=0;
	}
  }

  if(mode_display==4)
  {
    
    
    if(S4_count_ad==1)
	VH_limit+=500;

	if(S4_count_ad==2)
	VL_limit+=500;
  }
}

void sub_set()
{
    
    
  if(mode_display==3)
  {
    
    
    if(S4_count_time==1)
	{
    
    
	  if(shi>0)
	  shi--;
	}
	if(S4_count_time==2)
	{
    
    
	  if(fen>0)
	  fen--;
	}
	if(S4_count_time==3)
	{
    
    
	  if(miao>0)
	  miao--;
	}
  }

  if(mode_display==4)
  {
    
    
    if(S4_count_ad==1)
	VH_limit-=500;

	if(S4_count_ad==2)
	VL_limit-=500;
  }
}

void smg_blink()
{
    
    
  if(mode_display==3)
  {
    
    
    set_time_display[2]=0x40;  
    set_time_display[5]=0x40;

	if(blink_flag)
    {
    
    
	  if(S4_count_time==1)
	  {
    
    
		set_time_display[0]=smg_tab[shi/10];
  		set_time_display[1]=smg_tab[shi%10];
	  }

	  if(S4_count_time==2)
	  {
    
    
		set_time_display[3]=smg_tab[fen/10];
  		set_time_display[4]=smg_tab[fen%10];
	  }

	  if(S4_count_time==3)
	  {
    
    
		set_time_display[6]=smg_tab[miao/10];
        set_time_display[7]=smg_tab[miao%10];
	  }
    }
	else
	{
    
    
	  if(S4_count_time==1)
	  {
    
    
		set_time_display[0]=0x00;
  		set_time_display[1]=0x00;
	  }
	  else
	  {
    
    
		set_time_display[0]=smg_tab[shi/10];
  		set_time_display[1]=smg_tab[shi%10];
	  }

	  if(S4_count_time==2)
	  {
    
    
		set_time_display[3]=0x00;
  		set_time_display[4]=0x00;
	  }
	  else
	  {
    
    
	    set_time_display[3]=smg_tab[fen/10];
  		set_time_display[4]=smg_tab[fen%10];
	  }

	  if(S4_count_time==3)
	  {
    
    
		set_time_display[6]=0x00;
        set_time_display[7]=0x00;
	  }
	  else
	  {
    
    
		set_time_display[6]=smg_tab[miao/10];
        set_time_display[7]=smg_tab[miao%10];
	  }
	}
  }
}

显示模块

#include "display.h"
#include "ds1302.h"
#include "iic.h"

unsigned char code smg_tab[]={
    
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char smg_wei[]={
    
    0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

uchar time_display[8];
uchar AD_display[8];
uchar fre_display[8];
uchar set_time_display[8];
uchar set_ad_display[8];
uchar period_display[8];
uchar ask_display[8];
uchar ask_time_display[8];
	    
extern uchar hour,minute,second;	//时分秒
extern uint VH_limit,VL_limit;

uchar shi,fen,miao;
uchar ask_shi,ask_fen,ask_miao;
uchar kinds_of_things;

uint ad_value;				//A/D值
uint frequence;				//频率
uint period;				//周期 
uchar mode_display;			//显示模式

bit flag500ms;
extern bit blink_flag;

void time_display_init()
{
    
    
  hour=Ds1302_Single_Byte_Read(ds1302_hr_addr);
  minute=Ds1302_Single_Byte_Read(ds1302_min_addr);
  second=Ds1302_Single_Byte_Read(ds1302_sec_addr);

  time_display[0]=smg_tab[hour/16];
  time_display[1]=smg_tab[hour%16];
  time_display[2]=0x40;
  time_display[3]=smg_tab[minute/16];
  time_display[4]=smg_tab[minute%16];
  time_display[5]=0x40;
  time_display[6]=smg_tab[second/16];
  time_display[7]=smg_tab[second%16];
}

void AD_dispaly_init()
{
    
    
  ad_value=(read_pcf8591(0x03)*19.61f);
  Delay10ms();
  AD_display[0]=0x40;
  AD_display[1]=smg_tab[1];
  AD_display[2]=0x40;
  AD_display[3]=0x00;
  AD_display[4]=smg_tab[ad_value/1000];
  AD_display[5]=smg_tab[ad_value/100%10];
  AD_display[6]=smg_tab[ad_value/10%10];
  AD_display[7]=smg_tab[ad_value%10];
}

void set_ad_init()
{
    
    
  set_ad_display[0]=smg_tab[VH_limit/1000];
  set_ad_display[1]=smg_tab[VH_limit/100%10];
  set_ad_display[2]=smg_tab[VH_limit/10%10];
  set_ad_display[3]=smg_tab[VH_limit%10];
  set_ad_display[4]=smg_tab[VL_limit/1000];
  set_ad_display[5]=smg_tab[VL_limit/100%10];
  set_ad_display[6]=smg_tab[VL_limit/10%10];
  set_ad_display[7]=smg_tab[VL_limit%10];
}

void frequence_get()
{
    
    
  if(flag500ms)
  {
    
    
    flag500ms=0;
	TR0=0;					 //关闭定时器0计数
	frequence=TH0*256+TL0;	 //统计500ms内脉冲数
	frequence=frequence*2;	 //乘以2就是1s内脉冲数,就是频率
	TH0=TL0=0;
	TR0=1;
  }
}

void fre_display_init()
{
    
    
  frequence_get();
  fre_display[0]=0x40;
  fre_display[1]=smg_tab[2];
  fre_display[2]=0x40;
  fre_display[3]=smg_tab[frequence/10000];
  fre_display[4]=smg_tab[frequence/1000%10];
  fre_display[5]=smg_tab[frequence/100%10];
  fre_display[6]=smg_tab[frequence/10%10];
  fre_display[7]=smg_tab[frequence%10];
}

void period_display_init()
{
    
    
  period=1000000/frequence;
  
  period_display[0]=0x40;
  period_display[1]=smg_tab[2];
  period_display[2]=0x40;
  period_display[3]=smg_tab[period/10000];
  period_display[4]=smg_tab[period/1000%10];
  period_display[5]=smg_tab[period/100%10];
  period_display[6]=smg_tab[period/10%10];
  period_display[7]=smg_tab[period%10];
}

void ask_display_init()
{
    
    
  ask_display[0]=0x00;
  ask_display[1]=0x00;
  ask_display[2]=0x00;
  ask_display[3]=0x00;
  ask_display[4]=0x00;
  ask_display[5]=0x00;
  ask_display[6]=smg_tab[0];
  ask_display[7]=smg_tab[kinds_of_things];

  ask_time_display[0]=smg_tab[ask_shi/10];
  ask_time_display[1]=smg_tab[ask_shi%10];
  ask_time_display[2]=0x40;
  ask_time_display[3]=smg_tab[ask_fen/10];
  ask_time_display[4]=smg_tab[ask_fen%10];
  ask_time_display[5]=0x40;
  ask_time_display[6]=smg_tab[ask_miao/10];
  ask_time_display[7]=smg_tab[ask_miao%10];
}

void Timer1Init(void)		//1毫秒@11.0592MHz
{
    
    
	AUXR |= 0x40;		//定时器时钟1T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0xCD;		//设置定时初值
	TH1 = 0xD4;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1=1;
	EA=1;
}

void Timer0Init(void)		//1毫秒@11.0592MHz
{
    
    
	AUXR &= 0x7f;		//定时器时钟1T模式
	TMOD = 0x04;		//设置定时器模式
	TL0 = 0X00;		//设置定时初值
	TH0 = 0X00;		//设置定时初值	
	TR0 = 1;		//定时器0开始计时
	ET0=1;
}

void timer1() interrupt 3
{
    
    
  uchar i;
  uchar smg_count;
  uint ms500_count;

  smg_count++;
  ms500_count++;

  if(smg_count==3)
  {
    
    
    smg_count=0;

	P2=(P2&0X1F)|0XC0;
	P0=smg_wei[i];
	P2=P2&0X1F;

	if(mode_display==0)		//时间显示界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~time_display[i];
	  P2=P2&0X1F;
	}

	if(mode_display==1)	   //电压显示界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~AD_display[i];
	  P2=P2&0X1F;
	}

	if(mode_display==2)	   //频率显示界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~fre_display[i];
	  P2=P2&0X1F;
	}

	if(mode_display==3)	   //设置时间界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~set_time_display[i];
	  P2=P2&0X1F;
	}

	if(mode_display==4)	   //设置电压上下限界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~set_ad_display[i];	  
	  P2=P2&0X1F;
	}

	if(mode_display==5)	   //周期显示界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~period_display[i];	   
	  P2=P2&0X1F;
	}

	if(mode_display==6)	  //查询界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~ask_display[i];	   
	  P2=P2&0X1F;
	}

	if(mode_display==7)	  //查询事件发生时间界面
	{
    
    
	  P2=(P2&0X1F)|0Xe0;
	  P0=~ask_time_display[i];	   
	  P2=P2&0X1F;
	}

	i++;
	if(i==8)
	i=0;
  }

  if(ms500_count==500)
  {
    
    
    ms500_count=0;
	flag500ms=1;
	blink_flag=~blink_flag;
  }
}

void Delay10ms()		//@11.0592MHz
{
    
    
	unsigned char i, j;

	i = 108;
	j = 145;
	do
	{
    
    
		while (--j);
	} while (--i);
}

按键模块

#include "key.h"

unsigned char key_init()
{
    
    
  static unsigned char key_state=0;
  unsigned char key1,key2;
  unsigned char key_value=0;
  unsigned char key_press;
  
  P30=0; P31=0; P32=0; P33=0; P34=1; P35=1; P42=1; P44=1;
  if(P34==0) key1=0xe0;
  if(P35==0) key1=0xd0;
  if(P42==0) key1=0xb0;
  if(P44==0) key1=0x70;
  if((P34==1)&&(P35==1)&&(P42==1)&&(P44==1))
  key1=0xf0;
  
  P30=1; P31=1; P32=1; P33=1; P34=0; P35=0; P42=0; P44=0;
  if(P30==0) key2=0x0e;
  if(P31==0) key2=0x0d;
  if(P32==0) key2=0x0b;
  if(P33==0) key2=0x07;
  if((P30==1)&&(P31==1)&&(P32==1)&&(P33==1))
  key2=0x0f;
  
  key_press=key1|key2;
  
  switch(key_state)
  {
    
    
    case 0:
	if(key_press!=0xff)
	key_state=1;
	break;

	case 1:
    if(key_press!=0xff)
	{
    
    
	  if(key_press==0x7e)  key_value=7;
	  if(key_press==0x7d)  key_value=6;
	  if(key_press==0x7b)  key_value=5;
	  if(key_press==0x77)  key_value=4;
	  
	  if(key_press==0xBe)  key_value=11;
	  if(key_press==0xBd)  key_value=10;
	  if(key_press==0xBb)  key_value=9;
	  if(key_press==0xB7)  key_value=8;
	  
	  if(key_press==0xDe)  key_value=15;
	  if(key_press==0xDd)  key_value=14;
	  if(key_press==0xDb)  key_value=13;
	  if(key_press==0xD7)  key_value=12;
	  
	  if(key_press==0xEe)  key_value=19;
	  if(key_press==0xEd)  key_value=18;
	  if(key_press==0xEb)  key_value=17;
	  if(key_press==0xE7)  key_value=16;
	  
	  key_state=2; 
	}
	else
	key_state=0;
	break;

	case 2:
	if(key_press==0xff)
	key_state=0;
	break;
  }  
  return key_value; 
}

EEPROM模块

/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台(12MHz)
  日    期: 2011-8-9
*/

#include "iic.h"

//总线启动条件
void IIC_Start(void)
{
    
    
	SDA = 1;
	SCL = 1;
	somenop;
	SDA = 0;
	somenop;
	SCL = 0;	
}

//总线停止条件
void IIC_Stop(void)
{
    
    
	SDA = 0;
	SCL = 1;
	somenop;
	SDA = 1;
}
/*IIC总线协议规定,每传送一个字节数据后,都要有一个应答信号,以确定数据传送是否被对方收到,
应答信号由接收设备产生,在SCL为高电平期间,接收设备将SDA拉为低电平表示数据传输正确,即产生了应答。*/
//应答位控制
void IIC_Ack(unsigned char ackbit)	 //当ackbit为1时,表示单片机对从设备发送来数据的应答
                          //当ackbit为0时,表示主机接收了最后一个字节,因此不再应答,结束通信
{
    
    
	if(ackbit) 
	{
    
    	
		SDA = 0;
	}
	else 
	{
    
    
		SDA = 1;
	}
	somenop;
	SCL = 1;
	somenop;
	SCL = 0;
	SDA = 1; 
	somenop;
}

//等待应答
bit IIC_WaitAck(void)
{
    
    
	SDA = 1;
	somenop;
	SCL = 1;
	somenop;
	if(SDA)    //在SCL为高电平期间,因为接收设备未将SDA拉低,所以默认未接收到应答,结束IIC通信
	{
    
       
		SCL = 0;
		IIC_Stop();
		return 0;
	}
	else  		//接收到应答,返回1,继续下一个数据字节的传输
	{
    
     
		SCL = 0;
		return 1;
	}
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
    
    
	unsigned char i;
	for(i=0;i<8;i++)
	{
    
       
		if(byt&0x80) 
		{
    
    	
			SDA = 1;
		}
		else 
		{
    
    
			SDA = 0;
		}
		somenop;
		SCL = 1;
		byt <<= 1;
		somenop;
		SCL = 0;
	}
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
    
    
	unsigned char da;
	unsigned char i;
	
	for(i=0;i<8;i++)
	{
    
       
		SCL = 1;
		somenop;
		da <<= 1;
		if(SDA) 
		da |= 0x01;
		SCL = 0;
		somenop;
	}
	return da;
}

unsigned char read_pcf8591(unsigned char addr)
{
    
    
  unsigned char temp;
  ET1=0;
  ET0=0;
  IIC_Start();
  IIC_SendByte(0x90);
  IIC_WaitAck();
  IIC_SendByte(addr);
  IIC_WaitAck();

  IIC_Start();
  IIC_SendByte(0x91);
  IIC_WaitAck();
  temp=IIC_RecByte();
  IIC_Ack(0);
  IIC_Stop();
  ET1=1;
  ET0=1;
  return temp;
}

void Write_eeprom(unsigned char addr,unsigned char dat)
{
    
    
  IIC_Start();
  IIC_SendByte(0xa0);
  IIC_WaitAck();
  IIC_SendByte(addr);
  IIC_WaitAck();
  IIC_SendByte(dat);
  IIC_WaitAck();
  IIC_Stop();
}

unsigned char Read_eeprom(unsigned char addr)
{
    
    
  unsigned char temp;
  
  IIC_Start();
  IIC_SendByte(0xa0);
  IIC_WaitAck();
  IIC_SendByte(addr);
  IIC_WaitAck();

  IIC_Start();
  IIC_SendByte(0xa1);
  IIC_WaitAck();
  temp=IIC_RecByte();
  IIC_Ack(0);
  IIC_Stop();
  return temp;
}

DS1302模块

#include "ds1302.h"
/********************************************************************/ 
/*单字节写入一字节数据*/
void Write_Ds1302_Byte(unsigned char dat) 
{
    
    
	unsigned char i;
	SCK = 0;
	for (i=0;i<8;i++) 
	{
    
     
		if (dat & 0x01) 	// 等价于if((addr & 0x01) ==1) 
		{
    
    
			SDA_SET;		//#define SDA_SET SDA=1 /*电平置高*/
		}
		else 
		{
    
    
			SDA_CLR;		//#define SDA_CLR SDA=0 /*电平置低*/
		}		 
		SCK_SET;
		SCK_CLR;		
		dat = dat >> 1; 
	} 
}
/********************************************************************/ 
/*单字节读出一字节数据*/
unsigned char Read_Ds1302_Byte(void) 
{
    
    
	unsigned char i, dat=0;	
	for (i=0;i<8;i++)
	{
    
    	
		dat = dat >> 1;
		if (SDA_R) 	  //等价于if(SDA_R==1)    #define SDA_R SDA /*电平读取*/	
		{
    
    
			dat |= 0x80;
		}
		else 
		{
    
    
			dat &= 0x7F;
		}
		SCK_SET;
		SCK_CLR;
	}
	return dat;
}

/********************************************************************/ 
/*向DS1302 单字节写入一字节数据*/
void Ds1302_Single_Byte_Write(unsigned char addr, unsigned char dat)
{
    
     

	RST_CLR;			/*RST脚置低,实现DS1302的初始化*/
	SCK_CLR;			/*SCK脚置低,实现DS1302的初始化*/

	RST_SET;			/*启动DS1302总线,RST=1电平置高 */
	addr = addr & 0xFE;	 
	Write_Ds1302_Byte(addr); /*写入目标地址:addr,保证是写操作,写之前将最低位置零*/	
	Write_Ds1302_Byte(dat);	 /*写入数据:dat*/
	RST_CLR;				 /*停止DS1302总线*/
}

/********************************************************************/ 
/*从DS1302单字节读出一字节数据*/
unsigned char Ds1302_Single_Byte_Read(unsigned char addr) 
{
    
     
	unsigned char temp;
	RST_CLR;			/*RST脚置低,实现DS1302的初始化*/
	SCK_CLR;			/*SCK脚置低,实现DS1302的初始化*/

	RST_SET;	/*启动DS1302总线,RST=1电平置高 */	
	addr = addr | 0x01;	 
	Write_Ds1302_Byte(addr); /*写入目标地址:addr,保证是读操作,写之前将最低位置高*/
	temp=Read_Ds1302_Byte(); /*从DS1302中读出一个字节的数据*/		
	RST_CLR;	/*停止DS1302总线*/

	SDA_CLR;
	return temp;
}

void ds1302_init()
{
    
    
  Ds1302_Single_Byte_Write(ds1302_control_addr,0x00);
  Ds1302_Single_Byte_Write(ds1302_sec_addr,0x55);
  Ds1302_Single_Byte_Write(ds1302_min_addr,0x59);
  Ds1302_Single_Byte_Write(ds1302_hr_addr,0x23);
  Ds1302_Single_Byte_Write(ds1302_control_addr,0x80);
}

以上就是代码全部内容,觉得有用的话,留个关注和赞再走吧~
附上工程包:蓝桥杯第七届国赛试题

猜你喜欢

转载自blog.csdn.net/qq_43280087/article/details/105310283