/*********************************************************************/
/* Digitronove hodiny */
/* Lukas Olivik */
/* l.olivik @ centrum.cz */
/* www.ollie.xf.cz */
/* */
/* v0.3 - 01.06.2011 */
/* podpora teplotniho cidla, uvodni animace */
/*********************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 4000000UL // interni oscilator 4 MHz kvuli I2C
#include <util/delay.h>
#include "twimaster.c"
#include "i2cmaster.h"
#define USE_DS18B20 // moznost volby kompilace podpory teplotniho cidla,
#ifdef USE_DS18B20 // zabira celkem asi 900 B Flash pameti
#include "myds.c"
#endif
//#define ANIMACE1 // moznost volby kompilace animace po zapnuti
#define ANIMACE2
#define DEL1 100 // zpozdeni pro uvodni animaci
///////////////// Adresy registru v obvodu RTC ////////////////////////////////
#define PCF8563 0xA2 // adresa PCF8563
#define I2C_READ 1 // PCF8563 cteni
#define I2C_WRITE 0 // PCF8563 zapis
#define RTCctrl1 0x00 // kontrolni registry
#define RTCctrl2 0x01
#define RTCsec 0x02 // adresa sekund, BCD
#define RTCmin 0x03 // minuty
#define RTChour 0x04 // hodiny
#define RTCday 0x05 // den
#define RTCdayw 0x06 // den v tydnu
#define RTCmonth 0x07 // mesic
#define RTCyear 0x08 // rok
///////////////// Makra pro obsluhu HW ////////////////////////////////////////
#define anode_off PORTA &=~(0b01110000); PORTC &=~(0b00111000);
#define anode1 PORTC |=(1<<PC5); // aktivace prislusne anody
#define anode2 PORTC |=(1<<PC3); // prelozi se spravne jako SBI
#define anode3 PORTC |=(1<<PC4);
#define anode4 PORTA |=(1<<PA6);
#define anode5 PORTA |=(1<<PA4);
#define anode6 PORTA |=(1<<PA5);
#define digits_off PORTA |=0b00001111; // BCD kod 15, dekoder vypne vse
#define nixie_io_init DDRA |=0b01111111; DDRC |=0b00111100; anode_off; digits_off;
// nastaveni Timeru0 pro multiplexni zobrazeni
#define TIM0_init TCCR0 |=(1<<CS01)|(1<<CS00)|(1<<WGM01); TIMSK |=(1<<OCIE0); OCR0 = 124;
// preddelicka 64, CTC rezim, preruseni Compare Match, vysledna frekvence preruseni 500 Hz, obnoveni cislic 83 Hz pro 4 MHz
// puvodni nastaveni - kratka perioda, obsluha preruseni zabirala zbytecne mnoho casu
//#define TIM0_init TCCR0 |=(1<<CS01); TIMSK |=(1<<TOIE0); // nastaveni Timeru0
// preddelicka 8, povolit preruseni, vysledna frekvence preruseni 1953 Hz, obnoveni cislic 325 Hz pro 4 MHz
#define dp_off PORTC &=~(1<<PC2); // ovladani desetinnych tecek v digitronkach
#define dp_on PORTC |= (1<<PC2);
#define led_btn_off DDRB &=~(0b00001111); PORTB &=~(0b00001111); // nastavit ovladaci piny na vysokou impedanci
#define scan_s3 DDRB |= (1<<PB1); PORTB |= (1<<PB1); asm("nop; \n nop; \n"); // skenovani tlacitek -
#define scan_s2 DDRB |= (1<<PB2); PORTB |= (1<<PB2); asm("nop; \n nop; \n"); // nastavit prislusny vystup
#define scan_s1 DDRB |= (1<<PB3); PORTB |= (1<<PB3); asm("nop; \n nop; \n"); // a pockat na "ustaleni" signalu
#define led5_on DDRB |= (1<<PB3); // rozsvitit LED5 - znamenko minus, podminka scan_s3
#define led6_on DDRB |= (1<<PB2); // rozsvitit LED6 - neosazena, podminka scan_s1
#define led7_on DDRB |= (1<<PB1); // rozsvitit LED7 - neosazena, podminka scan_s1
#define KEY_MENU 1 // prirazeni tlacitek
#define KEY_UP 2
#define KEY_OK 3
///////////////// Konstanty ///////////////////////////////////////////////////
static const char digits[11]={
// hodnoty jsou poskladane podle propojeni na plosnem spoji
// ....BCDA
0b11110001, // 0 //
0b11111001, // 1 //
0b11110100, // 2 //
0b11110000, // 3 //
0b11111100, // 4 //
0b11110010, // 5 //
0b11111101, // 6 //
0b11110101, // 7 //
0b11110011, // 8 //
0b11111000, // 9 //
0b11111111}; // 15 - vse vypnuto
///////////////// Globalni promenne
volatile unsigned char disp[6]={10,10,10,10,10,10}; // globalni promenna pro zobrazovaci rutinu, zapsanim 10 se cislice zhasne
volatile unsigned char time[6]={10,10,10,10,10,10}; // druhe pole, pouyite jen pro uvodni animaci
volatile unsigned char dp = 0b00010100; // desetinne tecky, MSB je LED pro znamenko minus.
volatile unsigned char key = 0; // cislo prave stisknute klavesy
volatile unsigned char blink = 0; // promenna udavajici ktere cislice maji blikat, platnych 6 nejnizsich bitu
volatile unsigned int blink_cnt = 0; // pomocna promenna, pocitadlo pro blikani
volatile unsigned char timeout = 0; // pomocna promenna, drzeni tlacitka / timeout pri nastaveni casu
volatile unsigned char i=0, temp=0, result=0, i2cdata=0;// pomocne promenne
#ifdef USE_DS18B20
int teplota = 250, temp3 = 0;
#endif
///////////////// Funkce //////////////////////////////////////////////////////
#ifdef USE_DS18B20
void temp_disp(int teplota)
{
dp = 0b00000100;
if (teplota < 0)
{
dp |= 128; // ovladani LEDky - znamenko minus
teplota = -teplota; // odstranit znamenko
}
else dp &= 127;
//if (teplota > 1000) // zobrazeni teploty nad 99,9 se zatim neresi
temp3 = teplota / 100; // desitky stupnu
disp[0] = (char)(temp3);
teplota = teplota - (disp[0]*100);
temp3 = teplota / 10; // jednotky stupnu
disp[1] = (char)(temp3);
teplota = teplota - (disp[1]*10);
temp3 = teplota; // desetiny stupnu
disp[2] = (char)(temp3);
disp[3] = 10; // ostatni zhasnout
disp[4] = 10;
disp[5] = 10;//ow_detect_presence();// zobrazeni pritomnosti cidla
}
#endif
inline void scan_key(unsigned char num)
{
if ((key == 0) || (key == num)) // testujeme jen pokud bylo stisknuto stejne tl. nebo zadne
{
if (PINB & 1) key = num; else key = 0;
}
}
void read(unsigned char reg)
{
i2c_start_wait(PCF8563+I2C_WRITE); // zapis do PCF8563
i2c_write(reg); // adresa bunky
i2c_rep_start(PCF8563+I2C_READ); // cteni z PCF8563
i2cdata = i2c_readNak(); // ulozit prectenou hodnotu do globalni promenne
i2c_stop(); // stop
};
void write(unsigned char reg)
{
i2c_start_wait(PCF8563+I2C_WRITE);
i2c_write(reg); // vyber adresy( hodiny minuty atd)
i2c_write(i2cdata); // zapise BCD na vybranou adresu
i2c_stop(); // stop
};
void read_time(char *dest) // funkce pro vycteni casu z RTC do zvoleneho pole
{
read(RTCsec);
dest[5]=(i2cdata & 0x0F);
dest[4]=((i2cdata >>4)& 0b00000111);
read(RTCmin);
dest[3]=(i2cdata & 0x0F);
dest[2]=((i2cdata >>4)& 0b00000111);
read(RTChour);
dest[1]=(i2cdata & 0x0F);
dest[0]=((i2cdata >>4)& 0b00000011);
blink = 0;
dp = 0b00010100;
}
// pro kompatibilitu, toto byla puvodne funkce pro vycteni do pole disp
#define read_disp_time() read_time(&disp[0]);
///////////////// Preruseni Timer0 - multiplexovani cislic ////////////////////
ISR(TIMER0_COMP_vect ) // pouzito preruseni na Compare Match
//ISR(TIMER0_OVF_vect)
{
static char pos; // index prave zobrazovane pozice
digits_off; // zhasnout vsechno
anode_off;
dp_off;
temp = (1<<pos); // vypocet bitove masky pro soucasnou pozici
led_btn_off;
//_delay_us(100); // male zpozdeni pro odstraneni prosvitani sousednich znaku
switch (pos) // vybrat anodu, skenovani tlacitek
{
case 0:
scan_s1;
scan_key(1);
if ((temp&dp)||(disp[0]<10)) anode1; // pokud neni nic zobrazeno, nepripoji se ani anoda,
break; // aby neprosvitala sousedni cislice
case 1:
scan_s1;
scan_key(1);
if ((temp&dp)||(disp[1]<10)) anode2;
break;
case 2:
scan_s2;
scan_key(2);
if ((temp&dp)||(disp[2]<10)) anode3;
break;
case 3:
scan_s2;
scan_key(2);
if ((temp&dp)||(disp[3]<10)) anode4;
break;
case 4:
scan_s3;
scan_key(3);
if ((temp&dp)||(disp[4]<10)) anode5;
if (dp & 128) // pozadujeme rozsvitit znamenko minus
led5_on;
break;
case 5:
scan_s3;
scan_key(3);
if ((temp&dp)||(disp[5]<10)) anode6;
if (dp & 128) // pozadujeme rozsvitit znamenko minus
led5_on;
break;
}// end switch
//_delay_us(100); // male zpozdeni pro odstraneni prosvitani sousednich znaku
if (dp & temp) dp_on; // rozsviceni pozadovane desetinne tecky
blink_cnt++;
if (blink_cnt > 500) blink_cnt = 0;// perioda blikani jedna sekunda //1952
if (blink & temp) // blikat touto cislici
{ // doba svitu umyslne delsi nez polovina
if (blink_cnt < 350)
{
PORTA &= (digits[disp[pos]]);
}
else
{ // misto uplneho zhasnuti svitit polovicnim svitem - nefunguje
//if (blink_cnt & 1) PORTA &= (digits[disp[pos]]);
}
}
else{ // trvale sviceni
PORTA &= (digits[disp[pos]]); // rozsvitit cislici z globalni promenne
};
pos++; // prepnout dalsi cislici
if (pos>5) pos = 0;
}
///////////////// Hlavni funkce ///////////////////////////////////////////////
int main (void)
{
nixie_io_init; // nasteveni I/O pinu
TIM0_init; // nastaveni Timeru pro multiplex digitronu
PORTC |=(1<<PC0); // zapnout pull-upy na I2C linkach, na DPS nejsou zadne
PORTC |=(1<<PC1);
i2c_init(); // inicializace rozhrani I2C pro komunikaci s RTC obvodem
_delay_ms(50);
i2cdata = 128;
write(0x0D); // povolit vystup 32,768 KHz z obvodu RTC, stejne nefunguje...?
_delay_ms(50);
read(RTCsec);
if (i2cdata > 127) // hodiny nenastaveny, nastavit nulovy vychozi cas
{
dp |= 128; // rozsvitit znamenko minus pro signalizaci ztraty casu
i2cdata = 0;
write(RTChour);
i2cdata = 0;
write(RTCmin);
i2cdata = 0;
write(RTCsec);
}
_delay_ms(50);
sei();
/////////////////// Uvodni animace ////////////////////////////////////////////
#ifdef ANIMACE1
dp = 0;
for (i=0;i<6;i++) disp[i] = 0;
_delay_ms(200);
while(1)
{
read_time(&time[0]);
dp = 0;
if (time[0] > disp[0]) disp[0]++;
else if (time[1] > disp[1]) disp[1]++;
else if (time[2] > disp[2]) disp[2]++;
else if (time[3] > disp[3]) disp[3]++;
else if (time[4] > disp[4]) disp[4]++;
else if (time[5] > disp[5]) disp[5]++;
else break;
_delay_ms(150);
}
#endif
#ifdef ANIMACE2
dp = 0;
disp[0] = 0;
read_time(&time[0]);
dp = 0;
while(1)
{_delay_ms(DEL1);
if (time[0] > disp[0]) disp[0]++;
else break;}
disp[1] = 0;
read_time(&time[0]);
dp = 0;
while(1)
{_delay_ms(DEL1);
if (time[1] > disp[1]) disp[1]++;
else break;}
disp[2] = 0;
read_time(&time[0]);
dp = 0b00000100;
while(1)
{_delay_ms(DEL1);
if (time[2] > disp[2]) disp[2]++;
else break;}
disp[3] = 0;
read_time(&time[0]);
dp = 0b00000100;
while(1)
{_delay_ms(DEL1);
if (time[3] > disp[3]) disp[3]++;
else break;}
disp[4] = 0;
read_time(&time[0]);
while(1)
{_delay_ms(DEL1);
if (time[4] > disp[4]) disp[4]++;
else break;}
disp[5] = 0;
read_time(&time[0]);
while(1)
{_delay_ms(DEL1);
if (time[5] > disp[5]) disp[5]++;
else break;}
#endif
/////////////////// Konec uvodni animace //////////////////////////////////////
while(1)
{
if (key == KEY_OK)
{
timeout++;
read_disp_time(); // vycteni casu a zobrazeni
_delay_ms(50);
if (timeout > 20) // tl. podrzeno alespon sekundu, chceme nastavit cas
{
timeout = 0;
read_disp_time();
disp[4] = 0; disp[5] = 0; // vynulovat sekundy
blink = 0b00000001; // blikat - desitky hodin
_delay_ms(20); //
while (key != 0); // pockat na pusteni tlacitka
while ((key != KEY_OK) && (timeout < 40)) // nastaveni desitek hodin
{ // opakujeme dokud nepotvrdime tlacitkem OK nebo nevyprsi 10 s
if (key == KEY_UP)
{ // zvyseni hodnoty
timeout = 0;
disp[0]++;
if (disp[0] > 2) disp[0] = 0;
}
timeout++;
_delay_ms(250); // pro drzeni tl. zvysovat 4x za sekundu
} // konec nastaveni desitek hodin
blink = 0b00000010; // blikat - jednotky hodin
if (disp[0]==2 && disp[1]>3) disp[1]=3; // max. 23 hodin
_delay_ms(20); //
while (key != 0); // pockat na pusteni tlacitka
while ((key != KEY_OK) && (timeout < 40)) // nastaveni jednotek hodin
{ // opakujeme dokud nepotvrdime tlacitkem OK nebo nevyprsi 10 s
if (key == KEY_UP)
{ // zvyseni hodnoty
timeout = 0;
disp[1]++;
if (disp[0] ==2)// nejde nastavit vic nez 23 hodin
{
if (disp[1]>3) disp[1] = 0;
}
else{
if (disp[1]>9) disp[1] = 0;
}
}
timeout++;
_delay_ms(250); // pro drzeni tl. zvysovat 4x za sekundu
} // konec nastaveni jednotek hodin
blink = 0b00000100; // blikat - desitky minut
_delay_ms(20); //
while (key != 0); // pockat na pusteni tlacitka
while ((key != KEY_OK) && (timeout < 40)) // nastaveni desitek minut
{ // opakujeme dokud nepotvrdime tlacitkem OK nebo nevyprsi 10 s
if (key == KEY_UP)
{ // zvyseni hodnoty
timeout = 0;
disp[2]++;
if (disp[2] > 5) disp[2] = 0; // max 59 minut
}
timeout++;
_delay_ms(250); // pro drzeni tl. zvysovat 4x za sekundu
} // konec nastaveni desitek minut
blink = 0b00001000; // blikat - jednotky minut
_delay_ms(20); //
while (key != 0); // pockat na pusteni tlacitka
while ((key != KEY_OK) && (timeout < 40)) // nastaveni jednotek minut
{ // opakujeme dokud nepotvrdime tlacitkem OK nebo nevyprsi 10 s
if (key == KEY_UP)
{ // zvyseni hodnoty
timeout = 0;
disp[3]++;
if (disp[3] > 9) disp[3] = 0; // max 59 minut
}
timeout++;
_delay_ms(250); // pro drzeni tl. zvysovat 4x za sekundu
} // konec nastaveni jednotek minut
blink = 0b00110000; // blikat - cele sekundy
_delay_ms(20); //
while (key != 0); // pockat na pusteni tlacitka
while ((key != KEY_OK) && (timeout < 40)) // nastaveni jednotek minut
{ // opakujeme dokud nepotvrdime tlacitkem OK nebo nevyprsi 10 s
if (key == KEY_UP)
{ // pouze vynulovat timeout, sekundy se vzdy nastavuji na 0
timeout = 0;
}
timeout++;
_delay_ms(250); // pro drzeni tl. zvysovat 4x za sekundu
} // konec nastaveni jednotek minut
// stisknuto OK nebo vyprsel cas
if (timeout < 40) // pokud nevyprsel cas
{
blink = 0; // vypnout blikani
i2cdata = ((disp[0]<<4)+disp[1]); // slozit BCD hodnotu
write(RTChour);
i2cdata = ((disp[2]<<4)+disp[3]); // slozit BCD hodnotu
write(RTCmin);
i2cdata = 0;
write(RTCsec);
blink = 0b00111111;
_delay_ms(20); //
while (key != 0); // pockat na pusteni tlacitka OK
while ((key != KEY_OK) && (timeout < 40))
{ // 4 sekundy blika nove nastaveny cas, muzeme preskocit tlacitkem OK
timeout++;
_delay_ms(100);
}
} // konec blikani nove nastavenym casem
blink = 0; // vypnout blikani
} // end if (timeout > 20), konec nastaveni casu
} // end if (key == KEY_OK)
#ifdef USE_DS18B20
else if (key == KEY_MENU) // zmereni teploty
{
timeout = 10; // zobrazeni 7,5 sekundy
while (timeout > 0)
{ // mereni trva 750 ms
timeout--;
if (key == KEY_UP) timeout = 40; // prodlouzit zobrazeni na 30 s
if (ds_conv()!=0) break;// zahajit mereni, jen pokud je detekovano cidlo
_delay_ms(250); // cekat 750 ms, moznost odejit stiskem OK
if (key == KEY_OK) break;
_delay_ms(250);
if (key == KEY_OK) break;
_delay_ms(250);
if (key == KEY_OK) break;
teplota = ds_read();// precist teplotu
temp_disp(teplota); // zobrazit teplotu
}
timeout = 0;
} // konec mereni teploty
#endif
else // zadne tlacitko nestisknuto, klidovy stav
{
timeout = 0;
read_disp_time(); // vycteni casu a zobrazeni
#ifdef USE_DS18B20 // dvakrat za minutu na okamzik ukazat teplotu
if ((disp[4]==1 || disp[4]==4) && disp[5]==5)
{
if (ds_conv()==0) // zahajit mereni, jen pokud je detekovano cidlo
{
_delay_ms(750);
teplota = ds_read();// precist teplotu
temp_disp(teplota); // zobrazit teplotu
_delay_ms(2000); // na dve sekundy
}
}
#endif
_delay_ms(50);
}
} // end main loop
} // end main