Egy egyszerű óra


Rengeteg óra kapcsolás, leírás kering a neten a legegyszerűbbektől a legbonyolultabb szuper órákig. Én mégis úgy gondoltam, hogy ez a honlap se teljes legalább egy használható óra projekt nélkül. Egy kicsit a buhera rovatba is berakhattam volna ezt a cikket, de mégis inkább az arduinos cuccok közé rakom hisz a mikrovezérlő nélkül ez az egész nem működne.
Az alapja az, hogy kaptam egyik barátomtól egy még működő set top boxot amit persze magában nem használható, ezért alkatrészként próbáltam hasznosítani. Az STB kijelző panelja indított el az óra projekt létre hozásában. A kijelző egy 4 digites közös anódos 7segmenses kijelző, amit egy shift regiszter hajt meg, plusz digitenkénti anódkivezetésekkel. A panel még tartalmaz néhány nyomógombot is amik közül hármat felhasználtam az óra menüjéhez.


A kapcsoláshoz felhasznált alkatrészek:

A kapcsolás a következő:


A kapcsolási rajzon nem jelöltem be de a shift regiszter kimenetei és a 7szegmenses kijelző bemenetei között kell el helyezni a 8db áramkorlátozó ellenállást.


A kijelző típusától függ a lábkiosztás, ezt mindenki maga derítse ki a saját kijelzőjéről.
Ha közös katódos kijelző áll rendelkezésre, akkor a programban kell aktiválni a megfelelő sorokat.

Az Uno helyet használható Pro Mini vagy Nano is, de akár Megát is befoghatunk erre a célra, a progi átalakításával.

Könyvtárakat a kijelzéshez nem használtam, a programba írtam bele a kijelző multiplexelését, illetve a 24 óránkénti reset érdekébe az avr watchdog könyvtárat alkalmaztam, ami alapértelmezett könyvtár.

De nézzük is a programot:

#include <avr/wdt.h>
#define data 10
#define clock 11
/* a közös anódos kijelzők szám szegmenseit 8bites bináris számokként tároljuk el, később
ezeket íratjuk ki a shift regiszter segítségével. */
//közös anódos kijelző
byte zero = B00000011;
byte one = B10011111;
byte two = B00100101;
byte three = B00001101;
byte four = B10011001;
byte five = B01001001;
byte six = B01000001;
byte seven = B00011111;
byte eight = B00000001;
byte nine = B00001001;
byte ponti = B11111110;
boolean ka_be = HIGH;
boolean ka_ki = LOW;

//közös katódos kijelző esetén a bináris értékek fordítottját használjuk
//közös katódos kijelző

/*
byte zero = B11111100;
byte one = B01100000;
byte two = B11011010;
byte three = B11110010;
byte four = B01100110;
byte five = B10110110;
byte six = B10111110;
byte seven = B11100000;
byte eight = B11111110;
byte nine = B11110110;
byte ponti = B00000001;
boolean kk_be = LOW;
boolean kk_ki = HIGH;
*/
byte tiz_perc,egy_perc;
byte egy_ora,tiz_ora;
byte b_szam;
long time,pro_time;
long ora,perc;
long ora_beal = 0,perc_beal = 0;
long menu_time;
byte menu_flag, be_ki;
void setup()
{
Serial.begin(9600);
pinMode(clock, OUTPUT); //óra pin
pinMode(data , OUTPUT); //adat pin
pinMode(4,OUTPUT); //közös anód 1.
pinMode(5,OUTPUT);//közös anód 2.
pinMode(6,OUTPUT);//közös anód 3.
pinMode(7,OUTPUT);//közös anód 4.
pinMode(2,INPUT);//le
pinMode(3,INPUT);//fel
pinMode(8,INPUT);//menu
}

void loop()
{
//a watchdogot mindaddig tiltjuk míg el nem jön a nullázás ideje
wdt_disable();
// idő kiszámítása és beállítása
time = millis()/1000;//sec számolás
time = perc_beal*60+ora_beal*3600+time;//beállított idő
Serial.println(time);
perc = time%3600/60;//perc kiszámítás
ora = time/3600%24;//óra kiszámítás
pont(be_ki);//választó pont
//menű meghívása menű gombbal
if(digitalRead(8)== LOW && menu_time+500<millis()){
menu_flag = 2;//menű állapojelző
menu_time = millis();
menu(menu_flag);
}
perc_kiir(perc); //perc kiíratás meghívása
ora_kiir(ora); //óra kiíratás meghívása
//24 óránként nullázás watchdoggal
if(time == 86399)
{ wdt_enable(WDTO_1S);
wdt_reset();
while(1);
}
}

void ora_kiir(long ora)
{
tiz_ora = ora/10;//óra tízes értékeinek kiíratása
egy_ora = ora%10;// az ora tízzel való osztásának maradéka adja az egyes számértékeket
shiftOut(data, clock, LSBFIRST, szamjegy(egy_ora));
digitalWrite(6,ka_be);
//digitalWrite(6,kk_be);//közös katódos kijelző
delay(3);
digitalWrite(6,ka_ki);
//digitalWrite(6,kk_ki);//közös katódos kijelző
shiftOut(data, clock, LSBFIRST, szamjegy(tiz_ora));
digitalWrite(7,ka_be);
//digitalWrite(6,kk_be);//közös katódos kijelző
delay(3);
digitalWrite(7,ka_ki);
//digitalWrite(6,kk_ki);//közös katódos kijelző
}

void perc_kiir(long perc)
{
tiz_perc = perc/10;
egy_perc = perc%10;
shiftOut(data, clock, LSBFIRST, szamjegy(egy_perc));
digitalWrite(4,ka_be);
//digitalWrite(6,kk_be);//közös katódos kijelző
delay(3);
digitalWrite(4,ka_ki);
//digitalWrite(6,kk_ki);//közös katódos kijelző
shiftOut(data, clock, LSBFIRST, szamjegy(tiz_perc));
digitalWrite(5,ka_be);
//digitalWrite(6,kk_be);//közös katódos kijelző
delay(3);
digitalWrite(5,ka_ki);
//digitalWrite(6,kk_ki);//közös katódos kijelző
}

byte szamjegy(byte szam)
{
//kiírandó számjegyek kódolása
switch (szam){
case 1:
b_szam = one;
break;
case 2:
b_szam = two;
break;
case 3:
b_szam = three;
break;
case 4:
b_szam = four;
break;
case 5:
b_szam = five;
break;
case 6:
b_szam = six;
break;
case 7:
b_szam = seven;
break;
case 8:
b_szam = eight;
break;
case 9:
b_szam = nine;
break;
case 0:
b_szam = zero;
break;
}
return b_szam;
}

void pont(byte be_ki)
{
//választó pont kiírása
shiftOut(data, clock, LSBFIRST, ponti);
digitalWrite(6,ka_be);
//digitalWrite(6,kk_be);//közös katódos kijelző
delay(3);
digitalWrite(6,ka_ki);
//digitalWrite(6,kk_ki);//közös katódos kijelző
}

void menu(byte menu_flag)
{
//beállítás menü
while(menu_flag <= 3){
if(digitalRead(8)==LOW && menu_time+500<millis()){
menu_flag++;
menu_time=millis();
}
switch (menu_flag){
case 1: //kilépés a menüből
menu_flag = 4;
break;
case 2: //óra beállítása
if(digitalRead(2)==LOW && menu_time+500<millis()){
ora_beal--;
if(ora_beal<0)
ora_beal = 23;
menu_time=millis();
}
if(digitalRead(3)==LOW && menu_time+500<millis()){
ora_beal++;
menu_time=millis();
}
time = ora_beal*3600+millis()/1000;
ora = time/3600%24;
ora_kiir(ora);//ora állítás
break;
case 3: //perc beállítása
if(digitalRead(2)==LOW && menu_time+500<millis()){
perc_beal--;
if(perc_beal < 0)
perc_beal = 59;
menu_time=millis();
}
if(digitalRead(3)==LOW && menu_time+500<millis()){
perc_beal++;
menu_time=millis();
}
time = perc_beal*60+millis()/1000;
perc = time%3600/60;
perc_kiir(perc);//perc állítás
break;
}
}
}

Maga a program a millis(); utasítást használja az idő mérésére, ebből nyerjük számításokkal az
órák és percek kiíratásához szükséges értékeket.

A főciklusban megtörténik az idő kiszámítása, majd a beállított értékek hozzáadása valamint az értékek kiíratását végrehajtó függvények meghívása. A ciklus még tartalmaz egy nyomógomb figyelést ami ha aktív a beállítás menübe ugrassza a program futását. Ezen kívül itt helyezkedik el a watchdogh aktiválásunk is.

Multiplexelés

Egy pár szót ejtsünk erről is, biztos van még aki nem találkozott ezzel az elektronikai megoldással.
Itt csak arról van szó, hogy a mi 4 digites(4 szám kijelzős) kijelzőnk egy-egy számjegyét felváltva
villantjuk fel egy kis időre. Tehát egyszerre csak egy számjegy szegmensei világítanak, először az első világít a többi sötét, aztán a második világít a többi sötét és így tovább.
A szemünk tehetetlenségéből adódóan (25 kép kockát már folyamatos mozgásnak érzékeljük) a sorozatos felvillanásokat folyamatos kijelzésnek érzékeljük mind a 4 digiten.
Hogy ez mért jó és mért kell? Mert így a hét szegmenst ugyan avval a hét kimenetel tudjuk meghajtani, nem kell külön hét-hét kimenet minden digithez, tehát evvel rengeteget spórolunk mind kimenetek, mind vezetékellés terén.

Technikailag úgy oldjuk meg, hogy kiadjuk a megfelelő hét jelet az adott digit hét szegmenséhez majd aktiváljuk a hozzá tartozó közös kimenetet (közös katód vagy közös anódot), majd ezt újra és újra megtesszük a soron következő digiteknél is.

A kijelzésnél, tehát amíg a szegmensek világítanak mindig kell egy kis késleltetés, különben erősen vibráló fényt kapunk, persze a túl hosszú késleltetési idő is ezt eredményezi.

Watchdog

Őrkutya vigyázza a program futását, pontosabban a watchdog intézi az újraindulást ha valamilyen
megakadást észlel a program futásában, ilyen például egy végtelen ciklus is.
A progiban én a Watchdog-ot resetelésre használtam, itt az volt a lényeg, hogy bizonyos időközönként indítsa újra a programot és főleg nullázza a milli()-t a túlcsordulás elkerülése véget.
A Watchdog engedélyezését feltételhez kötöttem (24 óra), majd megakasztottam a progi futását
egy végtelen ciklussal (while(1);). Így mindig éjfélkor újra indul az óra lenullázott értékekkel.
A program elején újra letiltom az egészet míg újra el nem telik a 24 óra.


A program letölthető: óra

Két napos tesztelés után 4-4.5 perces sietést vettem észre az órán. Ennek korrigálását a programba
kell beépíteni. Ez lehet, hogy vezérlő függő mindenki tesztelje ki maga.

A korrekcióval ellátott program letölthető: óra korrigálva

A végeredmény egy kezdetleges óra, minimális alkatrész felhasználással, lehetne ennél sokkal
profibb RTC-s óra modullal, LCD-s kijelzéssel, extra funkciókkal de azt talán majd egy másik cikkben.