Przedstawiona poniżej procedura odpowiedzialna jest za inicjalizację wyświetlacza po uruchomieniu urządzenia. Wykonuje ona sekwencję resetującą wewnętrzny procesor wyświetlacza oraz ustawia wstępne parametry pracy (tabela nr 3).
Interfejs | 4-ro bitowy |
Ilość linii | 2 |
Rozmiar czcionki | 5 x 7 pikseli |
Wyświetlacz włączony | Tak |
Wyświetlacz wyczyszczony | Tak |
Auto inkrementacja nadchodzących danych | Tak |
Tabela 3 - parametry wstępnej inicjalizacji wyświetlacza
void LCDinit(void) //inicjalizacja LCD
{
RSDDR|=(1<
CLR_E;
waitms(20);
CLR_D7;
CLR_D6;
SET_D4;
SET_D5;
unsigned char i;
for(i=0;i<3;i++) {SET_E;CLR_E;waitms(5);}
CLR_D4;
SET_E;
CLR_E;
LCDcmd(0x28); //function set - interface=4 bit,line=2,font=5x7
LCDcmd(ONDISP); //display ctrl - display on
LCDcmd(0x06); //entry mode set - increment
LCDcmd(0x01); //clear display
}
Podstawowe procedury obsługi wyświetlacza zaczerpnięte zostały z materiałów dostępnych na stronie www.avrfreaks.net. Jednak podczas pisania programu uległy tak poważnym zmianom, że obecnie tworzą bibliotekę stanowiącą własność autora niniejszej pracy.
LCDinit() wykorzystuje jedną z dwóch funkcji:
void LCDcmd(char d) //Wyślij komendę do LCD
{
CLR_RS;
LCDwrite(d);
}
void LCDchr(char d) //Wyślij 1 znak do LCD
{
SET_RS;
LCDwrite(d);
}
Wyświetlacz rozróżnia nadsyłane dane (znak lub komenda) poprzez stan linii RS w momencie nadawania danej. Aby w ogóle możliwe było wysłanie danych do wyświetlacza, potrzebna jest funkcja, która podzieli wysyłany bajt na dwie części po 4 bity, po czym kolejno je nada. Do tego celu służy LCDwrite(...):
void LCDresetdata(void) //wyzeruj szynę danych LCD
{
CLR_D4;
CLR_D5;
CLR_D6;
CLR_D7;
}
void LCDwrite(char d) //zapis bajtu do LCD
{
LCDresetdata();
if(d & 0x10){SET_D4;}
if(d & 0x20){SET_D5;}
if(d & 0x40){SET_D6;}
if(d & 0x80){SET_D7;}
SET_E;CLR_E;
LCDresetdata();
if(d & 0x01){SET_D4;}
if(d & 0x02){SET_D5;}
if(d & 0x04){SET_D6;}
if(d & 0x08){SET_D7;}
SET_E;CLR_E;
waitms(3); }
Kolejna z funkcji umożliwia ustawienie kursora w wybranej pozycji na ekranie:
void
LCDxy(char x,char y)
{
unsigned char com=0x80;
void LCDxy(char x,char y)
{
unsigned char com=0x80;
com|=(x|(y<<6));
LCDcmd(com);
}
Współpracuje ona z funkcją wyświetlającą tekst:
void LCDtext(char *txt)
{
while(*txt) {LCDchr(*txt++);}
}
Pozostałe funkcje służące do obsługi wyświetlacza są modyfikacją komendy LCDcmd(...).W pliku programu zdefiniowane są stałe (tabela nr 4), po których wysłaniu za pomocą LCDcmd() następuje żądana przez nas reakcja wyświetlacza.
CLRDISP 0x01 | //czysc wyswietlacz |
HOMEDISP 0x02 | //kursor na pozycje 0,0 |
OFFDISP 0x08 | //wylacz ekran |
ONDISP 0x0c | //wlacz ekran |
SHIFT 0x9f | // przesuwanie kursora |
Tabela 4 - pomocnicze stałe do obsługi wyświetlacza
Funkcje typu LCD_CURSOR, LCD_BLINK, LCD_SHIFT to jedynie uproszczenie dla programisty. Równie dobrze w pętli programu można np. zamiast komendy LCD_BLINK(0) użyć komendy LCDcmd(dana), tylko że przy wielokrotnym używaniu tego typu zapisów program w końcu staje się całkowicie nieczytelny nawet dla jego twórcy.
//wlączanie kurosra
//a=1 wł
//a=0 wył
void LCD_CURSOR(char a)
{
if (a) {LCDcmd(ONDISP | 0x02);}
else {LCDcmd(ONDISP);}
}
//******************************************************************************
//miganie kursora
//a=1 wł
//a=0 wył
void LCD_BLINK(char a)
{
if (a) {LCDcmd(ONDISP | 0x01);}
else {LCDcmd(ONDISP);}
}
//******************************************************************************
//przesuwanie tekstu
//a=1 w prawo
//a=2 w lewo
//a=0 wył
void LCD_SHIFT(char a)
{
if (a == 1) {LCDcmd(SHIFT | 0x04);} //w prawo
else if (a == 2){LCDcmd(SHIFT);} // w lewo
else {LCDcmd(ONDISP);} // wyłączenie przesuwania
}
//******************************************************************************
Sterownik „SPA – 1” zawiera w sobie oprogramowanie dwóch niezależnych urządzeń korzystających z przetwornika ciśnienia, dlatego zdecydowano się na wyodrębnienie głównej funkcji odczytującej zmierzoną wartość:
//***********************************************************
//odczytanie wartosci zmierzonej przez A/C
unsigned int getADC(void)
{
ADCSRA|=0b01000000;
zwrot=ADC;
return zwrot;
}
Następnie otrzymane dane są przetwarzane w zależności od trybu pracy urządzenia. Dla timera pneumatycznego jest to funkcja:
//***********************************************************
//timer pneumatyczny - operacja różniczkowania odczytu z A/C
unsigned int liczADC(void)
{
getADC();
zwrot=zwrot* 2;
por=(zwrot/ 8);
//...................
if(por>= cisnienie+czulosc)
{
ustaw=3;
TIMSK=0b00000100;
cisnienie=por;
//...................
}
else
{
cisnienie=por;
//...................
}
}
Zaś dla wskaźnika wysokości słupa wody:
void wyswADC(void)
{
getADC();
zwrot-=48;
value=zwrot/8+poziom;
..............
.............
}
Obliczenia w trybie timera pneumatycznego polegają na cyklicznym zapamiętywaniu zmierzonej wartości i porównywaniu jej z wartością poprzednią. Jeżeli wartość ta jest większa od poprzedniej o ustawioną przez użytkownika czułość, to układ zostaje wyzwolony. W przeciwnym wypadku zmiana zmierzonej wartości pozostaje zignorowana. W ten sposób uzyskuje się niezależność działania układu od zmian ciśnienia w pomieszczeniu maszynowni basenu.
Timer 0 wykorzystywany jest w 2 przypadkach: użytkownik wybrał opcję ustawienia trybu pracy urządzenia lub sterownik pracuje w trybie wskaźnika wysokości słupa wody. W tych sytuacjach timer wykorzystywany jest do cyklicznej zmiany wyświetlanego komunikatu. Timer ten jest jedynie 8-io bitowy i nie umożliwia bezpośredniego uzyskania odpowiednio dużego czasu opóźnienia (nawet z zastosowaniem preskalera). Dlatego potrzebne było zadeklarowanie dodatkowej zmiennej „plansza”, poprzez którą określany jest aktualny tekst do wyświetlenia. Listing przerwania od timera 0 znajduje się poniżej:
SIGNAL(SIG_OVERFLOW0)
{sei();
plansza++;
if(plansza==255){plansza=0;}
TCNT0=0b00000000;
}
Ze względu na dwubajtową pojemność licznika 1 możliwe jest uzyskanie za jego pomocą przerwania co 1 sekundę. Uzyskana dokładność odmierzania czasu jest całkowicie wystarczająca dla prezentowanego urządzenia, jednak przeprowadzone próby dowiodły, że stabilność długoterminowa takiego rozwiązania jest mało zadowalająca. Tak skonstruowany zegar może przekłamywać nawet o kilkadziesiąt sekund na dobę. Z tego właśnie powodu konstruktorzy Atmela umożliwili jednemu z timerów pracę z typowym nisko częstotliwościowym kwarcem zegarkowym. W moim przypadku zastosowanie dwóch kwarców było z ekonomicznego i funkcjonalnego punktu widzenia całkowicie nieuzasadnione.
SIGNAL(SIG_OVERFLOW1)
{
sekunda--;
if ((sekunda ==0) && (minuta ==0))
{
TIMSK=0b00000000;
sekunda = eeprom_read_byte(0x65);
minuta = eeprom_read_byte(0x66);
}
if (sekunda == 255)
{
sekunda=59;
if ((minuta ==0) && (sekunda < 60))
{minuta=0;}
else {minuta--;}
}
TCNT1=0x573F; //dla F=11059200, 43200 cykle zegara
//TCNT1=0x85ED; //dla F=8000000, 31250 cykle zegara
//TCNT1=0x0BDB; //dla F=16000000,62500 cykle zegara
}
Jak wynika z analizy listingu, możliwa jest praca z kilkoma różnymi częstotliwościami kwarcu. Wybrane zostały te najbardziej typowe, dla innych należałoby przeliczyć wartość jaką należy wpisać do rejestru TCNT1.
Obsługa watchdoga to jedna z wielu możliwości zaimplementowanych bibliotecznie w AVR Studio. Zasadniczo sprowadza się do uruchomienia układu nadzorcy z jednym z zadanych czasów oczekiwania, a następnie do ciągłego resetowania jego pamięci. W przypadku mojego programu wygląda to tak:
wdt_enable(WDTO_2S); //start watchdoga – 2 sek. czasu oczekiwania
wdt_reset(); //cykliczne resetowanie nadzorcy
Obsługa wejść / wyjść możliwa jest po ich zainicjowaniu poprzez wpisanie odpowiednich wartości do rejestrów PORTx i DDRx. Aby odczytać stan wejścia lub wyjścia, należy zbadać stan odpowiedniego bitu w rejestrze PINx. Przykład poniżej przedstawia w jaki sposób można to zrealizować najprościej (bez użycia aliasów wejść).
// USTAWIENIE PORTOW KLAWISZY JAKO WEJŚCIA
PORTC = (1<
Tym sposobem porty zostały skonfigurowane jako wejściowe z rezystorami podciągającymi do plusa zasilania. Poniższą procedurą można odczytać stan wejść:
if(bit_is_clear(PINC,6))
{
...........
}
Podczas pisania dużych programów warto jest przypisać wszystkim używanym portom aliasy. Poprawia to w znaczący sposób czytelność programu:
//*************************
#define KLAW_UP_pin PINC
#define KLAW_UP_bit 6
#define KLAW_UP_port PORTC
#define KLAW_UP_ddrb DDRC
Obsługa UART wykorzystywana w urządzeniu SPA umożliwia przesłanie wszelkich parametrów konfigurowalnych pomiędzy komputerem a sterownikiem za pomocą zwykłego portu RS232. Ponieważ magistrala RS485, wykorzystana w celu zwiększenia długości RS232, jest typu HALF – DUPLEX, należało stale czuwać nad kierunkiem transmisji układu MAX485. Zrealizowano to w przerwaniu od nadania znaku: układ w czasie normalnej pracy stale pozostaje w nasłuchu. Po odebraniu żądania wysłania danych, przełącza się na nadawanie, wysyła daną a następnie powraca do normalnej pracy. Magistrala zostaje przełączona znów do trybu nasłuchu dopiero w momencie wysłania ostatniego bitu, o czym informuje przerwanie od zakończenia transmisji.
Aby możliwa była praca USART’u należy go zainicjalizować. Służy do tego funkcja UART_init(MYUBRR):
//***********************************************************
#define BAUD 57600 // prędkość transmisji po RS232
#define MYUBRR F_CPU/16/BAUD-1
void UART_init(unsigned int ubrr) // inicjalizacja
{
UBRRH = (unsigned char) (ubrr>>8); // ustawienie prędkości
UBRRL = (unsigned char) ubrr;
UCSRB = (1<
UCSRC = (1<
Do wysłania pojedynczego znaku przez USART wystarczy funkcja USART_out(...), która jako argument przyjmuje znak do nadania:
void USART_out (unsigned char data) // wysłanie znaku
{
while ( !( UCSRA & (1<<UDRE)) );
UDR = data;
}
Aby w łatwy sposób wysłać ciąg znaków, np. napis, należy skorzystać z jednej z poniższych funkcji:
void rs_text(char *txt)
{
while(*txt)
{
USART_out(*txt++);
}
}
void rs_text_mem(const char *s)
{
char c;
while ((c = pgm_read_byte(s++)))
{
USART_out(c);
}
}
Pierwsza z nich wysyła na USART ciąg znaków podany jako jej argument, zaś druga ciąg znaków umieszczony w pamięci programu.
Procedura obsługi zegara RTC nie została jeszcze zaimplementowana ani w opro-gramowaniu mikrokontrolera, ani w programie „SPA – Soft”. Jednakże umieszczenie układu PCF na płytce modułu „SPA – 1” daje taką możliwość.
Copyright © 2008-2010 EPrace oraz autorzy prac.