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.
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.
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.
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.
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:
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.
Após a escolha do cristal selecione na IDE o board correspondente e instale o bootloader, conforme as instruções deste post.
Mantendo o kit desligado faça os seguintes passos:
A imagem com os passos rotulados, auxilia o procedimento:
Finalmente chegamos ao momento de utilizar o HC6800EM3 como um Arduino:
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):
A pasta com o código fonte foi instalada em "Documentos\HC6800EM3" e inclui:
Nota: o texto acima faz referência a pasta "Documentos", que pode estar com outro nome dependendo do seu computador e sistema operacional utilizado.
Este é um resumo do que foi necessário para transformar o HC6800EM3 em um Pseudo Arduino:
A seguir apresento de forma mais detalhada os vários elementos que ainda não foram abordados.
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:
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.
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:
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)
// +--------+
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):
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").
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:
Aceito sugestões!