• UFRJ Nautilus

Programando Microcontroladores em C

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!


Esquema de montagem 1. UFRJ Nautilus
Esquema de montagem 1.

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);


Torna