|
Część 4 - Magistrala I2C
W tym ćwiczeniu wykorzystamy dwa dodatkowe układy scalone typu PCF8574A
produkcji firmy Philips.
Układ ten jest 8-bitowym ekspanderem magistrali I2C. Linie SDA i SCL należy
podłączyć odpowiednio do pinów P2.0 i P2.1 mikrokontrolera. Do wyjść pierwszego
układu podłączamy diody LED, a do drugiego ukłądu naszą klawiaturę.
Ze względu na złożoność procedur obsługi magistrali I2C
przygotowano dla języka asemblera i C oddzielne pliki zwierające podstawowe
procedury obsługi tej magistrali. Ma to na celu uniknięcie ciągłego przepisywania
stosunkowo dużego kodu zawierającego niezbędne procedury. Zamieszczone
poniżej pliki zawierają procedury zapewniające:
- wygenerowanie sekwencji "START"
- wygenerowanie sekwencji "STOP"
- wysłanie na magistralę bajtu danych
- odczytanie z magistrali bajtu danych
Procedury te pozwalają na komunikację z większością typowych układów scalonych
zawierających interfejs I2C pracujący w trybie "MASTER-SLAVE".
Plik I2C.ASM
;
; Procedura generująca sekwencję START
;
I2C_START:
SETB SDA
SETB SCL
ACALL DELAY
CLR SDA
ACALL DELAY
CLR SCL
RET
;
; Procedura generująca sekwencję STOP.
; Stan linii SDA zostaje zapamiętany po zakończeniu
; transmisji we wskaˇniku przeniesienia C.
;
I2C_STOP:
CLR SDA
ACALL DELAY
SETB SCL
ACALL DELAY
SETB SDA
ACALL DELAY
MOV C,SDA
RET
;
; Procedura odczytująca bajt z magistrali I2C.
; Wartość bitu ACK należy przekazać do procedury poprzez
; wskaˇnik przeniesienia C.
; Odczytany bajt jest umieszczany w akumulatorze.
;
I2C_READ:
MOV ACK,C
SETB SDA
MOV R0,#8
L0: ACALL DELAY
SETB SCL
ACALL DELAY
MOV C,SDA
RLC A
CLR SCL
DJNZ R0,L0
MOV C,ACK
MOV SDA,C
ACALL DELAY
CLR SCL
RET
;
; Procedura wysyłająca bajt na magistralę I2C.
; Stan bitu ACK jest zwracany poprzez wskaˇnik przeniesienia C.
; Bajt do wysłania należy umieścić w akumulatorze.
;
I2C_WRITE:
MOV R0,#9
SETB C
L1: CLR SCL
RLC A
MOV SDA,C
ACALL DELAY
SETB SCL
ACALL DELAY
DJNZ R0,L1
MOV C,SDA
CLR SCL
RET
DELAY:
NOP
NOP
RET
Plik I2C.C
void delay(void)
{
asm{0x00};
asm{0x00};
}
void I2C_START(void)
{
SDA = SCL = 1;
delay();
SDA = 0;
delay();
SCL = 0;
}
bit I2C_STOP(void)
{
SDA = 0;
delay();
SCL = 1;
delay();
SDA = 1;
delay();
return (~SDA);
}
unsigned char I2C_READ(bit ACK)
{
unsigned char bitCount = 8, temp;
SDA = 1;
do
{
delay();
SCL = 1;
delay();
temp <<= 1;
if (SDA) temp++;
SCL = 0;
} while (--bitCount);
SDA = ACK;
delay();
SCL = 1;
delay();
SCL = 0;
return (temp);
}
bit I2C_WRITE(unsigned char byte)
{
unsigned char bitCount = 9;
bit temp;
do
{
SCL = 0;
SDA = byte & 0x80;
byte = (byte << 1) + 1;
delay();
SCL = 1;
delay();
}while(--bitCount);
temp = SDA;
SCL = 0;
return(temp);
}
Przykład 4.1
Przykład 4.1.ASM
NAME P04_01_ASM_SRC
$INCLUDE (ATMEL/REG8252.INC)
P04_01_ASM SEGMENT CODE
RSEG P04_01_ASM
SDA EQU P2.0
SCL EQU P2.1
ACK EQU 00h
ACALL I2C_START
MOV A, #70h
ACALL I2C_WRITE
MOV A, #254
ACALL I2C_WRITE
ACALL I2C_STOP
SJMP $
$INCLUDE (I2C.ASM)
END
Przykład 4.1.C
#include <ATMEL/REG8252.H>
#include "I2C.H"
void main(void)
{
I2C_START();
I2C_WRITE(0x70);
I2C_WRITE(0xFE);
I2C_STOP();
while(1);
}
Przykład 4.1.BAS
Config Sda = P2.0
Config Scl = P2.1
I2cstart
I2cwbyte &H70
I2cwbyte &HFE
I2cstop
Do
Loop
End
Efektem działania tego programu jest włączenie diody LED
D1. Trochę uwagi należy poświęcić programowi w asemblerze i języku C.
W asemblerze dołączenie wspomnianego wcześniej pliku z podstawowymi procedurami
nastąpiło przy użyciu dyrektywy "$INCLUDE". Dyrektywa ta powoduje
wstawianiu w jej miejsce przez kompilator asemblera zawartości pliku wskazanego
tą dyrektywą. Tak więc dwa fizyczne pliki zawierające kod ˇródłowy w czasie
kompilacji są łączone w jeden. Konsekwencją tego jest fakt, że cokolwiek
zadeklarujemy w pliku pierwszym będzie to obowiązywało również w pliku
drugim. Dlatego pomimo pozornego niewykorzystywania bitów SDA i SCL przez
program w pliku P04_01_ASM_SRC.A51 umieszczona tam deklaracja jest konieczna
do poprawnego działania procedur zawartych w pliku I2C.ASM.
Trochę inaczej ta sprawa przedstawia się w języku C. Plik I2C.C został
dodany do projektu za pomocą polecenia "Add node Source / Application"
z menu RIDE IDE. Pliku ˇródłowym naszego programu umieszczamy dyrektywę
"#include "I2C.H"".Jest to dyrektywa preprocesora
włączająca plik nagłówkowy do pliku ˇródłowego. ˇródłowego pliku nagłówkowym
umieszczone są prototypy procedur zawartych w pliku I2C.C.
W Bascomie sprawa przedstawia się zupełnie prosto, ponieważ obsługa magistrali
I2C jest wbudowana w kompilator i nie ma potrzeby zawracania sobie głowy
dołączaniem jakichkolwiek dodatkowych plików a przede wszystkim samodzielnego
oprogramowywania transmisji. W tym momencie przewaga Bascoma nad asemblerem
i C jest najbardziej widoczna. Aby korzystać z magistrali I2C nie potrzebujemy
znać dokładnie protokołu transmisji, wystarczy tylko podłączyć do procesora
odpowiednio linie SDA i SCL i możemy w prosty sposób sterować układami
przez I2C.
Przykład 4.2.ASM
NAME P04_02_ASM_SRC
$INCLUDE (ATMEL/REG8252.INC)
P04_02_ASM SEGMENT CODE
RSEG P04_02_ASM
SDA EQU P2.0
SCL EQU P2.1
ACK EQU 00h
START:
ACALL I2C_START
MOV A, #72h
ACALL I2C_WRITE
MOV A, #0FFh
ACALL I2C_WRITE
ACALL I2C_STOP
ACALL I2C_START
MOV A,#73h
ACALL I2C_WRITE
MOV A,#0
ACALL I2C_READ
ACALL I2C_STOP
MOV R4, A
ACALL I2C_START
MOV A,#70h
ACALL I2C_WRITE
MOV A,R4
ACALL I2C_WRITE
ACALL I2C_STOP
SJMP START
$INCLUDE (I2C.ASM)
END
Przykład 4.2.C
#include <ATMEL/REG8252.H>
#include "I2C.H"
void main(void)
{
unsigned char temp;
while(1)
{
I2C_START();
I2C_WRITE(0x72);
I2C_WRITE(0xFF);
I2C_STOP();
I2C_START();
I2C_WRITE(0x73);
temp = I2C_READ(1);
I2C_STOP();
I2C_START();
I2C_WRITE(0x70);
I2C_WRITE(temp);
I2C_STOP();
}
}
Przykład 4.2.BAS
Config Sda = P2.0
Config Scl = P2.1
Dim Temp As Byte
Do
I2cstart
I2cwbyte &H72
I2cwbyte &HFF
I2cstop
I2cstart
I2cwbyte &H73
I2crbyte Temp , 9
I2cstop
I2cstart
I2cwbyte &H70
I2cwbyte Temp
I2cstop
Loop
End
Powyższy program odczytuje stan klawiatury i zapala diodę
LED o numerze odpowiadającym naciśniętemu przyciskowi klawiatury, czyli
odczytuje stan wejść pierwszego układu PCF8574 i przesyła go na wyjścia
drugiego układu PCF8574.
|