terça-feira, janeiro 15, 2013

Leitor RFID YET-125K: Conectando ao JY-MEGA32

Vamos acrescentar inteligência ao leitor de RFID ligando-o a um microcontrolador. Para isto vou aproveitar a placa JY-MEGA32 que vimos recentemente. Além de ter um microcontrolador com interface serial, esta placa possui LEDs e teclas que serão úteis.


Hardware

Como vimos anteriormente, o leitor requer somente três conexões, nos pinos +5, GND e D0. Estas conexões serão feitas na barra de pinos da placa JY-MEGA32. As duas primeiras são óbvias (Vcc e GND), a terceira requer uma olhada no datasheet do ATmega32, para descobrir que o sinal Rx da USART é compartilhado com PD0.

Um outro detalhe não óbvio é que o sinal PD0 está ligado na placa ao conector USB (isto pode ser verificado no esquema da placa, que está nos arquivos do blog). Se o leitor e o cabo USB estiverem conectados simultaneamente, a comunicação com o leitor não será confiável. Após a carga do firmware é recomendável alimentar a placa ligando a alimentação por um dos outros conectores. No meu caso, usei o conector de programação SPI.

Software

Para este teste vamos acender os LEDs com os tags. O objetivo é associar cada tag a um LED. Incialmente não temos nenhuma associação; quando um novo tag é detectado, o próximo LED é associado a ele (desde que ainda tenhamos LEDs não associados). Quando um tag conhecido é detectado, o LED correspondente é aceso.

Para facilitar as coisas, os ids dos tags são convertidos em valores binários de 32 bits. Uma tabela de 8 posições guarda os ids dos tags conhecidos, o valor zero indica que a posição da tabela esta livre. Cada posição da tabela corresponde a um LED.

O ponto principal neste teste é a comunicação entre o leitor e o ATmega. O ATmega32 possui uma USART, que é programada com os parâmetros adequados (comunicação assíncrona no formato 8N1, a 9600bps). É também habilitada a interrupção de recepção:
  1. // Programa a USART para 9600 8N1  
  2.     UCSRA = _BV(U2X);                    // para maior resolução do baud rate  
  3.     UCSRB = _BV(RXEN)|_BV(RXCIE);     // liga recepção, com interrupção  
  4.     UCSRC = _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0) ;  // 8bit, 1 stop, sem paridade  
  5.     UBRRL = (CLK_CPU / (8 * 9600UL)) - 1;           // 9600 bps  
A rotina de interrupção utiliza uma pequena máquina de estados para conferir o formato da mensagem e armazenar o id do tag. Ao converter o id para binário eu considerei a possibilidade de vir um número hexadecimal (apesar dos meus testes terem sempre apresentado um número decimal). Recebida uma mensagem correta, o id do tag é colocado em uma variável global, desde que ela esteja livre (com zero).
  1. // Último tag detectado  
  2. volatile uint32_t tagLido;  
  3.   
  4. // Lista de tags conhecidos  
  5. volatile uint32_t tags[8];  
  6.   
  7. // Interrupção de recepção da USART  
  8. ISR(USART_RXC_vect)  
  9. {  
  10.     static uint8_t estado = 0;  
  11.     static uint32_t idTag;  
  12.     uint8_t c;  
  13.   
  14.     c = UDR;  
  15.     if (bit_is_clear(UCSRA, FE))  
  16.     {  
  17.         // recebeu caracter sem erro  
  18.         if (estado == 0)  
  19.         {  
  20.             // Aguardando o STX  
  21.             if (c == 0x02)  
  22.             {  
  23.                 estado = 1;  
  24.                 idTag = 0;  
  25.             }  
  26.         }  
  27.         else if (estado == 9)  
  28.         {  
  29.             // Aguardando o ETX  
  30.             if (c == 0x03)  
  31.             {  
  32.                 // Recebeu ID com sucesso  
  33.                 tagLido = idTag;  
  34.             }  
  35.             estado = 0;  
  36.         }  
  37.         else  
  38.         {  
  39.             // recebeu um dígito, vamos tratar como hexadecimal  
  40.             if ((c >= '0') && (c <= '9'))  
  41.                 idTag = (idTag << 4) | (c & 0xF);  
  42.             else if ((c >= 'A') && (c <= 'F'))  
  43.                 idTag = (idTag << 4) | (c - 'A' + 10);  
  44.             else if ((c >= 'a') && (c <= 'f'))  
  45.                 idTag = (idTag << 4) | (c - 'a' + 10);  
  46.             else  
  47.                 estado = 0xFF;  
  48.             estado++;  
  49.         }  
  50.     }  
  51.     else  
  52.         estado = 0;  
  53. }  
O programa principal fica monitorando a variável global que indica a detecção de um tag. Quando esta variável deixa de ser zero, indica que um tag foi detectado. O tag é então procurado na tabela, se achado o LED correspondente é aceso. Se o tag não estiver na tabela, e ela não estiver cheia, ele é acrescentado ao final. Em seguida a variável é retornada a zero, para indicar que está pronto para tratar uma nova leitura.
  1. for (;;)  
  2.     {  
  3.         if (tagLido != 0)  
  4.         {  
  5.             int i;  
  6.               
  7.             // Procura nos conhecidos  
  8.             for (i = 0; (i < 8) && (tags[i] != 0); i++)  
  9.             {  
  10.                 if (tags[i] == tagLido)  
  11.                 {  
  12.                     // achou, acende o LED correspondente  
  13.                     LED_PORT = 1 << i;  
  14.                     break;  
  15.                 }  
  16.             }  
  17.             if ((i < 8) && (tags[i] == 0))  
  18.             {  
  19.                 // Não encontrou e tem espaço, coloca na lista  
  20.                 tags[i] = tagLido;  
  21.                 LED_PORT = 1 << i;  
  22.             }  
  23.             tagLido = 0;  
  24.         }  
  25.           
  26.         //if (TIFR & _BV(OCF1A ))  
  27.         //{  
  28.         //    TIFR = _BV(OCF1A );         // Limpa indicação do timer  
  29.         //    LED_PORT ^= 0x80;  
  30.         //}  
  31.           
  32.     }  
  33. }  
Teste

O vídeo abaixo mostra o sistema em funcionamento. Cinco tags (três cartões e dois chaveiros) são apresentados primeiro em ordem e depois aleatoriamente. Para facilitar, os tag estão identificados com o número do LED correspondente.





Nenhum comentário: