【藍橋杯單片機組第八屆省賽】— 基於單片機的電子鐘程序設計與調試

目錄

 

1、初賽試題

2、試題分析與代碼

3、代碼分析


1、初賽試題

 1.1 基本要求

(1)使用 CT107D 單片機競賽板, 完成“電子鐘” 功能的程序設計與調試;
(2)設計與調試過程中,可參考組委會提供的“資源數據包”;
(3)Keil 工程文件以准考證號命名, 完成設計後, 提交完整、 可編譯的 Keil工程文件到服務器。

 1.2 硬件框圖

  1.3 功能描述

   1.3.1 初始化

 (1)關閉蜂鳴器、繼電器等無關外設;

 (2) 設備初始化時鐘爲 23 時 59 分 50 秒,鬧鐘提醒時間 0 時 0 分 0 秒。

   1.3.2 顯示功能

 (1)時間顯示格式

 (2)溫度顯示格式

   1.3.3 按鍵功能

 (1)按鍵 S7 定義爲“時鐘設置”按鍵,通過該按鍵可切換選擇待調整的時、分、秒, 當前選擇的顯示單元以 1 秒爲間隔亮滅, 時、分、秒的調整需注意數據邊界屬性。

 (2)按鍵 S6 定義爲“鬧鐘設置”按鍵,通過該按鍵可進入鬧鐘時間設置功能,數碼管顯示當前設定的鬧鐘時間。

 (3)按鍵 S5 定義爲“加”按鍵,在“時鐘設置”或“鬧鐘設置”狀態下,每次按下該按鍵當前選擇的單元(時、分或秒)增加 1 個單位。
 (4)按鍵 S4 定義爲“減”按鍵,在“時鐘設置”或“鬧鐘設置”狀態下,每次按下該按鍵當前選擇的單元(時、分或秒)減少 1 個單位。
 (5)按鍵功能說明:按鍵 S4、 S5 的“加”、“減” 功能只在“時鐘設置”或“鬧鐘設置”狀態下有效;在 “時鐘顯示”狀態下,按下 S4 按鍵,顯示溫度數據,鬆開按鍵,返回“時鐘顯示”界面。

 1.4 鬧鐘提示功能

 (1)指示燈 L1 以 0.2 秒爲間隔閃爍,持續 5 秒鐘;
 (2)鬧鐘提示狀態下,按下任意按鍵,關閉閃爍提示功能。

2、試題分析與代碼

首先需要掌握的模塊有:實時時鐘、DS18B02溫度傳感器、數碼管、獨立按鍵、led燈,這些都必須要掌握的,最終要的是模塊結合在一起實現功能,怎麼結合需要自己想,這點比較重要。對於按鍵函數,要把按鍵和數碼管結合在一起,控制加減運算。下面先給出代碼:

 ds1302.c

#include "ds1302.h"
unsigned char time[]={50,59,23,0,0,0,0};
/********************************************************************/ 
/*單字節寫入一字節數據*/
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/10<<4)|(dat%10));	 /*寫入數據:dat*/
	RST_CLR;				 /*停止DS1302總線*/
}

/********************************************************************/ 
/*從DS1302單字節讀出一字節數據*/
unsigned char Ds1302_Single_Byte_Read(unsigned char addr) 
{ 
	unsigned char temp;
	unsigned char dat1,dat2;
	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中讀出一個字節的數據*/

	dat1=temp/16;
	dat2=temp%16;
	temp=10*dat1+dat2;		
	//RST_CLR;	/*停止DS1302總線*/
	SD=0;
	return temp;
}

void Ds1302_init(){
unsigned char i,add;
add=0x80;
Ds1302_Single_Byte_Write(0x8e,0x00);
for(i=0;i<7;i++){
Ds1302_Single_Byte_Write(add,time[i]);
add+=2;
}
Ds1302_Single_Byte_Write(0x8e,0x80);
}

void Ds1302_Gettime(){
unsigned char i,add;
add=0x81;
Ds1302_Single_Byte_Write(0x8e,0x00);
for(i=0;i<7;i++){
time[i]=Ds1302_Single_Byte_Read(add);
add+=2;
}
Ds1302_Single_Byte_Write(0x8e,0x80);
}


 

 ds1302.h


#ifndef  __DS1302_H__
#define  __DS1302_H__

#include<stc15f2k60s2.h>
#include<intrins.h>
/********************************************************************/ 
sbit SCK=P1^7;		
sbit SD=P2^3;		
sbit RST=P1^3;
/********************************************************************/ 
/*復位腳*/
#define RST_CLR	RST=0	/*電平置低*/
#define RST_SET	RST=1	/*電平置高*/
/*雙向數據*/
#define SDA_CLR	SD=0	/*電平置低*/
#define SDA_SET	SD=1	/*電平置高*/
#define SDA_R	SD	/*電平讀取*/	
/*時鐘信號*/
#define SCK_CLR	SCK=0	/*時鐘信號*/
#define SCK_SET	SCK=1	/*電平置高*/
/********************************************************************/ 
#define ds1302_sec_addr			0x80		//秒數據地址
#define ds1302_min_addr			0x82		//分數據地址
#define ds1302_hr_addr			0x84		//時數據地址
#define ds1302_date_addr		0x86		//日數據地址
#define ds1302_month_addr		0x88		//月數據地址
#define ds1302_day_addr			0x8A		//星期數據地址
#define ds1302_year_addr		0x8C		//年數據地址

#define ds1302_control_addr		0x8Ee		//寫保護命令字單元地址
#define ds1302_charger_addr		0x90 		//涓電流充電命令字地址			 
#define ds1302_clkburst_addr	0xBE		//日曆、時鐘突發模式命令字地址

/********************************************************************/ 

/********************************************************************/ 
/*單字節寫入一字節數據*/
extern void Write_Ds1302_Byte(unsigned char dat);
/********************************************************************/ 
/*單字節讀出一字節數據*/
extern unsigned char Read_Ds1302_Byte(void);
  
/********************************************************************/ 
/********************************************************************/ 
/*向DS1302單字節寫入一字節數據*/
extern void Ds1302_Single_Byte_Write(unsigned char addr, unsigned char dat);
/********************************************************************/ 
/*從DS1302單字節讀出一字節數據*/
extern unsigned char Ds1302_Single_Byte_Read(unsigned char addr);

extern unsigned char time[];
extern void Ds1302_init();
extern void Ds1302_Gettime();

#endif	 
/********************************************************************/
//		     	END FILE
/********************************************************************/

 onewire.c

#include "onewire.h"

//單總線延時函數
void Delay_OneWire(unsigned int t)
{
  t=t*12;
  while(t--);
}

//DS18B20芯片初始化
bit Init_DS18B20(void)
{
	bit initflag = 0;
	DQ = 1;
	Delay_OneWire(12);
	DQ = 0;
	Delay_OneWire(80); 
	DQ = 1;
	Delay_OneWire(10); 
	initflag = DQ;    
	Delay_OneWire(5);
  
	return initflag;
}

//通過單總線向DS18B20寫一個字節
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}

//從DS18B20讀取一個字節
unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(5);
	}
	return dat;
}

unsigned char Get_wendu(){
unsigned char high,low;
unsigned char temp;
Init_DS18B20();
Write_DS18B20(0XCC);
Write_DS18B20(0X44);
Delay_OneWire(200);
Init_DS18B20();
Write_DS18B20(0XCC);
Write_DS18B20(0XBE);

low=Read_DS18B20();
high=Read_DS18B20();
temp=(low>>4)|(high<<4);

return temp;
}


 onewire.h

#ifndef _ONEWIRE_H
#define _ONEWIRE_H

#include "stc15f2k60s2.h"

#define OW_SKIP_ROM 0xcc
#define DS18B20_CONVERT 0x44
#define DS18B20_READ 0xbe

//IC引腳定義
sbit DQ = P1^4;

//函數聲明
void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
bit Init_DS18B20(void);
unsigned char Read_DS18B20(void);
unsigned char Get_wendu();
#endif

 text.c

#include<stc15f2k60s2.h>
#include"ds1302.h"
#include"onewire.h"
#define uchar unsigned char
#define uint unsigned int
uchar code tab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XFF,0XC6};
uchar f1,f2,f3,f4,f5,f6,f7,f8;
uchar naozhong[]={0,0,0};
uchar wendu;
uchar shijian=4,naoling=0;
uchar wendu_show=0;
uchar s4=0;
uchar n=0;
uchar led=0;
uchar led_show=0;
uint count=0;

void allinit();
void keyscan();
void delay(uchar ms);
void Time0_init();
void display12(uchar f1,uchar f2);
void display34(uchar f3,uchar f4);
void display56(uchar f5,uchar f6);
void display78(uchar f7,uchar f8);
void delay1();

void main(){
allinit();
Ds1302_init();
Time0_init();
while(1){
   wendu= Get_wendu();
   Ds1302_Gettime();
   if(wendu_show==0)
   {   
      if(shijian==4)
	  {
	     f1=time[2]/10;f2=time[2]%10;f3=10;
		 f4=time[1]/10;f5=time[1]%10;f6=10;
		 f7=time[0]/10;f8=time[0]%10;
	  }
	  else if(shijian==1)
	  {
	     if(time[0]%2==0)
		 {
		     f1=time[2]/10;f2=time[2]%10;f3=10;
		     f4=time[1]/10;f5=time[1]%10;f6=10;
		     f7=time[0]/10;f8=time[0]%10;
		 }
		 else
		 {
		     f1=11;f2=11;f3=10;
		     f4=time[1]/10;f5=time[1]%10;f6=10;
		     f7=time[0]/10;f8=time[0]%10;
		 }
	  }
	  else if(shijian==2)
	  {
	     if(time[0]%2==0)
		 {
		     f1=time[2]/10;f2=time[2]%10;f3=10;
		     f4=time[1]/10;f5=time[1]%10;f6=10;
		     f7=time[0]/10;f8=time[0]%10;
		 }
		 else
		 {
		     f1=time[2]/10;f2=time[2]%10;f3=10;
		     f4=11;f5=11;f6=10;
		     f7=time[0]/10;f8=time[0]%10;
		 }
	   }
	   else if(shijian==3)
	   {
	      if(time[0]%2==0)
		 {
		     f1=time[2]/10;f2=time[2]%10;f3=10;
		     f4=time[1]/10;f5=time[1]%10;f6=10;
		     f7=time[0]/10;f8=time[0]%10;
		 }
		 else
		 {
		     f1=time[2]/10;f2=time[2]%10;f3=10;
		     f4=time[1]/10;f5=time[1]%10;f6=10;
		     f7=11;f8=11;
		 }
	   }

	   if(naoling==4)
	   {
	     Ds1302_Gettime();
		 f1=naozhong[2]/10;f2=naozhong[2]%10;f3=10;
		 f4=naozhong[1]/10;f5=naozhong[1]%10;f6=10;
		 f7=naozhong[0]/10;f8=naozhong[0]%10;
	   }
	   else if(naoling==1)
	   {
	     if(time[0]%2==0)
		 {
		    f1=naozhong[2]/10;f2=naozhong[2]%10;f3=10;
		    f4=naozhong[1]/10;f5=naozhong[1]%10;f6=10;
		    f7=naozhong[0]/10;f8=naozhong[0]%10;
		 }
		 else
		 {
		    f1=11;f2=11;f3=10;
		    f4=naozhong[1]/10;f5=naozhong[1]%10;f6=10;
		    f7=naozhong[0]/10;f8=naozhong[0]%10;
		 }
		}
		else if(naoling==2)
		{
		   if(time[0]%2==0)
		 {
		    f1=naozhong[2]/10;f2=naozhong[2]%10;f3=10;
		    f4=naozhong[1]/10;f5=naozhong[1]%10;f6=10;
		    f7=naozhong[0]/10;f8=naozhong[0]%10;
		 }
		 else
		 {
		    f1=naozhong[2]/10;f2=naozhong[2]%10;f3=10;
		    f4=11;f5=11;f6=10;
		    f7=naozhong[0]/10;f8=naozhong[0]%10;
		 }
		}
		else if(naoling==3)
		{
		   if(time[0]%2==0)
		 {
		    f1=naozhong[2]/10;f2=naozhong[2]%10;f3=10;
		    f4=naozhong[1]/10;f5=naozhong[1]%10;f6=10;
		    f7=naozhong[0]/10;f8=naozhong[0]%10;
		 }
		 else
		 {
		    f1=naozhong[2]/10;f2=naozhong[2]%10;f3=10;
		    f4=naozhong[1]/10;f5=naozhong[1]%10;f6=10;
		    f7=11;f8=11;
		 }
		}
	 }
	 else if(wendu_show==1)
	 {
	   f1=11;f2=11;f3=11;f4=11;f5=11;f6=wendu/10;f7=wendu%10;f8=12;
	 }
	 
	 if((time[2]==naozhong[2])&&(time[1]==naozhong[1])&&(time[0]==naozhong[0]))
	 {
	   led_show=1;
	   EA=1;ET0=1;
	   
	 }
	 keyscan();
	 display12(f1,f2);
	 display34(f3,f4);
	 display56(f5,f6);
	 display78(f7,f8);
	 }

}

void keyscan(){
if(P30==0)
{
   delay(5);
   if(P30==0)
   {
       if(led_show==0)
	   {
	      if(shijian==0)
		  {
		    shijian=4;naoling=0;
		  }
		  else if(shijian==4) shijian=1;
		  else if(shijian==1) shijian=2;
		  else if(shijian==2) shijian=3;
		  else if(shijian==3) shijian=4;
	   }
	   else if(led_show==1)
	   {
	     led_show=0;
		 P2=0X80;
		 P0=0XFF;
	   }
	}
while(!P30);
}
else if(P31==0)
{
   delay(5);
   if(P31==0)
   {
      if(led_show==0)
	  {
	    if(naoling==0)
		{
		   shijian=0;naoling=4;
		}
		else if(naoling==4) naoling=1;
		else if(naoling==1) naoling=2;
		else if(naoling==2) naoling=3;
		else if(naoling==3) naoling=4;
	  }
	  else if(led_show==1)
	  {
	    led_show=0;
		P2=0X80;
		P0=0XFF;
      }
	}
while(!P31);
}
else if(P32==0)
{
   delay(5);
   if(P32==0)
   {
      if(led_show==0)
	  {
	     if(shijian==1)
		 {
		    if(time[2]==23) 
			time[2]=0;
			else
			time[2]++;
			Ds1302_init();
		 }
		 else if(shijian==2)
		 {
		    if(time[1]==59)
			time[1]=0;
			else
			time[1]++;
			Ds1302_init();
		 }
		 else if(shijian==3)
		 {
		    if(time[0]==59)
			time[0]=0;
			else
			time[0]++;
			Ds1302_init();
		 }
		 else if(naoling==1)
		 {
		    if(naozhong[2]==23)
			naozhong[2]=0;
			else
			naozhong[2]++;
		 }
		 else if(naoling==2)
		 {
		    if(naozhong[1]==59)
			naozhong[1]=0;
			else
			naozhong[1]++;
		 }
		 else if(naoling==3)
		 {
		    if(naozhong[0]==59)
			naozhong[0]=0;
			else
			naozhong[0]++;
		 }
		}
		else if(led_show==1)
		{
		  led_show=0;
		  P2=0X80;
		  P0=0XFF;
		}
	}
while(!P32);
}
else if(P33==0)
{
   delay(5);
   if(P33==0)
   {
     s4=1;
	 if(shijian==4) wendu_show=1;
   }
}

if((s4==1)&&(P33==1))
{
   s4=0;
   wendu_show=0;
   if(led_show==0)
   {
      if(shijian==1)
	  {
	     if(time[2]==0)
		 time[2]=23;
		 else
		 time[2]--;
		 Ds1302_init();
	  }
	  else if(shijian==2)
	  {
	     if(time[1]==0)
		 time[1]=59;
		 else
		 time[1]--;
		 Ds1302_init();
      }
	  else if(shijian==3)
	  {
	     if(time[0]==0)
		 time[0]=59;
		 else
		 time[0]--;
		 Ds1302_init();
	  }
	  else if(naoling==1)
	  {
	     if(naozhong[2]==0)
		 naozhong[2]=23;
		 else
		 naozhong[2]--;
	  }
	  else if(naoling==2)
	  {
	     if(naozhong[1]==0)
		 naozhong[1]=59;
		 else
		 naozhong[1]--;
	  }
	  else if(naoling==3)
	  {
	     if(naozhong[0]==0)
		 naozhong[0]=59;
		 else
		 naozhong[0]--;
	  }
	}
	else if(led_show==1)
	{
	  led_show=0;
	  P2=0X80;
	  P0=0XFF;
	}
}
}

void Time0_init(){
TMOD=0X01;	//定時器T0,工作方式2爲16爲定時/計數器;
TH0=(65536-5000)/256;  //高四位寄存器的值;
TL0=(65536-5000)%256;  //低四位寄存器的值;
TR0=1; //啓動定時器T0 ;

}

void Time0_service() interrupt 1
{
  TH0=(65536-5000)/256;  //高四位寄存器的值;
  TL0=(65536-5000)%256;  //低四位寄存器的值;
  n++;
  count++;
if((n==40)&&(led_show==1))
{
   n=0;
   if(led==0)
   { 
     P2=0X80;
	 P0=0XFF;
	 led=1;
   }
   else if(led==1)
   {
    P2=0X80;
	P0=0XFE;
    led=0;
   }
 }
 if(count==1000)
 {
   count=0;
   EA=0;ET0=0;
   led_show=0;
   P2=0X80;P0=0XFF;
}
}


void allinit(){
P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;
P2=0XC0;P0=0XFF;P2=0XFF;P0=0XFF;
}

void delay(uchar ms){
uchar k;
uchar i,j;
for(k=ms;k>0;k--)
{
  i=12;
  j=169;
  do
  {
	 while(j--);
  }
  while(i--);
 }
}

void delay1(){
unsigned char i,j;
for(i=0;i<11;i++)
for(j=0;j<200;j++);
}

void display12(uchar f1,uchar f2){
P2=0XC0;P0=0X01;P2=0XFF;P0=tab[f1];
delay1();
P2=0XC0;P0=0X02;P2=0XFF;P0=tab[f2];
delay1();
}

void display34(uchar f3,uchar f4){
P2=0XC0;P0=0X04;P2=0XFF;P0=tab[f3];
delay1();
P2=0XC0;P0=0X08;P2=0XFF;P0=tab[f4];
delay1();
}

void display56(uchar f5,uchar f6){
P2=0XC0;P0=0X10;P2=0XFF;P0=tab[f5];
delay1();
P2=0XC0;P0=0X20;P2=0XFF;P0=tab[f6];
delay1();
}

void display78(uchar f7,uchar f8){
P2=0XC0;P0=0X40;P2=0XFF;P0=tab[f7];
delay1();
P2=0XC0;P0=0X80;P2=0XFF;P0=tab[f8];
delay1();
}

3、代碼分析

text.c
主函數上有時間顯示模塊、鬧鐘模塊、溫度模塊、鬧鈴模塊、按鍵控制函數、數碼管顯示模塊。

keyscan()函數:

P30(S7):控制着時間顯示模塊,還有小時、分鐘、秒之間的切換。

P31(S6):控制着鬧鐘顯示模塊,還有小時、分鐘、秒之間的切換。

P32(S5):控制着時間、鬧鐘顯示模塊,可以使小時、分鐘、秒加1。

P33(S4):如果是在時間顯示模塊,按下S4顯示溫度,另外控制着時間、鬧鐘顯示模塊,可以使小時、分鐘、秒減1。

最後如果設置鬧鐘與時間顯示相等時,LED1以0.2秒亮滅閃爍5s,並且在閃爍過程中,按下任意鍵停止閃爍。

還有定時器0的相關寄存器配置。

注意:unsigned char和unsigned int的範圍不同。

       unsigned char 是無符號字符,數據長度是8位,表示值範圍從0~255
  unsigned int 是無符號整數,數據長度是16位(或者32位,看單片機的型號而定),表示範圍從0~65535(或者0~4294967295)

這點對於count的取值定義是十分重要的,因爲count要計數1000次,所以要定義成unsigned int。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章