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:
// Programa a USART para 9600 8N1
    UCSRA = _BV(U2X);                    // para maior resolução do baud rate
    UCSRB = _BV(RXEN)|_BV(RXCIE);     // liga recepção, com interrupção
    UCSRC = _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0) ;  // 8bit, 1 stop, sem paridade
    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).
// Último tag detectado
volatile uint32_t tagLido;

// Lista de tags conhecidos
volatile uint32_t tags[8];

// Interrupção de recepção da USART
ISR(USART_RXC_vect)
{
    static uint8_t estado = 0;
    static uint32_t idTag;
    uint8_t c;

    c = UDR;
    if (bit_is_clear(UCSRA, FE))
    {
        // recebeu caracter sem erro
        if (estado == 0)
        {
            // Aguardando o STX
            if (c == 0x02)
            {
                estado = 1;
                idTag = 0;
            }
        }
        else if (estado == 9)
        {
            // Aguardando o ETX
            if (c == 0x03)
            {
                // Recebeu ID com sucesso
                tagLido = idTag;
            }
            estado = 0;
        }
        else
        {
            // recebeu um dígito, vamos tratar como hexadecimal
            if ((c >= '0') && (c <= '9'))
                idTag = (idTag << 4) | (c & 0xF);
            else if ((c >= 'A') && (c <= 'F'))
                idTag = (idTag << 4) | (c - 'A' + 10);
            else if ((c >= 'a') && (c <= 'f'))
                idTag = (idTag << 4) | (c - 'a' + 10);
            else
                estado = 0xFF;
            estado++;
        }
    }
    else
        estado = 0;
}
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.
for (;;)
    {
        if (tagLido != 0)
        {
            int i;
            
            // Procura nos conhecidos
            for (i = 0; (i < 8) && (tags[i] != 0); i++)
            {
                if (tags[i] == tagLido)
                {
                    // achou, acende o LED correspondente
                    LED_PORT = 1 << i;
                    break;
                }
            }
            if ((i < 8) && (tags[i] == 0))
            {
                // Não encontrou e tem espaço, coloca na lista
                tags[i] = tagLido;
                LED_PORT = 1 << i;
            }
            tagLido = 0;
        }
        
        //if (TIFR & _BV(OCF1A ))
        //{
        //    TIFR = _BV(OCF1A );         // Limpa indicação do timer
        //    LED_PORT ^= 0x80;
        //}
        
    }
}
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: