Microcontroladores (MCUs) são pequenos computadores programáveis que podem
interagir com o mundo físico. São usados em todos os tipos de sistemas embarcados, de
foguetes a eletrodomésticos e dão infinitas possibilidades para nossos projetos.
Você deve estar familiarizado com a plataforma Arduíno, que nos poupa de todo o
trabalho sujo da programação em baixo nível e permite prototipar rapidamente. Mas por
que abandonar a simplicidade do Arduíno e se dar ao trabalho de programar o MCU
diretamente?
Criar seu projeto diretamente no MCU, sem a abstração proporcionada pelo arduíno permite usar totalmente a sua capacidade e rodar seu código muito mais eficientemente.
Se você pretende ter o seu projeto disponível comercialmente, esqueça, a licença do
ecossistema arduíno te obriga a tornar seu projeto open source. E por fim, programar seu
microcontrolador nativamente é uma grande oportunidade para entender a fundo seu
funcionamento e arquitetura, além de aprender a trabalhar com base em suas limitações.
Agora vamos conhecer o microcontrolador que irá nos acompanhar neste artigo!
Microcontroladores AVR
Neste artigo vamos trabalhar com microcontroladores 8 bits da família AVR (os
mesmos dos arduínos!). Se você já teve experiência com arduíno já sabe das
possibilidades que eles nos dão, mas não custa lembrar. Aqui usaremos o ATTiny85, mas tudo é análogo para outros MCUs da mesma família, qualquer dúvida consulte o datasheet.
Arquitetura de MCUs
Os microcontroladores AVR de 8 bits são baseados na arquitetura RISC, mas não
precisamos dar muito a importância a isso, vamos focar nas partes mais importantes,
comuns a todos os microcontroladores.
CPU: Responsável pelas operações lógicas e matemáticas.
Memória: Temos dois tipos:
ROM(Flash): Memória permanente, onde nosso código ficará guardado.
RAM: Memória temporária, armazena valores necessários a execução do código como variáveis e resultados.
Periféricos: São módulos independentes dentro do MCU que desempenham funções específicas, os mais úteis são:
I/O: Através da portas de entrada e saída podemos interagir com o mundo exterior usando sinais elétricos.
USART, I2C e SPI: São protocolos de comunicação serial, podemos comunicar MCU com um computador, sensores e até outro MCU.
ADC: O famoso “analogRead()” nos arduínos, os sensores normalmente nos dão dados analógicos (ou seja, uma grande gama de valores, e não o 0 ou 1 digital com o qual trabalhamos no MCU), e usando esse periférico nós podemos manipular os valores digitalmente.
ATTINY85
Agora vamos conhecer o nosso microcontrolador! O ATTINY85 tem 8kb de memória
Flash e 512 bytes de RAM, pode parecer pouco, mas o computador que levou o homem à
Lua tinha 4kb de RAM e 32 kb de ROM. Temos 6 entradas/saídas digitais, 4 entradas
analógicas (4 canais ADC) e até 2 saídas PWM. O ATTINY85 é conhecido pelo seu
consumo baixíssimo, podendo ser alimentado por uma bateria de relógio por anos.
Programando os AVRs
Precisaremos de hardware externo para programar nosso microcontrolador, um
programador ISP, usaremos o popular USBasp e a IDE Atmel Studio. Não vou me estender sobre a instalação e configuração do programador na nossa IDE, dê uma olhada neste tutorial. Se você tiver um arduino por aí também pode usá-lo como programador ISP!
Nosso primeiro projeto!
Vamos criar nosso projeto no Atmel Studio:
File->New->Project
Dê um nome ao projeto e selecione o device (Nosso MCU alvo) ATTiny85. Cole o
código abaixo, compile e envie ao ATTiny como demonstrado no link acima. É um código
simples para piscar um LED (Ritual de passagem na eletrônica). O LED é apenas uma
abstração, podemos usar os mesmos princípios para interagir com qualquer tipo de circuito
externo, como ponte H, relés e eletrônica “pura”.
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
//Define pino 4 como saída
DDRB |= (1 << DDB4);
while (1)
{
//Pino 4 HIGH
PORTB |= (1 << PB4);
//Delay(espera) de 1 segundo
_delay_ms(1000);
PORTB &= ~(1 << PB4);
//Pino 4 LOW
_delay_ms(1000);
}
}
Após enviar o código vamos montar o circuito.
Entendendo o código
Lógica Digital
Antes de mais nada vamos esclarecer alguns conceitos de lógica digital:
Bit: Representa um valor lógico. Verdadeiro ou falso, High ou Low, 1 ou 0, e no mundo físico: 5 Volts ou 0 Volts.
Byte: Conjunto de 8 bits(8 pode parecer um valor arbitrário, mas não se preocupe com isso).
Hardware Registers
São lugares especiais na memória, bytes, cujos valores afetam o hardware do MCU,
vamos entender melhor agora.
DDRx: Data Direction Register, indica se o pino é entrada ou saída. No código
usamos DDRB, o B indica que estamos trabalhando com os pinos da porta B. Os pinos nos
AVRs são divididos em portas, repare no pinout do Atmega 328P abaixo, temos PB, PC e
PD. No ATiny85 como existem poucos pinos temos apenas o PB.
Existem duas maneiras de alterar os valores nos registers:
1°: Essa é a forma mais intuitiva, associamos 0 (Input) ou 1 (Output) a cada bit,
lembrando que cada bit representa um pino a partir do pino 0, da direita para a esquerda.
DDRB = 0b00010000;
Apenas o pino 4 será um output, repare que é o bit na posição 4 (contando a partir de
0 da direita para a esquerda).
2°: Essa é a forma mais elegante.
DDRB |= (1 << DDB4);
Tornamos o bit na posição 4 um 1. Não podemos trocar o 1 por 0 e esperar que o bit
se torne 0, deveríamos fazer a seguinte operação para zerar o bit 4.
DDRB &= ~(1 << DDB4);
PORTx: Port Data Register, define se o pino está HIGH ou LOW.
Agora que sabemos o que fazem os hardware registers é fácil entender o código
acima. Primeiro definimos o pino 4 da porta B como saída, dentro do loop mudamos o
estado do pino com intervalos de 1s usando a função _delay_ms().
Tente adicionar outro LED, fazê-los piscar alternadamente, o céu é o limite.
Inputs!
Não podemos fazer muito apenas controlando o estado dos pinos não é mesmo ?
Agora vamos consultar o estado deles para receber informação do mundo exterior. Nesse
exemplo vamos controlar o nosso LED com um botão e fazê-lo piscar manualmente.
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
//Pino 4 como output
DDRB |= (1 << DDB4);
//Pino 0 com input
DDRB &= ~(1<<DDB0);
//Ativa o resistor Pull-up no pino 0
PORTB |= (1<<PB0);
//Pino 4 LOW
PORTB &= ~(1<<PB4);
while (1)
{
//Botão pressionado ? (0V no pino 0 ?)
if((PINB & (1 << PINB0)) == 0)
{
//LED ligado
PORTB|= (1<<PB4);
}
else
{
//LED desligado
PORTB &= ~(1<<PB4);
}
}
}
Temos um register novo no código acima, o PINx, responsável por guardar o estado
atual dos pinos.
Um pequeno adendo:
Você deve estar confuso com expressões como:
(PINB & (1 << PINB0))
PORTB &= ~(1<<PB4);
DDRB |= (1 << DDB4);
Essas são operações lógicas com os bits dos registers, explicá-las vai além do
escopo deste artigo. Mas aqui vai um guia para facilitar a sua vida:
Colocar 1 no bit x:
BYTE |= (1 << x);
Colocar 0 no bit x:
BYTE &= ~(1 << x);
Alternar o valor no bit x:
BYTE ^= (1 << i);
Conclusão
Passamos pelo básico da manipulação dos I/Os dos AVRs, só com isso já podemos
pensar em infinitas aplicações para esses MCUs. Muitas possibilidades não foram
exploradas aqui, conversão analógico digital, comunicação serial, interrupts, entre outras.
Escrito por Gabriel Guimarães.
コメント