Entrada e Saída – Manipulando Registradores
Para determinar os estados de suas entradas e saídas o microcontrolador possui registradores na qual esses dados são armazenados. Ao chamar as funções de entrada e saída fornecidas pela biblioteca padrão do Arduino o que fazemos é nada mais que modificar tais registradores. Então porque acessar estes registradores diretamente?
A manipulação direta de registradores permite que:
- A leitura e escrita em pinos seja feita muito mais rápida.
- Ler e escrever em mais de um pino de uma mesma porta por vez.
- O código produzido é menor, em alguns casos esse fator pode fazer a diferença entre seu código caber na memória flash ou não.
Essa técnica também possuí desvantagens:
- O código produzido é menos portável, difícil de debbugar e de ser mantido.
Na abordagem dos conceitos a seguir o microcontrolador atmega328 presente na placa Arduino Uno será utilizado. Para acompanhar esse tutorial é necessário algum conhecimento de lógica digital.
Todos os registradores desse microcontrolador possuem 8 bits. Os pinos de entrada e saída são divididos em PORTs. O atmega328 possui 3 PORTs, como podemos observar no diagrama a seguir:
Cada PORT possui 3 registradores com diferentes funções:
DDR
Os registradores do tipo DDR (Data Direction Register) são responsáveis por determinar se os pinos de um determinado PORT se comportarão como entrada ou saída. Cada bit do registrador DDR controla o estado do respectivo pino. Por exemplo: O bit 1 do registrador DDRB (DDB1) controlará o estado do pino PB1 e consequentemente o pino D9 do Arduino Uno como mostrado no mapa.
Para definir um pino como saída devemos setar seu respectivo bit do registrador DDR como 1 e para defini-lo como entrada seta-lo para 0.
/* Equivalente: pinMode(9,OUTPUT); pinMode(9,INPUT); */ DDRB |= (1 << DDB1); DDRB &= ~(1 << DDB1);
PORT
Os registradores do tipo PORT são responsáveis por determinar se um pino está definido como alto (HIGH) ou baixo (LOW).
Para definir um pino como alto devemos setar seu respectivo bit do registrador PORT como 1 e para defini-lo como baixo seta-lo para 0.
/* Equivalente: pinMode(9,OUTPUT); digitalWrite(9,LOW); */ DDRB |= (1 << DDB1); PORTB &= ~(1 << PORTB1);
Outro exemplo:
/* Equivalente: digitalWrite(8,HIGH); digitalWrite(9,HIGH); digitalWrite(10,HIGH); digitalWrite(11,HIGH); digitalWrite(12,HIGH); digitalWrite(13,HIGH); */ PORTB = 0xFF;
PIN
Os registradores do tipo PIN são responsáveis por guardar o estado lógico de um pino.
/* Equivalente: pinMode(9,INPUT); digitalWrite(9,HIGH); //Nesse contexto, ativa o pull-up interno. bool x = digitalRead(9); */ DDRB &= ~(1 << DDB1); PORTB |= (1 << PORTB1); bool x = (PINB & (1 << PINB1));Todo o conteúdo apresentado pode ser encontrado no datasheet do microcontrolador.
Olá. O meu ficou assim:
#define pin2 (PIND >> 2) & 1 // Retorna a leitura digital do pino 2
void setup() {
// put your setup code here, to run once:
DDRD &= ~(1 << 2); // Seta o bit 2 do registrador DDRD para 0 (define o pino DDD2 como entrada)
DDRD |= (1 << 3); // Seta o bit 3 do registrador DDRD para 1 (define o pino DDD3 como saída)
}
void loop() {
// put your main code here, to run repeatedly:
if (!pin2){ // Se pin2 estiver em nível baixo:
PORTD |= (1 << 3); // O pino 3 do arduino vai a nível alto
} else { // Caso contrário:
PORTD &= ~(1 << 3); // O pino 3 do arduino vai a nível baixo
}
}
Olá. Obrigado pelo post. Saberia dizer se é possível usar o #define para leitura das portas?
Usando desta forma:
#define pin2 digitalRead(2)
…
if (!pin2){
do something..
}
funciona, mas ao usar dessa forma
#define pin2 (PIND & (1 << PIND2))
…
if (!pin2){
do something..
}
só funciona uma única vez..