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