每個數(shù)字時鐘內(nèi)部都有一個晶體來跟蹤時間。這種晶體不僅存在于時鐘中,而且存在于所有計算實(shí)時系統(tǒng)中。該晶體產(chǎn)生時鐘脈沖,這是時序計算所必需的。雖然還有其他一些方法可以獲得更高的精度和頻率的時鐘脈沖,但最首選的方法是使用晶體來跟蹤時間。在這里,我們將DS3231 RTC IC 構(gòu)建一個基于 Atmega16 的數(shù)字掛鐘。DS3231 RTC 內(nèi)部有一個高精度晶體,因此不需要外部晶體振蕩器。
在這個數(shù)字時鐘項(xiàng)目中,十個 0.8 英寸的共陽極 7 段顯示器用于顯示時間和日期。這里使用七段顯示器來顯示小時、分鐘、日期、月份和年份。我們的 PCB 設(shè)計還具有顯示秒數(shù)和溫度的選項(xiàng),可以通過添加更多顯示單元來顯示。
所需組件
ATmega16 AVR 微控制器
共陽極0.8寸七段顯示器(比普通尺寸顯示器(0.56寸)大)
按鈕
紐扣電池 3v
7805穩(wěn)壓器
1000uf電容
蜂鳴器(可選)
晶體管 BC547 和 BC557
10uf電容
100 歐姆電阻
1k電阻
10k電阻
PCB板
跳線
小貼士
電源適配器
用戶也可以使用 Atmega32,它需要在生成十六進(jìn)制之前在編譯器中進(jìn)行配置。
電路圖及說明
這個數(shù)字掛鐘電路有兩個部分,一個是顯示部分,在五個不同的 PCB 板上有 5 對 7 段,另一個是控制單元部分,負(fù)責(zé)從 RTC 芯片獲取時間并將數(shù)據(jù)和時間發(fā)送到7段顯示。由于我們使用了 10 個七段顯示器,因此我們無法將每個顯示器連接到單獨(dú)的 IO 端口。因此,這里使用了多路復(fù)用技術(shù),使用較少的微控制器引腳連接多個七段。
七段顯示器的 LED 引腳 a、b、c、d、e、f、g、h 與 atmega16 的 PORTB 并聯(lián)。這里我們使用了 10 個七段顯示器,所以我們需要 10 個控制引腳連接在 PORTD、PORTA 和 PORTC。
具有內(nèi)部晶體的 RTC DS3231 連接到 PORTC 的 SDA 和 SCL 引腳,因?yàn)樵撔酒ぷ髟?I2C 通信上。該芯片的接口方法與 DS1307 相同。我們已經(jīng)將DS1307 與 Arduino、Raspberry Pi和8051 MCU一起使用。DS3231 和 DS1307 可以使用相同的代碼。
兩個 10k 上拉電阻連接在 SDA 和 SCL 線上。一個 3v 紐扣電池用于為 RTC 芯片供電,即使在主電源關(guān)閉時也能跟蹤時間。每當(dāng)電源再次恢復(fù)時,時間將開始顯示在七段顯示器上?,F(xiàn)在我們在 PORT A 有一些設(shè)置時間的按鈕,完整的過程在最后給出的視頻中解釋。5v 穩(wěn)壓器用于將輸入電壓轉(zhuǎn)換為 5v。所有連接都顯示在下面的電路圖中:
對于一個顯示板,使用兩個七段顯示器和 2 個 LED。所以這里我們有五個不同的顯示板來顯示小時和分鐘 (HH-MM) 中的時間,以及 DD-MM-YY 中的日期。
數(shù)字時鐘的 PCB 設(shè)計和制造
對于這個基于 Atmega16 的掛鐘項(xiàng)目,我們設(shè)計了兩個 PCB。一是控制單元,用于控制項(xiàng)目的所有操作,二是用于在七段顯示器上顯示時間和日期。顯示部分包含五對0.8英寸七段顯示器。因此,通過組裝 5 個零件,我們就擁有了完整的數(shù)字時鐘。多路復(fù)用 7 段顯示器,5 塊 PCB 的數(shù)據(jù)線將連接到控制單元的同一個端口,控制線連接到控制單元的不同引腳。
下面是一個顯示板的 PCB 布局的頂視圖和底視圖,該顯示板由兩個七段顯示器組成:
下面是控制單元 PCB 的頂視圖和底視圖
焊接后的幾張電路板圖片 如下所示。
測試數(shù)字時鐘
本教程末尾給出了完整的代碼,只需按照電路圖連接PCB并將代碼上傳到Atmega16。您將在十個七段顯示屏上看到時間和日期。
可以使用控制單元上的四個按鈕設(shè)置時間??和日期。
/*
* 數(shù)字時鐘.c
#include
#define F_CPU 8000000UL
#include
#include
int day=6,dd=1,mm=3,yy=19;
無符號整數(shù)秒,分鐘=13,小時=4;
常量無符號整數(shù)[]={0X40,0X79,0X24,0X30,0X19,0X12,0X02,0X78,0X00,0X10};
詮釋 d0,d1,d2,d3,d4,d5,d6,d7,d8,d9;
易失的無符號整數(shù)計數(shù),計數(shù)1;
#定義數(shù)字13
#define 數(shù)據(jù)端口 PORTB
#define controlPortD 端口
#define controlPortC PORTC
#define controlPortA PORTA
#define controlPortD_Mask 0x83
#define controlPortC_Mask 0x03
#define controlPortA_Mask 0x7F
#define 段關(guān)閉 -1
#define sw PINA
#定義集 4
#定義好 3
#向上定義2
#向下定義 1
#define setEvent (sw & (1<))<>
#define okEvent (sw & (1<))<>
#define upEvent (sw & (1<))<>
#define downEvent (sw & (1<))<>
#define LEDPORT PORTA
#define secLed 5
#define BUZPORT 端口
#定義蜂鳴器 7
字符閃爍標(biāo)志;
易失性字符 onFlag=0x00;
枚舉
{
小時=1,
分鐘,
日期,
月,
年,
};
字符 segOn[12]={0x04,0x08,0x10,0x20,0x40,0x80,0x40,0x80,0x10,0x20,0x04,0x08};
無效顯示();
無效更新時間();
ISR(TIMER1_OVF_vect)
{
展示();
TCNT1 = 64000;
}
無效選擇段(整數(shù)計數(shù))
{
如果(計數(shù) < 5)
{
controlPortA&=controlPortA_Mask;
controlPortC&=controlPortC_Mask;
controlPortD&=controlPortD_Mask;
controlPortD|=segOn[計數(shù)];
}
否則如果(計數(shù) == 5)
{
controlPortA|=segOn[計數(shù)];
controlPortC&=controlPortC_Mask;
controlPortD&=controlPortD_Mask;
}
否則如果(計數(shù) == -1)
{
controlPortA&=controlPortA_Mask;
controlPortC&=controlPortC_Mask;
controlPortD&=controlPortD_Mask;
}
別的
{
controlPortA&=controlPortA_Mask;
controlPortC&=controlPortC_Mask;
controlPortD&=controlPortD_Mask;
controlPortC|=segOn[計數(shù)];
}
}
無效段(整數(shù)計數(shù),整數(shù)段)
{
如果(閃爍標(biāo)志 == 段)
{
如果(onFlag)
選擇段(段關(guān)閉);
別的
選擇段(計數(shù));
}
別的
{
選擇段(計數(shù));
}
}
無效顯示()
{
計數(shù)1++;
如果(計數(shù) 1>400)
{
計數(shù)1=0;
onFlag=!onFlag;
}
計數(shù)++;
如果(計數(shù)>數(shù)字)
計數(shù)=0;
開關(guān)(count%digit)
{
案例0:
段(計數(shù),分鐘);
數(shù)據(jù)端口=num[d0];
休息;
情況1:
段(計數(shù),分鐘);
數(shù)據(jù)端口=num[d1];
休息;
案例2:
段(計數(shù),小時);
數(shù)據(jù)端口=num[d2];
休息;
案例3:
段(計數(shù),小時);
數(shù)據(jù)端口=數(shù)字[d3];
休息;
案例4:
段(計數(shù),日期);
數(shù)據(jù)端口=num[d4];
休息;
案例5:
段(計數(shù),日期);
數(shù)據(jù)端口=數(shù)字[d5];
休息;
案例6:
段(計數(shù),月);
數(shù)據(jù)端口=數(shù)字[d6];
休息;
案例7:
段(計數(shù),月);
數(shù)據(jù)端口=數(shù)字[d7];
休息;
案例8:
段(計數(shù),年份);
數(shù)據(jù)端口=數(shù)字[d8];
休息;
案例9:
段(計數(shù),年份);
數(shù)據(jù)端口=num[d9];
休息;
}
}
無效 timer1_init()
{
// 使用預(yù)分頻器 = 8 設(shè)置定時器
TCCR1B |= (1 << CS11);
//TCCR1B &= ~(1 << CS10);
//TCCR1B &= (1 << CS11);
//TCCR1B &= ~(1 << CS12);
TCNT1 = 63500;
TIMSK |= (1 << TOIE1);
sei();
}
int bcdtochar(字符數(shù))
{
返回 ((num/16 * 10) + (num % 16));
}
int decobcd(字符數(shù))
{
返回 ((num/10)<<4) + (num % 10);
}
無效 RTC_start()
{
TWCR=(1<)|(1<
而((TWCR&0x80)==0x00);
}
無效設(shè)備()
{
TWDR=0xD0;//RTC寫入(從地址)
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
TWDR=0x00;// 字地址寫入
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
無效 RTC_stp()
{
TWCR=(1<)|(1<
}
無效 RTC_read()
{
TWCR=(1<)|(1<
而((TWCR&0x80)==0x00);
TWDR=0xD0;//RTC寫入(從地址)
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
TWDR=0x00;//RTC寫入(字地址)
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
TWCR=(1<)|(1<
而 ((TWCR&0x80)==0x00);
TWDR=0xD1;// RTC 命令讀取
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
無效 sec_init(無符號字符 d)
{
TWDR=d; //第二次初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
void min_init(unsigned char d)
{
TWDR=d; //分鐘初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
無效 hr_init(無符號字符 d)
{
TWDR=d; //小時初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
void day_init(unsigned char d)
{
TWDR=d; //天數(shù)初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
無效日期初始化(無符號字符 d)
{
TWDR=d; //日期初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
無效month_init(無符號字符d)
{
TWDR=d; //月份初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
無效 yr_init(無符號字符 d)
{
TWDR=d; //年份初始化
TWCR=(1<)|(1<
而(!(TWCR&(1<)));<>
}
int sec_rw()
{
詮釋克[3];
TWCR|=(1<)|(1<
而((TWCR & 0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
int min_rw()
{
TWCR|=(1<);>
TWCR|=(1<);<>
而((TWCR & 0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
int hr_rw()
{
TWCR|=(1<)|(1<
而((TWCR & 0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
int day_rd()
{
TWCR|=(1<)|(1<
而((TWCR&0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
int date_rw()
{
TWCR|=(1<)|(1<
而((TWCR & 0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
int month_rw()
{
TWCR|=(1<)|(1<
而((TWCR & 0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
int yr_rw()
{
TWCR|=(1<);>
TWCR&=(~(1<));<>
而((TWCR & 0x80)==0x00);
返回 bcdtochar(TWDR)??;
}
無效的設(shè)置時間()
{
RTC_start();
設(shè)備();
sec_init(0);
min_init(dectobcd(min));
hr_init(dectobcd(hr));
day_init(dectobcd(天));
date_init(dectobcd(dd));
month_init(dectobcd(mm));
yr_init(dectobcd(yy));
RTC_stp();
}
無效 RTC()
{
RTC_read();
sec=sec_rw();
min=min_rw();
hr=hr_rw();
day=day_rd();
dd=date_rw();
mm=month_rw();
yy=yr_rw();
RTC_stp();
}
char getPara(字符數(shù))
{
而(1)
{
更新時間();
如果(!upEvent)
{
計數(shù)1=0;
onFlag=0x00;
計數(shù)++;
如果(閃爍標(biāo)志 == 小時)
{
如果(時間格式 == 12)
{
如果(計數(shù)>12)
計數(shù)=0;
}
別的
{
如果(計數(shù) > 23)
計數(shù)=0;
}
小時=計數(shù);
}
否則如果(blinkFlag == 分鐘)
{
如果(計數(shù)> 59)
計數(shù)=0;
分鐘=計數(shù);
}
否則如果(blinkFlag == 月)
{
如果(計數(shù) > 12)
計數(shù)=1;
毫米=計數(shù);
}
否則如果(blinkFlag == 日期)
{
if(mm == 4 || mm == 6 || mm == 9 || mm == 11)
{
如果(計數(shù) > 30)
計數(shù)=1;
}
否則 if(mm == 1 || mm == 3 || mm == 5 || mm == 7 || mm == 8 || mm == 10 || mm == 12)
{
如果(計數(shù) >31)
計數(shù)=1;
}
別的
{
int y=2000+yy;
如果(y/4 == 0 && y/400 == 0)
{
如果(計數(shù) > 29)
計數(shù)=1;
}
別的
{
如果(計數(shù) > 28)
計數(shù)=1;
}
}
dd=計數(shù);
}
否則如果(blinkFlag == 年)
{
如果(計數(shù) >99)
計數(shù)=0;
YY=計數(shù);
}
_delay_ms(200);
}
否則如果(!(downEvent))
{
數(shù)數(shù) - ;
如果(閃爍標(biāo)志 == 年)
{
如果(計數(shù)<0)
計數(shù)=99;
}
_delay_ms(100);
}
否則如果(!okEvent)
{
_delay_ms(1000);
返回計數(shù);
}
}
}
無效設(shè)置時間()
{
閃爍標(biāo)志=1;
hr=getPara(hr);
閃爍標(biāo)志++;
min=getPara(min);
閃爍標(biāo)志++;
dd=getPara(dd);
閃爍標(biāo)志++;
mm=getPara(mm);
閃爍標(biāo)志++;
yy=getPara(yy);
閃爍標(biāo)志=0;
}
無效更新時間()
{
d0=min%10;
d1=分鐘/10;
d2=hr%10;
d3=小時/10;
d4=dd%10;
d5=dd/10;
d6=mm%10;
d7=mm/10;
d8=yy%10;
d9=yy/10;
}
詮釋主要(無效)
{
無符號長整數(shù)時間;
DDRB=0xff;
DDRA=0xE0;
端口=0x1E;
DDRD=0xff;
DDRC=0xff;
timer1_init();
而(1)
{
實(shí)時時鐘();
更新時間();
_delay_ms(500);
LEDPORT|=1<;<>
_delay_ms(500);
LEDPORT&=~(1<);<>
如果(!setEvent)
{
設(shè)置時間();
設(shè)置時間();
}
}
}
-
ATmega16
+關(guān)注
關(guān)注
5文章
154瀏覽量
45740 -
DS3231
+關(guān)注
關(guān)注
2文章
51瀏覽量
23811 -
數(shù)字時鐘
+關(guān)注
關(guān)注
2文章
149瀏覽量
20287
發(fā)布評論請先 登錄
相關(guān)推薦
評論