电子闹钟
功能要求:
1、 实时时钟(由数码管显示时:分,中间的“:”一亮一灭表示1秒,可用小数点表示,也可以用一个LED表示);
2、 时钟的时、分可调节;
3、 闹钟时间可以设置,闹钟初值为8:00,(并保存在EEPROM中,掉电不丢失);
4、 闹钟时间到,蜂鸣器响10秒;
5、 按键使用数量不宜超过5个;
6、 整点时间,通过串行口上传一次时钟信息;
7、 自行补充新功能(选做项);
单片机型号:atmega16
流程图
proteus模拟电路图:
代码(适用于atmega16实物,如需用以上电路图模拟还需修改IO口配置)
#include <iom16v.h>
#include <macros.h>
unsigned char led_buf[] = { 0x3F, 0x06, 0x5B, 0x4F,0x66, 0x6D, 0x7D, 0x07,0x7F, 0x6F }; //共阴极数码管编码表,分别表示0~f;
unsigned char position[6] = { 0xfe, 0xfd, 0xef, 0xdf, 0xbf, 0x7f }; // 位选码
unsigned char show[] = "NOW IS : ";
unsigned char transmit_time[10] = "0123456789";
unsigned char maohao = ':', temp[6];
unsigned char time[3], time_set[3], YuSheTime[3] = { 0, 0, 8 }; //时、分、秒计数和设置单元及定时时间8:00,确保掉电不清除
unsigned char dis_buf[10]; //显示缓冲区,存放要显示的10个字符的段码值
unsigned char timecounterofkeys, timecounter;
unsigned char clock_state = 2, set_state, return_time;
int time_add, point, set_on, time_1second, key_stime_ok;
#define key_input PINB //按键输入口
#define key_mask 0B00001111 //按键输入屏蔽码
#define key_no 0
#define key_s1 1 //s1按键
#define key_s2 2 //s2按键
#define key_s3 3 //s3按键
#define key_s4 4 //s4按键
#define key_state_0 0 //按键状态0 1 2
#define key_state_1 1
#define key_state_2 2
void io_init(void);//I/O口初始化函数//
void T0_init(void);//T/C0初始化函数///
void usart_init(unsigned int baud);//usart初始化//
void usart_transmit_byte(unsigned char data);//发送一个字符//
void usart_transmit_string(unsigned char* buf);//发送一个字符串//
unsigned char usart_recieve_byte(void);//接收一个字符//
void time_to_disbuffer(unsigned char* time);//时钟时间送显示缓冲区函数//
void display(void);//时钟时间送显示缓冲区函数//
void timer0_comp_isr(void);//显示输出函数///
unsigned char read_key(void);//读取按键函数//
void time_dealer(void);//时间处理函数//
void zhengdianhanshu(void);//整点时间函数//
void main(void);//主函数
//I/O口初始化函数//
void io_init(void)
{
DDRC = 0xFF; //LED段码输出
DDRD = 0xfe; //LED位控输出(1111 1110)
PORTD = 0xFF; //输出高电平
DDRB = 0xe0; //按钮输入口(1110 0000)
PORTB = 0xFF;
DDRA = 0xff; //输出口
PORTA = 0xff; //输出高电平
}
///T/C0初始化函数///
void T0_init(void)
{
OCR0 = 0xF9; //OCR0 = 0xF9(249),(249+1)/125=2ms
TCCR0 = 0x0B; //内部时钟,CTC模式,64分频,8M/64=125KHz
}
usart初始化//
void usart_init(unsigned int baud)
{
UBRRH = (unsigned char)(baud >> 8);
UBRRL = (unsigned char)baud; // 波特率:4800,时钟频率:8MHz (0110 0111)
UCSRA = 0x20;
UCSRB = (1 << TXEN) | (1 << RXEN); //发送使能|接收时能(0001 1000)
UCSRC = (1 << URSEL) | (3 << UCSZ0); //异步模式,帧格式:1个起始位,8个数据位,无校验,1个停止位;
}
发送一个字符//
void usart_transmit_byte(unsigned char data)
{
while (!(UCSRA & (1 << UDRE)))
{
;
}
UDR = data;
}
计时器Timer0:比较匹配中断服务,定时:2ms
#pragma interrupt_handler timer0_comp_isr: iv_TIMER0_COMP //计时器Timer0:比较匹配输出
void timer0_comp_isr(void)
{
display();
if (++timecounterofkeys >= 5)
{
timecounterofkeys = 0;
key_stime_ok = 1; //10ms时,给key_stime_ok赋值为1
if (!(++timecounter % 25))
{
set_on = !set_on;
}
if (timecounter >= 100) //判断是否达到一秒
{
timecounter = 0;
time_1second = 1;
}
}
}
发送一个字符串//
void usart_transmit_string(unsigned char* databuf)
{
while (*databuf != '\0')
{
usart_transmit_byte(*databuf++);
}
}
接收一个字符//
unsigned char usart_recieve_byte(void)
{
// 等待接收数据
while (!(UCSRA & (1 << RXC)))
;
// 从缓冲器中获取并返回数据
return UDR;
}
时钟时间送显示缓冲区函数//
void time_to_disbuffer(unsigned char* time)
{
unsigned char i, j = 0;
for (i = 0; i <= 2; i++)
{
dis_buf[j++] = time[i] % 10;
dis_buf[j++] = time[i] / 10;
}
}
///显示输出函数///
void display(void)
{
static unsigned char posit = 2; //从分钟开始显示
PORTD = 0xFF; //!输出高电平
PORTC = led_buf[dis_buf[posit]]; //缓存段码
if (set_on && (posit == clock_state)) //被调位闪烁
{
PORTC = 0x00;
}
if (point && posit == 4) //控制第四位小数点闪烁
{
PORTC |= 0x80;
}
PORTD = position[posit]; //控制输出的位
if (++posit >= 6)
{
posit = 2; //从分钟开始显示
}
}
读取按键函数//
unsigned char read_key(void)
{
static unsigned char key_state = 0, key_press;
unsigned char key_return = key_no; //相当于key_return =0;
key_press = key_input & key_mask; //读取按键I/O电平
switch (key_state)
{
case key_state_0: //按键初始态
if (key_press != key_mask)
{
key_state = key_state_1;
}
break; //键被按下,状态转换到键确认态
case key_state_1: //按键确认态
if (key_press == (key_input & key_mask))
{
if (key_press == 0b00001110)
{
key_return = key_s1;
}
else if (key_press == 0b00001101)
{
key_return = key_s2;
}
else if (key_press == 0b00001011)
{
key_return = key_s3;
}
else if (key_press == 0b00000111)
{
key_return = key_s4;
}
key_state = key_state_2; // 状态转换到键释放态
}
else
{
key_state = key_state_0; // 按键已抬起,转换到按键初始态
}
break;
case key_state_2:
if (key_press == key_mask)
{
key_state = key_state_0;
}
break; //按键已释放,转换到按键初始态
}
return key_return;
}
//时间处理函数//
void time_dealer(void)
{
while (1)
{
if (++time[0] >= 60) // 秒加1
{
time[0] = 0; //秒超过60,秒数清0
if (++time[1] >= 60)
{
time[1] = 0; //分数超过60,分清0
if (++time[2] >= 24)
{
time[2] = 0; //时超过24,时清0
}
}
}
}
}
//整点时间函数//
void zhengdianhanshu(void)//整点时间,通过串行口上传一次时钟信息/
{
if ((time[1] == 0) && (time[0] == 0)) //分秒同时为零,表示整点,向串口上传一次时间
{
temp[5] = time[2] % 10;
temp[4] = (unsigned char)time[2] / 10;
temp[3] = time[1] % 10;
temp[2] = (unsigned char)time[1] / 10;
temp[1] = time[0] % 10;
temp[0] = (unsigned char)time[0] / 10;
usart_transmit_string(show);
usart_transmit_byte(transmit_time[temp[4]]);
usart_transmit_byte(transmit_time[temp[5]]);
usart_transmit_byte(maohao);
usart_transmit_byte(transmit_time[temp[2]]);
usart_transmit_byte(transmit_time[temp[3]]);
usart_transmit_byte(maohao);
usart_transmit_byte(transmit_time[temp[0]]);
usart_transmit_byte(transmit_time[temp[1]]);
usart_transmit_string("\r\n");
}
}
主函数//
void main(void)
{
unsigned char key_temp, i;
//初始化
T0_init(); //T/C0初始化
io_init(); //I/O口初始化
usart_init(103); //usart初始化,波特率设置为4800;
//设时间初值07:59:00
time[2] = 7;
time[1] = 59;
time[0] = 0;
TIMSK = 0x12; //允许T/C1比较匹配A中断,允许T/C0比较匹配中断,开放全局中断
SEI();
while (1)
{
if (time_1second) //1秒到
{
time_1second = 0;
point = ~point; //闪烁标志,一秒亮,一秒灭
// time_dealer();
if (++time[0] >= 60) //! 秒加1,
{
time[0] = 0; //!秒数超过60,秒数清0
if (++time[1] >= 60) //!分加1
{
time[1] = 0; //!分数超过60,分数清0
if (++time[2] >= 24) //!时加1
{
time[2] = 0; //!时数超过24,时数清0
}
}
}
//最初十秒不操作就进入初始设置时间
if ((++return_time >= 10) && (clock_state != 6))
{
clock_state = 6;
}
if (clock_state == 6)
{
time_to_disbuffer(time);
}
zhengdianhanshu();
/*整点时间,通过串行口上传一次时钟信息*/
/* if ((time[1] == 0) && (time[0] == 0)) //!分钟,秒钟为零,表示整点,就上传一次时间
{
temp[5] = time[2] % 10;
temp[4] = (unsigned char)time[2] / 10;
temp[3] = time[1] % 10;
temp[2] = (unsigned char)time[1] / 10;
temp[1] = time[0] % 10;
temp[0] = (unsigned char)time[0] / 10;
usart_transmit_string(show);
usart_transmit_byte(transmit_time[temp[4]]);
usart_transmit_byte(transmit_time[temp[5]]);
usart_transmit_byte(maohao);
usart_transmit_byte(transmit_time[temp[2]]);
usart_transmit_byte(transmit_time[temp[3]]);
usart_transmit_byte(maohao);
usart_transmit_byte(transmit_time[temp[0]]);
usart_transmit_byte(transmit_time[temp[1]]);
usart_transmit_string("\r\n");
}
*/
/*设定闹钟时间到,蜂鸣器响10秒*/
if ((time[2] == YuSheTime[2]) && (time[1] == YuSheTime[1]) && (time[0] == YuSheTime[0]))
{
PORTA = 0x7f; //(0111 1111),低电平蜂鸣器响
}
if ((time[2] == YuSheTime[2]) && (time[1] == YuSheTime[1]) && (time[0] == YuSheTime[0] + 10))
{
PORTA = 0xff; //(1111 1111),高电平蜂鸣器关
}
}
if (key_stime_ok) //10ms到,键处理
{
key_stime_ok = 0;
key_temp = read_key(); //调用按键接口程序
if (key_temp) //确认有按键按下
{
return_time = 0; //置零时间,直至等待十秒不操作
if (key_temp == key_s1) // s1键按下,设定时钟时间功能
{
if (++clock_state >= 7)
{
clock_state = 2;
}
if (clock_state == 2)
{
for (i = 0; i <= 2; i++)
{
time_set[i] = 0;
}
time_to_disbuffer(time_set);
}
if (clock_state == 6)
{
for (i = 0; i <= 2; i++)
{
time[i] = time_set[i];
}
time_to_disbuffer(time);
}
}
if (key_temp == key_s2) //s2键按下,设定闹钟时间功能
{
if (++clock_state >= 7)
{
clock_state = 2;
}
if (clock_state == 2) //不按按钮,设定闹钟初始值为08:00
{
time_to_disbuffer(YuSheTime);
}
if (clock_state == 6)
{
for (i = 0; i <= 2; i++)
{
YuSheTime[i] = time_set[i];
}
time_to_disbuffer(YuSheTime);
}
}
if ((clock_state != 6) && (key_temp == key_s3)) //每按一下s3,被调试位数值加一
{
if (clock_state % 2)
{
time_set[clock_state / 2] += 10;
}
else
{
if ((time_set[clock_state / 2] % 10) == 9)
{
time_set[clock_state / 2] -= 9;
}
else
{
time_set[clock_state / 2] += 1;
}
}
if (time_set[0] >= 60)
{
time_set[0] -= 60;
}
if (time_set[1] >= 60)
{
time_set[1] -= 60;
}
if (time_set[2] >= 24)
{
time_set[2] -= 10;
}
time_to_disbuffer(time_set); //设置时间送入显示缓存
}
if (key_temp == key_s4) //s4按下,向串口传输当前时间
{
temp[5] = time[2] % 10;
temp[4] = (unsigned char)time[2] / 10;
temp[3] = time[1] % 10;
temp[2] = (unsigned char)time[1] / 10;
temp[1] = time[0] % 10;
temp[0] = (unsigned char)time[0] / 10;
usart_transmit_string(show);
usart_transmit_byte(transmit_time[temp[4]]);
usart_transmit_byte(transmit_time[temp[5]]);
usart_transmit_byte(maohao);
usart_transmit_byte(transmit_time[temp[2]]);
usart_transmit_byte(transmit_time[temp[3]]);
usart_transmit_byte(maohao);
usart_transmit_byte(transmit_time[temp[0]]);
usart_transmit_byte(transmit_time[temp[1]]);
usart_transmit_string("\r\n");
}
}
}
}
}