Projekt uniwersalnego komputera pokładowego
Autor: Janusz Skop
Nr albumu: 4508
Grupa: 2zBinf
Spis treści
Opis urządzenia
Pętla główna programu
Funkcje pomocnicze
Obsługa urządzeń peryferyjnych
Wyświetlacz LCD
Magistrala 1-Wire oraz czujniki temperatury
Zegar czasu rzeczywistego
1. Opis urządzenia
Projektowane urządzenie, będące prostym uniwersalnym komputerem pokładowym ma za zadanie informować kierowcę o niezapiętych pasach, niewłączonych po uruchomieniu lub niewyłączonych po zgaszeniu silnika światłach mijania, aktualnej temperaturze zarówno zewnętrznej jak i wewnętrznej, aktualnej godzinie. Ostrzega także, gdy temperatura silnika osiągnie niebezpieczny poziom (domyślnie ustawiony na 102o C). Komputer powinien zostać zamknięty w niewielkiej obudowie. Na obudowie powinno znajdować się gniazdo 8-pinowe (rys. 1), gniazdo 1-pinowe (przewód od kostki stacyjki z podpiętym stabilizatorem napięcia LM7805) oraz dwa przyciski (H - dla ustawienia godziny, M - dla ustawienia minut). Wewnątrz obudowy znajduje się m.in. Popularny mikrokontroler 89C51, ekran ciekłokrystaliczny ze sterownikiem HD44780, a także czujnik temperatury DS18B20. Do gniazda (rys. 1) podłączone powinny być dodatkowo dwa czujniki temperatury DS18B20 (pin 0 oraz 1), przewody zasilające (stałe napięcie z akumulatora pin 2 - masa, pin 3 - plus), przewód włącznika krańcowego przy gnieździe pasa bezpieczeństwa kierowcy (pin 4), przewód od czujnika obrotów wału korbowego z podpiętym stabilizatorem napięcia LM7805 (pin 5), przewód od przekaźnika świateł z podpiętym stabilizatorem napięcia LM7805 (pin 6), przewód podający napięcie 5V do włącznika krańcowego (pin 7). Na rysunku poniżej (rys. 2) przedstawiono w sposób uproszczony schemat połączeń poszczególnych elementów komputera.
Rys. 1 Wtyczka komputera
Rys. 2 Połączenia
Do linii portu P0 podpięte są kolejno przewód z czujnika obrotów wału (P0.0), przewód z przekaźnika świateł mijania (P0.1), przewód powrotny włącznika krańcowego przy gnieździe pasa bezpieczeństwa kierowcy (P0.2), przewód wyjściowy włącznika krańcowego (P0.3), przewód podłączany pod kostkę stacyjki (P0.4), czujnik temperatury silnika (P0.5), czujnik temperatury zewnętrznej (P0.6), czujnik temperatury wewnętrznej (P0.7). Do linii portu P1 podpięta jest szyna danych sterownika wyświetlacza LCD, do portu P3 na liniach 5,6,7 podpięte są wyprowadzenia sygnałów sterujących wyświetlaczem. Linie 2,3 (I2C) sterują zegarem DS1307 taktowanym rezonatorem kwarcowym 32.768kHz, natomiast linia 0 podłączona jest do przycisku H, a linia 1 do przycisku M.
2. Pętla główna programu
Algorytm działania oprogramowania sterownika opiera się na pętli głównej, w której kolejno sprawdzane są stany wszystkich linii portu P0 mikrokontrolera, następnie następuje przetwarzanie pobranych w ten sposób danych oraz wyświetlenie odpowiednich informacji na ekranie LCD sterowanym przez port P1 oraz trzy linie portu P3.
Kod programu został napisany w języku mikro C, przy czym ciała wszystkich funkcji są w języku asembler z wykorzystaniem kompilatora pochodzącego z pakietu SDCC.
char *bladP0, *bladP0_2;
int main ()
{
short int tempwew, tempzew;
unsigned char h,m,s, kursor;
initLCD(); // funkcja inicjująca wyświetlacz
init1WireP0_5();
init1WireP0_6(); // funkcje inicjujące czujniki DS18B20 na poszczególnych
init1WireP0_7(); // liniach portu P0
initRTC(0,0,0); // funkcje inicjujące zegar na szynie IIC (P3.3 P3.4)
do
{
If (!sprawdzP0() && !wygas()) // jeżeli funkcje zwrócą zero, to wyświetla
// temperatury i godzinę
{
wlaczLCD();
rdkb(h,m);
tempwew = czytajP0_7(); // wykonuje odczyt temperatury wew.
tempzew = czytajP0_6(); // wykonuje odczyt temperatury zew.
RTC(h,m,s); // pobiera do zmiennych h,m,s aktualną godzinę
czyscLCD();
kursor = napiszLCD(0,”temp. wew. #");
kursor = napiszLCDv(kursor,tempwew);
kursor = napiszLCD(0x40,”temp. zew. #");
kursor = napiszLCDv(kursor,tempzew);
wait05();
wait05();
czyscLCD();
napiszLCD(0x4,”Godzina#”);
kursor = napiszLCDv(0x40, h);
kursor = napiszLCD(kursor,”:#”);
kursor = napiszLCDv(kursor,m);
wait05();
wait05();
}
else
If (wygas()) wygasLCD;
else
{
wlaczLCD();
czyscLCD();
napiszLCD(0,bladP0)
napiszLCD(0x40,bladP0_2)
waitkb();
}
} while(1);
return 0;
}
3. Funkcje pomocnicze
W niniejszym rozdziale zostaną kolejno opisane funkcje pomocnicze niezwiązane bezpośrednio z urządzeniami peryferyjnymi oraz funkcja sprawdzP0()
void wait05() // funkcja wstrzymująca program na ok. 0.5 s
{
asm
{
MOV R7, #0FAh
DJNZ R7, $
}
}
void waitkb() // funkcja oczekuje na wciśnięcie klawisza H lub M
{
asm
{
CLR P3.0
CLR P3.1
waitk:
MOV C, P3.0
JC endk
MOV C, P3.1
JC endk
JMP waitk
endk:
NOP
}
}
void rdkb(unsigned char h, unsigned char m) // funkcja odczytuje klawisz
{
asm
{
PUSH R6
MOV R6, _h
MOV C, P3.0
JNC rdnext
INC R6
MOV _h, R6
JMP rdend
rdnext:
MOV R6, _m
MOV C, P3.1
JNC rdend
INC R6
MOV _m, R6
rdend:
POP R6
}
setRTC(h,m);
}
int sprawdzP0()
{
unsigned char v=0, vp=0;
// sprawdzenie czy silnik jest uruchomiony
// na biegu jałowym jeden obrót silnika trwa co najwyżej 0.09 s
// 50 powtórzeń pętli wystarczy aby zaczekać na impuls z czujnika obrotów wału
asm
{
spr1:
MOV R7, #50
MOV C, P0.0
JC C1_1
DJNZ R7, spr1
C1_1:
MOV _v, #1
spr1end:
NOP
}
// sprawdzenie stanu przekaźnika świateł mijania
asm
{
CLR A
MOV C, P0.1
MOV R0, @_v
ORL ACC.1, C
ORL @R0, A
}
// sprawdzenie włącznika krańcowego przy gnieździe pasa bezpieczeństwa
asm
{
CLR A
CLR P0.2
CLR P0.3
SETB P0.3
MOV C, P0.2
CLR P0.3
MOV R0, @_v
ORL ACC.2,C
ORL @R0, A
}
// sprawdzenie temperatury silnika
vp = czytajP0_5();
if (vp>102)
v|=32;
if (v&32)
{
bladP0 = “Uwaga silnik#”;
bladP0_2 = "sie przegrzewa#”;
return 1;
}
if (v&1)
{
if ((v&2)!=2) bladP0=”Wlacz swiatla#”;
if ((v&4)!=4) bladP0_2=”Zapnij pasy#”;
}
else
if (v&2) bladP0=”Wylacz swiatla#”;
if ((v&7)=7 && (!(v&32))) v=0; // jeżeli silnik uruchomiony, światła się świecą, pasy
// pasy zapięte oraz silnik się nie przegrzewa to v=0
return v;
}
int wygas()
{
int v=0;
asm
{
MOV C, P0.4
JC wygasend
MOV _v,#1
wygasend:
NOP
}
return v;
}
4. Obsługa urządzeń peryferyjnych
4.1. Wyświetlacz LCD
Obsługa wyświetlacza sprowadza się do kilku funkcji initLCD(), napiszLCD(unsigned char kursor, char *napis), wygasLCD(), wlaczLCD(), czyscLCD(), napiszLCDv (unsigned char kursor, short int v)
void initLCD() //inicjuje wyświetlacz
{
asm
{
CLR P3.7
MOV DPTR, #0FFFFh
DJNZ DPH, $
DJNZ DPL, $
MOV P1, #30h
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV DPTR, #0FFFFh
DJNZ DPH, $
DJNZ DPL, $
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV DPTR, #0FFFFh
DJNZ DPH, $
DJNZ DPL, $
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV DPTR, #0FFFFh
DJNZ DPH, $
DJNZ DPL, $
MOV P1, #38h
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV P1, #0FFh
CLR P3.5
SETB P3.6
CheckAgain1:
SETB P3.7
MOV A, P1
CLR P3.7
JB ACC.7, CheckAgain1
CLR P3.5
CLR P3.6
MOV P1, #8
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV P1, #0FFh
CLR P3.5
SETB P3.6
CheckAgain2:
SETB P3.7
MOV A, P1
CLR P3.7
JB ACC.7, CheckAgain2
CLR P3.5
CLR P3.6
MOV P1, #1
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV P1, #0FFh
CLR P3.5
SETB P3.6
CheckAgain3:
SETB P3.7
MOV A, P1
CLR P3.7
JB ACC.7, CheckAgain3
CLR P3.5
CLR P3.6
MOV P1, #6
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7call
MOV P1, #0FFh
CLR P3.5
SETB P3.6
CheckAgain4:
SETB P3.7
MOV A, P1
CLR P3.7
JB ACC.7, CheckAgain4
CLR P3.5
CLR P3.6
MOV P1, #0Fh
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
}
}
void wygasLCD() // funkcja wyłącza LCD
{
asm
{
CLR P3.5
CLR P3.6
MOV P1, #8
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
}
}
void wlaczLCD() // funkcja włącza LCD
{
asm
{
CLR P3.5
CLR P3.6
MOV P1, #0Fh
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
}
}
void czyscLCD() // funkcja kasuje zawartość wyświetlacza LCD
{
asm
{
CLR P3.5
CLR P3.6
MOV P1, #1
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
}
unsigned char napiszLCD(unsigned char kursor, char *napis)
{
unsigned char retv=0;
// ustawianie kursora
asm
{
MOV A, _kursor
SETB ACC.7
PUSH ACC
MOV P1, #0FFh
CLR P3.5
SETB P3.6
cxyl1:
SETB P3.7
MOV A, P1
CLR P3.7
JB ACC.7, cxyl1
POP P1
CLR P3.5
CLR P3.6
SETB P3.7
CLR P3.7
MOV R4, _retv
}
// wyświetlanie napisu na lcd
asm
{
MOV DPTR, @_napis
ZapiszString:
CLR ACC
MOVC A, @A+DPTR
CJNE A, 0, NastepnyZnak
JMP nlkoniec
NastepnyZnak:
PUSH DPH
PUSH DPL
; zapis znaku do LCD
PUSH ACC
MOV P1, #0FFh
CLR P3.5
SETB P3.6
chkagain:
SETB P3.7
MOV A, P1
CLR P3.7
JB ACC.7, chkagain
POP P1
CLR P3.6
SETB P3.5
SETB P3.7
CLR P3.7
; koniec bloku zapisu danych do LCD
POP DPL
POP DPH
INC DPTR
INC R4
JMP ZapiszString
nlkoniec:
MOV _retv, R4
}
return retv+kursor;
}
unsigned char napiszLCDv(unsigned char kursor, short int v)
{
int* znaki[11] = {`0','1','2','3','4','5','6','7','8','9','0','-`};
char napis[4];
short int v2;
v2=v/10;
if (v<0)
{
napis[0]=znaki[10];
if (v2<0) v2*=-1;
}
else napis[0]=” ";
napis[1]=znaki[v2];
v2=v%10;
napis[2]=znaki[v2];
napis[3]=”#”;
return napiszlcd(kursor,napis);
}
4.2 Magistrala 1-Wire oraz czujniki temperatury
Komunikacja z czujnikami temperatury odbywa się za pomocą magistrali 1-Wire (standardu opracowanego przez firmę Dallas Semiconductor). Inicjacja urządzeń odbywa się poprzez wywołanie funkcji init1WireP0_5(), init1WireP0_6(), init1WireP0_7(), natomiast odczyt temperatury za pomocą czytajP0_5(), czytajP0_6() oraz czytajP0_7().
void init1WireP0_5()
{
asm
{
CLR P0.5
MOV B, #0FFh
DJNZ B, $
SETB P0.5
MOV B, #30
DJNZ B, $
}
}
void init1WireP0_6()
{
asm
{
CLR P0.6
MOV B, #0FFh
DJNZ B, $
SETB P0.6
MOV B, #30
DJNZ B, $
}
}
void init1WireP0_7()
{
asm
{
CLR P0.7
MOV B, #0FFh
DJNZ B, $
SETB P0.7
MOV B, #30
DJNZ B, $
}
}
short int czytajP0_5()
{
short int retv;
unsigned char minus;
init1WireP0_5();
asm
{
MOV A, #0CCh
MOV R0, #8
nextbit1:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.5
DJNZ B, $
SETB P0.5
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit1
MOV A, #044h
MOV R0, #8
nextbit2:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.5
DJNZ B, $
SETB P0.5
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit2
MOV R7, #6
waitlong1:
MOV R6, #250
DJNZ R6, $
DJNZ R7, waitlong1
}
init1WireP0_5(); // ponowna inicjalizacja urządzenia<=>reset
asm
{
MOV A, #0CCh
MOV R0, #8
nextbit3:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.5
DJNZ B, $
SETB P0.5
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit3
MOV A. #0BEh
MOV R0, #8
nextbit4:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.5
DJNZ B, $
SETB P0.5
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit4
MOV R0, #8 ;pętla odczytu danych z czujnika
nextbit5:
CLR P0.5
SETB P0.5
MOV B, #5
NOP
DJNZ B, $
MOV C, P0.5
MOV B, #60
DJNZ B, $
RRC A
DJNZ R0, nextbit5
MOV R1, A
MOV R0, #8
nextbit6:
CLR P0.5
SETB P0.5
MOV B, #5
NOP
DJNZ B, $
MOV C, P0.5
MOV B, #60
DJNZ B, $
RRC A
DJNZ R0, nextbit6
JNB ACC.5, tplus
MOV _minus, #1
tplus:
SWAP A
ANL A, #11110000b
MOV R0, A
MOV A, R1
SWAP A
ANL A, #00001111b
ADD A, R0
MOV _retv, A
}
if (minus) retv*=-1;
return retv;
}
short int czytajP0_6()
{
short int retv;
unsigned char minus;
init1WireP0_6();
asm
{
MOV A, #0CCh
MOV R0, #8
nextbit1:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.6
DJNZ B, $
SETB P0.6
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit1
MOV A, #044h
MOV R0, #8
nextbit2:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.6
DJNZ B, $
SETB P0.6
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit2
MOV R7, #6
waitlong1:
MOV R6, #250
DJNZ R6, $
DJNZ R7, waitlong1
}
init1WireP0_6(); // ponowna inicjalizacja urządzenia<=>reset
asm
{
MOV A, #0CCh
MOV R0, #8
nextbit3:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.6
DJNZ B, $
SETB P0.6
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit3
MOV A. #0BEh
MOV R0, #8
nextbit4:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.6
DJNZ B, $
SETB P0.6
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit4
MOV R0, #8 ;pętla odczytu danych z czujnika
nextbit5:
CLR P0.6
SETB P0.6
MOV B, #5
NOP
DJNZ B, $
MOV C, P0.6
MOV B, #60
DJNZ B, $
RRC A
DJNZ R0, nextbit5
MOV R1, A
MOV R0, #8
nextbit6:
CLR P0.6
SETB P0.6
MOV B, #5
NOP
DJNZ B, $
MOV C, P0.6
MOV B, #60
DJNZ B, $
RRC A
DJNZ R0, nextbit6
JNB ACC.5, tplus
MOV _minus, #1
tplus:
SWAP A
ANL A, #11110000b
MOV R0, A
MOV A, R1
SWAP A
ANL A, #00001111b
ADD A, R0
MOV _retv, A
}
if (minus) retv*=-1;
return retv;
}
short int czytajP0_7()
{
short int retv;
unsigned char minus;
init1WireP0_7();
asm
{
MOV A, #0CCh
MOV R0, #8
nextbit1:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.7
DJNZ B, $
SETB P0.7
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit1
MOV A, #044h
MOV R0, #8
nextbit2:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.7
DJNZ B, $
SETB P0.7
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit2
MOV R7, #6
waitlong1:
MOV R6, #250
DJNZ R6, $
DJNZ R7, waitlong1
}
init1WireP0_7(); // ponowna inicjalizacja urządzenia<=>reset
asm
{
MOV A, #0CCh
MOV R0, #8
nextbit3:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.7
DJNZ B, $
SETB P0.7
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit3
MOV A. #0BEh
MOV R0, #8
nextbit4:
RRC A
MOV B, #1
CPL C
MOV B.5, C
CLR P0.7
DJNZ B, $
SETB P0.7
MOV B, #30
DJNZ B, $
DJNZ R0, nextbit4
MOV R0, #8 ;pętla odczytu danych z czujnika
nextbit5:
CLR P0.7
SETB P0.7
MOV B, #5
NOP
DJNZ B, $
MOV C, P0.7
MOV B, #60
DJNZ B, $
RRC A
DJNZ R0, nextbit5
MOV R1, A
MOV R0, #8
nextbit6:
CLR P0.7
SETB P0.7
MOV B, #5
NOP
DJNZ B, $
MOV C, P0.7
MOV B, #60
DJNZ B, $
RRC A
DJNZ R0, nextbit6
JNB ACC.5, tplus
MOV _minus, #1
tplus:
SWAP A
ANL A, #11110000b
MOV R0, A
MOV A, R1
SWAP A
ANL A, #00001111b
ADD A, R0
MOV _retv, A
}
if (minus) retv*=-1;
return retv;
}
4.3 Zegar czasu rzeczywistego
Komunikacja mikrokontrolera z zegarem odbywa się za pomocą szyny I2C. Funkcja RTC(unsigned char h, unsigned char m) oraz setRTC(unsigned char h, unsigned char m) korzystają dodatkowo z funkcji I2Cinit(), I2Cstop(), I2Cwrite(unsigned char v), unsigned char I2Cread().
void I2Cinit()
{
asm
{
CLR P3.2 ;SDA
MOV B, #5
DJNZ B, $
CLR P3.3 ;SCL
MOV B, #5
DJNZ B, $
}
}
void I2Cstop()
{
asm
{
CLR P3.2
SETB P3.3
JNB P3.3, $
MOV B, #5
DJNZ B, $
SETB P3.2
MOV B, #5
DJNZ B, $
}
}
void I2Cwrite(unsigned char v)
{
asm
{
MOV R7, #8
wrloop:
RLC A
MOV P3.2, C
SETB P3.3
JNB P3.3, $
MOV B, #5
DJNZ B, $
CLS P3.3
MOV B, #5
DJNZ B, $
DJNZ R7, wrloop
}
}
unsigned char I2Cread()
{
unsigned char retv;
asm
{
MOV R7, #8
rdloop:
SETB P3.3
JNB P3.3, $
MOV B, #5
DJNZ B, $
MOV C, P3.2
RLC A
CLR P3.3
MOV B, #5
DJNZ B, $
DJNZ R7, rdloop
}
}
void RTC(unsigned char h, unsigned char m)
{
unsigned char ds1307w=208, ds1307r=209, s=0;
I2Cinit();
I2Cwrite(ds1307w);
I2Cwrite(0);
I2Cinit();
I2Cwrite(ds1307r);
s=I2Cread();
m=I2Cread();
h=I2Cread();
asm
{
MOV R7, #2
wait500:
MOV B, #250
DJNZ B, $
DJNZ R7, wait500
}
}
void setRTC(unsigned char h, unsigned char m)
{
unsigned char ds1307w=208;
I2Cinit();
I2Cwrite(ds1307w);
I2Cwrite(0);
I2Cwrite(0);
I2Cwrite(m);
I2Cwrite(h);
I2Cstop();
}
79
69
59
49
39
29
09
19
P1.0
P1.1
P1.2
P1.3
P1.4
P1.5
P1.6
P1.7
P3.5
P3.6
P3.7
P3.3
P3.2
P3.1
P3.0
D0
D1
D2
D3
D4
D5
D6
D7
RS
R/W
E
HD44780
89C51
Wtyczka
P0.0
…
P0.6
P0.7
DS18B20
DS1307
H
M