<Status > <Home> <Fim da Página>

< 16.09.2011>           

Um PIPPO  =>  xPIPPO

Um PIPPO nasceu em 2002 e ainda está vivo, apesar de um pouco capenga!. Mas, após quase 10 anos de idade, achei que ele merecia uma atualização física e lógica. Com essa premissa, está nascendo seu herdeiro xPIPPO. O "x" no nome vem do microcontrolador que vou usar na sua construção o ATxmega128A1 da Atmel.

Vocês poderiam perguntar: porque esse microcontrolador?

Porque o ATxmega128A1 (Ducumentação completa) é um microcontrolador que tem características que o tornam adequado ao desenvolvimento de um robô complexo como pretende ser o xPIPPO. Uma característa excepcional é o seu sistema de eventos.

Vejam algumas:

  • Memória FLASH - 128KB + 8KB

  • Memória SRAM - 8KB

  • Memória EEPROM - 2KB

  • Sistema de eventos - 8 canais

  • Timers/Counters - 8

  • USARTs - 8

  • TWI (I2C - SMBus) - 4

  • SPI - 4

  • Real Time Counter - 1

  • ADCs - 16

  • DACs - 4

  • Comparadores analógicos - 4

  • Interrupts externos - em todos os pinos de uso geral

  • Controle do nível de interrupção

  • Watchdog

Nos últimos anos tenho trabalhado com os excelentes produtos da chip45 e para o xPIPPO escolhi o módulo Crumb128A1.

  • ATxmega128A1 - 16MHz

  • PDI + JTAG

  • CP2102 USARTF0/USB - conector miniUSB B

  • Conector micro SD

  • Bootloader

A linguagem que vou usar é o ANSI C e o compilador é o CodeVisionAVR V2 que eu uso há muitos anos com excelentes resultados.

<11.10.2011>

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:

  1. 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.

  2. Fazemos o reset do Crumb128A1.

  3. 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

= 0x6578656D706C6F => 1º parâmetro => valor "exemplo"

= 0x17 => 2º parâmetro => valor 23

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

}

 


 

<Início da Página>