Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

niedziela, 20 marca 2011

DIY: Zegar Nixie LC-516 (ATmega8 + PCF8574 + PCF8563 + DS18B20)


Autor: dipcray06
Redakcja: Dondu

Zegary na lampach Nixie są często budowane przez elektroników hobbystów. W Internecie można znaleźć wiele interesujących projektów, na których można się wzorować. A posiadanie tak nietypowego czasomierza daje dużą przyjemność.

Połączenie takich lampowych wyświetlaczy z dzisiejszą elektroniką daje duże możliwości. W prezentowanym zegarze wyświetlanie realizowane jest przez multipleksowanie co znacznie upraszcza sterowanie, a dodatkowo w moim odczuciu zwiększa też żywotność lamp.

Zegar w wersji prototypowej powstał już jakiś czas temu, ale program napisany był w Bascomie, którego do tej pory używałem. Jednak już od jakiegoś czasu chciałem zacząć programować w C i ten projekt jest właśnie moim pierwszym napisanym w tym języku.

Funkcjonalność tego urządzenia to:
  • wyświetlanie aktualnej godziny w formacie 24 godzinnym,
  • wyświetlanie temperatury z dokładnością do 1/10 stopnia C,
  • podtrzymywanie bateryjne godziny po wyłączeniu zasilania,
  • czujnik natężenia światła, który decyduje o jasności wyświetlanych cyfr oraz o włączeniu podświetlenia,
  • efekt odtruwania katod wyświetlaczy po każdej zmianie minuty,
  • wygaszenie pierwszego wyświetlacza dla godzin od 0:00 do 9:59, dzięki czemu zero przed tymi godzinami nie jest wyświetlane,
  • wygaszenie ostatniej cyfry wyświetlacza przy wyświetlaniu temperatury.





Zegar powstał z wykorzystaniem darmowego oprogramowania. Schematy ideowe i płytki PCB powstały w programie EAGLE w wersji freeware. Ze względu na ograniczenia darmowej wersji programu co do wielkości płytek, zegar ma konstrukcję modułową. Urządzenie składa się z ośmiu oddzielnych płytek PCB. Płytki zrobione zostały samodzielnie metodą termotransferu. Program powstał w Eclipse Indigo w całości w języku C.

A tak prezentuje się z bliska:



Zegar Nixie 5





Schematy elektryczne

Zasilanie tworzy jeden moduł, przedstawiony na poniższym schemacie.


Moduł zasilania

Zegar zasilany jest z 12V zasilacza stabilizowanego DC. Logika urządzenia zasilania jest ze stabilizatora 7805 obniżającego napięcie do 5V. Ponieważ wyświetlacze Nixie do działania wymagają napięcia zasilania 170V, zastosowałem impulsową przetwornicę podwyższającą MC34063.

Najważniejsze jej elementy to dławik L2 330uH, szybka dioda prostownicza D1 BYV29 oraz tranzystor Q1 IRF840. Potencjometrem R5 można regulować napięcie wyjściowe. Przetwornica spisuje się bardzo dobrze. Prąd katody dla zastosowanych wyświetlaczy LC-516 to około 2mA.

Kolejny moduł to logika sterująca całym wyświetlaczem.


Logika


Wszystkim steruje ATmega8. Początkowo miała być taktowana kwarcem 16MHz, ale program został napisany tak, aby można było wykorzystać wewnętrzny oscylator 8MHz, co okazuje się wystarczające. Procesor wyrabia się ze wszystkimi zadaniami, więc zewnętrznego kwarcu nie trzeba lutować.

Oczywiście sygnał Resetu jest typowo podciągnięty do VCC, ale zegar można zresetować wciskając przycisk S1. Ważna jest również filtracja zasilania. Impulsowe załączanie wyświetlaczy może generować zakłócenia, dlatego obwody zasilania wszystkich układów scalonych na PCB są filtrowane przez kondensatory 100nF, a mikroprocesor dodatkowo przez kondensator elektrolityczny C8 o wartości 10uF.

Ponieważ mikrokontroler miał za mało pinów I/O, użyłem scalonego układu PCF8574, który zwiększył liczbę pinów o 8, co zupełnie wystarcza. Obsługa PCF8574 jest prosta i odbywa się za pomocą dwóch linii SDA i SCL w standardzie I2C.

Na tej samej magistrali pracuje też zegar RTC PCF8563. Linie danych i zegarowa są podciągnięte do VCC przez rezystory R3, R4 o wartości 4,3k. Do zegara RTC dołączona jest mała 3V bateria G2 CR2032, która podtrzymuje pracę PCF8563 po odłączeniu zasilania.

Złącze SV3 to typowe złącze ISP w standardzie Kanda, bez podłączonego zasilania VCC. W czasie programowania zegar może być cały czas podłączony do zasilacza.

Jeśli chodzi o połączenia poszczególnych pinów, to nie będę ich opisywał, wszystko jest opisane na schemacie. Wyjaśnienia mogą wymagać tylko wyjścia układu PCF8574 (złącze SV4).

Zaczynając od P0 jest to katoda 9, anoda 1, anoda 2, anoda 3, anoda 4, neonówka 1, neonówka 2 i buzzer.

Kolejny moduł to tranzystory, którymi załączane są poszczególne anody i katody wyświetlaczy, oraz neonówki. Schemat poniżej.


Tranzystory

Katody załączane są przez tranzystory npn BF422, neonówki przez npn MPSA42, a anody z konieczności załączania napięć 170V załączane są dwustopniowo przez tranzystory npn MPSA42 i pnp MPSA92. Diody D1..D10 wraz z rezystorami R10, R11 obniżają napięcie na aktualnie niewybranych lampach poniżej napięcia zapłonu.


Kolejne dwa moduły to wyświetlacze. Schematy poniżej.


Wyświetlacz prawy



Wyświetlacz lewy


Rezystory dołączone do anod wyświetlaczy ograniczają prąd lampy. Ponieważ lampy są zasilane impulsowo, to można je trochę zmniejszyć np. do 15k.

Kolejny moduł to czujniki:


Czujniki


Procesor ATtiny2313 jest opcjonalny i miałem na nim zrobić odbiór podczerwieni w standardzie RC5, ale ponieważ nie zdążyłem, to można go nie lutować, jak i elementów do niego dołączonych. To co należy polutować to układ DS18b20, czyli termometr z rezystorem podciągającym R8 oraz fotorezystor, rezystor R6 i potencjometr R7, które tworzą czujnik światła.

Ostatnie już moduły i w sumie najmniejsze, to PCB do której przylutowane są neonówki i druga, gdzie znajdują się przyciski do obsługi zegara. Schemat niżej.


Przyciski:


Przyciski


Neonówki:

Neonówki



Uwaga! Należy pamiętać, aby do neonówki dolutować szeregowo rezystor ograniczający prąd, a dopiero tak przygotowaną neonówkę wlutować w PCB. Nie podaję tu konkretnych wartości rezystorów, bo ich wielkość zależy od zastosowanych neonówek.




Program


Tak jak wspominałem na wstępie, program został napisany w języku C w środowisku programistycznym Eclipse Indigo z użyciem kompilatora avr-gcc. Program po kompilacji zajmuje 38.5% pamięci flash mikrokontrolera. Ustawienie fusebitów jest następujące: HIGH 0xC9, LOW 0xE4.

Kod jest dość długi, ale starałem się robić możliwie dużo komentarzy, więc myślę, że nie będzie trudny do przeanalizowania.

Starałem się, aby w przerwaniach nie umieszczać skomplikowanych obliczeń i oczywiście nie robić oczekiwania za pomocą poleceń _delay. Dlatego operacje wysyłania lub odbierania danych z RTC i czujnika temperatury DS18B20 są robione w pętli głównej programu, a w przerwaniach od timerów ustawiane są tylko odpowiednie flagi.

CZUJNIK NATĘŻENA ŚWIATŁA


W pętli głównej znajduje się jeszcze obsługa komparatora wbudowanego w ATmegie8. Zasada działania jest prosta. Porównywane są napięcia na wejściach AIN0 i AIN1. Jeśli AIN0 jest mniejsza od AIN1 to zmienna jasność jest ustawiana na 4, w przeciwnym wypadku na 20.Po włączeniu wyświetlacza ustawiana jest flaga_wl i zerowany jest licznik_wyl. Następnie co 256us występuje przerwanie od timera.

Jeśli flaga_wl jest ustawiona, to zwiększany jest licznik_wyl i jeśli osiągnie wartość większą od zmiennej jasność, nastąpi wyłączenie wyświetlacza i neonówek. Jeśli zmienna jasność ma wartość 4, to wyłączenie wyświetlacza nastąpi po około 1,3ms od czasu włączenia wyświetlacza, natomiast dla wartości jasność=20 wyłączenie nie nastąpi, bo wcześniej licznik_wyl zostanie wyzerowany w przerwaniu od timera 0.

W efekcie otrzymujemy proste sterowanie jasnością przez PWM. W dzień, kiedy natężenie światła jest duże, wyświetlacze świecą jasno, natomiast w nocy jasność wyświetlaczy jest obniżana, a dodatkowo załączana jest dioda LED podświetlająca przezroczystą obudowę.


TERMOMETR DS18B20 1-WIRE


Obsługa termometru realizowana jest w pętli głównej programu w ośmiu krokach. Licznik rozkazów rozkaz_1wire jest zwiększany za każdym razem kiedy zostaje ustawiona flaga wyslij_rozkaz w przerwaniu od timer0 co 4ms. Jeśli rozkaz_1wire = 4 ustawiana jest flaga_konwersji, która jest zerowana po upływie 1s. Odczytana temperatura umieszczana jest w dwóch bajtach MSB i LSB, a następnie konwertowana do postaci możliwej do wyświetlenia przez wywołanie funkcji convert_1wire().

Tak przygotowana temperatura po zamianie w funkcji makebcd() na postać BCD może zostać już wyświetlona. Obsługa protokołu komunikacyjnego 1-Wire jest standardowa, a przebiegi czasowe zgodne z tymi, które są podane w nocie katalogowej układu DS18B20. Odczyt temperatury realizują procedury:

void reset_1wire(void);
void write_1wire(uint8_t data_1wire);
uint8_t read_1wire(void);


ZEGAR RTC, PCF8574 I2C


Do obsługi zegara RTC jak i PCF8574 konieczne było wykorzystanie procedur obsługi I2C. Komunikacja realizowana jest sprzętowo, więc wszystko ogranicza się do ustawiania odpowiednich bitów w rejestrze TWCR i oczekiwania aż dane zostaną wysłane lub odebrane. Obsługę I2C realizują procedury:

void twistart(void);
void twistop(void);
void twiwrite(char data);
char twiread(char ack); 


OBSŁUGA KLAWISZY


Aby wyeliminować drgania styków użyłem licznika switch_delay, który ustawiony jest na 4, a przy naciśnięciu klawisza jest zmniejszany i jeśli osiągnie wartość 0, to następuje reakcja na naciśnięcie przycisku. Linie mikroprocesora do których podłączone są przyciski, czyli PC0..PC3 ustawione są jako wejścia z podciągnięciem do VCC. Naciśnięcie przycisku to podanie na wejście stanu niskiego. Przyciski należy kabelkami dolutować do złącza SV1 na PCB z tranzystorami.

PRZERWANIE TIMER0

Timer 0 został tak skonfigurowany aby przerwania były wywoływane co 4ms, co daje multipleksowanie z częstotliwością 62,5Hz. Częstotliwość ta wystarcza żeby mieć wrażenie, że wszystkie lampy świecą naraz. Tak jak już wspominałem w przerwaniu obsługiwane są klawisze, jak również ustawiane flagi odbioru danych z czujnika temperatury, jak i włączenia wyświetlaczy. Przy każdym wywołaniu przerwania zapalana jest kolejna lampa przez wybranie odpowiedniej anody i katody wyświetlacza. Ważne jest to, aby wyłączyć wyświetlacze na początku obsługi przerwania co eliminuje efekt ‘duchów’ na wyświetlaczu.

MONTAŻ I URUCHOMIENE

Montaż jak zwykle zaczynamy od elementów najmniejszych, a na największych kończymy. Wyjaśnienia może wymagać sposób montaży tranzystora Q1. Jest to tranzystor w obudowie TO-220, gdzie środkowa nóżka drenu została przycięta przy obudowie, a wyprowadzenie drenu z tyłu obudowy zostało przykręcone do radiatora i dolutowane do PCB. Dzięki temu odstęp między padami jest większy i jest to rozwiązanie bezpieczniejsze przy napięciu 170V.

Nietypowy jest też montaż tranzystorów BF422, gdzie środkową nóżkę trzeba wygiąć w stronę spłaszczonej części obudowy. Ogólnie starałem się, aby odległości między ścieżkami wysokonapięciowymi i pozostałymi były możliwie duże. Dodatkowo między poszczególnymi płytkami trzeba zrobić połączenia zasilania przewodami według przedstawionych schematów.

PODSUMOWANIE

Ogólnie jestem zadowolony z projektu, a wyświetlacze Nixie robią ogromne wrażenie zwłaszcza w półmroku lub całkowitej ciemności. To co chciałbym w przyszłości jeszcze zmienić to dopisać obsługę budzika dlatego w projekcie znajduje się buzzer z generatorem oraz może napisać obsługę RC5.

Jeszcze słowo odnośnie obudowy. Całość została zaprojektowana w Corelu, a następnie wycięta z pleksi laserem. Przód to czarna pleksi 3mm, reszta obudowy to przezroczysta pleksi 4mm. Miejsca po cięciu laserem są gładkie i przezroczyste, więc wszystko wygląda estetycznie, a co najważniejsze chroni użytkowników przed niebezpiecznym napięciem znajdującym się w środku.

ZDJĘCIA



Zegar Nixie 1

Zegar Nixie 2

Zegar Nixie 3 - termometr


Zegar Nixie 4


Zegar Nixie 5


Zegar Nixie - widok od góry

Zegar Nixie - widok od tyłu

Zegar Nixie - widok z boku

Zegar Nixie - w częściach :-)



PLIK ŹRÓDŁOWY



/*
 * main.c
 *
 *  Created on: 27-02-2014
 */

/*ZEGAR NIXIE*/
#define F_CPU 8000000UL  // F_CPU na 8MHz
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>

 /*deklarazcja zmiennych globalnych*/
 volatile uint8_t sekundy,minuty,godziny, tryb;
 volatile uint8_t sekdzie,sekjed,mindzie,minjed,godzdzie,godzjed;
 volatile uint8_t pomoc,mux,pcf8574_data,maxi,flaga,bufor,a;
 volatile uint8_t zmiana_min,ustawienia,pomoc2,temp,switch_delay,licznik,licznik1,flaga1;
 volatile uint8_t tablica_a[] = {4,2,1,0,0,0,0,32,16,8};
 volatile uint8_t tablica_b[] = {0,0,0,0,4,2,1,0,0,0};
 volatile uint8_t i,j,tmp,odczyt_1wire,LSB,MSB,tempjed,tempdzie;
 volatile uint8_t wyslij_rozkaz, rozkaz_1wire,czas_konwersji,flaga_konwersji;
 volatile uint8_t jasnosc,flaga_wl,licznik_wyl,flaga_termometr,temperatura,temp_ulamek;
 volatile uint8_t zmiana_sek,lampa1,lampa2,lampa3,lampa4,temp2,temp3;

#define PCF8563_ADRR 0xA2
#define PCF8574_ADRR 0x70
#define ACK 1
#define NOACK 0


// procedura transmisji sygnału START
void twistart(void);
// procedura transmisji sygnału STOP
void twistop(void);
// procedura transmisji bajtu danych
void twiwrite(char data);
//procedura odczytu bajtu danych
char twiread(char ack);
uint8_t makebcd(uint8_t dec);
uint8_t makedec(uint8_t bcd);

//procedury transmisji 1WIRE
void reset_1wire(void);
void write_1wire(uint8_t data_1wire);
uint8_t read_1wire(void);
uint8_t convert_1wire(void);




int main(void){



 /*wartosci poczatkowe zmiennych*/
 switch_delay=4;
 maxi=100;
 pcf8574_data|=0x80;/*wylaczenie buzera*/
 ustawienia=0;
 tryb=0;


 /*konfiguracja portow*/
 DDRB=0xFF;/*wszystkie linie portu jako wyjscia*/
 PORTB=0x00;//0xFF;/*wszystkie linie w stanie wysokim*/
 DDRC=0x30;/*linie PC4 i PC5 jako wyjscia, pozostale linie jako wejscia*/
 PORTC=0x0F;/*linie PC0..PC4 podciagniete do VCC, pozostale linie w stanie niskim*/
 DDRD=0x3F;/*PD6 i PD7 jako wejscia, pozostale piny jako wyjscia*/
 PORTD=0x00;//*wszystkie linie w stanie niskim*/
 /*konfiguracja timerow*/
 TCCR0|=(1<<CS02);/*prescaler 256, Timer0 zwiekszany co 32us*/
 TIMSK|=(1<<TOIE0);/*odblokowanie przerwania od timer0*/
 TCCR1B|=(1<<CS12);/*prescaler 256, Timer1 zwiekszany co 32us*/
 TIMSK|=(1<<TOIE1);/*odblokowanie przerwania od timer1*/
 TCCR2|=(1<<CS22)|(1<<CS21);/*prescaler 256, Timer2 zwiekszany co 32us */
 TIMSK|=(1<<TOIE2);/*odblokowanie przerwania od timer2*/

 /*globalne zezwolenie na przerwania*/
 sei();

 /*petla nieskonczona*/
 while(1){
  //komparator
  //zmiana jasnosci wyswietlaczy
  if(ACSR & (1<<ACO)){
   jasnosc=4; //jeśli AIN0>AIN1
   PORTD&=~0x20;// wlacz podswietlenie
  }
   else{
    jasnosc=20;
    PORTD|=0x20;//wylacz podswietlenie
   }

  if (flaga & 0x01){
   //jesli flaga ustawiona
   //Odczyt danych z RTC
    flaga&=~0x01;//wyzeruj flage
    twistart();
    twiwrite(PCF8563_ADRR);//adres zegara RTC
    twiwrite(0x02);
    twistart();
    twiwrite(0xA3);
    sekundy=twiread(ACK);
    minuty=twiread(ACK);
    godziny=twiread(NOACK);
    twistop();

    minuty&=~0x80;//wyzerowanie bitu 7
    godziny&=~0xC0;//wyzerowanie bitow 6 i 7

  }
  if (flaga & 0x02){
   //jesli flaga ustawiona (flaga=00000010)
   //wyslij dane do RTC
    flaga&=~0x02;//wyzeruj flage
    twistart();
    twiwrite(PCF8563_ADRR);//adres zegara RTC
    twiwrite(0x02);
    twiwrite(0x00);
    twiwrite(minuty);
    twiwrite(godziny);
    twistop();
  }

  if(wyslij_rozkaz==1){
   wyslij_rozkaz=0;
   if(rozkaz_1wire==4){
    if(czas_konwersji==250){
     rozkaz_1wire++;
     flaga_konwersji=0;
     czas_konwersji=0;
    }

   }
   else rozkaz_1wire++;
   if(rozkaz_1wire==9) rozkaz_1wire=1;

   switch(rozkaz_1wire)
   {
   case 1:
    reset_1wire();
    if(flaga_termometr==1) rozkaz_1wire=0; //jesli ds18b20 nie odpowie,
              //zacznij transmisje od poczatku
   break;
   case 2:
    write_1wire(0xCC);
   break;
   case 3:
    write_1wire(0x44);
   break;
   case 4:
    flaga_konwersji=1;
   break;
   case 5:
    reset_1wire();
   break;
   case 6:
    write_1wire(0xCC);
   break;
   case 7:
    write_1wire(0xBE);
   break;
   case 8:
    LSB=read_1wire();
    MSB=read_1wire();
    temperatura=convert_1wire();
    temperatura=makebcd(temperatura);
   break;
   }
  }

 }


}

/*OBSLUGA PRZERWANIA OD TIMER 1*/
//co 1s
ISR(TIMER1_OVF_vect){
//skrocenie dlugosci licznika do 31250
 TCNT1H=0x85;
 TCNT1L=0xED;

 //odtruwanie katod
 if(flaga1==1){
  licznik1--;//zmniejsz licznik
  if(licznik1==0) flaga1=0;//wyzeruj flage

 }
}


/*OBSLUGA PRZERWANIA OD TIMER 2*/
//co 256us
ISR(TIMER2_OVF_vect){
//skrocenie dlugosci licznika do 247
 TCNT2=0xF7;
if(flaga_wl==1){
 licznik_wyl++;
 if(licznik_wyl>jasnosc){
  licznik_wyl=0;
  flaga_wl=0;
  pcf8574_data&=~(0x1E);
  pcf8574_data&=~(0x60);//wylacznie neonowek
  //wyslanie danych do pcf8574
  twistart();
  twiwrite(PCF8574_ADRR);
  twiwrite(pcf8574_data);
  twistop();
 }
}

}

/*OBSLUGA PRZERWANIA OD TIMER 0*/
//co 4ms
ISR(TIMER0_OVF_vect){
 TCNT0=0x82;//skrocenie dlugosci licznika do 125


//wylaczenie wyswietlaczy
PORTD&=~(0x07);
PORTB&=~(0x3F);
pcf8574_data&=~(0x1F);

wyslij_rozkaz=1;
if(flaga_konwersji==1) czas_konwersji++;

/*sprawdzenie czy minuty sie zmienily*/
if(tryb==0){
 // efekt odrtuwania katod
 if(zmiana_min!=minuty){
  flaga1=1;//ustawienie flagi
  licznik1=5;
 }
}
//NEONOWKI
/*sprawdzenie zmiany sekund*/

if(tryb==0){
 sekundy&=~0x80;//wyzerowanie bitu 7
 sekundy= makedec(sekundy);
 zmiana_sek=sekundy%2;
 if(zmiana_sek) pcf8574_data&=~(0x60);
 else pcf8574_data|=0x60;
}
if(tryb==1){
 pcf8574_data&=~0x20;//wygaszenie neonowki gornej
 pcf8574_data|=0x40;//zapalenie naeonowki dolnej
}

//wyslanie danych do pcf8574
twistart();
twiwrite(PCF8574_ADRR);
twiwrite(pcf8574_data);
twistop();



//zamiana godziny z BCD na DEC
minuty= makedec(minuty);
godziny=makedec(godziny);


/*OBSLUGA PRZYCISKOW*/
switch_delay--;
if((PINC&0x04)==0){   //Ustawianie czasu_______zmiana ustawień______
 if(switch_delay==0){ //licznik pomocniczy switch_delay
  tryb++;
  if(tryb==2) tryb=0;
  if(ustawienia!=0) tryb=0;
 }
}
else if((PINC&0x08)==0){   //Ustawianie czasu_______zmiana ustawień______
 if(switch_delay==0){ //licznik pomocniczy switch_delay
  tryb=0;
  ustawienia++;
  if(ustawienia==3) ustawienia=0;
 }
}


else if((PINC&0x01)==0){   //Ustawianie czasu_______+___________
 if(switch_delay==0){ //licznik pomocniczy switch_delay

  if(ustawienia==2){
   minuty++;
   if(minuty==60) minuty=0;
  }
  if(ustawienia==1){
   godziny++;
   if(godziny==24) godziny=0;
  }
 }
flaga|=0x02;//ustawienie flagi wyslania danych do RTC
}

else if((PINC&0x02)==0){   //Ustawianie czasu_______-___________
 if(switch_delay==0){ //licznik pomocniczy switch_delay

  if(ustawienia==2){
   if(minuty==0) minuty=60;
   minuty--;
  }
  if(ustawienia==1){
   if(godziny==0) godziny=24;
   godziny--;
  }
 }
flaga|=0x02;//ustawienie flagi wyslania danych do RTC
}
else
{
 switch_delay=4;
}

if(ustawienia==0) flaga|=0x01;//ustawienie flagi odczytu danych z RTC
//zamiana godziny z DEC na BCD
minuty= makebcd(minuty);
godziny=makebcd(godziny);



/*rozdzial godziny na jednosci i dziesiatki*/
minjed=minuty & 0x0F;
mindzie=minuty;
mindzie=mindzie>>4;

godzjed=godziny & 0x0F;
godzdzie=godziny;
godzdzie=godzdzie>>4;

/*rozdzielenie temperatury na jednosci i dziesiatki*/
tempjed=temperatura & 0x0F;
tempdzie=temperatura;
tempdzie=tempdzie>>4;

/*wyswietlenie jednej cyfry na wyswietlaczu*/
mux++;
if(mux==4){
mux=0;
}
a++;
if(a==6){
 licznik++;
 a=0;
}


/*WYBOR DANYCH DO WYSWIETLENIA*/
if (tryb==0){ //godzina
 lampa1=godzdzie;//na lampie 1 wyswietl dziesiatki godzin
 lampa2=godzjed;//na lampie 2 wyswietl jednosci godzin
 lampa3=mindzie;//na lampie 3 wyswietl dziesiatki minut
 lampa4=minjed;//na lampie 4 wyswietl jednosci minut
}
if (tryb==1){ //temperatura
 lampa1=tempdzie;
 lampa2=tempjed;
 lampa3=temp_ulamek;
}

if(licznik==10) licznik=0;
switch(mux)
{
/*JEDNOSTKI MINUT*/
 case 0:
  if(licznik1<=1){
   //wystawienie na porty odpowiednich stanow katod wyswietlacza
   temp=tablica_a[lampa4];//minjed];
   PORTB|=temp;
   temp=tablica_b[lampa4];//minjed];
   PORTD|=temp;


   if(lampa4==3){
    pcf8574_data|=0x01;//ustaw bit 0 w pcf8574
   }
   else{
    pcf8574_data&=~0x01;//zeruj bit 0 w pcf8574
   }
  }
  if(licznik1>1){
   temp=tablica_a[licznik];
   PORTB|=temp;
   temp=tablica_b[licznik];
   PORTD|=temp;
  }
if(tryb==1) pcf8574_data&=~(0x10);//wylaczenie anody lampy 4
else pcf8574_data|=0x10;//zalaczenie anody lampy 4
 break;
/*DZIESIATKI MINUT*/
 case 1:
  if(licznik1<=4){
   //wystawienie na porty odpowiednich stanow katod wyswietlacza
   temp=tablica_a[lampa3];
   PORTB|=temp;
   temp=tablica_b[lampa3];
   PORTD|=temp;



   if(lampa3==3){
    pcf8574_data|=0x01;//ustaw bit 0 w pcf8574
   }
   else{
    pcf8574_data&=~0x01;//zeruj bit 0 w pcf8574
   }
  }
  if(licznik1>4){
   temp=tablica_a[licznik];
   PORTB|=temp;
   temp=tablica_b[licznik];
   PORTD|=temp;
  }
  pcf8574_data|=0x08;//zalaczenie anody wyswietlacza dziesiatek minut
 break;
/*JEDNOSTKI GODZIN*/
 case 2:
  if(licznik1<=3){
   //wystawienie na porty odpowiednich stanow katod wyswietlacza
   temp=tablica_a[lampa2];
   PORTB|=temp;
   temp=tablica_b[lampa2];
   PORTD|=temp;



   if(lampa2==3){
    pcf8574_data|=0x01;//ustaw bit 0 w pcf8574
   }
   else{
    pcf8574_data&=~0x01;//zeruj bit 0 w pcf8574
   }
  }
  if(licznik1>3){
   temp=tablica_a[licznik];
   PORTB|=temp;
   temp=tablica_b[licznik];
   PORTD|=temp;
   }
  pcf8574_data|=0x04;//zalaczenie anody wyswietlacza jednostek godzin
 break;
/*DZIESIATKI GODZIN*/
 case 3:
  if(licznik1<=2){
   //wystawienie na porty odpowiednich stanow katod wyswietlacza
   temp=tablica_a[lampa1];
   PORTB|=temp;
   temp=tablica_b[lampa1];
   PORTD|=temp;



   if(lampa1==3){
    pcf8574_data|=0x01;//ustaw bit 0 w pcf8574
   }
   else{
    pcf8574_data&=~0x01;//zeruj bit 0 w pcf8574
   }
  }
  if(licznik1>2){
   temp=tablica_a[licznik];
   PORTB|=temp;
   temp=tablica_b[licznik];
   PORTD|=temp;
  }

if(tryb==0){
 if(lampa1==0) pcf8574_data&=~(0x02);//wylaczenie anody lampy 1
 //jesli dziesiatki godzin beda rowne 0 to lampa bedzie wylaczona
 else pcf8574_data|=0x02;//zalaczenie anody lampy 1
}
if(tryb==1) pcf8574_data|=0x02;
 break;
}

flaga_wl=1;//ustawienie flag, wyswietlacze wlaczone
licznik_wyl=0;// wyzerowanie licznika odliczajacego czas do wylaczenia wyswietlaczy

//zapamietanie obecej wartosci  minut
zmiana_min=minuty;

pomoc2++;
/*miganie wyswietlacza w trybie ustawien godziny*/
if(pomoc2==120) pomoc2=0;
if(ustawienia==2) if(pomoc2<60) pcf8574_data&=~0x18; //w trybie ustawiania czasu wyswietlacz miga
if(ustawienia==1) if(pomoc2<60) pcf8574_data&=~0x06;

//wyslanie danych do pcf8574
twistart();
twiwrite(PCF8574_ADRR);
twiwrite(pcf8574_data);
twistop();
}





/*I2C*/
// procedura transmisji sygnału START
void twistart(void)
{
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)));
}

// procedura transmisji sygnału STOP
void twistop(void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
while ((TWCR & (1<<TWSTO)));
}

// procedura transmisji bajtu danych
void twiwrite(char data)
{
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
}

//procedura odczytu bajtu danych
char twiread(char ack)
{
TWCR = ack ? ((1 << TWINT) | (1 << TWEN) | (1 << TWEA)) : ((1 << TWINT) | (1 << TWEN)) ;
while (!(TWCR & (1<<TWINT)));
return TWDR;
}


// konwersja liczby dziesiętnej na BCD
uint8_t makebcd(uint8_t dec) {
return ((dec / 10)<<4) | (dec % 10);
}

// konwersja liczby BCD na dziesiętną
uint8_t makedec(uint8_t bcd) {
    return ((((bcd) >> 4) & 0x0F) * 10) + ((bcd) & 0x0F);
}



//1WIRE RESET
void reset_1wire(void){
DDRD|=(1<<4);//PD4 jako wyjscie ---->
PORTD&=~(1<<4);//GND na PD4 \__
_delay_us(500);//MASTER robi sygnal RESET, zwierajac linie danych do masy na min 480us
DDRD &=~(1<<4);//PD4 jako wejscie----<
_delay_us(65);//czas potrzebny na na zwarci lini danch do masy przez SLAVE
flaga_termometr=PIND&0x10;//0-potwerdzenie obecosci przez slave, 0x10 -brak potwierdzenia
_delay_us(435);//odczekanie jeszcze 435us bo minimalna dlugosc ramki to 480us
_delay_us(5);//czas REC
}


//1WIRE WRITE
void write_1wire(uint8_t data_1wire){
j=1;
for(i=0;i<8;i++){
 _delay_us(5);//czas REC
 tmp=data_1wire&j;
 if(tmp){ //wyslij 1
  DDRD|=(1<<4);//PD4 jako wyjscie ---->
  PORTD&=~(1<<4);//GND na PD4 \__
    _delay_us(5);
    //PORTD|=(1<<4);//VCC na PD4
    DDRD &=~(1<<4);//PD4 jako wejscie ---<
    _delay_us(80);
 }
 else{ //wyslij 0
  DDRD|=(1<<4);//PD4 jako wyjscie ---->
  PORTD&=~(1<<4);//GND na PD4 \__
  _delay_us(100);
  DDRD &=~(1<<4);//PD4 jako wejscie ---<
 }
 j=j*2;
}
}



//1WIRE READ
uint8_t read_1wire(void){
j=1;
odczyt_1wire=0;
for(i=0;i<8;i++){
  _delay_us(5);//czas REC
  DDRD|=(1<<4);//PD4 jako wyjscie
  PORTD&=~(1<<4);//GND na PD4
 _delay_us(5);
 DDRD &=~(1<<4);//PD4 jako wejscie
 _delay_us(5);
 tmp=PIND&0x10;
 if(tmp) odczyt_1wire|=j; //jesli odczytana jedynak to ustaw bit
 j=j*2;
 _delay_us(80);
}
return odczyt_1wire;
}

//1WIRE CONVERT
uint8_t convert_1wire(void){
 temp_ulamek=0;
  if(LSB&0x08) temp_ulamek=temp_ulamek+5;
  if(LSB&0x04) temp_ulamek=temp_ulamek+2;
  if(LSB&0x02) temp_ulamek=temp_ulamek+1;
  if(LSB&0x01) temp_ulamek=temp_ulamek+1;
  MSB=MSB<<4;
  LSB=LSB>>4;
return (LSB|MSB);
}


DO POBRANIA

Pliki Eagle: nixie_eagle.rar (kopia)
Pliki Eclipse (kody źródłowe): Nixie_clock_program.rar (kopia)
Pliki Corel - obudowa: obudowa.rar (kopia)

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

7 komentarzy:

  1. Łał, nixie zawsze w modzie - super projekt :). Mógłbyś zdradzić gdzie kupiłeś lampy i ile za nie dałeś? Widzę tylko oferty na poziomie 10 zł/szt, co nie jest zbyt zachęcającą ceną.
    Gratuluję efektu, w nocy musi to wyglądać genialnie...

    OdpowiedzUsuń
    Odpowiedzi
    1. Ja swoje kupiłem na znanym portalu aukcyjnym. LC-516 to jedne z mniejszych i tańszych lamp. Niestety trzeba się z tym pogodzić, że lampy trochę kosztują i na pewno tańsze już nie będą. Ale i tak warto wydać te pieniądze i mieć coś oryginalnego, bo kto ze znajomych może pochwalić się takim zegarem :) Warto też poszukać lamp nie używanych wcześniej, które na pewno posłużą dłużej.

      Usuń
  2. Pikny projekt i wykonanie, jest gdzieś wycena, koszty?

    OdpowiedzUsuń
    Odpowiedzi
    1. Co do kosztów ciężko powiedzieć. Zegar powstawał w wolnych chwilach od dłuższego czasu. Obudowa z plexi ciętej laserem to koszt około 60zł, lampy w granicach 10zł za sztukę. Część elektroniki miałem, część dokupiłem. Ogólnie może nie wyszłoby tak drogo ale na same przesyłki trzeba sporo liczyć bo nie da się wszystkiego kupić w jednym sklepie.

      Usuń
    2. Pozwolę sobie odkopać. Nie chcę robić kryptoreklamy, ale mam wrażenie że strasznie przepłaciłeś za tę obudowę. Zlecałem większe zamówienia i cena rzadko kiedy przekraczała 30zł. Oczywiście dużo zależy od grubości i koloru pleksi, niemniej i tak proponuję zapoznać się z tym gościem: http://www.jklaser.pl/ . Zamówienia wysyłam mailowo w formie PDFa albo pliku SVG.

      Usuń
  3. Jest możliwość kupna wytrawionych pcb dwustronnych? Nigdy mi się ze sobą nie pokrywają gdy próbuję je wykonać :)

    OdpowiedzUsuń
  4. Złożyłem moduł przetwornicy, logiki, tranzystorów i lewego modułu lamp. Mega zaprogramowana, szybki test i...obydwie lampy działają non stop podświetlając na raz wszystkie cyfry...to wina nie podłączenia wszystkich modułów czy po prostu coś źle zrobiłem?

    OdpowiedzUsuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.