wtorek, 24 stycznia 2017

Porty I/O



Najważniejsze rejestry służące do konfiguracji portów wejścia/wyjścia w mikrokontrolerach PIC to:

  • TRISx - ustawia port jako wejście lub wyjście
  • PORTx - odczyt/zapis faktycznej wartości portu
  • LATx - rejestr zatrzaskowy portu (zapis ostatniej wartości portu lub odczyt zapisanej wartości)
  • ODCx - ustawia piny wyjściowe jako zwykłe piny lub z otwartym drenem
  • AD1PCFGLx - umożliwia ustawienie niektórych pinów wejścia jako analogowe lub cyfrowe (piny oznaczone ANx domyślnie są analogowe) . Piny do których trzeba zastosować ustawienia tego rejestru oznaczone są jako ANx

Konfiguracja portów jako wejścia lub wyjścia
Każdy port (port to zbiór pojedynczych pinów), posiada swój rejestr TRISx, który odpowiada za ustawienie czy określone piny są wejściem czy wyjściem. Jeżeli do bitu powiązanego z określonym pinem jest zapisane "1" pin jest wejściem, natomiast jeżeli zapiszemy "0", port staje się wyjściem.
Port TRISx może być również oczywiście odczytany, dostaniemy wtedy ostatnio zapisaną tam wartość.
Po włączeniu zasilania, wszystkie piny są skonfigurowane jako wejścia.

Zapis/odczyt wartości portu
Jeśli chodzi o zapis i odczyt portów wejścia/wyjścia mamy do dyspozycji dwa rejestry PORTx oraz LATx. Odczytując rejestr PORTx, odczytujemy bezpośrednio wartość logiczną panującą na pinach portu, zapisując do tego rejestru zmieniamy bezpośrednio wartości pinów portu.
Zapis do rejestru PORTx powoduje również zapis do odpowiadającego mu rejestru LATx.

Rejestr LATx, można odczytywać jakby był to zwykły rejestr, w którym jest duplikat ostatnio zapisanej wartości do portu, zapis do LATx powoduje zmianę stanów na pinach portu, tak samo jak zapis do rejestru PORTx, ale odczyt z tego rejestru jest odporny na zakłócenia na liniach portu, bo nie jest fizycznie podczas tej operacji czytany port, tylko jego rejestr, to znaczy, że nie możemy odczytać napięcia panującego faktycznie na pinie, jedynie wartość jaką ostatnio zapisaliśmy.

Podsumowując:
  • Zapis do portu możemy dokonywać przez zapis do PORTx lub LATx
  • Odczyt wartości z końcówek portu dokonujemy przez odczyt PORTx
  • Odczyt ostatnio zapisanej wartości do portu, dokonujemy przez odczyt rejestru LATx
  • jeśli chcemy uzyskać podwyższoną odporność na zakłócenia zapis i odczyt realizujmy za pomocą rejestru LATx .

Konfiguracja portów jako cyfrowe lub analogowe
Wszystkie porty które mogą być wejściami analogowymi domyślnie nimi są, ponieważ rejestr AD1PCFGLx, który odpowiada za to ustawienie domyślnie ma zapisaną wartość 0x0000. Jeżeli chcemy używać portów analogowych z cyfrowymi peryferiami, takimi jak Timery czy UART, musimy ustawić je jako cyfrowe przez ustawienie określonych bitów rejestru AD1PCFGLx, dodatkowo kiedy porty są ustawione jako analogowe wejścia, próba odczytania ich wartości przez rejestr PORTx skończy się pobraniem samych zer, tak jakby na pinach panował logiczny stan 0.

W przypadku mikrokontrolera PIC24HJ128GP502 piny analogowe przyporzadkowane są do AN0-AN5 i AN9-AN12. Po uruchomienu procka piny te są ustawione jako analogowe wejścia. Aby to zmienić należy wpisać wartość hex 0x1E3F (operujemy na 16 bitach) do rejestru i wtedy mamy wszystkie piny jako cyfrowe.

Do rejestrów możemy zapisywać całe wartości portów, lub odwoływać się do indywidualnych pinów. Do dyspozycji mamy wygodne makra i struktury zdefiniowane w pliku nagłówkowym naszego mikrokontrolera . Plik znajduje się w katalogu kompilatora XC16. W przypadku Linuxa jest dostępny pod ścieżką : /opt/microchip/xc16/v1.30/support/PIC24H/h

Poniżej fragment pliku z makrami i strukturami do sterowania wyjściami i wejściami. :

Dyrektywy dla portów : zainkludować plik p24HJ128GP502.h z xc16/v1.30/support/PIC24H/h

/* TRISA */
#define _TRISA0 TRISAbits.TRISA0
#define _TRISA1 TRISAbits.TRISA1
#define _TRISA2 TRISAbits.TRISA2
#define _TRISA3 TRISAbits.TRISA3
#define _TRISA4 TRISAbits.TRISA4

/* PORTA */
#define _RA0 PORTAbits.RA0
#define _RA1 PORTAbits.RA1
#define _RA2 PORTAbits.RA2
#define _RA3 PORTAbits.RA3
#define _RA4 PORTAbits.RA4

/* LATA */
#define _LATA0 LATAbits.LATA0
#define _LATA1 LATAbits.LATA1
#define _LATA2 LATAbits.LATA2
#define _LATA3 LATAbits.LATA3
#define _LATA4 LATAbits.LATA4


/* TRISB */
#define _TRISB0 TRISBbits.TRISB0
#define _TRISB1 TRISBbits.TRISB1
#define _TRISB2 TRISBbits.TRISB2
#define _TRISB3 TRISBbits.TRISB3
#define _TRISB4 TRISBbits.TRISB4

#define _TRISB5 TRISBbits.TRISB5
#define _TRISB6 TRISBbits.TRISB6
#define _TRISB7 TRISBbits.TRISB7
#define _TRISB8 TRISBbits.TRISB8
#define _TRISB9 TRISBbits.TRISB9
#define _TRISB10 TRISBbits.TRISB10
#define _TRISB11 TRISBbits.TRISB11
#define _TRISB12 TRISBbits.TRISB12
#define _TRISB13 TRISBbits.TRISB13
#define _TRISB14 TRISBbits.TRISB14
#define _TRISB15 TRISBbits.TRISB15

/* PORTB */
#define _RB0 PORTBbits.RB0
#define _RB1 PORTBbits.RB1
#define _RB2 PORTBbits.RB2
#define _RB3 PORTBbits.RB3
#define _RB4 PORTBbits.RB4
#define _RB5 PORTBbits.RB5
#define _RB6 PORTBbits.RB6
#define _RB7 PORTBbits.RB7
#define _RB8 PORTBbits.RB8
#define _RB9 PORTBbits.RB9
#define _RB10 PORTBbits.RB10
#define _RB11 PORTBbits.RB11
#define _RB12 PORTBbits.RB12
#define _RB13 PORTBbits.RB13
#define _RB14 PORTBbits.RB14
#define _RB15 PORTBbits.RB15

/* LATB */
#define _LATB0 LATBbits.LATB0
#define _LATB1 LATBbits.LATB1
#define _LATB2 LATBbits.LATB2
#define _LATB3 LATBbits.LATB3
#define _LATB4 LATBbits.LATB4
#define _LATB5 LATBbits.LATB5
#define _LATB6 LATBbits.LATB6
#define _LATB7 LATBbits.LATB7
#define _LATB8 LATBbits.LATB8
#define _LATB9 LATBbits.LATB9
#define _LATB10 LATBbits.LATB10
#define _LATB11 LATBbits.LATB11
#define _LATB12 LATBbits.LATB12
#define _LATB13 LATBbits.LATB13
#define _LATB14 LATBbits.LATB14
#define _LATB15 LATBbits.LATB15

/* ODCB */

#define _ODCB5 ODCBbits.ODCB5
#define _ODCB6 ODCBbits.ODCB6
#define _ODCB7 ODCBbits.ODCB7
#define _ODCB8 ODCBbits.ODCB8
#define _ODCB9 ODCBbits.ODCB9
#define _ODCB10 ODCBbits.ODCB10
#define _ODCB11 ODCBbits.ODCB11

#define TRISA TRISA
extern volatile unsigned int  TRISA __attribute__((__sfr__));
typedef struct tagTRISABITS {
  unsigned TRISA0:1;
  unsigned TRISA1:1;
  unsigned TRISA2:1;
  unsigned TRISA3:1;
  unsigned TRISA4:1;
} TRISABITS;
extern volatile TRISABITS TRISAbits __attribute__((__sfr__));

#define PORTA PORTA
extern volatile unsigned int  PORTA __attribute__((__sfr__));
typedef struct tagPORTABITS {
  unsigned RA0:1;
  unsigned RA1:1;
  unsigned RA2:1;
  unsigned RA3:1;
  unsigned RA4:1;
} PORTABITS;
extern volatile PORTABITS PORTAbits __attribute__((__sfr__));

#define LATA LATA
extern volatile unsigned int  LATA __attribute__((__sfr__));
typedef struct tagLATABITS {
  unsigned LATA0:1;
  unsigned LATA1:1;
  unsigned LATA2:1;
  unsigned LATA3:1;
  unsigned LATA4:1;
} LATABITS;
extern volatile LATABITS LATAbits __attribute__((__sfr__));


#define TRISB TRISB
extern volatile unsigned int  TRISB __attribute__((__sfr__));
typedef struct tagTRISBBITS {
  unsigned TRISB0:1;
  unsigned TRISB1:1;
  unsigned TRISB2:1;
  unsigned TRISB3:1;
  unsigned TRISB4:1;
  unsigned TRISB5:1;
  unsigned TRISB6:1;
  unsigned TRISB7:1;
  unsigned TRISB8:1;
  unsigned TRISB9:1;
  unsigned TRISB10:1;
  unsigned TRISB11:1;
  unsigned TRISB12:1;
  unsigned TRISB13:1;
  unsigned TRISB14:1;
  unsigned TRISB15:1;
} TRISBBITS;
extern volatile TRISBBITS TRISBbits __attribute__((__sfr__));

#define PORTB PORTB
extern volatile unsigned int  PORTB __attribute__((__sfr__));
typedef struct tagPORTBBITS {
  unsigned RB0:1;
  unsigned RB1:1;
  unsigned RB2:1;
  unsigned RB3:1;
  unsigned RB4:1;
  unsigned RB5:1;
  unsigned RB6:1;
  unsigned RB7:1;
  unsigned RB8:1;
  unsigned RB9:1;
  unsigned RB10:1;
  unsigned RB11:1;
  unsigned RB12:1;
  unsigned RB13:1;
  unsigned RB14:1;
  unsigned RB15:1;
} PORTBBITS;
extern volatile PORTBBITS PORTBbits __attribute__((__sfr__));

#define LATB LATB
extern volatile unsigned int  LATB __attribute__((__sfr__));
typedef struct tagLATBBITS {
  unsigned LATB0:1;
  unsigned LATB1:1;
  unsigned LATB2:1;
  unsigned LATB3:1;
  unsigned LATB4:1;
  unsigned LATB5:1;
  unsigned LATB6:1;
  unsigned LATB7:1;
  unsigned LATB8:1;
  unsigned LATB9:1;
  unsigned LATB10:1;
  unsigned LATB11:1;
  unsigned LATB12:1;
  unsigned LATB13:1;
  unsigned LATB14:1;
  unsigned LATB15:1;
} LATBBITS;
extern volatile LATBBITS LATBbits __attribute__((__sfr__));

#define ODCB ODCB
extern volatile unsigned int ODCB __attribute__((__sfr__));
typedef struct tagODCBBITS {
unsigned ODCB5:1;
unsigned ODCB6:1;
unsigned ODCB7:1;
unsigned ODCB8:1;
unsigned ODCB9:1;
unsigned ODCB10:1;
unsigned ODCB11:1;
} ODCBBITS;
extern volatile ODCBBITS ODCBbits __attribute__((__sfr__));

#define _PORTA_RA0_POSITION                      0x00000000
#define _PORTA_RA1_POSITION                      0x00000001
#define _PORTA_RA2_POSITION                      0x00000002
.......itd

Z powyższych makr wynika , że mamy do dyspozycji 5 x I /O (RA0 – RA4) na porcie A i 16 x I/O (RB0-RB15) na porcie B.

Poniżej układ bitów w rejestrach sterujących I/O.



Jeśli chcemy korzystać z wyjść jako cyfrowe to musimy wyłaczyć ADC w rejestrze AD1PCFGL na tych wyjściach gdzie są (nie wszystkie), patrz oznaczenia na nóżkach ANx 

 
Przykłady :

Ustawiamy kierunek czyli wejście lub wyjście za pomocą rejestru TRISx :

TRISA = 0x1F ;// ustawiamy 5 pierwszych bitów rejestru na 1 czyli wszystkie piny portu A są wejściami
TRISA = 0 ;// ustawiamy bity rejestru na 0 czyli wszystkie piny portu A są wyjściami

za pomocą zdefiniowanych struktur :

TRISAbits.TRISA1 = 1 ; // ustaw drugi bit rejestru na 1 czyli na tym pinie mamy wejście
TRISAbits.TRISA1 = 0 ; // ustaw drugi bit rejestru na 0 czyli na tym pinie mamy wyjście.


Ustawiamy stan na wyjściach za pomocą rejestru PORTx :


PORTA = 0x1F ; // ustawiamy 5 pierwszych bitów rejestru na 1 czyli na wszystkich pinach portu A mamy stan wysoki
PORTA = 0 ; // ustawiamy bity rejestru na 0 czyli na wszystkich pinach portu A mamy stan niski

za pomocą zdefiniowanych struktur :

PORTAbits.RA1 = 1 ; // ustaw drugi bit rejestru na 1 czyli na pinie RA1 mamy stan wysoki
PORTAbits.RA2 = 0 ; // ustaw drugi bit rejestru na 0 czyli na pinie RA1 mamy stan niski.

Możemy również zastosować zapis analogicznie jak w AVR-ach :

PORTA | = (1<<_PORTA_RA1_POSITION) ; // ustaw drugi bit rejestru na 1 czyli na pinie RA1 mamy stan wysoki
PORTA & = ~ (1<<_PORTA_RA1_POSITION) ; // ustaw drugi bit rejestru na 0 czyli na pinie RA1 mamy stan niski.


Ustawiamy stan na wyjściach za pomocą rejestru LATx :


Wartość jaką wpiszemy w rejestr zatrzaskowy LATx jest przepisywana automatycznie do rejestru PORTx.

LATA = 0x1F ; // ustawiamy 5 pierwszych bitów rejestru na 1 czyli na wszystkich pinach portu A mamy stan wysoki
LATA = 0 ; // ustawiamy bity rejestru na 0 czyli na wszystkich pinach portu A mamy stan niski

za pomocą zdefiniowanych struktur :

LATAbits.LATA1 = 1 ; // ustaw drugi bit rejestru na 1 czyli na pinie RA1 mamy stan wysoki
LATAbits.LATA1 = 0 ; // ustaw drugi bit rejestru na 0 czyli na pinie RA1 mamy stan niski.


Odczyt wartości za pomocą rejestru PORTx :

int val ;
val = PORTA ; // zapisz wszystkie wartości pinów portu A do zmiennej val
val = PORTAbits.RA1 ; // zapisz tylko wartość pinu RA1 do zmiennej val


Odczyt wartości za pomocą rejestru LATx :

int val ;
val = LATA ; // zapisz wszystkie wartości rejestru do zmiennej val
val = LATAbits.LATA1 ; // zapisz tylko wartość wybranego bitu z rejestru do zmiennej val


Reasumując dostęp do rejestrów I/O i operowanie na nich w PIC jest stosunkowo sympatyczne.
Przyczynia się do tego ładne opisanie rejestrów dyrektywami i strukturami. 
Prezentowana w artykule specyfika rejestrów jest typowa dla PIC.

Link :
http://ww1.microchip.com/downloads/en/DeviceDoc/70230C.pdf

Brak komentarzy:

Prześlij komentarz