Menu

arduino_3_ptb

Convertendo o HC6800EM3 em um Arduino (Parte 3)

Objetivo

Este post finaliza a série que descreve o processo de conversão de um kit 8051 chinês (genérico) em uma plataforma de desenvolvimento compatível com a IDE Arduino.

Pré-requisitos

Será necessário repetir a gravação da nova bootloader para o ATmega162, conforme apresentado na Parte 2 ou Parte 2B, mas utilizando um novo Pacote de integração HC6800EM3 no Arduino. O link ao lado aponta para a nova versão 0.2, que inclui o código fonte utilizado para esta empreitada.

Instalação do Pacote de Integração

Inicialmente descreveremos um breve roteiro para a instalação da versão atualizada do pacote de integração e mais tarde descrevo quais foram os truques utilizados para esta realização.

Download do Programa Instalador

Faça do download do Instala_hc6800em3_para_Arduino_0.2.exe no link fornecido. Neste pacote temos tudo o que é necessário para configurar a IDE Arduino para operar com o Kit Prechin HC6800EM3.


Instalação do Pacote

Rode o instalador Instala_hc6800em3_para_Arduino.exe a partir da pasta de download. A tela a seguir é apresentada:

Avance a instalação, para obter a tela contendo as opções de instalação:

Nesta tela você pode escolher o que vai ser instalado, que descrevo rapidamente.
A opção Arquivos de Bootloader instala os bootloaders para o ATmega162 configurados para 7,373MHz, 8MHz, 12MHz e 16MHz, conforme o cristal instalado em seu Kit. Os arquivos são instalados em "C:\Program Files (x86)\Arduino\hardware\arduino\bootloaders\hc6800em3". Mais pra frente volto a falar sobre estas opções de clock.
Se você não tem a placa FT232R breakout ou equivalente, desmarque a opção Configurar gravador FT232RL. Lembre-se que alternativamente, é possível utilizar uma placa Arduino ou outro tipo de gravador para realizar a gravação do bootloader de forma mais eficiente.
A terceira opção Configurar placa HC6800EM3 no Arduino modifica o arquivo "C:\Program Files (x86)\Arduino\hardware\arduino\boards.txt" e inclui as 4 opções de clock para o nosso Kit.
Marque a última opção para instalar o código fonte deste projeto e é sugerido para fins didáticos. Seu conteúdo será colocado em "Documentos\hc6800em3" ficando à sua disposição para referência.
Ao finalizar a instalação temos a tela de encerramento:

Com esta etapa, encerramos a instalação do software de integração.

Se estiver curioso, abra o seu Arduino para observar as novas "Boards" que foram instaladas, como mostra esta imagem:

Escolha do Cristal de Operação

Como o kit Prechin HC6800EM3 permite selecionar o cristal de trabalho, uma das etapas importantes é decidir com qual das 4 configurações trabalharemos.
O Kit vem montado de fábrica com um cristal de 12 MHz, mas é necessário utilizar o ATmega162-16 para esta velocidade. No meu caso estou utilizando o ATmega162V-8 que é especificado para um máximo de 8 MHz. Mesmo assim, para validar os meus testes fiz um overclock para 12 Mhz e o componente está operando corretamente utilizando a tensão de 5V.
Além do clock de 12 MHz, o kit vem com um cristal de 11,059 Mhz e outro de 24 Mhz. Nenhum destes é uma opção. A recomendação é adquirir no comércio um cristal de 16 Mhz, ou mesmo 8 MHz, para a melhor compatibilidade possível com o Arduino.
Como eu tinha um cristal de 7,3728 MHz "sobrando" aqui, construi um bootloader com esta opção, mas não recomendo devido a três macros "mal projetadas" presentes no pacote Arduino. Estas macros causarão erros nos cálculos das rotinas de temporização que você venha desenvolver. Para àqueles que estão curiosos colocarei mais detalhes nos comentários.

Gravação do Bootloader

Após a escolha do cristal selecione na IDE o board correspondente e instale o bootloader, conforme as instruções deste post.

Setup do Kit Prechin HC6800EM3

Mantendo o kit desligado faça os seguintes passos:

  1. Instale o ATmega162 no soquete ZIF;
  2. Instale o cristal selecionado;
  3. Selecione a polaridade de RESET no J9, colocando-o na posição LL (Nota: a polaridade do reset no AVR é invertida em comparação ao 8051);
  4. Desconecte o jumper "ON", localizado junto ao relé de controle de alimentação da placa (este é um truque importante que vou comentar no tópico sobre o patch do avrdude).

A imagem com os passos rotulados, auxilia o procedimento:

Compilando e Instalando um Sketch no nosso Pseudo Arduino

Finalmente chegamos ao momento de utilizar o HC6800EM3 como um Arduino:

  • Abra a IDE do Arduino;
  • Selecione [File], [Examples], [01.Basics] e [Blink];
  • Selecione [Tools], [Board] e [ATmega162 @12 MHz on HC6800EM3] ou a configuração compatível com o cristal que você escolheu;
  • Conecte um jumper de 8 vias fornecido entre o JP8 e o JP1 localizado no lado direito e em baixo. Observando o silk-screen, este cabo liga as saídas P1.0 a P1.7 aos LEDs do canto da placa;
  • Encaixe o plugue USB e ligue o interruptor da placa HC6800EM3;
  • Se necessário, aguarde o Windows durante a detecção do novo hardware;
  • Selecione em [Tools], [Serial Port] a porta serial conforme a configuração de sua máquina;
  • Compile e instale comandando [File] e [Upload].

A IDE fará a compilação e o upload do resultado para o nosso Pseudo-Arduino.
As imagens abaixo demonstram a compilação e execução do programa exemplo Blink, que pisca o LED conectado ao pino 13 (falarei mais sobre pinos adiante):

O Código Fonte Incluído

A pasta com o código fonte foi instalada em "Documentos\HC6800EM3" e inclui:

  • Atalho para abrir um Prompt de Comando configurado para compilar utilizando o compilador WinAVR instalado com o Arduino (arquivo "Documentos\hc6800em3\SetEnv.bat");
  • Arquivo pins_arduino.h que realiza os ajustes para que a compilação ocorra sem erros, quando utilizar o µC ATmega162. É neste arquivo que definimos a pinagem do ATmega (arquivo "Documentos\hc6800em3\Compat\pins_arduino.h");
  • Código fonte do bootloader utilizado para ATmega162 com makefile e as 4 saídas já compiladas (os arquivos estão na pasta "Documentos\hc6800em3\Bootloader");
  • Algumas imagens utilizadas nesse blog (instalados na pasta "Documentos\hc6800em3\Images");
  • Código fonte do instalador NSISque foi executando acima (arquivo "Documentos\hc6800em3\Installer\PorteArduino.nsi");
  • Código fonte de ferramenta que substitui o avrdude.exe para executar o bootloader no HC6800EM3 sempre que compilamos uma sketch no Arduino. Falarei mais sobre isso lá no final.

Nota: o texto acima faz referência a pasta "Documentos", que pode estar com outro nome dependendo do seu computador e sistema operacional utilizado.

Detalhes desta Implementação

Este é um resumo do que foi necessário para transformar o HC6800EM3 em um Pseudo Arduino:

  • Alteração do bootloader para execução em um µC ATmega162, compilação e instalação no ambiente Arduino;
  • Instalação deste bootloader em um chip usando o gravador de sua preferência e a IDE Arduino;
  • Alteração do Ambiente Arduino para apresentar as boards configuradas para nosso kit;
  • Inclusão de arquivo para compatibilidade com o ATmega162. Também inclui definição dos pinos de E/S;
  • Patch para que o avrdude.exe possa carregar o bootloader sempre que um Upload é realizado através da IDE Arduino

A seguir apresento de forma mais detalhada os vários elementos que ainda não foram abordados.

Como a Alteração do Bootloader Foi Realizada

Tomamos o arquivo "C:\Program Files (x86)\Arduino\hardware\arduino\bootloaders\atmega\ATmegaBOOT_168.c" e inserimos as seguintes linhas a partir da linha 391:

    ...
    DDRD &= ~_BV(PIND0);
    PORTD |= _BV(PIND0);
#elif defined(__AVR_ATmega162__)
    /* The UBRRH Register shares the same I/O location 
    as the UCSRC Register. Therefore some special 
    consideration must be taken when accessing this 
    I/O location.
    When doing a write access of this I/O location, 
    the high bit of the value written, the USART 
    Register Select (URSEL) bit, controls which one 
    of the two registers that will be written. If 
    URSEL is zero during a write operation, the UBRRH 
    value will be updated. 
    If URSEL is one, the UCSRC setting will be 
    updated.  */
    UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
    UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
    UCSR0B = (1<#elif defined __AVR_ATmega8__
    /* m8 */
    ...

Basicamente, o código do ATmega162 é idêntico ao ATmega168, com três peculiaridades:

  1. O ATmega162 possui duas portas seriais, então o nome dos registros indicam o número da porta;
  2. O ATmega162 compartilha UBRR0H e UCSR0C no mesmo endereço e é necessário utilizar o bit URSEL quando se refere a UCSR0C. É uma pegadinha que eu não tinha vista na versão 0.1 deste pacote!
  3. Os fuses Low, High e Extended possuem valores individualizados por variante AVR e precisaram ser escolhidos com critério. Uma ferramenta bem interessante que utilizei é o Engbedded AVR Fuse Calculator.

Se desejar compilar o bootloader por conta própria, abra o WinAVR Prompt, instalado com a ferramenta e comande:

cd Bootloader
make atmega162_7

ou

make atmega162_8

ou

make atmega162_12

ou

make atmega162_16

conforme o cristal utilizado e já discutido anteriormente.

Inclusão das Boards para o HC6800EM3

As boards instaladas no Arduino ficam armazenadas no arquivo "C:\Program Files (x86)\Arduino\hardware\arduino\boards.txt". Para as nossas 4 opções apresentadas, acrescentamos as seguintes linhas a este arquivo:

atmega162_7.name=ATMega162V @7.373 MHz on HC6800EM3
atmega162_7.upload.protocol=hc6800em3
atmega162_7.upload.speed=19200
atmega162_7.bootloader.low_fuses=0xFD
atmega162_7.bootloader.high_fuses=0xD8
atmega162_7.bootloader.extended_fuses=0xFB
atmega162_7.bootloader.path=hc6800em3
atmega162_7.bootloader.file=ATmegaBOOT_atmega162_7.hex
atmega162_7.bootloader.unlock_bits=0x3F
atmega162_7.bootloader.lock_bits=0x0F
atmega162_7.build.mcu=atmega162
atmega162_7.build.f_cpu=7372800L
atmega162_7.build.core=arduino
atmega162_7.build.variant=hc6800em3

atmega162_8.name=ATMega162V @8 MHz on HC6800EM3
atmega162_8.upload.protocol=hc6800em3
atmega162_8.upload.speed=19200
atmega162_8.bootloader.low_fuses=0xFF
atmega162_8.bootloader.high_fuses=0xD8
atmega162_8.bootloader.extended_fuses=0xFB
atmega162_8.bootloader.path=hc6800em3
atmega162_8.bootloader.file=ATmegaBOOT_atmega162_8.hex
atmega162_8.bootloader.unlock_bits=0x3F
atmega162_8.bootloader.lock_bits=0x0F
atmega162_8.build.mcu=atmega162
atmega162_8.build.f_cpu=8000000L
atmega162_8.build.core=arduino
atmega162_8.build.variant=hc6800em3

atmega162_12.name=ATMega162 @12 MHz on HC6800EM3
atmega162_12.upload.protocol=hc6800em3
atmega162_12.upload.speed=19200
atmega162_12.bootloader.low_fuses=0xFF
atmega162_12.bootloader.high_fuses=0xD8
atmega162_12.bootloader.extended_fuses=0xFB
atmega162_12.bootloader.path=hc6800em3
atmega162_12.bootloader.file=ATmegaBOOT_atmega162_12.hex
atmega162_12.bootloader.unlock_bits=0x3F
atmega162_12.bootloader.lock_bits=0x0F
atmega162_12.build.mcu=atmega162
atmega162_12.build.f_cpu=12000000L
atmega162_12.build.core=arduino
atmega162_12.build.variant=hc6800em3

atmega162_16.name=ATMega162 @16 MHz on HC6800EM3
atmega162_16.upload.protocol=hc6800em3
atmega162_16.upload.speed=19200
atmega162_16.bootloader.low_fuses=0xFF
atmega162_16.bootloader.high_fuses=0xD8
atmega162_16.bootloader.extended_fuses=0xFB
atmega162_16.bootloader.path=hc6800em3
atmega162_16.bootloader.file=ATmegaBOOT_atmega162_16.hex
atmega162_16.bootloader.unlock_bits=0x3F
atmega162_16.bootloader.lock_bits=0x0F
atmega162_16.build.mcu=atmega162
atmega162_16.build.f_cpu=12000000L
atmega162_16.build.core=arduino
atmega162_16.build.variant=hc6800em3

Cada bloco tem os seguintes atributos a saber:

  • name: O nome que aparece no menu do Arduino
  • upload.protocol: O ID de programador utilizado no avrdude (veja a opção -c da documentação do avrdude). Note que o ID hc6800em3 não está nesta documentação e é utilizado apenas no patch do avrdude que fiz e será apresentado lá no final;
  • upload.speed: A velocidade de comunicação da porta serial. Este valor é travado em 19200, mas poderá ser ajustado se produzirmos um novo bootloader;
  • bootloader.low_fuses, bootloader.high_fuses e bootloader.extended_fuses: Os fuses foram obtidos utilizando a calculadora AVR;
  • bootloader.path: Nome da pasta onde o bootloader pode ser localizado relativo a "C:\Program Files (x86)\Arduino\hardware\arduino\bootloaders"
  • bootloader.file: Nome do arquivo contendo o bootloader em formato Intel Hex;
  • bootloader.unlock_bits: Usado para desbloqueio da região de bootloader;
  • bootloader.lock_bits: Usado para proteger a região de bootloader, impedindo que o mesmo seja apagado nas operações de erase;
  • bootloader.build.mcu: Valor utilizado para a opção -mmcu do WinAVR;
  • bootloader.build.f_cpu: Clock utilizado no µC, conforme o cristal selecionado;
  • bootloader.core: Nome da pasta contendo o conjunto de bibliotecas usadas na compilação de um sketch, relativa a pasta "C:\Program Files (x86)\Arduino\hardware\arduino\cores";
  • bootloader.variant: Nome da pasta contendo os detalhes da variante do µC utilizado. Contém o arquivo pins_arduino.h com os detalhes da variante, como vermos em breve.

Compatibilização da Variante e seus Pinos

Para abstrair as diferenças entre os diferentes modelos de hardware, há um mecanismo específico para lidar as variantes dos µC AVR e como verificamos no tópico anterior é determinado pelo atributo bootloader.variant no arquivo boards.txt e permite incluir um arquivo pins_arduino.h específico para cada variante durante a compilação de um sketch.

Vou resumir os principais pontos.

Considerando que o circuito do kit HC6800EM3 não permite reaproveitar os pinos do Port E do ATmega162, definimos 24 pinos digitais e 8 analógicos:

#define NUM_DIGITAL_PINS            24
#define NUM_ANALOG_INPUTS           8

Os pinos 4, 5, 8, 9 e 12 possuem PWM e definido em:

#define digitalPinHasPWM(p)
     ((p) == 4 || (p) == 5 || (p) == 8 
     || (p) == 9 || (p) == 12)

Os pinos SS, MOSI, MISO, SCK e LED_BUILTIN são de importância para o ambiente Arduino e definidos:

static const uint8_t SS   = 12;
static const uint8_t MOSI = 13;
static const uint8_t MISO = 14;
static const uint8_t SCK  = 15;
static const uint8_t LED_BUILTIN = 23;

As oito entradas do ADC são definidas:

static const uint8_t A0 = 24;
static const uint8_t A1 = 25;
static const uint8_t A2 = 26;
static const uint8_t A3 = 27;
static const uint8_t A4 = 28;
static const uint8_t A5 = 29;
static const uint8_t A6 = 30;
static const uint8_t A7 = 31;

Finalmente o mapeamento de pinos é resumido na representação textart de um chip ATmega162:

// ATMEL ATMEGA162
//
//                  +---\/---+
//  PWM (D 8) PB0  1|        |40  VCC
//  PWM (D 9) PB1  2|        |39  PA0 (AI 0)
//      (D10) PB2  3|        |38  PA1 (AI 1)
//      (D11) PB3  4|        |37  PA2 (AI 2)
//  PWM (D12) PB4  5|        |36  PA3 (AI 3)
//      (D13) PB5  6|        |35  PA4 (AI 4)
//      (D14) PB6  7|        |34  PA5 (AI 5)
//      (D15) PB7  8|        |33  PA6 (AI 6)
//         _RESET  9|        |32  PA7 (AI 7)
//      (D 0) PD0 10|        |31  PE0 (N.C.)
//      (D 1) PD1 11|        |30  PE1 (N.C.)
//      (D 2) PD2 12|        |29  PE2 (N.C.)
//      (D 3) PD3 13|        |28  PC7 (D23)
//  PWM (D 4) PD4 14|        |27  PC6 (D22)
//  PWM (D 5) PD5 15|        |26  PC5 (D21)
//      (D 6) PD6 16|        |25  PC4 (D20)
//      (D 7) PD7 17|        |24  PC3 (D19)
//          XTAL2 18|        |23  PC2 (D18)
//          XTAL1 19|        |22  PC1 (D17)
//            GND 20|        |21  PC0 (D16)
//                  +--------+

Patch do AvrDude

Dediquei algum tempo para garantir a usabilidade desta solução e foi estabelecer uma maneira do bootloader ser executado sempre que se inicia um upload a partir da IDE.

No caso do Arduino oficial, o avrdude possui um ID de gravador denominado "arduino" que nada mais é que um STK500/AVRISP acrescido de uma pequena diferença: a linha de DTR/RTS da porta serial é pulsada para reiniciar o µC. Neste momento o bootloader é executado e aguarda algum tempo, o suficiente para que a comunicação seja estabelecida e então o protocolo STK500 é emulado e utilizado durante a gravação.

Nas tentativas iniciais era necessário pressionar o botão de RESET do kit sempre que eu fazia o upload de um sketch. O problema é que esta tarefa exigia muita precisão, já que o bootloader após algum tempo sem comunicação, transfere a execução para o programa instalado e a gravação falha. Ou seja, o pressionamento do botão de reset deve ocorrer em tempo exato para que tudo funcione corretamente.

Logo fiquei convencido que a solução "manual" não era satisfatória.

Examinando o esquema do HC6800EM3, decidi que eu deveria utilizar o relé Power que controla a alimentação do circuito para fazer o reset. Este relé é controlado por dois transistores e a polarização é determinada por Q0, ligado em DTR e RTS da porta serial. Se DTR estiver em 5V e RTS em 0V, o transistor conduz e desliga o circuito, pois a ligação do relé está na posição "normalmente fechado".

Então estudei o código fonte do avrdude com mais calma e verifiquei que o controle poderia ser implementado facilmente se eu modificasse a função ser_set_dtr_rts() do arquivo ser_win32.c. No fim, mudei de ideia, pois não tenho a intenção de manter um fork desta ferramenta. Assim eu precisava de uma alteração menos invasiva.

O caminho tomado foi deixar o avrdude intocado, apenas renomeando-o para avrdude.orig.exe na pasta de sua instalação "C:\Program Files (x86)\Arduino\hardware\tools\avr\bin" e colocar um avrdude.exe adaptado que só fizesse o RESET e então passasse o controle diretamente para o avrdude.orig.exe para a tarefa de gravação. Esta adaptação funcionaria como um "gancho" e a IDE não teria como saber desta troca.

Criei então um "Programmer ID" novo que chamei de hc6800EM3 (muito criativo, né) para saber que a gravação em questão é para o Kit da Prechin. Desta forma, ao receber um outro "Programmer ID" a execução é passada diretamente para o avrdude real, sem interferência. Assim se a IDE precisar fazer upload para outro hardware, como por exemplo uma placa Arduino original, tudo funciona como antigamente.

O código fonte que eu disponibilizo foi feito com o Visual Studio 2013 Professional usando a biblioteca C e alguma coisa da MFC.

Segue o resumo de funcionamento (acho melhor acompanhar no código fonte):

  • Determinar o nome do executável atual e preparar o caminho para o executável para o avrdude original, que deverá ser instalado na mesma pasta e possuir o nome "avrdude.orig.exe";
  • Varrer todas as opções de linha de comando passada pela IDE e localizar o argumento que informa a porta serial para que possamos ter o acesso para realizar o reset;
  • Ao varrer os argumentos de entrada devemos determinar se o nome do Programmer ID é HC6800EM3 e isso indicará se temos o kit Prechin conectado. Um detalhe importante é que neste caso o valor do Programmer ID precisa ser trocado para 'AVRISP', já que o avrdude desconhece 'HC6800EM3' e causaria um erro;
  • Se solicitado, realizamos o RESET do Kit Prechin comandando as linhas RTS e DTR (note que o nível lógico nas flags de software são complementares aos níveis de tensão medidos nos pinos do U3 (CH340)). O reset dura 1 segundo e após a liberação segue uma pausa de 250 ms para iniciar a execução do bootloader;
  • Antes de executar o avrdude original é necessário preparar os argumentos que possuírem espaços em branco, colocando-os entre aspas (aqui você vê a filosofia Microsoft: API mixuruca onde o programador precisar tratar de coisas básicas, ao invés de se dedicar no projeto em si -- você rala e o Bill fica bilionário).

Desculpem àqueles que não tem acesso ao VS2013 pago com MFC, mas é que me nego trabalhar com a STL que tem uma classe string muito meia-boca (na minha opinião a grande responsável pela linguagem C++ não ter decolado -- e olha que sou teimoso e trabalho com C++ desde 95 até hoje -- e antes que alguém me xingue: CString, QString, wxString, TString, UString, xString com x tendendo a infinito, provam que a STL é meia-boca! Assim diria um colega de trabalho: "contra fatos não há argumentos").

Conclusão

Vou terminar por aqui, já que esse projetinho acabou consumindo mais tempo que eu pretendia. Mas o mais importante é que o resultado esperado foi atingido.
Seguem algumas ideias para os próximos posts:

  • Apresentar o SDCC para o 8051, integrado à IDE CodeBlocks, como forma de eliminar o uso de softwares pirata para desenvolvimento no 8051. Vale lembrar que o SDCC ainda suporta outros processadores como os da linha PIC;
  • Fazer o mesmo que o ponto acima só que utilizando um compilador ARM e fazer depuração utilizando uma interface JTAG (ainda estou esperando os correios entregarem o meu exemplar);
  • Conectar um processador da linha TI MSP430 ao Kit, já que este maravilhoso µC é tão desconhecido aqui no Brasil e já o utilizo profissionalmente desde 2003 com absoluto sucesso e baixo consumo (é claro);
  • Utilizar o SysProgs VisualGDB integrado ao Visual Studio 2013 Professional (ambos pagos) para fazer uma IDE de desenvolvimento embarcado matadora para MSP430, ARM, Linux ou mesmo Raspberry Pi e Android. Considero esta a melhor opção para quem tem um Visual Studio licenciado e deseja realizar desenvolvimento embarcado profissional por um custo acessível. Por exemplo, no passado utilizei o Rowley CrossWorks que é uma IDE para desenvolvimento embarcado fantástica e deixa os IAR e cia. no chinelo, porém na última versão do firmware de um equipamento que desenvolvi migrei para o MSPGCC + VisualGDB + VS2013 (também tentei o Eclipzzzzz)...
  • Desenvolver um pequeno programa para a leitura de um cartão SD e apresentar na tela o fabricante real do cartão (o mercado é cheio de OEM's) além de outros dados de fabricação. O programa deverá ter uma estrutura com uma camada de abstração de hardware para permitir compilação multi-plataforma: ARM STM32, MSP430, AVR/Arduino e 80C51.

Aceito sugestões!


Related

Wiki: Home
Wiki: arduino_2_ptb
Wiki: arduino_2b_ptb