Dioda D1 to Schotky 1N5817 czy pierwsza z brzegu dioda ? Bo podświetlanie innych segmentów, szczególnie takie co zanika po czasie wskazuje na byle jaką D1 albo uszkodzony goldcap. Co do reszty problemów, to pewnie problemy z softem. Użyj najnowszej wersji softu, czyli 3.4 i wszystko powinno hulać jak powinno. Tu nie da się przekleić fragmentu z najnowszego kodu do jakiegoś archaicznego. Po drodze było baaaardzo dużo zmian. Edit: Dobra, projekt stary jak węgiel, więc daję najnowsze źródła v 3.4 (sprzed 6 lat :-) )
#ifndef DELAY_H
#define DELAY_H
/* prototypes */
// uS delay
void delay(unsigned int us);
// mS delay
void delayms(unsigned int ms);
#endif
RSTDISBL = 1
WDTON = 1
SPIEN = 0
CKOPT = 0
EESAVE = 0
BOOTSZ1 = 1
BOOTSZ0 = 1
BOOTRST = 1
BODLEVEL = 1
BODEN = 1
SUT1 = 1
SUT0 = 0
CKSEL3 = 0
CKSEL2 = 0
CKSEL1 = 1
CKSEL0 = 0
/*
**************************************
**************************************
** **
** ZEGAREK NA ATMEGA 8 **
** wersja 3.4 **
** **
** (C) 2008-2011 **
** Romuald Bia³y **
** romek_b@o2.pl **
** **
**************************************
**************************************
ATMEGA8, Generator wewnetrzny 2MHz + Timer2 na kwarcu 32.768kHz
Fuses:
RSTDISBL = 1
WDTON = 1
SPIEN = 0
CKOPT = 0
EESAVE = 0
BOOTSZ1 = 1
BOOTSZ0 = 1
BOOTRST = 1
BODLEVEL = 1
BODEN = 1
SUT1 = 1
SUT0 = 0
CKSEL3 = 0
CKSEL2 = 0
CKSEL1 = 1
CKSEL0 = 0
-----------------------------------------------------------------------------------------------
Historia:
2008-03-22 - Wersja 2.0 z obs³ug¹ sekundnika
- Zmieniono sposob dzialania PWM'a do wyswietlaczy, wykorzystano sprzetowe mechanizmy timera1
- Zmieniono obliczenia korekcji na 1 minutowe (zamiast 10 minutowe) co daje wieksza dokladnosc korekcji.
Obliczona korekcje przy starym programie trzeba pomnozyc przez 10 dla uzyskania tego samego efektu.
2008-04-02 - Dodano pomiar temperatury
- Dodano setup i programy P1-P5
- Dodano opónienie filtru dla regulatora jasnoæi
2009-03-14 - Dodano obsluge analogowego sekundnika
- Poprawiono procedury obslugi DS18B20
- Dodano obslugê dodatkowych driverów anod i/lub katod wyswietlacza
2009-04-06 - Wersja 3.0 - klawisze zawsze na PC4 i PC5
- Spora optymalizacja kodu
- Dodano program P6 umo¿liwiaj¹cy pewn¹ korektê czêstotliwoæi pracy
procesora co umo¿liwia minimalizacjê wp³ywu generowanych zak³óceñ
(np. na odbiorniki DCF'a), oraz mo¿liwosc zdudniania wywietlacza z jarzeniówkami
- Wybór migania dwukropkiem przeniesiono do programu P7
- Dodano program P7 umo¿liwiaj¹ce ustawienie opcji zegarka od tej wersji nie ma ju¿
oddzielnych HEX'ow dla 4 i 6 cyfr. Opis opcji programu P7 poni¿ej.
2010-06-15 - Wersja 3.1 - Definitywnie poprawiono obs³uge DS18B20 przy dwuprzewodowym pod³¹czeniu
- Poprawiono procedury obs³ugi sekundnika analogowego
2011-05-23 - wersja 3.2 - Odblokowano mo¿liwoæ wywietlania temperatury poni¿ej -19.9 stopnia. Wywietla sie bez znaku " - " .
2011-07-07 - wersja 3.3 - dodano mo¿liwoæ u¿ycia kropki w multiplexowanych wywietlaczach zamiast dwukropka. Nowa opcja w P7.
2011-12-03 - wersja 3.4 - dodano mo¿liwoæ u¿ycia kropek 2 i 3 wyswietlacza jako dwukropka. Nowa opcja w P7.
==============================================================================================
Programy:
P1 - Ustawianie czasu wywietlania godzin (czasu),
P2 - Ustawianie czasu wywietlania temperatury,
P3 - Ustawianie korekcji dok³adnoci pracy zegara,
P4 - Ustawianie opónienia (filtru) reakcji na zmianê jasnoci otoczenia,
P5 - Ustawianie minimalnej jasnoci wywietlacza.
P6 - Ustawienie czêstotliwoci multipleksowania wywietlaczy
P7 - Wybór opcji i trybu pracy zegarka.
Wartoæ w programie P7 to suma wartoci poni¿szych opcji (sumujemy za³¹czone)
1 - wersja 6 cyfrowa
2 - migaj dwukropkiem przy wywietlaniu czasu
4 - w wersji 4 cyfrowej termometr wywietla dziesi¹te czêci stopnia i nie wywietla znaczka stC
8 - sekundnik analogowy jako wêdruj¹cy punkt, a nie linijka
16 - anody diod dwukropka pod³¹czone do PB5 a nie do +5,6V (w wersji 4 cyfrowej)
32 - dwukropek multipleksowany razem z wywietlaczami, zaswieca sie tylko przy wywietlaniu 2 cyfry
64 - przy multipleksowanym dwukropku migaj tak¿e kropk¹ 3 cyfry
Przyk³ady:
P7 = 0 - 4 cyfrowy zegarek, dwukropek nie miga
P7 = 2 - j.w, dwukropek miga
P7 = 6 - j.w, dwukropek miga, temperatura z rozdz. 0.1 stopnia
P7 = 30 - 4 cyfry, dwukropek miga, termometr 0,1 st, analog. sekundnik jako punkt, diody do PB5
P7 = 1 - 6 cyfrowy zegarek, dwukropek nie miga
P7 = 3 - 6 cyfrowy zegarek, dwukropek miga
******************************************************************************************** */
#include & lt; avr/io.h & gt;
#include & lt; avr/interrupt.h & gt;
#include & lt; avr/pgmspace.h & gt;
#include & lt; avr/eeprom.h & gt;
#include & lt; util/crc16.h & gt;
#include " delay.h "
// -----------------------------------------------------------------------------------------
// PINY
#define KATODY PORTD
#define ANODY PORTB
#define seg_a 64 // przypisanie segment - & gt; pin
#define seg_b 8
#define seg_c 1
#define seg_d 2
#define seg_e 4
#define seg_f 16
#define seg_g 32
#define seg_dp 128
#define KEYPINS PINC
#define KEY1 PC5 // Klawisz Minuty
#define KEY2 PC4 // Klawisz Godziny
#define KEY_SWI PC3
#define PWR_FAIL PC1 // kontrola obecnoci zasilania sieciowego
#define OWI PC2 // one wire port dla DS18x20
#define SER_CLK PC3 // linia zegara dla rejestru sekundnika analogowego
#define SER_DAT PC4 // linia danych dla rejestru sekundnika analogowego
// -----------------------------------------------------------------------------------------
// Zmienne globalne i definicje skrotow
// basic unsigned types
//
typedef unsigned char u08;
typedef unsigned short u16;
// basic signed types
//
typedef char s08;
typedef short s16;
// struktura zawieraj¹ca bierz¹cy czas
typedef struct{
unsigned char second; // sekundy
unsigned char min; // minuty
unsigned char hour; // godziny
unsigned int corr; // minuty dla korekcji dokladnosci
}time;
time t; // deklaracja zmiennej w postaci struktury
typedef struct{
u08 sekundnik :1; // 0=4 cyfry, 1=6 cyfr
u08 migaj :1; // 0=nie migaj dwukropkiem przy wywietlaniu czasu, 1=migaj
u08 tempprec :1; // 0=przy 4 cyfrach dokladnosc do 1 stopnia, 1=do 0.1 stopnia
u08 anaver :1; // 0=sekundnik analogowy jako linijka , 1=jako wêdruj¹cy punkt
u08 dwukPB5 :1; // 0=anody diod dwukropka do +5,6V, 1=do PB5
u08 dwukMult :1; // 0=dwukropek bez multipleksowania, 1=dwukropek pracuj¹cy razem z 2 wywietlaczem w multipleksie
u08 dwukMult3 :1; // 0=dwukropek tylko na 2 wywietlaczu w multipleksie, 1=dwukropek na 2 i 3 wywietlaczu w multipleksie
u08 res3 :1;
}bitfields;
bitfields opt; // bajt opcji pracy jako pole bitowe opisane powy¿ej
u08 ledbuf[6]; // bufor wyswietlacza LED
u08 dwukropek,czas_godzin,czas_temp,ds_ok,opozn,minjas;
u08 neg_anod, neg_katod; // zmienne ustawiane przy detekcji driverów
s16 poprawka, temper;
volatile u08 sekunda;
volatile u16 licz;
double jafil; // filtr regulacji jasnoci
u08 GENZNAK[32] PROGMEM = { ~(seg_a | seg_b | seg_c | seg_d | seg_e | seg_f), // 0
~(seg_b | seg_c), // 1
~(seg_a | seg_b | seg_d | seg_e | seg_g), // 2
~(seg_a | seg_b | seg_c | seg_d | seg_g), // 3
~(seg_b | seg_c | seg_f | seg_g), // 4
~(seg_a | seg_c | seg_d | seg_f | seg_g), // 5
~(seg_a | seg_c | seg_d | seg_e | seg_f | seg_g), // 6
~(seg_a | seg_b | seg_c), // 7
~(seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g), // 8
~(seg_a | seg_b | seg_c | seg_d | seg_f | seg_g), // 9
~(seg_d | seg_e | seg_g), // c , 10
~(seg_c | seg_d | seg_e | seg_g), // o , 11
~(seg_e | seg_g), // r , 12
~(seg_b | seg_c | seg_g), // -1, 13
~(seg_g), // - , 14
0xFF, // puste , 15
~(seg_a | seg_d | seg_e | seg_f | seg_g), // E , 16
~(seg_d | seg_e | seg_f | seg_g), // t , 17
~(seg_a | seg_b | seg_e | seg_f | seg_g), // P , 18
~(seg_a | seg_d | seg_e | seg_f), // C , 19
~(seg_a | seg_b | seg_c | seg_e | seg_f | seg_g), // A , 20
~(seg_d | seg_e | seg_f), // L , 21
~(seg_a | seg_b | seg_f | seg_g), // symbol stopnia , 22
~(seg_e), // przecinek , 23
0xFF }; // puste , 24
// *****************************************************************************************
// Przerwanie timera 2 odliczajace czas (wywolywane co 1 sekunde)
// *****************************************************************************************
SIGNAL(SIG_OVERFLOW2) // RTC, przerwanie co 1 sekunde
{
sekunda++; // zwieksz pomocniczy licznik sekund
if (++t.second == 60) // zwieksz licznik sekund i sprawdz czy 60
{
t.second=0; // zeruj licznik sekund
if (++t.min == 60) // zwieksz licznik minut i sprawdz czy 60
{
t.min = 0; // zeruj licznik minut
if (++t.hour == 24) // zwieksz licznik godzin i sprawdz czy 24
t.hour=0; // zeruj licznik godzin
}
t.corr++; // co minute zwieksz równie¿ licznik korekcji
}
}
// *****************************************************************************************
// Przerwanie overflow timera 1 - obsluga multipleksowania wyswietlaczy
// *****************************************************************************************
SIGNAL(SIG_OVERFLOW1) // Multiplex LED
{
static u08 cyfra,anod;
u08 tmp,tmp2;
if(licz)
licz--; // pomocniczy licznik czasu do wielu zastosowan
KATODY = neg_katod; // zgas wyswietlacz
anod & lt; & lt; = 1; // wartoæ dla wybrania kolejnej anody
tmp = 4;
if(opt.sekundnik) // max licznika cyfr to 4 lub 6
tmp = 6;
if(++cyfra == tmp) // zwiêksz licznik cyfr i sprawd czy osi¹gn¹³ max ?
{
cyfra = 0; // zeruj licznik cyfr
anod = 1; // wartosc dla wybrania pierwszej anody
}
ANODY = neg_anod ^ anod; // wybierz kolejn¹ anodê (XOR umozliwia proste
// sterowanie niezale¿nie od negacji na anodach)
if( !opt.sekundnik & & opt.dwukPB5 ) // oddzielna obsluga dwukropka podpiêtego do PB5
{
if(dwukropek)
ANODY |= 1 & lt; & lt; PB5; // zaswieæ (1 na PB5) niezale¿nie od negacji anod
else
ANODY & = ~(1 & lt; & lt; PB5); // zga (0 na PB5) niezale¿nie od negacji anod
}
tmp = ledbuf[cyfra]; // pobierz z bufora wyswietlacza znak do wyswietlenia
if(cyfra == 0 & & tmp == 0) // jesli na pozycji dziesiatek godzin ma byæ wywietlone 0
tmp2 = 0xFF; // to wygas dziesiatki godzin
else
tmp2 = pgm_read_byte(GENZNAK+tmp); // lub pobierz uklad kadod z generatora znakow
if(opt.dwukPB5 || (dwukropek & & // oddzielna obsluga dwukropka na katodach
((cyfra == 1) || (cyfra == 2 & & opt.dwukMult3) || !opt.dwukMult)))
tmp2 & = ~(seg_dp); // zaswiec dwukropek lub u¿yj PD7 tylko do PWM
KATODY = (neg_katod) ? tmp2 : ~tmp2; // wyswietl cyfre
}
// *****************************************************************************************
// Przerwanie Compare A timera 1 - obsluga regulacji jasnoci
// *****************************************************************************************
// minimalizacja dlugoci przerwania
void SIG_OUTPUT_COMPARE1A(void)__attribute__ ((naked));
SIGNAL(SIG_OUTPUT_COMPARE1A) // Softwarowy PWM do wyswietlacza
{
asm volatile( " PUSH R24 " ); // Zachowaj R24
KATODY = neg_katod; // zgas wyswietlacz jeli TCNT1 == OCR1
asm volatile( " POP R24 " ); // Odtwórz R24
asm volatile( " RETI " ); // Powrót z ISR (atrybut " naked " nie generuje powrotu)
}
// -----------------------------------------------------------------------------------------
// *****************************************************************************************
// Procedury pomocnicze
// *****************************************************************************************
u16 absint(s16 val) // funkcja ABS dla formatu signed int (16 bit)
{
if(val & lt; 0)
return (u16)(65536 - val);
else
return (u16)val;
}
// -----------------------------------------------------------------------------------------
void disp_update(void) // Syswietlenie aktualnego czasu
{
ledbuf[0] = t.hour / 10;
ledbuf[1] = t.hour % 10;
ledbuf[2] = t.min / 10;
ledbuf[3] = t.min % 10;
ledbuf[4] = t.second / 10;
ledbuf[5] = t.second % 10;
}
// -----------------------------------------------------------------------------------------
void fillwys(u08 d) // Wypelnienie wyswietlacza
{
u08 i;
for(i=0; i & lt; 6; i++)
ledbuf[i] = d;
}
// -----------------------------------------------------------------------------------------
void clk_pulse(void) // impuls CLK dla rejestru
{
PORTC & = ~(1 & lt; & lt; SER_CLK);
asm volatile( " NOP " );
asm volatile( " NOP " );
asm volatile( " NOP " );
asm volatile( " NOP " );
PORTC |= (1 & lt; & lt; SER_CLK);
asm volatile( " NOP " );
}
void clr_rejestr(void) // Kasowanie rejestru analogowego sekundnika
{
DDRC |= (1 & lt; & lt; SER_DAT); // PC4 jako wyjcie
u08 l = 64;
PORTC & = ~(1 & lt; & lt; SER_DAT); // Serial Data = 0
while(l--) // ga sekundnik (wyslij 64 * 0)
clk_pulse();
DDRC & = ~(1 & lt; & lt; SER_DAT); // przywróc PC4 jako wejcie z klawisza
PORTC |= (1 & lt; & lt; SER_DAT); // Za³¹cz Pullup
}
void set_rejestr(void)
{
u08 tmp = t.second;
clr_rejestr(); // kasuj rejestr
u08 l = 0;
DDRC |= (1 & lt; & lt; SER_DAT); // PC4 jako wyjcie
PORTC |= (1 & lt; & lt; SER_DAT); // Serial Data = 1
clk_pulse(); // zawieæ pierwsz¹ diodê
if(opt.anaver) // jeli wêdruj¹cy punkt
PORTC & = ~(1 & lt; & lt; SER_DAT); // Serial Data = 0
while(l++ & lt; tmp) // zawiecaj kolejne a¿ do zrównania z aktualn¹ iloci¹ sekund
clk_pulse();
DDRC & = ~(1 & lt; & lt; SER_DAT); // przywróc PC4 jako wejcie z klawisza
PORTC |= (1 & lt; & lt; SER_DAT);
}
// -----------------------------------------------------------------------------------------
void dispint(s16 value) // wywietl wartoæ od -1999 do 9999 na wywietlaczu
{
u16 tmp = absint(value);
ledbuf[5] = 15;
ledbuf[4] = 15;
ledbuf[3] = tmp % 10;
tmp /= 10;
ledbuf[2] = tmp % 10;
tmp /= 10;
ledbuf[1] = tmp % 10;
if(value & lt; 0)
ledbuf[0] = (tmp & lt; 10) ? 14 : 13;
else
ledbuf[0] = tmp / 10;
}
// *****************************************************************************************
// Procedury One Wire dla DS18x20
// *****************************************************************************************
void reset_onewire(void)
{
ds_ok = 1;
PORTC & = ~(1 & lt; & lt; OWI);
DDRC |= 1 & lt; & lt; OWI;
delay(250); // impuls RESET
DDRC & = ~(1 & lt; & lt; OWI);
PORTC |= 1 & lt; & lt; OWI; // pin = wejscie z pullupem
delay(30);
if(PINC & (1 & lt; & lt; OWI)) // sprawdz impuls presence
{
delay(10);
if(PINC & (1 & lt; & lt; OWI)) // sprawdz ponownie impuls presence
{
ds_ok = 0; // brak presence = brak czujnika temperatury
return;
}
}
delay(250);
if(!(PINC & (1 & lt; & lt; OWI))) // dodatkowy test na przypadek
ds_ok = 0; // zwarcia pinu OWI do masy
}
void write_onewire(u08 bit) // Wyslij bit na onewire
{
u08 sreg = SREG;
cli();
PORTC & = ~(1 & lt; & lt; OWI);
DDRC |= 1 & lt; & lt; OWI; // pin = L
delay(bit ? 1:20);
PORTC |= 1 & lt; & lt; OWI; // pin = H
SREG = sreg;
delay(bit ? 20:1);
}
u08 read_onewire(void) // odbierz bit danych z onewire
{
u08 result = 0;
u08 sreg = SREG;
cli();
PORTC & = ~(1 & lt; & lt; OWI);
DDRC |= 1 & lt; & lt; OWI; // pin = L
delay(1);
PORTC |= 1 & lt; & lt; OWI; // pin = H (prze³adowanie pojemnosci)
asm volatile( " NOP " );
DDRC & = ~(1 & lt; & lt; OWI); // pin = wejcie z pullupem
delay(2); // czekaj na odpowiedz (ok 7us)
if(bit_is_set(PINC, OWI)) // odczytaj bit
result = 1;
SREG = sreg;
delay(15); // odczekaj reszte d³ugoci ramki bitu
return result;
}
void write_onewire_byte(u08 command) // Wyslij bajt przez onewire
{
u08 i;
for (i = 0; i & lt; 8; i++)
{
write_onewire(command & 1);
command & gt; & gt; = 1;
}
}
u08 read_onewire_byte(void) // odczytaj bajt przez onewire
{
u08 r,i;
r = 0;
for (i = 0; i & lt; 8; i++)
{
r & gt; & gt; = 1;
if(read_onewire())
r |= 0x80;
}
return r;
}
void convert_temp(u08 reset)
{
if(reset)
reset_onewire();
if(ds_ok)
{
write_onewire_byte(0xCC); // komenda skip ROM command
write_onewire_byte(0x44); // komenda convert T
}
}
s16 read_temp(void) // odczytaj temperature z 12 bitowa dokladnoscia
{
static u08 scratchpad[8];
u08 i;
u08 crc = 0;
s16 result = temper;
write_onewire_byte(0xCC); // komenda skip ROM
write_onewire_byte(0xBE); // komenda read scratchpad
DDRC & = ~(1 & lt; & lt; OWI); // pin = wejcie z pullupem
for (i = 0; i & lt; 8; i++) // czytaj scratchpad i licz CRC
crc = _crc_ibutton_update(crc, scratchpad[i] = read_onewire_byte());
if(crc == read_onewire_byte()) // Test CRC
{
result = (scratchpad[1] & lt; & lt; 8) | scratchpad[0]; // dla DS18B20 bezposrednio 12 bitowa wartosc
if((scratchpad[4] & 0x80) == 0x80) // Autorozpoznanie typu dallasa
{
result & lt; & lt; = 3; // dla DS18S20 8 bitowa wartoæ
result & = 0xFFF0; // obetnij polowki stopni
result += (16*(scratchpad[7]-scratchpad[6])) / scratchpad[7]; // oblicz poprawke dla uzyskania 12 bitowej wartosci
result -= 4; // -0,25st
}
}
return result;
}
// *****************************************************************************************
// Petla ustawienia wartosci programów
// *****************************************************************************************
s16 get_value(s16 val, s16 min, s16 max, u08 sj) // ustawianie wartosci val w zakresie od min do max
{ // sj = 1 powoduje regulacje jasnosci
u08 speed = 0;
goto dis;
do{
if(bit_is_clear(KEYPINS,KEY1)) // zwieksz ustawiana wartoæ
{
if(val & lt; max)
val++;
goto dis;
}
if(bit_is_clear(KEYPINS,KEY2)) // zmniejsz ustawiana wartoæ
{
if(val & gt; min)
val--;
dis:
if(speed & lt; 10)
speed++;
dispint(val); // wyswietl aktualna wartosc
if(sj == 1)
OCR1AL = val; // uaktualnij jasnoæ na bierz¹co
else if(sj == 2)
OSCCAL = (s08)val+128; // uaktualnij czêstotliwoæ na bierz¹co
delayms((speed == 10) ? 10 : 200);
sekunda = 0;
}
if(bit_is_set(KEYPINS,KEY1) & & bit_is_set(KEYPINS,KEY2))
speed = 0;
if(bit_is_clear(PINC,PWR_FAIL)) // jesli brak zasilania sieciowego
return val;
}while(sekunda & lt; 5); // autopowrót po 5 sekundach
return val;
}
void disp_p(u08 p)
{
fillwys(15);
ledbuf[0] = 18; // Na wyswietlaczu " P "
ledbuf[1] = p+1; // numer programu
sekunda = 0;
}
u08 pusc_key(void)
{
while(bit_is_clear(KEYPINS,KEY1) || bit_is_clear(KEYPINS,KEY2))
{
if(bit_is_clear(PINC,PWR_FAIL)) // jesli brak zasilania sieciowego
return 1; // zwroc 1 aby wyskoczyc z innych petli
delayms(10);
}
delayms(200);
while(bit_is_clear(KEYPINS,KEY1) || bit_is_clear(KEYPINS,KEY2))
{
if(bit_is_clear(PINC,PWR_FAIL)) // jesli brak zasilania sieciowego
return 1; // zwroc 1 aby wyskoczyc z innych petli
delayms(10);
}
return 0;
}
// *****************************************************************************************
// Ustawianie parametrów pracy zegara (7 programów P1 - P7)
// *****************************************************************************************
u08 check_setup(void)
{
u08 prog = 0;
u08 ent = 0;
u08 *pp;
s08 frq = eeprom_read_byte((uint8_t*)7); // odczyt poprawki czêstotliwoci
if(bit_is_clear(KEYPINS,KEY1) & & bit_is_clear(KEYPINS,KEY2)) // jesli wcisnieto oba klawisze
{ // podczas zalaczania zasilania
delayms(20);
if(bit_is_clear(KEYPINS,KEY1) & & bit_is_clear(KEYPINS,KEY2))
{
dwukropek = 0;
ledbuf[0] = 5; // Na wyswietlaczu " SEt "
ledbuf[1] = 16;
ledbuf[2] = 17;
ledbuf[3] = 15;
ledbuf[4] = 15;
ledbuf[5] = 15;
if(pusc_key()) // czekaj na puszczenie klawisza i jesli
return 1; // wykryto zanik zasilania wyjdz z ustawiania
disp_p(prog); // wyswietl numer programu
ent = 1;
while(sekunda & lt; 10) // autopowrot z ustawiania po 10 sekundach
{
if(bit_is_clear(PINC,PWR_FAIL)) // jesli brak zasilania sieciowego
return 1;
if(bit_is_clear(KEYPINS,KEY2)) // zmiana programu
{
prog++;
if(prog == 7) // max 7 programów
prog = 0;
disp_p(prog); // wyswietl numer programu
if(pusc_key()) // czekaj na puszczenie klawisza i jesli
return 1; // wykryto zanik zasilania wyjdz z ustawiania
}
if(bit_is_clear(KEYPINS,KEY1)) // wybrano program
{
if(pusc_key()) // czekaj na puszczenie klawisza i jesli
return 1; // wykryto zanik zasilania wyjdz z ustawiania
switch(prog)
{
case 0: // ustawianie czasu wywietlania godzin
czas_godzin = get_value(czas_godzin, 0, 99, 0);
eeprom_write_byte((uint8_t*)3, czas_godzin);
break;
case 1: // ustawianie czasu wywietlania temperatury
czas_temp = get_value(czas_temp, 0, 99, 0);
eeprom_write_byte((uint8_t*)4, czas_temp);
break;
case 2: // ustawianie korekcji dok³adnoci
poprawka = get_value(poprawka, -1999, 9999, 0);
eeprom_write_word((uint16_t*)1, poprawka);
t.corr = 0;
break;
case 3: // ustawianie opónienia filtra do jasnoci
opozn = get_value(opozn, 0, 99, 0);
eeprom_write_byte((uint8_t*)5, opozn);
break;
case 4: // ustawianie minimalnej jasnoci wywietlacza
minjas = get_value(minjas, 5, 254 ,1);
eeprom_write_byte((uint8_t*)6, minjas);
OCR1AL = 100;
break;
case 5: // korekta czêstotliwoci pracy procesora (minimalizacja zak³óceñ)
frq = get_value(frq, -63, 63 ,2);
eeprom_write_byte((uint8_t*)7, frq);
break;
case 6: // wybór opcji (suma wartoci opcji 0-127)
pp = (u08*) & opt;
*pp = get_value(*pp, 0, 127 ,0);
eeprom_write_byte((uint8_t*)8, *pp);
break;
}
disp_p(prog); // po wyjciu z funkcji wyswietl numer programu
}
}
}
}
return (ent);
}
// *****************************************************************************************
// Inicializacja
// *****************************************************************************************
int main(void)
{
u08 state = 0;
u08 oldsec = 0;
u08 n = 0;
u08 zs = 0;
u08 dispczas = 0;
u08 wyslicz = 0;
u08 ksw = 0; // klawisz prze³¹czania czas/temp
neg_anod = 0x3F; // standardowe pod³¹czenie
neg_katod = 0xFF; // standardowe pod³¹czenie
// --------------------------------------
// Autodetekcja driverów anod i katod
// --------------------------------------
DDRB = 0x00; // port anod jako wejscie
PORTB = 0x00;
delayms(1);
if((PINB & 0x03) == 0) // jesli piny anod obci¹¿one do masy (przez wejscia UDN2981)
neg_anod = 0;
else
ANODY = 0x3F; // ustaw wylaczone anody
DDRB = 0x3F; // PB0 - PB5 anody (wyjscia)
PORTD = 0x00;
DDRD = 0x00; // Katody Wyswietlacza jako wejscie
ANODY = neg_anod ^ 1; // zalacz driver pierwszej anody
delayms(1);
if((PIND & 0x03) == 0) // jesli piny katod obci¹¿one do masy
neg_katod = 0x00; // katody pod³¹czone przez ULN2803
ANODY = neg_anod; // ustaw wylaczone anody
KATODY = neg_katod;
DDRD = 0xFF; // Katody Wyswietlacza jako wyjscie
//--------------------------------------
PORTC = 0x3C; // PC4 i PC5 - klawisze z pullupem, PC3, - CLK, PC2 - Dallas
DDRC = 0xC8; // PC0 - wejscie z ADC sterowania jasnoscia, PC1 - wejscie zaniku zasilania, PC3 - Wyjscie CLK
MCUCR = 0x80; // Dozwolony tryb Idle sleep
t.second= 0;
t.min = 0;
t.hour = 0;
t.corr = 0;
dwukropek = 1;
fillwys(14); // Na wyswietlaczu " --:-- -- "
poprawka = eeprom_read_word((uint16_t*)1); // odczyt poprawki dokladnosci z EEPROM'a
if(poprawka == -1)
poprawka = 0;
czas_godzin = eeprom_read_byte((uint8_t*)3); // odczyt czasu wyswietlania godziny
if(czas_godzin & gt; 99)
czas_godzin = 10;
czas_temp = eeprom_read_byte((uint8_t*)4); // odczyt czasu wyswietlania temperatury
if(czas_temp & gt; 99)
czas_temp = 2;
opozn = eeprom_read_byte((uint8_t*)5); // odczyt opoznienia na zmiane jasnosci
if(opozn & gt; 100)
opozn = 2;
minjas = eeprom_read_byte((uint8_t*)6); // odczyt minimalnej jasnosci wyswietlacza
if(minjas & lt; 5 || minjas & gt; 254)
minjas = 30;
s08 frq = eeprom_read_byte((uint8_t*)7); // odczyt poprawki czêstotliwoci
if(frq & gt; -64 & & frq & lt; 64)
OSCCAL = frq+128;
u08 *pp = (u08*) & opt;
*pp = eeprom_read_byte((uint8_t*)8); // odczyt bajtu opcji
if(*pp & gt; 127)
*pp = 0;
TCNT1 = 0; // Timer0 - multiplex wyswietlacza
OCR1AL = 100; // Wstêpna jasnosc wyswietlacza
TIMSK = (1 & lt; & lt; OCIE1A) | (1 & lt; & lt; TOIE1); // Przerwanie z OCR1A i Overflow
TCCR1A = 1; // Start multiplexu wyswietlacza
TCCR1B = (1 & lt; & lt; WGM12) | 2; // Mode 5, 8-bit Fast PWM , CK/8
sei(); // za³¹cz przerwania
ACSR = (1 & lt; & lt; ACD); // wylaczenie komparatora analogowego (oszczednosc pradu)
ADMUX = 0x40; // REF = AVCC
ADCSRA = (1 & lt; & lt; ADEN)|(1 & lt; & lt; ADSC)|6; // start ADC
ASSR |= (1 & lt; & lt; AS2); // ustaw Timer/Counter2 jako asynchroniczny, taktowany
// z kwarcu 32,768kHz
TCNT2 = 0x00; // przerwanie dok³adnie co 1 sekundê (32768/128/256 = 1)
TCCR2 = 0x05;
while(ASSR & 0x07); // czekaj na aktualizacjê TC2
TIMSK |= (1 & lt; & lt; TOIE2); // odblokuj przerwanie z TC2
clr_rejestr(); // kasuj analogowy sekundnik
dispczas = check_setup(); // test na wejscie w ustawianie parametrów
wyslicz = 0;
convert_temp(1); // uruchom pomiar temperatury
// *****************************************************************************************
// Petla glowna
// *****************************************************************************************
while(1)
{
if(bit_is_clear(PINC,PWR_FAIL)) // jesli brak zasilania sieciowego
{
TIMSK & =~((1 & lt; & lt; OCIE1A) | (1 & lt; & lt; TOIE1)); // zablokuj przerwania multiplexu
TCCR1B = 0; // zatrzymaj timer multiplexu
DDRB = 0x00;
ANODY = 0;
DDRD = 0x00;
KATODY = 0;
PORTC = 0x00;
DDRC = 0x00; // Wszystkie porty na wejscia
ADCSRA = 0; // Stop ADC
// tylko ten fragment (oraz przerwanie z Timera 2) dzia³a jeli nie ma zasilania sieciowego
do {
MCUCR = 0xB0; // power save mode
asm volatile( " SLEEP " ); // SLEEP
asm volatile( " NOP " );
TCCR2=0x05; // Write dummy value to Control register
while(ASSR & 0x07); // czekaj na aktualizacje rejestru TC2
}while(bit_is_clear(PINC,PWR_FAIL)); // sprawdz status zasilania
MCUCR = 0x80; // przywracanie normalnego stanu pracy.
ANODY = neg_anod; // ustaw wylaczone anody
DDRB = 0x3F; // PB0 - PB5 anody (wyjscia)
KATODY = neg_katod;
DDRD = 0xFF; // Katody Wyswietlacza jako wyjscie
PORTC = 0x3C; // PC4 i PC5 - klawisze z pullupem, PC2 - Dallas
DDRC = 0xC8; // PC0 - wejscie z ADC sterowania jasnoscia, PC1 - wejscie zaniku zasilania
fillwys(15);
TIMSK |= (1 & lt; & lt; OCIE1A) | (1 & lt; & lt; TOIE1); // odblokuj przerwania z timera1 (multiplex)
TCCR1B = (1 & lt; & lt; WGM12) | 2; // Start multiplexu wyswietlacza
ADMUX = 0x40;
ADCSRA = (1 & lt; & lt; ADEN)|(1 & lt; & lt; ADSC)|6; // start ADC
clr_rejestr(); // kasuj analogowy sekundnik
check_setup(); // test na wejscie w ustawianie parametrów
convert_temp(1); // uruchom obs³uge Dallasa
if(poprawka == 0) // dla zapominalskich i tylko jesli poprawka = 0
{ // wyswietlenie minut od ostatniego resetu poprawki
dispint(t.corr); // ulatwia jej obliczenie (to jest czas pracy zegara w minutach)
dwukropek = 0;
delayms(1000);
}
state = 0;
wyslicz = 0;
oldsec = sekunda;
set_rejestr(); // ustaw analogowy sekundnik zgodnie z czasem
}
//-------------------------------------------------------------------------------------------
// Obsluga ustawiania zegara (klawiszy)
switch(state)
{
default: break;
case 0: if(bit_is_set(KEYPINS,KEY1) & & bit_is_set(KEYPINS,KEY2)) // oba puszczone ?
break;
licz = 100; // eliminacja smieci na liniach klawiszy
state = 1; // nastepny stan = 1
break;
case 1: if(licz == 0) // odczekano czas eliminacji zaklucen
{
if(bit_is_set(KEYPINS,KEY1) & & bit_is_set(KEYPINS,KEY2))
{
state = 0; // a jednak to bylo zaklucenie (nacisniecie & lt; 50ms)
break;
}
dwukropek = 0; // zgas dwukropek
wyslicz = 0;
dispczas = 1;
zs = 1; // zezwolenie na samo wyzerowanie sekund
licz = 500;
if(bit_is_clear(KEYPINS,KEY1))
{
state = 2; // bedziemy zmieniac minuty
break;
}
if(bit_is_clear(KEYPINS,KEY2))
{
state = 3; // bedziemy zmieniac godziny
break;
}
state = 5;
}
break;
case 2: if (zs == 0 || t.second & gt; 30)
{
if (++t.min == 60) // zwieksz licznik minut
t.min = 0;
}
zs = 0; // zabronienie samego zerowania sekund
TCNT2 = 0x00;
t.second = 0; // zeruj licznik sekund
t.corr = 0; // i poprawki
disp_update();
state = 4;
break;
case 3: if (++t.hour == 24) // zwieksz licznik godzin
t.hour=0;
disp_update();
state = 4;
break;
case 4: wyslicz = 0;
if(bit_is_set(KEYPINS,KEY1) & & bit_is_set(KEYPINS,KEY2))
{
state = 6; // puszczone oba klawisze
licz = 1150;
break;
}
if(licz == 0)
state = 5;
break;
case 5: if(bit_is_clear(KEYPINS,KEY1))
{
state = 2;
licz = (licz & lt; 200) ? 80:500; // repetycja klawisza
break;
}
if(bit_is_clear(KEYPINS,KEY2))
{
state = 3;
licz = (licz & lt; 200) ? 100:500; // repetycja klawisza
break;
}
if(licz == 0) // puszczone oba klawisze przez 2 sekundy
{
dwukropek = 1;
set_rejestr(); // ustaw analogowy sekundnik zgodnie z czasem
state = 0; // wroc do trybu normal
}
break;
case 6: if(licz & lt; 1000) // dla zwiekszonego opoznienia
state = 5; // po puszczeniu i ponownym nacisnieciu
break; // klawisza
}
//-------------------------------------------------------------------------------------------
// Obsluga regulacji jasnosci
if(state == 0 & & licz == 0) // co ilestam milisekund
{
licz = 20;
jafil = (opozn * jafil + ADC) / (opozn + 1); // odczytaj i filtruj przetwornik A/C
u16 adcval = jafil; // konwersja z float na int
ADMUX = 0x40;
adcval += minjas; // dodaj ofset dla minimalnej jasnosci
if(adcval & gt; 255)
adcval = 255; // maksymalna jasnosc
OCR1AL = adcval; // wpis ustawionej jasnoci do OCR1A
ADCSRA = (1 & lt; & lt; ADEN)|(1 & lt; & lt; ADSC)|6; // start kolejnej konwersji ADC
if(czas_godzin == 0 & & czas_temp == 0)
{
DDRC & = ~(1 & lt; & lt; KEY_SWI); // ustaw pin KEY_SWI jako wejscie
asm volatile( " NOP " );
asm volatile( " NOP " );
asm volatile( " NOP " );
asm volatile( " NOP " );
if(bit_is_clear(PINC,KEY_SWI))
{
if(ksw & lt; 200) ksw++;
}
else
ksw = 0;
DDRC |= 1 & lt; & lt; KEY_SWI; // ustaw pin KEY_SWI jako wyjscie CLK
}
}
//-------------------------------------------------------------------------------------------
// Obsluga korekcji dokladnosci zegarka
if(sekunda != oldsec) // sprawdzane co sekunde
{
oldsec = sekunda;
if(t.second == 30) // jesli wartosc sekund = 30 (a czemu nie ?)
{
if(poprawka != 0 & & t.corr & gt; = absint(poprawka)) // jesli ustawiona poprawka i licznik t.corr (minut)
{ // doszedl do zadanej wartosci poprawki
t.corr -= absint(poprawka); // odejmij poprawke od licznika
if(poprawka & lt; 0)
t.second--; // jesli wartosc poprawki jest ujemna to odejmij sekunde
else
t.second++; // jesli wartosc poprawki jest dodatnia to dodaj sekunde
}
}
//-------------------------------------------------------------------------------------------
// aktualizacja wskazan czasu, temperatury i miganie dwukropka - tez co sekunde
if(dispczas & & (czas_godzin || czas_temp)) // jesli zezwolono na wyswietlanie czasu
{
if(wyslicz & gt; = czas_godzin + (czas_temp * ds_ok))
{
reset_onewire();
if(ds_ok)
{
fillwys(15); // Zgas wyswietlacz
dwukropek = 0;
convert_temp(0); // zainicjuj kolejn¹ konwersje temperatury
}
wyslicz = 0;
}
if(wyslicz & lt; czas_godzin)
{
disp_update(); // wyswietl czas
if(opt.migaj) // jeli zezwolenie na mruganie dwukropka
{
if(state == 0)
dwukropek = !dwukropek; // mrugaj dwukropkiem co 1 sekunde (0.5Hz)
}
else
{
dwukropek = (!state); // zaswiec dwukropek na sta³e
}
n=0;
}
else if((wyslicz & lt; czas_godzin + czas_temp) & & ds_ok & & n==0)
{
reset_onewire();
fillwys(15); // Zgas wyswietlacz
dwukropek = 0;
temper = read_temp(); // odczytaj temperature z dallasa
s16 tt = ((double)temper / 1.60);
u08 td = 15;
if(tt & gt; 999) // jesli temperatuta wieksza niz 99.9 stopnia
tt = 999; // wyswietl 99.9 stopnia
if(tt & lt; 0) // jesli temperarura ujemna
{
tt = absint(tt);
td = (tt & lt; 100) ? 14 : 13; // wyswietl znak minusa lub -1
if(tt & gt; 199)
td = tt/100; // ponizej -19.9 stopnia nie pokazuj minusa
}
else if(tt & gt; = 100)
td = tt / 100;
if(!opt.sekundnik & & !opt.tempprec)
{
ledbuf[2] = 22; // Na wyswietlaczu minut " stC "
ledbuf[3] = 19;
}
else
{
ledbuf[2] = 23; // przecinek
ledbuf[3] = tt % 10; // dziesiate czesci stopnia
ledbuf[4] = 22; // Na wyswietlaczu sekund " stC "
ledbuf[5] = 19;
}
tt /= 10;
ledbuf[1] = tt % 10; // jednosci stopni
ledbuf[0] = td; // dziesiatki stopni
n=1;
}
wyslicz++;
}
if(dispczas & & czas_godzin == 0 & & czas_temp == 0) // rêczne prze³¹czanie czas/temp
{
if(ksw == 200)
{
ksw = 210;
wyslicz = !wyslicz;
}
if(wyslicz == 0)
{
disp_update(); // wyswietl czas
if(opt.migaj) // jeli zezwolenie na mruganie dwukropka
{
if(state == 0)
dwukropek = !dwukropek; // mrugaj dwukropkiem co 1 sekunde (0.5Hz)
}
else
{
dwukropek = (!state); // zaswiec dwukropek na sta³e
}
}
else if(ds_ok)
{
reset_onewire();
fillwys(15); // Zgas wyswietlacz
dwukropek = 0;
temper = read_temp(); // odczytaj temperature z dallasa
s16 tt = ((double)temper / 1.60);
u08 td = 15;
if(tt & gt; 999) // jesli temperatuta wieksza niz 99.9 stopnia
tt = 999; // wyswietl 99.9 stopnia
if(tt & lt; 0) // jesli temperarura ujemna
{
tt = absint(tt);
td = (tt & lt; 100) ? 14 : 13; // wyswietl znak minusa lub -1
if(tt & gt; 199)
td = tt/100; // ponizej -19.9 stopnia nie pokazuj minusa
}
else if(tt & gt; = 100)
td = tt / 100;
if(!opt.sekundnik & & !opt.tempprec)
{
ledbuf[2] = 22; // Na wyswietlaczu minut " stC "
ledbuf[3] = 19;
}
else
{
ledbuf[2] = 23; // przecinek
ledbuf[3] = tt % 10; // dziesiate czesci stopnia
ledbuf[4] = 22; // Na wyswietlaczu sekund " stC "
ledbuf[5] = 19;
}
tt /= 10;
ledbuf[1] = tt % 10; // jednosci stopni
ledbuf[0] = td; // dziesiatki stopni
reset_onewire();
convert_temp(0); // zainicjuj kolejn¹ konwersje temperatury
}
else
wyslicz = 0;
}
//---------------------------------------------------------------------------------------
// sterowanie rejestrem przesuwnym do analogowego sekundnika
if(t.second == 0)
clr_rejestr(); // jeli sekundy = 0, kasuj rejestr
DDRC |= (1 & lt; & lt; SER_DAT); // PC4 jako wyjcie
PORTC |= (1 & lt; & lt; SER_DAT); // Serial Data = 1
if(opt.anaver & & t.second != 0) // jeli wêdruj¹cy punkt
PORTC & = ~(1 & lt; & lt; SER_DAT); // to od pierwszej sekundy Serial Data = 0
clk_pulse(); // impuls na linie CLK
DDRC & = ~(1 & lt; & lt; SER_DAT);
PORTC |= (1 & lt; & lt; SER_DAT); // przywróæ PC4 jako wejcie z klawisza
}
//-------------------------------------------------------------------------------------------
asm volatile( " SLEEP " ); // nudy ? To sie zdrzemnij :-)
asm volatile( " NOP " );
}
}