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
#define _PORTA_RA0_POSITION 0x00000000
#define _PORTA_RA1_POSITION 0x00000001
#define _PORTA_RA2_POSITION 0x00000002
.......itd
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.RA1
= 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.
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 = PORTAbits.RA1 ; // zapisz tylko wartość pinu RA1 do zmiennej val
Odczyt
wartości za pomocą rejestru LATx :
int val ;
val
= LATAbits.LATA1
; // zapisz
tylko wartość wybranego
bitu z rejestru do zmiennej val
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
Pozdrawiam
picmajster.blog@gmail.com
Brak komentarzy:
Prześlij komentarz