A maioria dos Arduinos, principalmente os primeiros lançamentos usam um micro controlador da linha ATMega em suas placas. O que a Arduino fez foi deixar a placa com pinos de fácil acesso, criar um bootloader onde é possível carregar o firmware de forma fácil via USB-Serial e criar uma interface IDE com bibliotecas de fácil entendimento. Mas lá no fundo de todo processo o que está rodando são programações AVR. Na programação AVR fora do Arduino, o processo de carga de firmware é feito via
.
Vou mostrar aqui em três, quatro ou até mais postagens do básico de uma programação em AVR, será aquele norte para qualquer um que queira se aventurar e depois poder aprofundar sozinho, porém usando uma placa e IDE Arduino para ficar um pouco mais fácil.
Mas porque programar em AVR e não em Arduino? A programação em AVR é imensamente mais flexível e abrangente que Arduino, além de outras vantagens como velocidades de trabalho, só deixa a desejar mesmo por não ser muito didática.
Todas as postagens irão usar uma placa
Arduino UNO que utiliza micro controlador ATMega 328P e a própria
IDE Arduino.
Com a IDE Arduino instalada, primeiramente não usaremos as funções padrão void setup() e void loop().
Segundo detalhe que quando programamos em AVR fora da IDE Arduino, é sempre necessário definir as bibliotecas como essas abaixo que são mais usadas. Porém pela IDE Arduino não faz diferença entre usar ou não já que o processo está embutido na própria IDE, nos exemplos não usarei mas fica o alerta.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
Um guia de pinagem como este abaixo ajuda bastante, já que a pinagem do CI ATMega 328P é diferente do Arduino. Além disso o datasheet do CI é de extrema importancia.
Com tudo em mãos vamos primeiro acender o led embutido no proprio Arduino que trabalha junto ao pino 13 dele, o pino 13 conforme a figura dos pinos acima é o PB5. O AT 328P possui três blocos de portas PBx, PCx e PDx que são controlados por registradores de 8 bits. Na pagina 100 do datasheet temos a disposição dos bits de portas desses registradores conforme abaixo
O que interessa no primeiro momento são apenas os registrador DDRB, PORTB e seus bits que vão de PORTB0 até PORTB7 e DDB0 a DDB7. Detalhe, o compilador AVR entende tanto PORTBx ou de forma abreviada PBx, portanto podemos usar por exemplo PB5 no lugar de PORTB5.
Na IDE do Arduino cole o código abaixo e carregue ele no Arduino (não esqueça de deletar as linhas que estiverem lá):
int main(void) {
DDRB |= (1 << DDB5);
PORTB |= (1 << PB5);
}
O led irá acender. Para apagar use o código abaixo:
int main(void) {
DDRB |= (1 << DDB5);
PORTB &= ~(1 << PB5);
}
O que mudou foi apenas a terceira linha.
Entendendo o código, na segunda linha temos o registrador DDRB (Data Direction Register), ele controla a direção das portas B como entrada e saída, por padrão o micro controlador liga configurando as portas como entrada, então temos que configurar ou setar o bit correspondente com 1 para a porta passar a ser uma saída (com 0 será entrada).
Após DDRB temos "|= (1 << DDB5);" onde "
|=" é um operador
OR e "
(1 << DDB5)" está fazendo um
bit shift com 1. O DDB5 indica o sexto bit do registrador e pode também ser usado apenas o numeral
5, ou em binário
0b00000101 ou em hex
0x05. Assim por exemplo:
PORTB &= ~(1 << 0b00000101);
Mas porque usar OR e bit shift? Imagina um código onde esse registrador DDRB já tenha algumas portas setadas com 1 (entrada), se usar por exemplo "DDRB
= (1 << PB5);", ou seja
atribuição no lugar de OR, ele vai setar as outras portas como 0, já que foi definido apenas o sexto bit. Para um melhor entendimento visual do OR imaginando um terceiro bit sendo setado:
0 0 1 1 operando1
0 1 0 0 operando2
-------
0 1 1 1 resultado
Veja que o primeiro, segundo e ultimo bit continuaram com o mesmo valor, apenas o terceiro foi alterado.
Se for usado atribuição "=" ficaria:
0 0 1 1 operando1
0 1 0 0 operando2
-------
0 1 0 0 resultado
Ou seja as duas primeiras portas voltariam a ser 0.
Já o bit shift ajuda na visualização da programação, fica fácil saber que é o DDB5 ou o sexto bit que está sendo setado.
A linha "
PORTB |= (1 << PB5);" é a que realmente seta (ou liga) a porta, neste caso recebendo 1 liga e 0 desliga, mesmo entendimento do DDRB sendo setado. Para o segundo código foi usado "
PORTB &= ~(0 << PB5);" para desligar a porta, se fosse usado o OR a porta não iria ser setada com 0, já que em OR 1 e 0 é igual a 1, então é usado
AND "|=" porém com inversão dos bits usando
NOT "~". Invertendo os bits, os outros 7 bits do registrador PORTB não sofre alterações, todos os outros bits ficarão como 1 e apenas o que precisa ser setado ficará como 0 na operação, se já tiver algum outro setado ele permanecerá em 1. Para um melhor entendimento visual do AND imaginando um terceiro bit sendo setado:
0 1 1 1 operando1
1 0 1 1 operando2
-------
0 0 1 1 resultado
Veja que o primeiro, segundo e ultimo bit continuaram com o mesmo valor, apenas o terceiro foi alterado.
Um detalhe, se tentar rodar o código como abaixo após o primeiro código de exemplo acima que liga a porta, colocando 0 no bit shift da terceira linha, o led não irá acender, mas não por causa do código e sim porque o micro controlador sempre liga com os registradores em valor 0. Este exemplo é apenas porque muita gente que está começando sempre erra tentando usar atribuição "=" ou até OR com 0. Isso causa a falsa impressão que o código funcionou.
int main(void) {
DDRB |= (1 << DDB5);
PORTB = (0 << PB5);
}
O entendimento dos registradores acima será o mesmo para outros registradores nas próximas postagens, importante entender muito bem esse assunto.
Abaixo outro código acendendo e apagando o led a cada segundo, neste caso foi apenas adicionado um loop infinto while(1) e a função _delay_ms() que para o processamento durante 1seg a cada mudança de estado da porta. Futuramente na postagem sobre Timers veremos como fazer o mesmo sem parar o processamento.
int main(void) {
DDRB |= (1 << DDB5);
while (1) {
PORTB |= (1 << PB5);
_delay_ms(1000);
PORTB &= ~(1 << PB5);
_delay_ms(1000);
}
}
Por fim vamos ver um exemplo usando a porta Arduino 2 (PD2) como entrada. Para esse teste será preciso um hardware adicional além do Arduino Uno, com um resistor com valor próximo de 1kohm e uma chave push-button conforme abaixo:
Carregue o código e ao pressionar o botão o led irá acender:
int main(void) {
DDRB |= (1 << DDB5);
DDRD |= (0 << DDD2);
PORTB |= (0 << PB5);
while (1) {
while (PIND & (1 << PD2)) {
PORTB |= (1 << PB5);
}
PORTB &= ~(1 << PB5);
}
}
Neste código temos um loop dentro do loop infinito sendo controlado pelo valor que o registrador PIND e sua porta PD2 irá receber, caso seja 1 (botão pressionado) ele entra no loop e acende o led, caso 0 ele fica fora do loop e o led apaga. De novo o entendimento do registrador PIND é o mesmo do explicado acima e usando AND apenas para "pegar", ler ou testar o valor lido apenas do bit PB5. Neste caso não se usa NOT. Para um melhor entendimento visual deste AND imaginando o terceiro bit sendo testado:
0 1 0 1 operando1
0 1 0 0 operando2
-------
0 1 0 0 resultado
Veja que apesar do primeiro bit também estar em 1, com um código construído da forma acima você consegue testar apenas um bit especifico.
Referência:
Códigos de exemplo do livro AVR Programming:
Comentários
Postar um comentário