Programando o Atmega328p com registradores

Eletrônica Arduino 17 de Novembro de 2019 às 13:36

O microcontrolador mais comum da família AVR 8 bits é o Atmega328p, sua popularidade foi difundida principalmente pela plataforma de desenvolvimento Arduino. Em um tutorial postado no Tec Dicas, apresentamos como montar um Arduino Standalone, que basicamente é a configuração elementar do Atmega328p com comunicação serial utilizando o módulo Ch340g ou FTDI. No mesmo tutorial você aprende alguns conceitos técnicos resumidos do microcontrolador, como a organização das memórias, tipos de comunicações oferecidas, e operação das instruções internas da arquitetura RISC. Agora para complementar os estudos, vamos analisar suas portas digitais de I/O e registradores.

Para este artigo será apresentado como as portas de I/O do Atmega328p funcionam internamente com seus registradores de 8 bits. O software Atmel Studio 7 permite o desenvolvimento de códigos em C/C++ acessando estes registradores, sem a necessidade de bootloader gravado, como o optiboot utilizado nas placas Arduino. Como uma das funções do bootloader é gerenciar a comunicação serial para enviar o firmware ao endereço específico da memória flash do microcontrolador, para isso será utilizado o programador em linha de comando AVRDude e o gravador USBasp, onde esta comunicação é chamada de ICSP ou ISP (In Circuit Serial Programming).

Análise das portas I/O

É normal em projetos com Arduino a utilização de um display LCD 16x2 ou gráfico de 128x64, onde sua ligação é realizada diretamente nas portas digitais do microcontrolador sem danificá-las, isso só é possível com a presença de um driver robusto feito com transistor em cada porta digital. Além disso, cada porta possui resistores de pull-ups configuráveis e diodos de proteção para o VCC e GND. Podemos observar esses componentes internos do microcontrolador na imagem abaixo.

Esquema driver e diodos de proteção de cada porta digital I/O.

Os registradores e referências de bits para cada pino de porta recebe o nome de Pxn, a letra "x" representa o nome da porta, como PB (PORTB) ou PD (PORTD), e a letra "n" representa o número do bit, como PB[0-7] (PORTB[0-7]) ou PB[0-7] (PORTB[0-7]), vale ressaltar que as portas analógicas são chamadas de PC (PORTC) que passam por um conversor A/D. Esses nomes estão indicados na pinagem do microcontrolador e são usados na programação.

Pinagem do Atmega328p - Datasheet Atmel

Para cada porta digital três endereços de memória I/O são alocados, sendo eles:

  1. PORTx (leitura/escrita): O registro de dados, responsável em determinar o estado do pino (HIGH/LOW).
  2. DDRx (leitura/escrita): A direção dos dados, responsável pela configuração de entrada ou saída do pino (OUTPUT/INPUT).
  3. PINx (leitura): A entrada da porta, responsável em armazenar o estado do pino, onde a execução de uma função de escrita no PINx, resultará na alteração no valor do PORTx.

E três bits de registros são setados, sendo eles:

  1. PORTxn: Se PORTxn for setado 1/verdadeiro e o pino estiver configurado como entrada, o resistor de pull-up interno será ativado. Para desabilitar o resistor pull-up, o PORTxn deve ser escrito como 0/falso ou o pino deve ser configurado como saída. Por padrão todas as portas são inicializadas como tri-state HI-Z.
  2. DDxn: O bit DDxn no endereço DDRx seleciona a direção desse pino. Se DDxn for escrito como 1/verdadeiro, Pxn será configurado como saída. Se DDxn for escrito como 0/falso, Pxn será configurado como entrada.
  3. PINxn: Os bits são acessados pelo endereço PINx.
Esquema de um pino de porta I/O - Pxn

Indo além de suas funções básicas, os pinos das portas podem apresentar outras funções alternativas, como o exemplo da Port B (PB), onde os pinos de XTAL, e da comunicação ICSP  e SPI estão localizados.

Tabela do pino da porta B (PB) com funções alternativas.

Com estas informações podemos utilizar o Atmel Studio 7 para programar um Atmega328p acessando seus registradores internos com C/C++, assimilando teoria e prática.

Instalando e configurando o Atmel Studio 7

O Atmel Studio 7 é um ambiente integrado de desenvolvimento e depuração para microcontroladores AVR e SAM de 8 bits e 32 bits. É um software compatível com ferramentas de gravação e depuração AVR e SAM, que auxiliam a criação de projetos nas linguagens C/C++ e assembly. Possuí a vantagem de importação de bibliotecas de terceiros, como também sketches da plataforma Arduino.

Para uma instalação bem sucedida, compare os requisitos de seu computador com os requisitos fornecidos pelo site oficial da Microchip.

Requisitos de sistema 86x/64x (Atmel Studio 7)

  • Windows 7 Service Pack 1 ou maior
  • Windows Server 2008 R2 Service Pack 1 ou maior
  • Windows 8/8.1
  • Windows Server 2012 e Windows Server 2012 R2
  • Windows 10

Requisitos de hardware

  • Processador de 1.6GHz ou melhor
  • RAM:
  • 1GB de RAM em sistemas x86
  • 2GB de RAM em sistemas x64
  • Em máquinas virtuais, adicionar mais 512MB de RAM
  • 6GB de espaço disponível em disco rígido.

Se todos os requisitos estão correspondentes ou melhores dos citados acima, acesse o site oficial do Atmel Studio para realizar o download da versão Windows (x86/x64) "Atmel Studio 7.0 (build 1931) web installer (recommended)" conforme a imagem abaixo.

Página oficial do Atmel Studio 7 - Download

Para a instalação, ative o Windows Update, selecione somente microcontroladores AVR de 8 bits e instale todos os drivers que o instalador solicitar. Aproximadamente em 30 minutos, o Atmel Studio estará instalado em seu computador com sucesso. Abrindo o software, você verá esta tela inicial.

Tela inicial do Atmel Studio 7 (2019).

Provavelmente sua tela virá em tons de branco e azul, que é o estilo padrão do Atmel Studio. Você pode personalizar em Tools/Options/Environment/General alternando o Color Theme para Dark, Blue ou Light. Uma questão de costume pessoal em IDEs de desenvolvimento.

Antes de criar um novo projeto, é necessário baixar o programador em linha de comando AVRDude. Descompacte a pasta e escolha um diretório de fácil acesso, como a C:/ ou D:/ do seu computador, aqui estamos utilizando a D:/avrdude.

Para configurar o AVRDude e o gravador USBasp no Atmel Studio, navegue no menu superior em Tools/External Tools. Na janela External Tools coloque conforme as configurações abaixo.

  • Title: AVRDude USBasp
  • Command:
D:\avrdude\avrdude.exe
  • Arguments:
-C "D:\avrdude\avrdude.conf" -c usbasp -p ATmega328P -U flash:w:"$(TargetDir)$(TargetName).hex":i
  • Selecione "Use Output window"
  • Apply e OK.
Configuração do AVRDude e USBasp em External Tools - Atmel Studio 7.

Realizando esta configuração do programador e gravador, podemos iniciar o desenvolvimento de um código simples para piscar um LED.

Desenvolvimento do código para piscar um LED

Para criar um novo projeto no Atmel Studio, navegue no menu superior em File/New/Project...

Criando um novo projeto Atmel Studio - Passo 1

Na tela New Project, selecione "GCC C++ Executable Project", no campo Name coloque o nome do projeto de "PiscaLedAtmega328p" e em Location coloque o diretório que você deseja salvar o projeto, clique em OK para finalizar.

Criando um novo projeto Atmel Studio - Passo 2

Na tela Device Selection, digite no campo de pesquisa "atmega328p", selecione ele na lista e clique em OK para criar o projeto.

Criando um novo projeto Atmel Studio - Passo 3

Com o projeto criado, você verá uma estrutura possuindo um int main() e while (1), o que seria o setup() e o loop() do código Arduino.

Cole este código abaixo que faz piscar um LED na porta PB5, pino 19 do Atmega328p. O que seria o LED interno "L" da placa Arduino.

/*
 * PiscaLedAtmega328p.cpp
 *
 * Created: 27/09/2019 00:10:12
 * Author : tecdicas
 */ 

#define F_CPU 16000000UL // Cristal de 16MHz

#include <avr/io.h>
#include "util/delay.h"  // Biblioteca para delay

int main(void)
{
    DDRB |= (1 << DDB5); // Configura o PB5 como OUTPUT
	
    while (1)
    {
        PORTB |= (1 << PORTB5);  // Liga LED
        _delay_ms(500);
        PORTB &= ~(1 << PORTB5); // Desliga LED
        _delay_ms(500);
    }
}

Compile o código em Build/Build Solution no menu superior, ou pressione F7. Se a janela de Output apresentar "Build Succeeded", indica que o código foi compilado com sucesso.

Build succeeded.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

Na etapa de gravação do código é necessário um Arduino Uno com adaptador 6-10 pinos ICSP ou um Arduino Standalone Atmega328p com conector ICSP de 10 pinos para realizar a comunicação com o gravador USBasp. Aqui utilizamos um Arduino Uno com adaptador.

Adaptador para USBasp e Arduino Uno ICSP.

Conectando o cabo ICSP no Arduino, conecte o USBasp no computador, e navegue no menu superior em Tools/AVRDude USBasp. O código será enviado ao Atmega328p, e o LED na porta PB5 ou LED "L" irá piscar a cada 500ms. A execução do AVRDude irá aparecer na janela de Output do Atmel Studio.

Se a gravação for bem sucedida..

avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude.exe: Device signature = 0x1e950f
avrdude.exe: NOTE: FLASH memory has been specified, an erase cycle will be performed
             To disable this feature, specify the -D option.
avrdude.exe: erasing chip
avrdude.exe: reading input file "D:\Projetos\AtmelStudio\PiscaLedAtmega328p\PiscaLedAtmega328p\Debug\PiscaLedAtmega328p.hex"
avrdude.exe: writing flash (176 bytes):

Writing | ################################################## | 100% 0.08s

avrdude.exe: 176 bytes of flash written
avrdude.exe: verifying flash memory against D:\Projetos\AtmelStudio\PiscaLedAtmega328p\PiscaLedAtmega328p\Debug\PiscaLedAtmega328p.hex:
avrdude.exe: load data flash data from input file D:\Projetos\AtmelStudio\PiscaLedAtmega328p\PiscaLedAtmega328p\Debug\PiscaLedAtmega328p.hex:
avrdude.exe: input file D:\Projetos\AtmelStudio\PiscaLedAtmega328p\PiscaLedAtmega328p\Debug\PiscaLedAtmega328p.hex contains 176 bytes
avrdude.exe: reading on-chip flash data:

Reading | ################################################## | 100% 0.06s

avrdude.exe: verifying ...
avrdude.exe: 176 bytes of flash verified

avrdude.exe: safemode: Fuses OK

avrdude.exe done.  Thank you.
Arduino Uno e USBasp gravando firmware com AVRDude via Atmel Studio.

Se o AVRDude apresentar erros na programação, ou não estiver reconhecendo o dispositivo como um USBasp, primeiramente verifique as conexões MISO, MOSI, SCK, RESET, VCC e GND. Verifique a instalação do driver libusb-win32 para o USBasp, utilize o software Zadig para uma instalação bem sucedida no Windows 10.

Zadig e USBasp para instalação do driver libusb-win32

Caso nenhuma dessas opções resolveram o problema de conexão, talvez o seu dispositivo "USBasp" não seja um USBasp original, situação que ocorreu com o dispositivo utilizado neste artigo. Muitos clones chineses deste gravador repletos de modificações de hardware e firmware estão sendo comercializados e gerando muitos problemas. Alterando o firmware do gravador, ele funciona perfeitamente com AVRDude e outros programadores. Para mais informações, leia Usando o firmware original do gravador AVR USBasp no MX-USBASP (clone chinês)

Analisando o código

Vamos analisar a forma em que os registradores foram utilizados na programação em C/C++.

Direção da porta

Dentro da estrutura int main() configuramos a porta PB5 como OUTPUT, utilizando o registrador DDRB, e o quinto bit DDB5 setamos como 1/verdadeiro utilizando a técnica Bitwise de OR e deslocamento de bits a esquerda.

DDRB |= (1 << DDB5); // Configura o PB5 como OUTPUT
Registrador DDRB - Atmega328p

O código equivalente utilizando a "linguagem Arduino" seria:

pinMode(LED_BUILTIN, OUTPUT);

Estados da porta

Dentro da estrutura while (1) programamos para o LED piscar, fazendo a porta PB5 ligar com bitwise OR setando o quinto bit PORTB5 em 1/verdadeiro, e desligar com bitwise NAND (NOT AND) setando o quinto bit PORTB5 em 0/falso, e aplicando os delays de 500ms.

PORTB |= (1 << PORTB5);  // Liga LED
_delay_ms(500);
PORTB &= ~(1 << PORTB5); // Desliga LED
_delay_ms(500);

Também podemos escrever o código desta forma.

PORTB |= (1 << 5);  // Liga LED 0b00100000
_delay_ms(500);
PORTB &= ~(1 << 5); // Desliga LED 0b00000000
_delay_ms(500);
Registrador PORTB - Atmega328p.

O código equivalente utilizando a "linguagem Arduino" seria:

digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);

(Opcional) Voltando as configurações originais do Arduino Uno

Se você utilizou a placa Arduino Uno para realizar os testes deste artigo, você pode enviar este firmware com AVRDude e USBasp para deixa-lo original. que possui bootloader e o código abaixo de piscar o LED.

/*
   Código para piscar o LED "L" interno da placa Arduino.
*/
void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);   
  delay(200);                       
  digitalWrite(LED_BUILTIN, LOW);    
  delay(200);                       
}

Utilize este comando no prompt de comando (CMD) para a gravação do Atmega328p.

avrdude -e -c usbasp -p ATmega328P -U flash:w:PiscaLEDBootloader.hex:i

Se a gravação for bem sucedida..

D:\avrdude>avrdude -e -c usbasp -p ATmega328P -U flash:w:PiscaLEDBootloader.hex:i

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.08s

avrdude: Device signature = 0x1e950f
avrdude: erasing chip
avrdude: reading input file "PiscaLEDBootloader.hex"
avrdude: writing flash (32768 bytes):

Writing | ################################################## | 100% 17.78s



avrdude: 32768 bytes of flash written
avrdude: verifying flash memory against PiscaLEDBootloader.hex:
avrdude: load data flash data from input file PiscaLEDBootloader.hex:
avrdude: input file PiscaLEDBootloader.hex contains 32768 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 12.10s



avrdude: verifying ...
avrdude: 32768 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Depois da gravação do microcontrolador, você verá o LED interno "L" piscar algumas vezes e piscar a cada 200ms. Como também utilizar novamente a placa Arduino com sua comunicação serial e IDE oficial.

Referência

  1. ATMEGA328P: 8-bit AVR Microcontroller with 32K Bytes In-System Programmable Flash - Datasheet Atmel.