Wiki ADA

Projetos em Engenharia de Computação

Ferramentas do usuário

Ferramentas do site


programacao-atmega328

Programação ATmega328

Nesta página são apresentados os conceitos básicos para programação de ATmega328 e ATmega328p utilizando programação em C e o compilador AVR-GCC. A programação é feita tipicamente em um Sistema Operacional Linux.

Informações sobre a compilação e gravação de códigos aqui mostrados nos microcontroladores podem ser encontradas em Gravação ATmega328.


Portas Digitais

Uma das coisas mais essenciais na programação de um microcontrolador é a entrada e a saída de sinais por seus pinos. No ATmega328 e ATmega328p, temos três portas programáveis:

  • Porta B, com 8 pinos;
  • Porta C, com 7 pinos;
  • Porta D, com 8 pinos.

Contudo, não conseguimos trabalhar com pinos separadamente durante a programação, ficando restritos à configuração da porta inteira. Então precisamos fazer operações bit a bit que envolvam bytes representantes das portas e não bits representantes dos pinos (no que é conhecido como Máscara de bits).

Para realizar as ações descritas abaixo, é necessário importar no código a biblioteca de entrada e saída do microcontrolador, ou seja, é necessário incluir no início do código:

#include <avr/io.h>

Direção do Pino

Antes de usar um pino, é necessário definir se o mesmo é um pino de entrada ou de saída (se vai ler o sinal elétrico no pino ou se vai escrever um sinal elétrico no mesmo). Esta definição é feita através da configuração dos registradores DDR (Data Direction Register), sendo que há um registrador DDR para cada uma das portas: DDRB, DDRC e DDRD.

Um bit configurado como '0' significa que o pino correspondente será de entrada, e caso seja configurado como '1', será saída. Exemplo: caso queiramos que o pino B4 seja configurado como entrada, colocaremos '0' no bit 4 de DDRB; caso queiramos que seja saída, colocaremos '1' no bit 4 de DDRB.

Desta forma, se formos configurar o pino C3 como entrada e o pino D7 como saída, usaremos os comandos

DDRC &= ~(1<<DDC3);
DDRD |=  (1<<DDD7);

Onde DDC3 e DDD7 são macros definidas pela biblioteca AVR e representam, respectivamente, 3 e 7. De forma semelhante, há DDB1, DDB5, DDC0 e DDD4. Os números puros poderiam ser usados no lugar das macros, mas elas são usadas para fins de legibilidade de código, pois explicitam qual pino está sendo configurado.

Informações sobre os operadores bit a bit e sobre as máscaras de bits podem ser encontradas aqui.

Entrada Digital

Depois que a direção de um pino foi configurada como entrada, o valor presente nele pode ser lido. Assim como na definição da direção da porta, as operações de leitura também são feitas usando bytes, então precisaremos usar a máscara de bits na posição desejada para ler apenas o valor de interesse.

Para ler o conteúdo de uma porta, usaremos os registradores PIN, sendo que há um registrador desse para cada porta: PINB, PINC e PIND. Além disso, usaremos uma variável do tipo unsigned char para receber o dado de uma porta, pois é a representação que temos de um byte “puro” na linguagem C.

Desta forma, se formos ler o conteúdo presente no pino C3, usaremos os comandos

DDRC &= ~(1<<DDC3);
unsigned char valor;
valorLido = (PINC & (1<<PINC3));

Onde PINC3 é uma macro de valor 3 semelhante às macros de DDR.

Uma coisa que vale notar é que, seguindo o código anterior, se houver sinal baixo na entrada C3, valorLido == 0. Contudo, se houver sinal alto na entrada C3, valorLido == 0b00001000 == 8. Isso não será problema caso valorLido seja usado como variável condicional, pois o C interpreta variável igual a 0 como Falso e variável diferente de 0 como Verdadeiro. Contudo, é preciso cuidado na hora de fazer comparações entre valores lidos de pinos ou caso deseja-se (por algum motivo obscuro) fazer operações matemáticas usando o valor.

Saída Digital

Depois que a direção de um pino foi configurada como saída, pode-se definir qual valor ele terá. Assim como na definição da direção da porta, as operações de escrita também são feitas usando bytes, então precisaremos usar a máscara de bits na posição desejada para escrever apenas no valor de interesse.

Para escrever em uma porta, usaremos os registradores PORT, sendo que há um registrador desse para cada porta: PORTB, PORTC e PORTD. Assim como na definição de direção do pino e na leitura do mesmo, há macros usadas para o número do pino a ser escrito, elas são como PB2, PC5 e PD0.

Desta forma, se quisermos escrever '1' no pino B0 e escrever '0' no pino D7, usaremos os comandos

DDRB |=  (1<<DDB0);
DDRD |=  (1<<DDD7);
PORTB |=  (1<<PB0);
PORTD &= ~(1<<PD7);

Facilitando a Vida

Muitas vezes é chato ficar fazendo as máscaras de bits na mão, então há maneiras de simplificar a programação.

_BV

A primeira delas é a macro de sistema _BV, acrônimo de “Bit Value”. Por exemplo, _BV(6) é igual a (1«6). Desta forma, se quisermos configurar o pino D0 como saída, podemos escrever:

DDRD |= (1<<DDD0);
//ou
DDRD |= _BV(DDD0);

E se quisermos configurar o pino B3 como entrada, podemos escrever:

DDRB &= ~(1<<DDB3);
//ou
DDRD &= ~_BV(DDB3);

Macros extras

Além da macro _BV, podemos definir nossas próprias macros. Algumas macros úteis que podem ser definidas no código são: 1)

#define setBit(valor,bit) (valor |= (1<<bit))   // Define como '1' um bit em um byte
#define clearBit(valor,bit) (valor &= ~(1<<bit))   // Define como '0' um bit em um byte
#define toogleBit(valor,bit) (valor ^= (1<<bit))   // Inverte o valor de um bit
#define testBit(valor,bit)    (valor & (1<<bit))  // Testa se um bit é '0' ou '1'

Referências

programacao-atmega328.txt · Última modificação: 2019/01/21 19:57 por amador