引语
记录学习路程,抛砖引玉。如有更好的算法或者出现错误,欢迎指点。
题目
- 数码管显示时钟(年、月、日、时、分、秒)
- 时间可调
- 嵌入闹钟系统
- 嵌入秒表系统
实现过程
1.操作说明
- 按键1:时间调节时减一
- 按键2:时间调节时加一
- 按键3:进入时间调节状态(日期调整循环切换顺序为:分、时、日、月、年、秒、完成调整)调整对应的时间会闪烁提示
- 按键4:时间显示页面切换查看时间(顺序循环切换秒、时分、月日、年)
- 按键5:秒表(秒表按顺序循环切换记表、停表、归零)
- 按键6:闹钟一(显示闹钟时间)
- 按键7:闹钟二(显示闹钟时间)
- 按键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];
}
}
}