#include #include #defineucharunsigned char
#defineuintunsignedint
#include "DS1302_drive.h"
uchar K1_FLAG=0; //定义按键标志位,当按下K1键时,该位置1,K1键未按下时,该位为0。
uchar const bit_tab[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//位选表,用来选择哪一只数码管进行显示
uchar const seg_data[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf};
//0~F、熄灭符和字符"-"的显示码(字形码) //0~F、熄灭符和字符"-"的显示码(字形码)
#define beep_0(PORTD=PORTD&0x7f) //PD7上的蜂鸣器发声
#define beep_1(PORTD=PORTD|0x80) //PD7上的蜂鸣器不发声
uchar disp_buf[8] ={0x00}; //定义显示缓冲区
uchar time_buf[7] ={0,0,0x12,0,0,0,0};//DS1302时间缓冲区,存放秒、分、时、日、月、星期、年
uchartemp [2]={0}; //用来存放设置时的小时、分钟的中间值
/********函数功能:延时函数********/
void Delay_ms(uint xms)
{
int i,j;
for(i=0;i
{ for(j=0;j<1140;j++) ; }
}
/*********以下是蜂鸣器响一声函数********/
voidbeep()
{
beep_0; //蜂鸣器响
Delay_ms(100);
beep_1; //关闭蜂鸣器
Delay_ms(100);
}
/********端口设置函数********/
void port_init(void)
{
PORTA = 0xFF; //输出高电平
DDRA= 0xFF;//设为输出
PORTC = 0xff; //输出高电平
DDRC= 0xFF; //设为输出
DDRD =(0<
PORTD = 0xFF;//PD7输出高电平,其它为带上拉的输入
}
/********以下是走时转换函数,负责将走时数据转换为适合数码管显示的数据********/
void conv(uchar in1,uchar in2,uchar in3) //形参in1、in2、in3接收实参time_buf[2]、time_buf[1]、time_buf[0]传来的时/分/秒数据
{
disp_buf[0] =in1/10; // 小时十位
disp_buf[1] = in1%10;// 小时个位
disp_buf[3] = in2/10;// 分钟十位
disp_buf[4] = in2%10;// 分钟个位
disp_buf[6] = in3/10;// 秒十位
disp_buf[7] = in3%10;// 秒个位
disp_buf[2] = 17; // 第3只数码管显示"-"(在 seg_data表的第17位)
disp_buf[5] = 17; // 第6只数码管显示"-"
}
/********以下是显示函数********/
void Display()
{
uchar tmp; //定义显示暂存
static uchar disp_sel=0;//显示位选计数器,显示程序通过它得知现正显示哪个数码管,初始值为0
tmp=bit_tab[disp_sel];//根据当前的位选计数值决定显示哪只数码管
PORTC=tmp; //送P2控制被选取的数码管点亮
tmp=disp_buf[disp_sel];//根据当前的位选计数值查的数字的显示码
tmp=seg_data[tmp]; //取显示码
PORTA=tmp; //送到P0口显示出相应的数字
disp_sel++; //位选计数值加1,指向下一个数码管
if(disp_sel==8)
disp_sel=0; //如果8个数码管显示了一遍,则让其回0,重新再扫描
}
/********以下是定时器T0中断函数, 用于数码管的动态扫描********/
#pragma interrupt_handler timer0_ovf:10
void timer0_ovf(void)
{
TIFR=0x01; //写1清除定时器T0标志位
TCNT0=240; //置计数初值,设置定时时间为2ms
Display(); //调显示函数
}
/********以下是按键处理函数********/
void KeyProcess()
{
uchar min16,hour16; //定义16进制的分钟和小时变量
write_ds1302(0x8e,0x00); //DS1302写保护控制字,允许写
write_ds1302(0x80,0x80); //时钟停止运行
if((PIND&0x3C)!=0x3C)//如果K1~K4键有一个被按下
Delay_ms(10); //延时10ms
if((PIND&0x3C)!=0x3C)//如果仍被按下,说明不抖动引起
{
if((PIND&0x08)==0) //K2键用来对小时进行加1调整
{
while(!(PIND&0x08)); //等待K2键释放
beep();
time_buf[2]=time_buf[2]+1; //小时加1
if(time_buf[2]==24) time_buf[2]=0; //当变成24时初始化为0
hour16=time_buf[2]/10*16+time_buf[2]%10;//将所得的小时数据转变成16进制数据
write_ds1302(0x84,hour16);//将调整后的小时数据写入DS1302
}
if((PIND&0x10)==0)// K3键用来对分钟进行加1调整
{
while(!(PIND&0x10)); //等待K3键释放
beep();
time_buf[1]=time_buf[1]+1; //分钟加1
if(time_buf[1]==60) time_buf[1]=0; //当分钟加到60时初始化为0
min16=time_buf[1]/10*16+time_buf[1]%10;//将所得的分钟数据转变成16进制数据
write_ds1302(0x82,min16);//将调整后的分钟数据写入DS1302
}
if((PIND&0x20)==0) //K4键是确认键
{
while(!(PIND&0x20));//等待K4键释放
beep();
write_ds1302(0x80,0x00);//调整完毕后,启动时钟运行
write_ds1302(0x8e,0x80);//写保护控制字,禁止写
K1_FLAG=0;//将K1键按下标志位清0
}
}
}
/********以下是读取时间函数,负责读取当前的时间,并将读取到的时间转换为10进制数********/
void get_time()
{
uchar sec,min,hour; //定义秒、分和小时变量
write_ds1302(0x8e,0x00);//控制命令,WP=0,允许写操作
write_ds1302(0x90,0xab);//涓流充电控制
sec=read_ds1302(0x81); //读取秒
min=read_ds1302(0x83);//读取分
hour=read_ds1302(0x85);//读取时
time_buf[0]=sec/16*10+sec%16; //将读取到的16进制数转化为10进制
time_buf[1]=min/16*10+min%16; //将读取到的16进制数转化为10进制
time_buf[2]=hour/16*10+hour%16; //将读取到的16进制数转化为10进制
}
/********定时器0初始化********/
void timer0_init()
{
SREG = 0x80; //使能全局中断
TIMSK|=(1<
TCCR0|=(1<
TCNT0 = 240; //定时初值设置,定时时间2ms
}
/********以下是主函数********/
void main(void)
{
port_init();
timer0_init(); //调定时器T0、T1初始化函数
init_ds1302(); //DS1302初始化
while(1)
{
get_time(); //读取当前时间
if((PIND&0x04)==0) //若K1键按下
{
Delay_ms(10);//延时10ms去抖
if((PIND&0x04)==0)
{
while(!(PIND&0x04)); //等待K1键释放
beep();//蜂鸣器响一声
K1_FLAG=1;//K1键标志位置1,以便进行时钟调整
}
}
if(K1_FLAG==1)KeyProcess();//若K1_FLAG为1,则进行走时调整
conv(time_buf[2],time_buf[1],time_buf[0]); //将DS1302的小时/分/秒传送到转换函数
}
}
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号
享受更多官方福利
推荐阅读
AVR单片机需要设置合适的熔丝位才能实现其功能,如果熔丝位设置的不对将有可能导致单片机自锁,这是我整理的AVR单片机熔丝位的设置及拯救方,希望对大家有帮助。
发表于 -09-17
单片机源程序如下:/***版权所有(c),艁ukasz Marcin Podkalicki*12月13日*简单定时器(启动/复位/停止),使用基于TM1637的一个按钮和7段显示模块。 **注意,这个ATtiny13项目使用的内部时钟并不精确 
发表于 -09-16
为0到9,分别表示LED的占空比为0/9到9/9。比如,当占空比为4/9时,在9毫秒的周期中,前4毫秒LED亮,后5毫秒LED不亮。可以看见,占空比越大,LED亮度也越高。原来,在亮与暗之间,LED还有中间的状态。我们不是通过让引脚输出一个0V和5V之间的电压,而是让引脚电平迅速地在高低之间变化来实现的。这种通过电平的快速跳变来实现模拟量效果的技术,称为脉冲宽度调制,简称PWM。定时器大多数单片机的定时器都可以输出PWM波,外设丰富的AVR单片机自然不例外。上一讲提到定时器0有四种工作模式,后两种就是快速PWM模式与相位修正PWM模式。在快速PWM模式中,TCNT0寄存器的动作与普通模式相同,但还可以把OCR0A作为上限。对于非反转输出
发表于 -09-09
7)接到一个单片机引脚上。关于为什么会有这种诡异的接法,这是设计时的失误(也可能是不得已吧,毕竟单片机的32个IO已经占满了),参见:一个低电平引发的思考。协议1602与单片机之间是通过并行总线通信的。AVR单片机硬件上不支持并行总线,需要通过软件模拟时序来实现。写操作的时序如下:进行一个写操作,需要先让RS根据写的类型设置电平,R/W输出低电平,D0~D7输出要发送的数据,然后在E的上升沿数据被对方读取,并保持R/W与D0~D7电平不变,直到E的下降沿之后。两次E的上升沿之间至少需要400us时间间隔。1602共有8条指令,都是一字节长度的。从高位到低位,每一条指令都由若干个0、一个1和有效指令组成,使得没有两条指令会有相同的二进制
发表于 -09-09
在第一期中,我们已经开始使用UART来实现单片机开发板与计算机之间的通信,但只是简单地讲了讲一些概念和库函数的使用。在这一篇教程中,我们将从硬件与软件等各方面更深入地了解UART。USART组件一直在讲的UART其实是USART组件的一部分,USART比UART多了同步的一部分,但这一部分用得太少(我从来没用过),而且缺乏实例,所以就略过了。然而,单片机的设计者很机智地把这个鸡肋功能升华了一下,USART组件可以支持SPI模式。SPI是一种同步串行总线,可以支持很高的传输速率。这个功能使得ATmega324PA支持最多3个SPI通道,其中一个是纯SPI,另两个就是SPI模式下的USART。我们将在下一讲中揭开SPI的神秘面纱。回到
发表于 -09-08
开发板上有4个按键,我们可以把每一个按键连接到一个单片机引脚上,来实现按键状态的检测。但是常见的键盘有104键,是每一个键分别连接到一个引脚上的吗?我没有考证过,但我们确实有节省引脚的方法。矩阵键盘这是一个4*4的矩阵键盘,共有16个按键只需要8个引脚就可以驱动。我们先来看看它的原理。每个按键有两个引脚,当按键按下时接通。每一行的一个引脚接在一起,分别连接到左边4个端口,称为“行引脚”;每一列的另一个引脚接在一起,分别连接到右边的4个端口,称为“列引脚”。这就是矩阵键盘内部的电路连接方式。那么如何驱动它呢?首先我们简化一下,只考虑第一排:这样就很简单了吧,只要让行引脚保持低电平,4个列引脚设置为输入并开启上拉电阻,读到低电平
发表于 -09-08