Witam wszystkich.
Zaprojektowałem i uruchomiłem zegarek z wyświetlaczami LED(SA10-21GWA) i układem PCF8583 współpracującym z mikroprocesorem Atmega8 oraz układami PCF8574AP na szynie TWI(i2c).
A teraz chciałbym go wam zaprezentować.
Na początek podręcznik języka C w formie pliku pdf -> 'C.pdf' i plik -> 'atmega16mmr.pdf' ,oraz obrazki -> 'Atmega8.gif' i 'Atmega16.gif' (obie Atmegi różnią się ilością wyprowadzeń, natomiast rejestry sterujące mają takie same). Ja wykorzystuje opis 'atmega16mmr.pdf' do ustawiania rejestrów procesora Atmega8.
A oto program 'twiAtmega8_zegarLED.c' jest to ostateczna wersja mojego w pełni działającego programu zegara LED.
Jest to zestaw modułów używanych w moim programie. I tu taka uwaga dotycząca kompilatora otóż ja używam 'WinAVR-20081205-install.exe' ponieważ ten kompilator tworzy najmniejsze pliki 'twiAtmega8.hex' około 2,690kB(około 32,8% całej pamięci procesora), natomiast 'WinAVR-20090313-install.exe' jak i również 'WinAVR-20080610-install.exe' tworzyły pliki *.hex bliskie 7kB(zajmowały prawie całą pamięć procesora) kompilując ten sam program.
Jest to ustalenie adresów układów PCF8574AP pod wyświetlacze LED i adresu zegara PCF8583.
Układy PCF8574A(PCF8574AP) i PCF8574(PCF8574P) "Remote 8-bit I/O expander for I2C-bus" różnią się od siebie tylko adresowaniem (na stałe zaprogramowane przez producenta) po za tym są identyczne:
Adres PCF8574A -> 0111 <- bit 7 do 4 -> na stałe zaprogramowane przez producenta
Adres PCF8574 -> 0100 <- bit 7 do 4 -> na stałe zaprogramowane przez producenta
Pozostałe bity obu układów są identyczne.
Są to stałe określające kształt cyfr na wyświetlaczach LED, oraz stała kropki oddzielającej sekundy od minut i dwukropka między godzinami i minutami.
Zastosowałem wyświetlacz jedno-calowy zielony o nazwie 'SA10-21EWA'
Pola bitowe -> nieraz potrzeba tylko jednego bitu żeby coś włączyć lub wyłączyć i szkoda marnować całego bajta skoro w bajcie jest od 0 do 7 bitów(osiem bitów)
Prototypy funkcji, procedur -> przy takim zapisie program jest bardziej przejrzysty, ponieważ "ciała" funkcji, procedur mogą być poniżej funkcji 'int main(void)' czyli programu głównego i kompilator doskonale wie gdzie ich ma szukać.
Instrukcje wykonania jednokrotnego podczas startu procesora.
Czyli ustawienie portów, start procedur(TWI,PWM,INT0,itp), zerowanie znaczników jako całych bitów(opcja ->> typedef union, patrz język C.pdf), watchog, globalne zezwolenie na przerwania procesora. Watchdog to taki niezależny układ, który w razie zawieszenia się procesora sam go po pewnym czasie(np. 250 milisekund) resetuje.
Po naciśnięciu klawisza przez czas 30 milisekund program czeka na zakończenie drgań styków klawisza, nadaje mu specyficzny numer, oraz sprawdza czy klawisz naciśnięty zastał już zwolniony.
Tu jest cała logika (zawarta w instrukcji 'switch (klawiatura)') sterowania klawiaturą -> główne wykorzystanie znaczników pól bitowych, oraz wolnych bitów portu C (muszą być w najważniejszych miejscach pełniąc role szybkich przełączników).
Klawisze są rozmieszczone w kształcie "krzyża" -> góra, środek, dół oraz -> lewy, środek, prawy.
Klawisz środek zajmuje centralne miejsce i on służy do przełączania klawiatury:
a) regulator generatora PWM (regulacja stopnia świecenia wyświetlaczy LED)
b) wybór segmentów wyświetlaczy LED do ustawienia czasu i zapisu do czasu zegara
Regulator generatora PWM to klawisze -> góra, dół
Wybór segmentów wyświetlaczy LED to klawisze -> lewy, prawy
Dodawanie lub odejmowanie cyfr wyświetlacza Led to klawisze -> góra, dół (po przełączeniu klawiszem środek).
No i klawisz -> środek , który zajmuje centralne miejsce, a służy do przełączeń klawiatury z funkcji zapisu do zegara do funkcji normalnej pracy(ciągłego wyświetlania bieżącego czasu).
Ustalanie cyfr na wyświetlaczach LED do zapisu w zegarze PCF8583.
Najpierw wygaszone zostaną nieaktywne segmenty pozostawiając segmenty, które są ustawiane. Później zależnie od potrzeby albo odejmuje albo dodaje cyfry, oraz zapisuje wynik do tablicy 'taktZapisu'. Zwalnia i ustawia zależnie od potrzeby znacznika (flagi) i wyświetla wynik na wyświetlaczach LED.
Tu resetujemy Watchdog'a jeżeli z jakiś powodów czas wykonywania programu się wydłuży ponad czas ustalony dla Watchdog'a wtedy Watchdog zadziała -> zresetuje procesor
Całe przerwanie w,którym odbywa się odczyt czasu. Jest tu też umieszczony bit "test procesora" przy podłączeniu diody led do portu B0 (dioda led z portu przez opornik do plusa zasilania) powinna ona migać z częstotliwością 1Hz. Co oznacza prawidłową współpracę procesora Atmega8 i zegara PCF8583.
Dalej już jest zestaw procedur i funkcji pracujących w tym programie.
Poniżej kilka plików (typu *.pdf) do poczytania - po polsku!
Przyjemnej lektury.
Piotr
A oto zapakowany pliki zawierający plik c i plik hex (twiAtmega8_zegar.zip)
Zaprojektowałem i uruchomiłem zegarek z wyświetlaczami LED(SA10-21GWA) i układem PCF8583 współpracującym z mikroprocesorem Atmega8 oraz układami PCF8574AP na szynie TWI(i2c).
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/21_1249760449_thumb.jpg)
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/58_1249760602_thumb.jpg)
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/95_1249760569_thumb.jpg)
A teraz chciałbym go wam zaprezentować.
Na początek podręcznik języka C w formie pliku pdf -> 'C.pdf' i plik -> 'atmega16mmr.pdf' ,oraz obrazki -> 'Atmega8.gif' i 'Atmega16.gif' (obie Atmegi różnią się ilością wyprowadzeń, natomiast rejestry sterujące mają takie same). Ja wykorzystuje opis 'atmega16mmr.pdf' do ustawiania rejestrów procesora Atmega8.
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/34_1249754127.gif)
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/21_1249754219.gif)
A oto program 'twiAtmega8_zegarLED.c' jest to ostateczna wersja mojego w pełni działającego programu zegara LED.
Code:
#define F_CPU 8000000UL // częstotliwosci zegara procesora Atmega8 (musi byc przed "#include <util/delay.h>")
#include <avr/io.h>
#include <util/delay.h> // moduł -> (C:\WinAVR-20081205\avr\include\util\delay.h)
#include <avr/wdt.h> // moduł -> Watchdog (C:\WinAVR-20081205\avr\include\avr\wdt.h)
#include <avr/interrupt.h> // moduł -> (C:\WinAVR-20081205\avr\include\util\interrupt.h)
Jest to zestaw modułów używanych w moim programie. I tu taka uwaga dotycząca kompilatora otóż ja używam 'WinAVR-20081205-install.exe' ponieważ ten kompilator tworzy najmniejsze pliki 'twiAtmega8.hex' około 2,690kB(około 32,8% całej pamięci procesora), natomiast 'WinAVR-20090313-install.exe' jak i również 'WinAVR-20080610-install.exe' tworzyły pliki *.hex bliskie 7kB(zajmowały prawie całą pamięć procesora) kompilując ten sam program.
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/55_1249755003_thumb.jpg)
Code:
/* Adres układu PCF8574A w zależności od podpięcia wyprowadzeń nr 1,2,3(nóżka układu PCF8574A o nazwie A0,A1,A2)
bit 7 do 4 -> na stałe zaprogramowane przez producenta, bit 3 do 1 -> to adres, który ustawiasz, bit 0 -> to bit zapisu lub odczytu.
01110100(R/~W) sekundy -> adres = 0x74
01110110(R/~W) dziesiątki sekund -> adres = 0x76
01111000(R/~W) minuty -> adres = 0x78
01111010(R/~W) dziesiątki minut -> adres = 0x7A
01111100(R/~W) godziny -> adres = 0x7C
01111110(R/~W) dziesiątki godzin -> adres = 0x7E */
#define sekundy 0x74
#define dziesiątki_sekund 0x76
#define minuty 0x78
#define dziesiątki_minut 0x7A
#define godziny 0x7C
#define dziesiątki_godzin 0x7E
//-------------------------------------------------------------------------------------------------
/* Adres układu PCF8583 w zależności od podpięcia wyprowadzenia nr 3(nóżka układu PCF8583 o nazwie A0)
bit 7 do 2 -> na stałe zaprogramowane przez producenta, bit 1 -> to adres, który ustawiasz, bit 0 -> to bit zapisu lub odczytu.
10100000(R/~W) - jeśli wyprowadzenie nr 3 jest podpięte do GND -> adres = 0xA0
10100010(R/~W) - jeśli wyprowadzenie nr 3 jest podpięte do Vcc -> adres = 0xA2 */
#define zegar 0xA0
//-------------------------------------------------------------------------------------------------
Jest to ustalenie adresów układów PCF8574AP pod wyświetlacze LED i adresu zegara PCF8583.
Układy PCF8574A(PCF8574AP) i PCF8574(PCF8574P) "Remote 8-bit I/O expander for I2C-bus" różnią się od siebie tylko adresowaniem (na stałe zaprogramowane przez producenta) po za tym są identyczne:
Adres PCF8574A -> 0111 <- bit 7 do 4 -> na stałe zaprogramowane przez producenta
Adres PCF8574 -> 0100 <- bit 7 do 4 -> na stałe zaprogramowane przez producenta
Pozostałe bity obu układów są identyczne.
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/37_1249755195.jpg)
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/16_1249755225_thumb.jpg)
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/36_1249756278.gif)
Code:
const unsigned char tablica_7seg[] ={
0x88,/*cyfra 0*/
0xEB,/*cyfra 1*/
0x4C,/*cyfra 2*/
0x49,/*cyfra 3*/
0x2B,/*cyfra 4*/
0x19,/*cyfra 5*/
0x18,/*cyfra 6*/
0xCB,/*cyfra 7*/
0x08,/*cyfra 8*/
0x09 /*cyfra 9*/
};
/* Segmenty: (PCF8574A, SA10-21EWA, nr_segmentu) -> aktywny stan niski L
12(P7) G 10
11(P6) F 9
10(P5) A 7
9(P4) B 6
7(P3) diody LED
6(P2) C 4
5(P1) D 2
4(P0) E 1
*/
const unsigned char kropka =0b00001000; // dioda LED wyświetlaczy [PCF8574A nóżka7(P3)] -> kropka i dwukropek
Są to stałe określające kształt cyfr na wyświetlaczach LED, oraz stała kropki oddzielającej sekundy od minut i dwukropka między godzinami i minutami.
Zastosowałem wyświetlacz jedno-calowy zielony o nazwie 'SA10-21EWA'
![[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED](https://obrazki.elektroda.pl/61_1249755614_thumb.jpg)
Code:
// volatile -> zmienna nie optymalizowana przez kompilator (występuje w przerwaniu i programie głównym)
volatile unsigned char czas[6] ={0,}; // zerowanie wszystkich elementów tablicy
unsigned char czasPCF8583;
unsigned char liczba_pwm, test_pwm;
unsigned char klawiatura;
unsigned char segment =2; // wyświetlacze przy zapisie z klawiatury
unsigned char takt0, takt1, takt2, takt3, takt4, takt5; // dla ustawiania czasu do zapisu
unsigned char taktZapisu[3] ={0,}; // zerowanie wszystkich elementów tablicy
/* --- POLA BITOWE ---
volatile Flagi polaBitowe; <- znienna - polaBitowe(bit lub bajt) typy volatile Flagi
(wywołwnie pojedyńczego bita -> polaBitowe.Bity.bit1 =0; itp.)
(wywołwnie całeg bajta -> polaBitowe.Bajt =0;)
*/
typedef struct // Liczba po dwukropku oznacza ilość bitów.
{
unsigned char bit0:1; // jeden bit
unsigned char bit1:1; // jeden bit
unsigned char bit2:1; // jeden bit
unsigned char bit3:1; // jeden bit
unsigned char bit4:1; // jeden bit
unsigned char bit5:1; // jeden bit
unsigned char bit6:1; // jeden bit
unsigned char bit7:1; // jeden bit
} FlagiBitow;
typedef union
{
FlagiBitow Bit; // flaga jednego z ośmiu(pojedyńczego) bitu
unsigned char Bajt; // jeden bajt zawiera w sobie 8 pojedyńczych bitów
} Flagi;
volatile Flagi wborDoZapisu;
volatile Flagi wborZnacznika;
Pola bitowe -> nieraz potrzeba tylko jednego bitu żeby coś włączyć lub wyłączyć i szkoda marnować całego bajta skoro w bajcie jest od 0 do 7 bitów(osiem bitów)
Code:
// Prototypy funkcji <- deklaracja funkcji, które znajdują się poniżej funkcji 'int main(void) {...}'
void TWI_inicjacja(void);
unsigned char TWI_odczyt(unsigned char, unsigned char);
void TWI_zapis(unsigned char, unsigned char, unsigned char);
void TWI_wswietlacz(unsigned char, unsigned char);
void PWM_inicjacja(void);
void wartoscPWM(unsigned char);
void INT0_inicjacja(void);
void minusCzas(unsigned char *, unsigned char *);
void plusCzas(unsigned char *, unsigned char *);
Prototypy funkcji, procedur -> przy takim zapisie program jest bardziej przejrzysty, ponieważ "ciała" funkcji, procedur mogą być poniżej funkcji 'int main(void)' czyli programu głównego i kompilator doskonale wie gdzie ich ma szukać.
Code:
//-------------------------------------------------------------------------------------------------
// ************************************************************************************************
//-------------------------------------------------------------------------------------------------
// *** PROGRAM GŁÓWNY *** //
int main(void)
{
/* Instrukcje jednokrotnego wykonania */
//UWAGA ! Porty: PC5(SCL) PC4(SDA) - wejścia TWI
// Porty: PB2(OC1B) PB1(OC1A) - wyjścia PWM
// Porty: PD0{góora} PD1{lewy} PD3{środek} PD4{prawy} PD5{dol} - wejścia klawiatury z pull-up'em
// Port: PD2(INT0) - wejście sygnałowe z zegara PCF8583 z pull-up'em
// Port: PB0 - bit "test" z pull-up'em
// Port: PC[0-3], PD[6-7] -> bity pomocnicze
// PC0 -> znacznik jednokrotnego wykonania
// PC1 -> znacznik naciśnięcia klawiszy
// PC2 -> znacznik trybu dodać/odjąć
// PC3 -> znacznik zapis/odczyt zegara
//---------------------------
// 1 -> wyjście
// 0 -> wejście
DDRB =0b00000111;
DDRC =0b00001111;
DDRD =0b11000000;
// 1 -> z podciągnięciem do VCC (pull-up)
// 0 -> bez podciągniecia do VCC
PORTB =0b00000001;
PORTC =0b00110000;
PORTD =0b00111111;
TWI_inicjacja();
TWI_zapis(zegar,0x00,0b00000000); // ustawienie początkowe zegara PCF8583
PWM_inicjacja();
liczba_pwm =TWI_odczyt(zegar,0x10); // odczyt liczby_pwm w pamięci RAM zegara o adresie 0x10
test_pwm =0;
for(unsigned char i=1; i<=17; ++i) // test poprawności liczby pamięci RAM zegara o adresie 0x10
{
if((i *15) ==liczba_pwm) test_pwm =liczba_pwm;
}
if(test_pwm ==0) liczba_pwm =135; // 135 okolo polowa zakresu (0 do 255)
wartoscPWM(liczba_pwm);
INT0_inicjacja();
wborDoZapisu.Bajt =0; // system bitów
wborZnacznika.Bajt =0; // system bitów
wdt_reset(); // konieczny restart Watchdoga
wdt_enable(WDTO_250MS); // Watchdog -> restart procesora co 0,25 sekundy
sei(); // Pozwalamy na wykonywanie wszelkich przerwań !!!
// -----------------------------
Instrukcje wykonania jednokrotnego podczas startu procesora.
Czyli ustawienie portów, start procedur(TWI,PWM,INT0,itp), zerowanie znaczników jako całych bitów(opcja ->> typedef union, patrz język C.pdf), watchog, globalne zezwolenie na przerwania procesora. Watchdog to taki niezależny układ, który w razie zawieszenia się procesora sam go po pewnym czasie(np. 250 milisekund) resetuje.
Code:
while(1) /* Instrukcje wielokrotnego wykonania */
{
/*
1. Tylko jeden klawisz może być aktywny (wciśnięcie kilku klawiszy na raz jest ignorowane)
2. Podwójna funkcja klawiatury jako:
a) regulator generatora PWM (regulacja stopnia świecenia wyświetlaczy LED)
b) wybór segmentów wyświetlaczy LED do ustawienia czasu i zapisu do zegara
*/
//---------------------------
// początek
// test klawisza
//---------------------------
klawiatura =0b00111011;
if(bit_is_clear(PIND,PIND0)) { _delay_ms(30); klawiatura =klawiatura & 0b00111010; } //gora
if(bit_is_clear(PIND,PIND1)) { _delay_ms(30); klawiatura =klawiatura & 0b00111001; } //lewy
if(bit_is_clear(PIND,PIND3)) { _delay_ms(30); klawiatura =klawiatura & 0b00110011; } //srodek
if(bit_is_clear(PIND,PIND4)) { _delay_ms(30); klawiatura =klawiatura & 0b00101011; } //prawy
if(bit_is_clear(PIND,PIND5)) { _delay_ms(30); klawiatura =klawiatura & 0b00011011; } //dol
if(klawiatura ==0b00111011) PORTC &=~(1<<1); // znacznik -> znacznik zwolnienia klawisza
Po naciśnięciu klawisza przez czas 30 milisekund program czeka na zakończenie drgań styków klawisza, nadaje mu specyficzny numer, oraz sprawdza czy klawisz naciśnięty zastał już zwolniony.
Code:
//---------------------------
// początek
// wybór klawisza
//---------------------------
if(bit_is_clear(PINC,PINC1)) // start klawiatury
{
switch (klawiatura)
{
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
case 0b00111010: if(bit_is_clear(PINC,PINC3)) { if(liczba_pwm <=240) wartoscPWM(liczba_pwm +=15); } // regulacja świecenia wyświetlaczy LED
if(bit_is_set(PINC,PINC3))
/* góra */ { // DODAWANIE
PORTC |=(1<<2); // znacznik -> dodac
PORTC |=(1<<0); // znacznik -> (zezwolenie) jedno naciśnięcie jedno wykonanie dodać/odjąć
}
PORTC |=(1<<1); // znacznik -> wcisnietego klawisza
break;
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
case 0b00111001: if(bit_is_set(PINC,PINC3)) { if(segment >=2) segment -=1; } // segment lewy =1
/* lewy */ PORTC |=(1<<1); // znacznik -> wcisnietego klawisza
break;
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
case 0b00110011: if(bit_is_clear(PINC,PINC3)) // ***ODCZYT CZASU Z ZEGARA***
{
/* środek */ segment =2; // segment srodek =2
PORTC |=(1<<3); // znacznik -> przelacanie - zapis zegara
}
if(bit_is_set(PINC,PINC3)) // ***ZAPIS CZASU DO ZEGARA***
{
GICR = 0b00000000; // wyłaczamy przerwanie INT0 ->stop (INT0 = 0)
// ----------------------------------------------------------
if(wborDoZapisu.Bajt !=0)
{
TWI_zapis(zegar,0x00,0b10000000); //* 1. stop zliczania i wyzerowanie dzielnika
if(wborDoZapisu.Bit.bit1 ==1) TWI_zapis(zegar,0x04,taktZapisu[0]); //* 2. wpis czasu do zegara godzin
if(wborDoZapisu.Bit.bit2 ==1) TWI_zapis(zegar,0x03,taktZapisu[1]); //* 3. wpis czasu do zegara minut
if(wborDoZapisu.Bit.bit3 ==1) TWI_zapis(zegar,0x02,taktZapisu[2]); //* 4. wpis czasu do zegara sekund
TWI_zapis(zegar,0x01,0b00000000); //* 4a. zerowanie setnych i dziesiętnych sekundy
TWI_zapis(zegar,0x00,0b00000000); //* 5. start zliczania
wborDoZapisu.Bajt =0;
}
// ----------------------------------------------------------
PORTC &=~(1<<3); // znacznik -> przelacanie - odczyt zegara
wborZnacznika.Bajt =0;
}
GICR = 0b01000000; // wlaczamy przerwanie INT0 ->start (INT0 = 1)
PORTC |=(1<<1); // znacznik -> wcisnietego klawisza
break;
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
case 0b00101011: if(bit_is_set(PINC,PINC3)) { if(segment <=2) segment +=1; } // segment prawy =3
/* prawy */ PORTC |=(1<<1); // znacznik -> wcisnietego klawisza
/* korekcja */ break;
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
case 0b00011011: if(bit_is_clear(PINC,PINC3)) { if(liczba_pwm >=30) wartoscPWM(liczba_pwm -=15); } // regulacja świecenia wyświetlaczy LED
if(bit_is_set(PINC,PINC3))
/* dół */ { // ODEJMOWANIE
PORTC &=~(1<<2); //znacznik -> odjac
PORTC |=(1<<0); // znacznik -> (zezwolenie) jedno naciśnięcie jedno wykonanie dodać/odjąć
}
PORTC |=(1<<1); // znacznik -> wcisnietego klawisza
break;
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
default: /* klawisze poza wyborem */
break;
} // END switch (klawiatura)
//---------------------------
// koniec
// test klawisza
// wybór klawisza
//---------------------------
Tu jest cała logika (zawarta w instrukcji 'switch (klawiatura)') sterowania klawiaturą -> główne wykorzystanie znaczników pól bitowych, oraz wolnych bitów portu C (muszą być w najważniejszych miejscach pełniąc role szybkich przełączników).
Klawisze są rozmieszczone w kształcie "krzyża" -> góra, środek, dół oraz -> lewy, środek, prawy.
Klawisz środek zajmuje centralne miejsce i on służy do przełączania klawiatury:
a) regulator generatora PWM (regulacja stopnia świecenia wyświetlaczy LED)
b) wybór segmentów wyświetlaczy LED do ustawienia czasu i zapisu do czasu zegara
Regulator generatora PWM to klawisze -> góra, dół
Wybór segmentów wyświetlaczy LED to klawisze -> lewy, prawy
Dodawanie lub odejmowanie cyfr wyświetlacza Led to klawisze -> góra, dół (po przełączeniu klawiszem środek).
No i klawisz -> środek , który zajmuje centralne miejsce, a służy do przełączeń klawiatury z funkcji zapisu do zegara do funkcji normalnej pracy(ciągłego wyświetlania bieżącego czasu).
Code:
//---------------------------
// początek
// zapisu do zegara PCF8583
//---------------------------
if(bit_is_set(PINC,PINC3))
{
// ***********************************
if(segment ==1) // sekcja godz.
{
TWI_wswietlacz(dziesatki_minut,0xFF);
TWI_wswietlacz(minuty,0xFF);
TWI_wswietlacz(dziesatki_sekund,0xFF);
TWI_wswietlacz(sekundy,0xFF);
if(wborZnacznika.Bit.bit1 ==0) // deaktywne ustawienia do zapisu min.(czas PCF8583)
{
takt0 =czas[0]; // dziesiątki
takt1 =czas[1]; // jednośći
}
if(bit_is_set(PINC,PINC0)) // zezwolenie -> jedno naciśnięcie jedno wykonanie dodać/odjąć
{
if(bit_is_set(PINC,PINC2)) { if(!((takt0 ==2) && (takt1 ==3))) plusCzas(&takt0,&takt1); } //----- dodawanie cyfr -----
if(bit_is_clear(PINC,PINC2)) minusCzas(&takt0,&takt1); //----- odejmowanie cyfr -----
wborDoZapisu.Bit.bit1 =1;
taktZapisu[0] =(takt0<<4)|takt1;
PORTC &=~(1<<0);// blokada -> jedno naciśnięcie jedno wykonanie dodać/odjąć
wborZnacznika.Bit.bit1 =1; // znacznik -> aktywacji ustawienia do zapisu min.(czas ustawień)
}
TWI_wswietlacz(dziesatki_godzin,tablica_7seg[takt0]);
TWI_wswietlacz(godziny,tablica_7seg[takt1]);
}
// ***********************************
if(segment ==2) // sekcja min.
{
TWI_wswietlacz(dziesatki_godzin,0xFF);
TWI_wswietlacz(godziny,0xFF);
TWI_wswietlacz(dziesatki_sekund,0xFF);
TWI_wswietlacz(sekundy,0xFF);
if(wborZnacznika.Bit.bit2 ==0) // deaktywne ustawienia do zapisu min.(czas PCF8583)
{
takt2 =czas[2]; // dziesiątki
takt3 =czas[3]; // jednośći
}
if(bit_is_set(PINC,PINC0)) // zezwolenie -> jedno naciśnięcie jedno wykonanie dodać/odjąć
{
if(bit_is_set(PINC,PINC2)) plusCzas(&takt2,&takt3); //----- dodawanie cyfr -----
if(bit_is_clear(PINC,PINC2)) minusCzas(&takt2,&takt3); //----- odejmowanie cyfr -----
wborDoZapisu.Bit.bit2 =1;
taktZapisu[1] =(takt2<<4)|takt3;
PORTC &=~(1<<0);// blokada -> jedno naciśnięcie jedno wykonanie dodać/odjąć
wborZnacznika.Bit.bit2 =1; // znacznik -> aktywacji ustawienia do zapisu min.(czas ustawień)
}
TWI_wswietlacz(dziesatki_minut,tablica_7seg[takt2]);
TWI_wswietlacz(minuty,tablica_7seg[takt3]);
}
// ***********************************
if(segment ==3) // sekcja sek.
{
TWI_wswietlacz(dziesatki_godzin,0xFF);
TWI_wswietlacz(godziny,0xFF);
TWI_wswietlacz(dziesatki_minut,0xFF);
TWI_wswietlacz(minuty,0xFF);
if(wborZnacznika.Bit.bit3 ==0) // deaktywne ustawienia do zapisu min.(czas PCF8583)
{
takt4 =czas[4]; // dziesiątki
takt5 =czas[5]; // jednośći
}
if(bit_is_set(PINC,PINC0)) // zezwolenie -> jedno naciśnięcie jedno wykonanie dodać/odjąć
{
if(bit_is_set(PINC,PINC2)) plusCzas(&takt4,&takt5); //----- dodawanie cyfr -----
if(bit_is_clear(PINC,PINC2)) minusCzas(&takt4,&takt5); //----- odejmowanie cyfr -----
wborDoZapisu.Bit.bit3 =1;
taktZapisu[2] =(takt4<<4)|takt5;
PORTC &=~(1<<0);// blokada -> jedno naciśnięcie jedno wykonanie dodać/odjąć
wborZnacznika.Bit.bit3 =1; // znacznik -> aktywacji ustawienia do zapisu min.(czas ustawień)
}
TWI_wswietlacz(dziesatki_sekund,tablica_7seg[takt4]);
TWI_wswietlacz(sekundy,tablica_7seg[takt5]);
}
// ***********************************
}
//---------------------------
// koniec
// zapisu do zegara PCF8583
//---------------------------
}// stop klawiatury
Ustalanie cyfr na wyświetlaczach LED do zapisu w zegarze PCF8583.
Najpierw wygaszone zostaną nieaktywne segmenty pozostawiając segmenty, które są ustawiane. Później zależnie od potrzeby albo odejmuje albo dodaje cyfry, oraz zapisuje wynik do tablicy 'taktZapisu'. Zwalnia i ustawia zależnie od potrzeby znacznika (flagi) i wyświetla wynik na wyświetlaczach LED.
Code:
wdt_reset(); // tu nalezy zresetować Watchdoga
}
wdt_disable(); // wylaczenie Watchdog
return 0;
}
//-------------------------------------------------------------------------------------------------
// ************************************************************************************************
//-------------------------------------------------------------------------------------------------
Tu resetujemy Watchdog'a jeżeli z jakiś powodów czas wykonywania programu się wydłuży ponad czas ustalony dla Watchdog'a wtedy Watchdog zadziała -> zresetuje procesor
Code:
// ************************************************************************************************
//-------------------------------------------------------------------------------------------------
// ************************************************************************************************
// --- PRZERWANIE --- //
ISR(INT0_vect) /* aktywne zbocza: opadajace i narastajace */
{
//-------------------------------------
if(bit_is_clear(PINC,PINC3)) // zezwolenie na odczyt i wyswietlenie aktualnego czasy
{
if(bit_is_clear(PIND,PIND2))
{
// ------------------------------
czasPCF8583 =TWI_odczyt(zegar,0x04);
czas[0] =(czasPCF8583 & 0b00110000) >> 4; // dziesatki_godzin
czas[1] =czasPCF8583 & 0b00001111; // godziny
czasPCF8583 =TWI_odczyt(zegar,0x03);
czas[2] =czasPCF8583 >> 4; // dziesatki_minut
czas[3] =czasPCF8583 & 0b00001111; // minuty
czasPCF8583 =TWI_odczyt(zegar,0x02);
czas[4] =czasPCF8583 >> 4; // dziesatki_sekund
czas[5] =czasPCF8583 & 0b00001111; // sekundy
if(czas[0] == 0x00) TWI_wswietlacz(dziesatki_godzin,0xFF); // zero wygaszone
else TWI_wswietlacz(dziesatki_godzin,tablica_7seg[czas[0]]);
TWI_wswietlacz(godziny,tablica_7seg[czas[1]] - kropka); // świeci migajacy dwukropek
TWI_wswietlacz(dziesatki_minut,tablica_7seg[czas[2]]);
TWI_wswietlacz(minuty,tablica_7seg[czas[3]] - kropka); // swieci kropka
TWI_wswietlacz(dziesatki_sekund,tablica_7seg[czas[4]]);
TWI_wswietlacz(sekundy,tablica_7seg[czas[5]]);
// ------------------------------
} else TWI_wswietlacz(godziny,tablica_7seg[czas[1]]); // nie świeci migajacy dwukropek
}
// ---------- test procesora ----------
PORTB ^=(1<<0); // takt -> 1Hz
// ---------- test procesora ----------
}
// ************************************************************************************************
//-------------------------------------------------------------------------------------------------
// ************************************************************************************************
Całe przerwanie w,którym odbywa się odczyt czasu. Jest tu też umieszczony bit "test procesora" przy podłączeniu diody led do portu B0 (dioda led z portu przez opornik do plusa zasilania) powinna ona migać z częstotliwością 1Hz. Co oznacza prawidłową współpracę procesora Atmega8 i zegara PCF8583.
Dalej już jest zestaw procedur i funkcji pracujących w tym programie.
Code:
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
// *** FUNKCJE I PROCEDURY ***
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void TWI_inicjacja(void)
{
// Czestotliwosc TWI(i2c) = 100kHz (max 100kHz) -> atmega8 taktowana wewnetrznie 8MHz (RC=8MHz)
// ------------------------------------------------
/* TWSR = TWS7 TWS6 TWS5 TWS4 TWS3 - TWPS1 TWPS0 */
TWSR =0b00000000; // Preskaler = 1 ->> TWPS1=0 TWPS0=0
// ------------------------------------------------------
/* TWBR = TWBR7 TWBR6 TWBR5 TWBR4 TWBR3 TWBR2 TWBR1 TWBR0 */
TWBR =0b00100000;
/* Rejestr odpowiedzialny za wybór współczynnika podziału dla generatora.
Generator ten odpowiada za czestotliwosc która jest dzielona przez
sygnał zegarowy SCL w trybie pracy Master.
->> TWBR musi byc wieksze od 10 dla stabilnej pracy TWI(i2c)
Czyli ((8MHz/100kHz)-16)/2=32 => TWBR=32 lub TWBR=0x20 lub TWBR=0b00100000 */
}
unsigned char TWI_odczyt(unsigned char scalak,
unsigned char adres_rejestru)
{
unsigned char odczyt;
TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
while(!(TWCR & (1 << TWINT)));
TWDR =scalak;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWDR =adres_rejestru;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
while(!(TWCR & (1 << TWINT)));
TWDR =scalak | 0x01;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
odczyt =TWDR;
TWCR =(1 << TWINT) | (1<<TWEN) | (1<<TWSTO);
return(odczyt);
}
void TWI_zapis(unsigned char scalak,
unsigned char adres_rejestru,
unsigned char liczba)
{
TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
while(!(TWCR & (1 << TWINT)));
TWDR =scalak;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWDR =adres_rejestru;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWDR =liczba;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWCR =(1 << TWINT) | (1<<TWEN) | (1<<TWSTO);
}
void TWI_wswietlacz(unsigned char scalak,
unsigned char liczba)
{
TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
while(!(TWCR & (1 << TWINT)));
TWDR =scalak;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWDR =liczba;
TWCR =(1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
TWCR =(1 << TWINT) | (1<<TWEN) | (1<<TWSTO);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void PWM_inicjacja(void)
{ /* Tryb szybkie PWM(FastPWM) -> Generator 8-bitowy */
/* PWM na Timer1:
Takt_Atmega8 = 8MHz, Preskaler_Atmega8 = 256, Generator 8-bitowy = 256
Czestotliwosc_PWM = Takt_Atmega8/Preskaler_Atmega8/Generator 8-bitowy
czyli
Czestotliwosc_generatoraPWM = 8000000Hz / 256 / 256 ==> okolo 122Hz */
// ------------------------------------------------------------
/* TCCR1A = COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10 */
TCCR1A = 0b11110001;
/* Ustawia OC1A przy zrównaniu, kasuje OC1A przy TOP -> COM1A1=1 COM1A0=1
Kasuje OC1A przy zrównaniu,ustawia OC1A przy TOP -> COM1A1=1 COM1A0=0
Ustawia OC1B przy zrównaniu, kasuje OC1B przy TOP -> COM1B1=1 COM1B0=1
Kasuje OC1B przy zrównaniu,ustawia OC1B przy TOP -> COM1B1=1 COM1B0=0
Tryby PWM 8-bitowy -> WGM11=0 WGM10=1 */
// ----------------------------------------------------
/* TCCR1B = ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10 */
TCCR1B = 0b00001100;
/* Tryby PWM 8-bitowy -> WGM13=0 WGM12=1
Preskaler = 256 ->> CS12=1 CS11=0 CS10=0 */
}
void wartoscPWM(unsigned char wartosc)
{
/* niezależne sterowanie dwoma wyjściami generatora PWM */
OCR1A = 255;
OCR1B = wartosc;
// zapis w pamieci RAM zegara PCF8583 liczby 'wartosc' pod adresem 0x10
TWI_zapis(zegar,0x10,wartosc);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void INT0_inicjacja(void)
{
// ----------------------------------------------
/* MCUCR = SE SM2 SM1 SM0 ISC11 ISC10 ISC01 ISC00 */
MCUCR = 0b00000001; // ISC01=0 ISC00=1
/* Dowolna zmiana stanu logicznego na INT0 wywola przerwanie typu ISR(INT0_vect) */
// ------------------------------------
/* GICUR = INT1 INT0 - - - - IVSEL IVCE */
GICR = 0b01000000; // wlaczamy przerwanie INT0
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/* Przekazanie parametru przez wskazanie na zmienną (wskaźnik) umożliwia dokonania modyfikacji tej zmiennej wewnątrz funkcji.
Czyli do funkcji wkładamy przez parametr jakąś wartość, tam zostaje ta wartość przerobiona
i po zakończeniu funkcji funkcja oddaje zmienioną wartość przez ten sam parametr
void funkcja(typ_zmiennej *wartosc1, typ_zmiennej *wartosc2)
{
typ_zmiennej zmienna_pomocnicza =0;
zmienna_pomocnicza =*wartosc1;
*wartosc1=*wartosc2;
*wartosc2=zmienna_pomocnicza;
} // wywolanie funkcji np.-> funkcja(&zmienna1, &zmienna2);
Uwaga! po wykonaniu funkcji " funkcja(&zmienna1, &zmienna2) "
"zmienna1" i "zmienna2" beda zmodyfikowane przez tą funkcje
("zmienna1" i "zmienna2" mają inną wartość przed funkcją i inną wartość po wykonaniu tej funkcji)
*/
void minusCzas(unsigned char *dziesiatki, unsigned char *jednosci)
{
signed char pomoc =*jednosci;
if(!((*dziesiatki ==0) && (*jednosci ==0))) pomoc -=1;
if(pomoc ==-1)
{
pomoc =9;
*dziesiatki -=1;
}
*jednosci =pomoc;
} // wywolanie funkcji np.-> minusCzas(&wartosc1,&wartosc2);
void plusCzas(unsigned char *dziesiatki, unsigned char *jednosci)
{
if(!((*dziesiatki ==5) && (*jednosci ==9))) *jednosci +=1;
if(*jednosci ==10)
{
*jednosci =0;
*dziesiatki +=1;
}
} // wywolanie funkcji np.-> plusCzas(&wartosc1,&wartosc2);
/*
//--------------------------------
Kiedy wskażnik pokazuje już na konkretnie miejsce możemy odnieśćc się do tego obiektu na
który on wskazuje, odczytać jego wartość, lub wpisać wartość pod wskazany adres.
Podstawowa operacja na wskażniku jest wyłuskanie, czyli odwolanie się do obiektu
wskazywanego przez wskażnik. Operacja ta nazywa się adresowaniem pośrednim.
Operatorem adresowania pośredniego jest jednoargumentowy * zapisywana jako przedrostek.
Zastosowana do wskażnika daje zawartość obiektu wskazanego przez ten wskażnik np.:
unsigned char *wsk; // tworzona zmienna jest zmienną wskaźnikową
unsigned char inne_cos =0, cos =3; // inne_cos i cos maja wartości dowolne dla zilustrowania przykładu
wsk =&cos; // <-- Uwaga!!! wsk przechowuje adres zmiennej cos.
inne_cos =*wsk; // <-- inne_cos posiada wartość z pod adresu cos (czyli inne_cos =3)
*wsk =200; // <-- wstaw 200(wartość dowolna np.200) pod adres wskazywany przez zmienną wsk (czyli cos =200)
//--------------------------------
*/
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
// KONIEC KONIEC KONIEC KONIEC KONIEC KONIEC KONIEC KONIEC
//-------------------------------------------------------------------------------------------------
Poniżej kilka plików (typu *.pdf) do poczytania - po polsku!
Przyjemnej lektury.
Piotr
A oto zapakowany pliki zawierający plik c i plik hex (twiAtmega8_zegar.zip)