avr单片机小作业|时钟闹钟

AVRStudio&Proteus 8 Professional:clock

引语

记录学习路程,抛砖引玉。如有更好的算法或者出现错误,欢迎指点。

题目

  1. 数码管显示时钟(年、月、日、时、分、秒
  2. 时间可调
  3. 嵌入闹钟系统
  4. 嵌入秒表系统

实现过程

1.操作说明

  1. 按键1:时间调节时减一
  2. 按键2:时间调节时加一
  3. 按键3:进入时间调节状态(日期调整循环切换顺序为:分、时、日、月、年、秒、完成调整)调整对应的时间会闪烁提示
  4. 按键4:时间显示页面切换查看时间(顺序循环切换秒、时分、月日、年
  5. 按键5:秒表(秒表按顺序循环切换记表、停表、归零
  6. 按键6:闹钟一(显示闹钟时间)
  7. 按键7:闹钟二(显示闹钟时间)
  8. 按键8:设定闹钟(在时分显示页进行设定)

2.电路图与代码

在这里插入图片描述

#include  <avr/io.h>
#include  <util/delay.h>
#define   delay_ms(x)   _delay_ms(x)
#include  <avr/interrupt.h>

const unsigned char disp[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char ledbuf[]={0x00,0x00,0x00,0x00};// 显示缓冲区,分别存放的是千位、百位、十位、个位的段码





int ms10=0,sec=0,min=10,hour=14,day=4,month=12,year=2019;
int clock11=0,clock12=24,clock21=0,clock22=24,CLOCK=0,CLO=-1,STOP_music=0;
int biao_ms=0,biao_sec=0,biao_min=0,biao_hour=0,T=-1,a,b,c,d;



static unsigned char x=-1,k=-1,shanshuo=1,turn=-1,led=0;










/******定时器1的初始化,CTC模式,8分频,中断周期5ms******/
void  disp_init(void)
{
	OCR1A = 4999;		//100Hz=8MHz/(2*8*(1+OCR1A))
	TCCR1A = 0x00;
	TCCR1B = (1 << WGM12);        //CTC模式
	TCCR1B |= (1 << CS11);	//8分频
	TIMSK |= (1 << OCIE1A);	//开比较匹配中断A
}








/******数码管显示函数 ******/
void display(char num,char pos)
{ 	SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0);
	PORTB &= 0x0F; 		//关位选
	PORTB &=~(1<<0);
	SPDR=num;
	while(0==(SPSR&0X80)); 	//等待 SPIF中断结束置位
	PORTB|=(1<<0);
	PORTB |= 1<<(7-pos);
}







/********数码管控制IO初始化*******/
void io_init(void)

{
	DDRD = 0X00;	//八位独立按键
	PORTD = 0XFF;	//PD口8个按键端口输入,上拉
	
	DDRC=0xFF;
	PORTC&=~(1<<7); //74HC595使能

	PORTA = 0x00;   //流水灯
	DDRA  = 0xFF;	//流水灯
	DDRE=0xFF;		//74hc573使能
	PORTE=0xFF;

	PORTB = 0xFF;	//PB4\5\6\7控制数码管位码
	DDRB |= 0xFF;   //(1<<PB7) | (1<<PB6) | (1<<PB5) | (1<<PB4);

	DDRG = 1<<4;
	PORTG = ~1<<4;
}









//扫描键盘,获得键码,更新显示缓冲区、实现翻页 时间调整功能
void key_read(void)
{
	unsigned char i,j; 	 	 	 	 	 	//键码记录
	unsigned char key_num;
	i=PIND; 	 	 	 					//按键表示的数字 并记录
	delay_ms(20);  	 	 					//去按键颤抖
	j = PIND; 								//j=除抖后的按键情况

	if(i == j) 	 	 	 	//二次对比确定按键操作,且有按键按下
	{
		switch (i) 							 //将按键码转换成键值
		{
			case  0x7F: 	CLO=(++CLO)%4;      break;    //8  闹钟浏览  设置
			case  0xBF:   CLOCK=(++CLOCK)%3;
			STOP_music=0;  		break;    //7  闹钟1数据写入EEPROW
			case  0xDF:   STOP_music=1; 	break;	  	  //6  闹钟2数据写入EEPROW
			case  0xEF: 	T=(++T)%3;		break;	  	  //5  秒表计时界面切换 0为秒表清零  1为开始计时 2为计时记录
			case  0xF7: 	x=(++x)%4;		break;	   	  //4  时间、日期显示翻页 0秒毫秒  1分钟小时 2日期 3年份
			
			case  0xFB:   turn=(++turn)%7;break;   	//3  时间日期调整,0分钟 1小时 2日 3月 4年 5秒
			case  0xFE: 								//1
			if(turn==0) min--;       	//时间日期调整,0分钟 1小时 2日 3月 4年 5秒  第一键对应减一      
			if(turn==1) hour--;
			if(turn==2) day--;
			if(turn==3) month--;
			if(turn==4) year--;
			if(turn==5) sec--;

			if(CLO==0)  clock11--;
			if(CLO==1)  clock12--;
			if(CLO==2)  clock21--;
			if(CLO==3)  clock22--;
			break;
			case  0xFD: 			//2 
			if(turn==0) min++;		  //时间日期调整,0分钟 1小时 2日 3月 4年 5秒  第二键对应加一
			if(turn==1) hour++;
			if(turn==2) day++;
			if(turn==3) month++;
			if(turn==4) year++;
			if(turn==5) sec++;

			if(CLO==0)  clock11++;
			if(CLO==1)  clock12++;
			if(CLO==2)  clock21++;
			if(CLO==3)  clock22++;
			break;
			default:      break;
		}
		while(PIND!=0xFF)  ; 				//等待按键松开
	}
}










/******中断服务程序的功能:刷新段码与位控制,用变量k实现轮流刷新的目的******/
ISR(TIMER1_COMPA_vect)

{
	static unsigned char i=0,j=0,timer=0,c=0; //显示刷新标志
	i++;j++;c++;
	k=(++k)%4; 				     	//k 实现轮流刷新数码管

	if(T==1)   timer++;				//计时开始是  秒表开始运作
	if(j>99)  {shanshuo ^= 1;j=0;}  //小数点闪烁周期设置   1s=0.05*100*2

	
	
	
	
	/*************闹钟写操作**************/
	if(c>199)
	{
		c=0;
		if(CLOCK==1)      {EEPROM_write(100,clock11);EEPROM_write(101,clock12);}		//将闹钟1数据写入EEPROW
		if(CLOCK==2)	  {EEPROM_write(102,clock21);EEPROM_write(103,clock22);}		//将闹钟2数据写入EEPROW
	}
	
	
	
	
	
	
	/*******时间调整范围及数字变换*******/
	if(i>1)       {ms10++;i=0;}      //10ms 加一
	if(ms10>99)   {ms10=0;sec++;}	  //100 * 10 =1s
	if(sec>59)    {sec=0;min++;ms10=0;sec=0;}
	if(sec<0)      sec=59;
	if(min>59)    {min=0;hour++;}
	if(min<0)      min=59;
	if(hour>23)   {hour=0;day++;}
	if(hour<0)     hour=23;
	if(day<1)      day=31;
	if(  ((year%4!=0)||(year%100==0 && year%400!=0)) && (day>28)&&(month==2)  )
	{day=1;month++;}			//2月

	if(((year%4==0&&year%100!=0)||year%400==0)&&(day>29)&&(month==2))
	{day=1;month++;}				//闰年2月

	if((day>30)&&(month==4||month==6||month==9||(month==11)))
	{day=1;month++;}				//小月

	if((day>31)&&((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12)))
	{day=1;month++;}  			//大月

	if(month>12)  {month=1;year++;}
	if(month<1)	month=12;
	if(year>2030)  year=2000;
	if(year<2000)	year=2030;

	
	
	
	
	
	/**********秒表数值更新设置**********/
	if(timer>1)          {biao_ms++; timer=0;}     //10ms 加一
	if(biao_ms>99)       {biao_ms=0;biao_sec++;}   //100 * 10 =1s
	if(biao_sec>99)      {biao_sec=0;biao_min++;}


	if(STOP_music==1) {PORTG = 0x00;}        	//停闹钟;
	
	if(clock11>59)		clock11=0;      //0-59min;  max
	if(clock11<0)		clock11=59;

	if(clock12>24)      clock12=0;      //0-23h;
	if(clock12<0)       clock12=24;

	if(clock21>59)		clock21=0;      //2  min;
	if(clock21<0)		clock21=59;

	if(clock22>24)		clock22=0;      // 2  h;
	if(clock22<0)		clock22=24;

	

	/******时间日期调整*******/
	if(turn==0)     //分钟调整
	{
		if(shanshuo==1)
		ledbuf[3] = disp[min%10];
		else
		ledbuf[3] = 0x00;
		
		ledbuf[2] = disp[min/10];
		ledbuf[1] = disp[hour%10];
		ledbuf[0] = disp[hour/10];
		display(ledbuf[k],k);
	}

	if(turn==1)   //小时调整
	{
		if(shanshuo==1)
		ledbuf[1] = disp[hour%10];
		else
		ledbuf[1] = 0x00;

		ledbuf[3] = disp[min%10];
		ledbuf[2] = disp[min/10];
		ledbuf[0] = disp[hour/10];
		display(ledbuf[k],k);
		
	}

	if(turn==2)		//日期调整
	{
		if(shanshuo==1)
		ledbuf[3] = disp[day%10];
		else
		ledbuf[3] = 0x00;
		
		ledbuf[2] = disp[day/10];
		ledbuf[1] = disp[month%10];
		ledbuf[0] = disp[month/10];
		display(ledbuf[k],k);
	}

	if(turn==3)		//月份调整
	{
		if(shanshuo==1)
		ledbuf[1] = disp[month%10];
		else
		ledbuf[1] = 0x00;
		
		ledbuf[3] = disp[day%10];
		ledbuf[2] = disp[day/10];
		ledbuf[0] = disp[month/10];
		display(ledbuf[k],k);
	}

	if(turn==4)		//年份调整
	{
		if(shanshuo==1)
		ledbuf[3] = disp[year%10];
		else
		ledbuf[3] = 0x00;

		ledbuf[2] = disp[(year/10)%10];
		ledbuf[1] = disp[(year/100)%10];
		ledbuf[0] = disp[year/1000];
		display(ledbuf[k],k);
	}

	if(turn==5)     //秒调整
	{
		if(shanshuo==1)
		ledbuf[1] = disp[sec%10];
		else
		ledbuf[1] = 0x00;
		
		ledbuf[3] = disp[ms10%10];
		ledbuf[2] = disp[ms10/10];
		ledbuf[0] = disp[sec/10];
		display(ledbuf[k],k);
	}
	if(turn==6)   {x=0;turn=-1;}			//时间调整结束,返回时间显示 (hour:min)

	display(ledbuf[k],k);      //数码管显示函数

}










/************EEPROW写操作***闹钟*********/

void EEPROM_write(unsigned int Address,unsigned char Data)
{
	while(EECR&(1<<EEWE));			//等待上一次写操作结束
	EEAR = Address;  				//地址
	EEDR = Data;  					//数据
	EECR |= (1<<EEMWE);  			//置位EEMWE
	EECR |= (1<<EEWE);				//置位EEWE启动写操作
}








/************EEPROW读操作***闹钟*********/
int EEPROM_read(unsigned int Address)
{
	while(EECR&(1<<EEWE));			//等待上一次写操作结束
	EEAR = Address;  				//地址
	EECR |= (1<<EERE); 				//置位EEMWE
	return EEDR;  					//返回读取结果
}















//主 函 数 
int main()
{
	ledbuf[3] = 0x00;
	ledbuf[2] = 0x00;
	ledbuf[1] = 0x00;
	ledbuf[0] = 0x00;   //初始显示为 0000
	io_init();
	disp_init();
	sei();

	/********读闹钟设置*********/
	if(CLOCK==0)
	{
		clock11=EEPROM_read(100);
		clock12=EEPROM_read(101);
		clock21=EEPROM_read(102);
		clock22=EEPROM_read(103);
	}

	while (1)
	{
		key_read(); 	 	//键盘扫描
		delay_ms(50); 		//键盘扫描间隔
		
		
		
		
		
		/************闹钟时间未匹配 STOP==0 使得闹钟到达并超时后铃声依旧响************/
		if((((hour==clock12)&&(min==clock11)) || ((hour==clock22)&&(min==clock21)))&&(STOP_music==0))
		{
			PORTG ^=( 1<<4);
			delay_ms(100);               //闹钟一直响,直到第三个按键被按下,显示小时分钟
			x=1;
			led=-1;
			PORTA ^= 0xFF;				//闹钟到时led灯全亮灭闪烁
		}
		
		
		
		
		
		/*秒*/
		if(x==0)
		{
			T=-1;CLO=-1;			//消除其他功能时间显示干扰
			ledbuf[3] = disp[ms10%10];
			ledbuf[2] = disp[ms10/10];
			if(shanshuo==1)  ledbuf[1] = disp[sec%10]|0x80;
			else	ledbuf[1] = disp[sec%10];            //小数点闪烁,周期1s
			ledbuf[0] = disp[sec/10];
			
		}
		
		
		
		
		
		/*小时*/
		if(x==1)
		{
			ledbuf[3] = disp[min%10];
			ledbuf[2] = disp[min/10];
			if(shanshuo==1)   ledbuf[1] = disp[hour%10]|0x80;
			else  ledbuf[1] = disp[hour%10];                  //小数点闪烁,周期1s
			ledbuf[0] = disp[hour/10];
			
		}

		
		
		
		
		/*日期*/
		if(x==2)
		{
			ledbuf[3] = disp[day%10];
			ledbuf[2] = disp[day/10];
			ledbuf[1] = disp[month%10]|0x80;
			ledbuf[0] = disp[month/10];
			
		}
		
		
		
		
		/*年份*/
		if(x==3)
		{
			ledbuf[3] = disp[year%10];
			ledbuf[2] = disp[(year/10)%10];
			ledbuf[1] = disp[(year/100)%10];
			ledbuf[0] = disp[year/1000];
		}
		

		
		
		
		
		/************秒表************/
		if(T==0)		//计时清零
		{
			CLO=-1;x=-1;			//消除其他功能时间显示干扰
			biao_ms=0;   biao_sec=0;
			biao_min=0;  biao_hour=0;

			ledbuf[3] = disp[biao_ms%10];
			ledbuf[2] = disp[biao_ms/10];
			ledbuf[1] = disp[biao_sec%10]|0x80;
			ledbuf[0] = disp[biao_sec/10];
		}
		if(T==1)		//开始计时
		{
			ledbuf[3] = disp[biao_ms%10];
			ledbuf[2] = disp[biao_ms/10];
			ledbuf[1] = disp[biao_sec%10]|0x80;
			ledbuf[0] = disp[biao_sec/10];
		}

		if(T==2)	//计时记录
		{
			a=biao_ms;
			b=biao_sec;

			ledbuf[3] = disp[a%10];
			ledbuf[2] = disp[a/10];
			ledbuf[1] = disp[b%10]|0x80;
			ledbuf[0] = disp[b/10];
		}

		
		
		
		
		
		
		/****************闹钟******************/
		if(CLO==0)		//闹钟1
		{
			T=-1;x=-1;		//消除其他功能时间显示干扰
			if(shanshuo==1)   ledbuf[3] = disp[clock11%10];
			else  ledbuf[3] = 0x00;
			ledbuf[2] = disp[clock11/10];
			ledbuf[1] = disp[clock12%10]|0x80;
			ledbuf[0] = disp[clock12/10];
		}

		if(CLO==1)			//闹钟1
		{
			ledbuf[3] = disp[clock11%10];
			ledbuf[2] = disp[clock11/10];
			if(shanshuo==1)   ledbuf[1] = disp[clock12%10];
			else  ledbuf[1] = 0x00;
			ledbuf[0] = disp[clock12/10];
		}

		if(CLO==2)		//闹钟2
		{
			if(shanshuo==1)   ledbuf[3] = disp[clock21%10];
			else  ledbuf[3] = 0x00;
			ledbuf[2] = disp[clock21/10];
			ledbuf[1] = disp[clock22%10];
			ledbuf[0] = disp[clock22/10];
		}

		if(CLO==3 )		//闹钟2
		{
			ledbuf[3] = disp[clock21%10];
			ledbuf[2] = disp[clock21/10];
			
			if(shanshuo==1)    ledbuf[1] = disp[clock22%10]|0x80;
			else  ledbuf[1] = 0x00;
			ledbuf[0] = disp[clock22/10];
		}
	}
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值