Antes de tudo, vou resolver um
problema que me atrapalhou bastante com o Um PIPPO. A versão
atual de Um PIPPO tem três microcontroladores: 2 - BasicX-01
e 1 - ATmega128. Toda vez que eu tinha que carregar versões novas
dos programas era uma ginástica danada porque, no caso dos BasicX-01,
tinha que desmontá-los e utilizar a placa de desenvolvimento que
usa a porta paralela do PC. Como para o xPIPPO vou usar um único
microcontrolador no módulo Crumb128A1 que tem o bootloader acessível via
a USARTF0, vou desenvolver um link wireless para carregar o
arquivo hex diretamente do PC. Para isso vou usar dois modems (9XCite
900MHz 38400 baud - da antiga MaxStream, hoje
Digi International)
que tenho há muitos anos, um conversor
CP2102 USB UART da Chip45 e o software gratuito para PC
chip45boot2 GUI também da chip45. Este software permite carregar o
arquivo hex, gerado pelo compilador CodeVisionAVR, via uma serial COM.

Este mesmo link vai ser usado para
enviar comandos remotos a xPIPPO e receber dados dele de volta no
PC. Como é possível, com o mesmo link, uma hora falar com o bootloader e
outra falar com o programa que foi carregado?
Isto é possível porque logo após
um reset o bootloader, durante 2 segundos, verifica se está recebendo
alguma coisa no seu pino RXD. Se ele estiver recebendo, ele mede os
tempos de subida e descida do sinal por quatro caracteres consecutivos.
Com isso ele consegue acertar sua velocidade em baud igual à velocidade
do PC e se os quatro caracteres que ele recebeu forem 'U', então ele
passa a se comunicar com o PC (no nosso caso com o chip45boot2 GUI) para
carregar o arquivo hex. Caso ele não receba os quatro caracteres 'U',
ele passa o controle para o programa que está na flash.
No nosso caso, quando quisermos
acionar o bootloader para carregar um arquivo hex, fazemos o seguinte:
-
Iniciamos o chip45boot2 GUI,
selecionamos a porta COM correspondente à entrada USB que está sendo
utilizada pelo CP2102 (o driver deve ter sido instalado) e a
velocidade em baud.
-
Fazemos o reset do Crumb128A1.
-
Acionamos o botão "Connect to
Bootloader" do chip45boot2 GUI.

O programa também utilizará a
USARTF0 normalmente para falar com o PC via um programa VB que vamos
desenvolver. Para isso, no nosso programa em C, vamos definir a USARTF0
com recepção via interrupt e na rotina de interrupt vamos programar a
recepção de comandos (que serão enviados pelo programa VB) como se
fossem funções. Os comandos terão então o seguinte formato (cada letra
corresponde a um byte e cada letra com expoente corresponde a um
conjunto de bytes):
NCP¹|P²|P³|...
N => número total de bytes
do comando (unsigned char 2 a 255)
C => código do comando,
indica a função (unsigned char 0 a 255)
P¹,P²,P³... =>
parâmetros representados por conjuntos de caracteres ASCII (0x20 a 0x7E
com exceção do 0x7C)
| => separador de
parâmetros (0x7C)
Exemplo (hexa):
120A6578656D706C6F7C177C36302E35307C
N = 0x12 => comando tem 18
bytes
C = 0x0A => código de
comando => valor 10
P¹ = 0x6578656D706C6F => 1º
parâmetro => valor "exemplo"
P² = 0x17 => 2º parâmetro =>
valor 23
P³ = 0x36302E3530 => 3º
parâmetro => valor 60.50
Uma idéia de como fazer isso
segue abaixo:
#define True 1;
#define False 0;
unsigned char * comando; //Área do
comando. Alocação dinâmica via calloc
unsigned char rx_wr_index_usartf0 = 0, rx_counter_usartf0 = 0;
unsigned char N;
bool comando_completo; //Flag para saber
quando o comando está completo
// Rotina de interrupt da USARTF0, ela é acionada
a cada byte recebido.
interrupt [USARTF0_RXC_vect] void usartf0_rx_isr(void)
{
unsigned char status;
char data;
status = USARTF0.STATUS;
data = USARTF0.DATA;
//Pega o byte que chegou
if ((status & (USART_FERR_bm | USART_PERR_bm |
USART_BUFOVF_bm)) == 0)
{
rx_counter_usartf0++; //Conta o byte que
chegou
if (rx_counter_usartf0 == 1) //Se for o
primeiro
{
comando_completo = False; //Marca o
comando como incompleto
N = data; //Número de bytes do comando
//Alocação dinâmica do vetor comando
comando = (unsigned char *) calloc (N,sizeof(unsigned char));
comando[rx_wr_index_usartf0++] = data;
//Guarda o primeiro byte
}
else if
((rx_counter_usartf0 > 1) && (rx_counter_usartf0 <= N))
{
comando[rx_wr_index_usartf0++] = data;
//Guarda os bytes de 2 a N
if (rx_counter_usartf0 == N) //Se for o
último byte do comando
{
//O comando inteiro (N bytes) está no vetor comando, vamos reinicializar
as variáveis
//para o próximo comando. Lembrar de processar o comando antes da
chegada de outro.
// comando[0] => N
// comando[1] => C
// comando[2] a comando[N-1] => parâmetros
rx_wr_index_usartf0 = 0;
rx_counter_usartf0 = 0;
comando_completo = True; //Sinaliza que o
comando está completo
}
}
}
}
//Rotina para processamento dos comandos
void verifica_comando(void)
{
if (comando_completo)
//Se o comando está completo
{
switch
(comando[1]) //Processa os diferentes
comandos
{
case 0:
//Obtém parâmetros do comando 0
//Processa comando 0
break;
case 1:
//Obtém parâmetros do comando 1
//Processa comando 1
break;
//E assim por diante ...
}
//Já
processamos o comando, vamos desalocar o vetor comando
//ele vai ser realocado pela rotina de interrupt.
free
(comando);
}
}
//Programa principal
void main(void)
{
while (1)
verifica_comando();
}