viernes, 26 de enero de 2024

SPI Identificacion rfID con RC522

Uso del modulo RC522 con PIC

Fig1. Tarjetas y modulo RC522 rfID
 
Hola, te doy la bienvenida por visitar este blog, creado para compartir el mutuo interés en la electrónica y en especial la programación de microcontroladores PIC, te recuerdo que estoy atento a cualquier sugerencia o critica constructiva relacionada con esta publicación, al final dejo mis datos de contacto.
En esta ocasión veremos como comunicar un microcontrolador PIC16F con el modulo RC522, el cual permite llevar a cabo al lectura de tarjetas basadas en la tecnología rfID. El propósito sera de establecer la comunicación SPI, recuperar el numero serial de una tarjeta MiFARE y enviar este numero como mensaje a través del puerto serie UART del PIC16F.
 
La programación del microcontrolador se realizara utilizando el software  de diseño  MPLABX  y el compilador de lenguaje C para PIC XC8 ambos disponibles en la pagina de microchip de forma gratuita. Aquí dejo los enlaces de las versiones utilizadas para nuestro ejemplo:  <<MPLABX v6.20>>   <<XC8 v2.45>>
Mencionar también que es necesario contar con conocimientos mínimos sobre programación en lenguaje C y el uso de microcontroladores PIC16F.
 
Introducción a la Tecnología rfID
El termino RFID(Radio Frequency Identification), hace referencia al sistema de identificación basado en radiofrecuencia, el cual posibilita el almacenamiento y recuperación de datos en forma remota utilizando etiquetas o tarjetas de identificación diseñadas para este fin.
El sistema opera en los rangos de 125 134kHz, 140 148.5kHz clasificado como frecuencia baja y 13.56MHz en frecuencia alta. El conjunto esta formado de dos partes:

Fig2. Sistema rfID
  • Etiqueta(Transpondedor): formado por una antena(RF), la unidad de control digital(MCU) y una memoria(EEPROM).
  • Lector(Interrogador): compuesto por la antena(RF), un decodificador y el puerto de comunicación serial UART/SPI/I2C.
El lector mantiene a su alrededor un campo electromagnético de modo que al acercar una etiqueta al lector, ésta se alimenta de la energía inducida por el campo electromagnético, estableciendo así la comunicación.
La distancia de lectura depende de la potencia del módulo lector, en nuestro caso son aproximadamente 50mm(RC522), pero existen lectores con mayor rango de alcance. 
En cuanto a la etiqueta que utilizaremos, MIFARE es una tecnología de tarjetas inteligentes sin contacto, propiedad de NXP Semiconductors que cumplen con el estándar ISO-14443 tipo A. Siendo actualmente una de las que mayor uso tiene a nivel mundial.
En forma general podemos encontrarlos bajo la siguiente clasificación:
  • MIFARE Ultralight 512 bit Sin Seguridad
  • MIFARE Classic 1K-4Kbytes Seguridad Crypto-1
  • MIFARE Desfire 4Kbit Seguridad 3DES
El ISO-14443 consta de cuatro partes y se describen dos tipos de tarjetas, el tipo A y el tipo B, las principales diferencias entre ambas, se centra en el mecanismo de modulación, codificación y protocolo de inicialización.
 
Descripción del Modulo RC522.
Es un lector para tarjetas rfID basado en el circuito integrado MFRC522, opera en frecuencia 13.56MHz y soporta ISO14443 tipo A. El modulo es de fácil conexión al microcontrolador mediante la interfaz serial SPI el cual puede opera hasta los 10MHz, con alimentación de 3.3V y consumo inferior a los 100mA.  La fig3. ilustra la distribución de pines del modulo RC522, la conexión al modulo sera directa siempre que el microcontrolador trabaje con 3.3V, caso contrario era necesario convertidores de nivel en las lineas MISO/MOSI/SCK/SS.
Fig3. Pines modulo RC522

Descripción de la Tarjeta MiFare 1K.
Esta tarjeta desarrollada por NXP posibilita llevar a cabo transacciones en menos de 100ms, lo cual resulta adecuado para aplicaciones como el cobro de peaje, boletería y control de acceso. En cuanto a sus características describiremos tres:
 
1. Identificación.  Provee un mecanismo de Anti-colisión que posibilita la lectura simultanea de varias tarjetas, el proceso dura aproximadamente 3ms, con incremento de 1ms por cada colisión detectada.
 
2. Almacenamiento. Consta de una memoria EEPROM con capacidad de 1kB, la misma que se encuentra distribuida en 16 sectores, cada sector formado por 4 bloques y cada bloque formado por 16 bytes, ver fig4.
Fig4. Mapa de memoria EEPROM 1kbyte
El ultimo bloque de cada sector se utiliza para guardar las llaves y los bits de configuracion de acceso por cada sector, cada llave A/B se compone de 6 bytes con un valor por defecto de 0xFF-FF-FF-FF-FF-FF-FF, la llave A no se puede leer por esta razón es utilizada para la autenticación, mientras que la llave B que es opcional si puede leerse,  en este bloque también se encuentran los bits de acceso con un valor por defecto de 0xFF078069, donde el ultimo byte, puede usarse para guardar datos del usuario.
3. Seguridad. Cuenta con identificador único (4 bytes), autenticación mutua de tres pasos y clave de acceso por sector (KeyA/B). El proceso de autenticación de tres pasos se ilustra en la fig5.
Fig5. Procedimiento de autenticación de tres pasos
Observe que de acuerdo al esquema que muestra la Fig5. las operaciones en memoria requieren completar el proceso de autenticación, el cual implica validar las llaves A/B para acceder a cada bloque y efectuar cambios incluyendo la actualización de llaves, el proceso de autenticación esta limitado a una cantidad de intentos, pasado la cantidad de intentos el acceso quedara bloqueado de manera irreversible.
Con el valor por defecto 0xFF0780 que tiene los bits de control de cada bloque, se permiten el acceso autenticado con llave A para los siguiente:
  • Modificar la llave A, pero no es posible leer su valor.
  • Modificar los bits de acceso y la llave B del bloque.
  • Realizar operaciones de lectura, escritura, incremento y decremento.
  • Se puede leer la llave B, siendo posible usar este campo para datos.
A raíz de la amplia aceptación de esta tecnología y el buen éxito de la familia MiFARE Classic, los requerimiento de seguridad en la industria también se han incrementado, y por lo tanto es  importante considerar el de nuevos productos como la MiFARE Plus.
 
 Esquema del circuito PIC

La figura 6 nos muestra el esquema del circuito utilizado para las pruebas de nuestro programa, y donde se puede observar la conexión entre el microcontrolador PIC16F887 y el modulo RC522. Parte del circuito esta alimentado por una fuente de 5V, y a través de un regulador de voltaje lineal MCP1702, se consiguen los 3.3V necesarios para el funcionamiento del modulo RC522, por esta razón para garantizar la compatibilidad de los niveles lógicos en las lineas de comunicación del modulo RFID, se utilizan las resistencias como divisores de tensión.  Por otro lado la comunicación serial con la PC, utiliza un convertidor USB a UART, en nuestro caso es un modulo CP2102, pero puede ser cualquier otro modelo. Finalmente se dispone de tres diodos LED, uno conectado al pin RE2 que servirá para indicar la actividad del programa y dos conectados al pin RD0 y RD1 que nos indicaran mediante el color si el acceso esta autorizado(Verde) o denegado(Rojo), además un pulsador de entrada en RB0 permite realizar un procedimiento de carga de valor en la memoria de la tarjeta RFID.

Fig6. Esquema del circuito PIC16-RC522

Programa del Microcontrolador PIC
El objetivo de este programa es comprobar la conectividad PIC16F - RC522, para lo cual se realizara la identificación de una tarjeta MiFARE, para obtener la etiqueta del modelo de tarjeta y el numero de serie correspondiente, sin entrar en la fase de autenticación y operaciones en la memoria EEPROM, que veremos mas adelante con el programa final presente en el archivo de proyecto MPLABX cuyo enlace se encuentra al final. 

En el programa se hace uso de las interrupciones del temporizador TMR0 y el receptor USART del PIC, cabe resaltar que ninguna de las tareas en el programa representa un proceso bloqueante, la codificación en si utiliza una técnica de programación por estados, si quieres conocer mas detalles al respecto, te recomiendo revises antes de continuar, la siguiente publicación. 

En programa haremos usos de diversos procedimientos y funciones declarados en los archivos de cabecera de las librerías mrc522.h y spi.h, aclarar que el  código de estas librerías se crearon a partir de otras fuentes que encontré en la red.

Ahora describiré las secciones que tiene el código principal main.c con una breve explicación de lo que se pretende realizar. Recuerda que al final de esta publicación esta el enlace para descarga del proyecto MPLABX que contiene todos lo archivos utilizados.

#pragma config FOSC=INTRC_NOCLKOUT, WDTE = OFF, BOREN = OFF, LVP = OFF
#include <xc.h>
#include <stdio.h>
#include "spi.h"
#include "mrc522.h"
#define LEDpin PORTEbits.RE2
//Led de la tarjeta
#define DS1pin PORTDbits.RD0
//Led Verde
#define DS2pin PORTDbits.RD1
//Led Rojo
#define SW1pin PORTBbits.RB0
//Entrada Selector
volatile __bit tickms;
uint8_t buff[16];    
uint16_t tagtype;
//2-byre para el tag_id
uint32_t tagserial;
//4-byte para el tag_serial
uint8_t tagkey[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
//Default

void main(void)
{
    setupMCU();
//Configura el PIC
    MRCInit();
//Inicializa el modulo RC522
    printf("\r\nLISTA RFID\r\n");
    while(1)
    {
        if(tickms)
//Ejecuta cada 1ms
        {
            tickms = 0;
            taskLED();
//Destella LED
            taskAPP();
//Lectura y cambio del RFID
        }
    }
}

En las lineas luego de la configuracn e inicialización de los modulo, el programa ingresa en un bucle condicionado por la bandera tick1ms, que se activa cada milisegundo de acuerdo a la interrupción del temporizador TMR0, para este fin se utiliza la interrupción del temporizador TMR0. Por lo tanto podemos resumir que dentro del bucle del programa principal se realiza la llamada concurrente cada milisegundo de dos procedimientos:

taskLED: Es un procedimiento que se ejecuta en cada milisegundo, internamente utiliza una variable estática como contador para determinar la condición de encendido y apagado del LED conectado al pin RE2, el objetivo es destellar la luz cada segundo con un ciclo activo(encendido) de un 20%, para indicar la actividad del programa que corre el PIC. las siguientes lineas describen el código de este procedimiento. 

void taskLED(void) //Destello de LED1 1Hz al 20%
{
    static uint16_t cnt = 0;
    if(cnt++ > 999)
    {
        cnt = 0;
        LEDpin = 1;
//Activa LED
    }
    if(cnt == 200) LEDpin = 0;
//Apaga LED
}

taskAPP: Este procedimiento que internamente subdivide el trabajo en estados, se encarga de la comunicación con el modulo RFID llevando a cabo las siguientes operaciones:

- Detectar una tarjeta valida en el lector
- Recuperar su numero de serie
- Seleccionar la tarjeta para la autenticacion de un sector
- Efectuar las operaciones respectivas.

 Las siguientes lineas muestran el código de este procedimiento que describiremos por etapas. 

void taskAPP(void)
{
    static uint8_t state = 0;
    static uint16_t cnt = 0;
    int32_t value = 0;
    uint8_t res, i;
    switch(state)
    {
        case 0:
//Detecta tarjeta RFID,  con intervalos de 250ms
            if(MRCRequest(PICC_REQIDL, buff)== MI_OK)
//step1
            {
                tagtype = buff[0];
                tagtype <<= 8;
                tagtype |= buff[1];        
                state = 1;
//Estado de Tarjeta detectada
            }
            else
            {
                MRCHalt();
//Finaliza comunicación RFID
                state = 2;
//Estado de espera 250ms
            }
            break;
        case 1:
//Lectura del serial ID
            if(MRCAntiColl(buff) == MI_OK)
//step2   
            {
                tagserial = buff[0];
                tagserial <<= 8;
                tagserial |= buff[1];
                tagserial <<= 8;
                tagserial |= buff[2];
                tagserial <<= 8;
                tagserial |= buff[3];
                printf("\r\nID Card:%lX\r\n",tagserial);
                if(MRCSelectTag(buff) == 0x08) state = 4;
//step3                             else
                {
                    printf("SAK Incorrecto");
                    MRCHalt();
//Finaliza comunicación RFID
                    state = 3;
                }
            }
            else
            {
                MRCHalt();
//Finaliza comunicación RFID
                state = 2;
            }
            break;
        case 2:
//Espera de 250ms para detección
            if(cnt++ > 249)
            {
                cnt = 0;
                state = 0;
//Reinicia estado
            } else _nop();
            break;    
        case 3:
//Espera de 2000ms entre lecturas  
            if(cnt++ > 1999)
//Espera dos segundos entre lecturas
            {
                DS2pin = 0;
                DS1pin = 0;
                cnt = 0;
                state = 0;
            } else _nop();
            break;
        case 4:
//Autenticación y actualización del valor del bloque 2
                if(MRCAuth(PICC_AUTHENT1A, 2, tagkey, buff) == MI_OK)
                {
                    if(SW1pin == 0)
//Si pulsador esta presionado
                    {
                        MRCEncodeValue(5, 2, buff);
//Codifica Valor 5
                        MRCWrite(2, buff);
//Escribe en el bloque 2
                        printf("Recargado");
                    }
                    else
                    {
                        MRCUpdateValue(2, PICC_DECREMENT, 1);
//Decrementa
                        MRCRead(2, buff);
//Lectura del bloque 2
                        value = MRCDecodeValue(buff);
//Decodifica valor
                        if(value >=0)
                        {
                            printf("Credito:%ld", value);
                            DS1pin = 1;
                        }
                        else
                        {
                            printf("Sin credito");
                            DS2pin = 1;
                        }
                    }                
                }
                MRCHalt();
                state = 3;
            break;
    }
}

El primer estado (case 0) dentro del procedimiento, verifica la disponibilidad de un chip rfID valido con lector RC522, si no hay un chip identificado el procedimiento realizara una espera de 200ms para repetir el proceso. La función utilizada es MRCRequest(PICC_REQIDL, buff), que retornara 0 (MI_OK) si existe una tarjeta como la MiFARE dentro de la zona de proximidad del lector RC522, y guardara en el vector buff dos bytes de identificación cuyos valores corresponden con la siguiente tabla:

  • 0x4400 = Mifare_UltraLight
  • 0x0400 = Mifare_One(S50)
  • 0x0200 = Mifare_One(S70)
  • 0x0800 = Mifare_Pro(X)
  • 0x4403 = Mifare_DESFire

Si la identificación es correcta, el procedimiento pasara el segundo estado (case 1) para realizar la lectura del numero serial de la tarjeta y preparar la etapa de autenticacion. 

El numero de serie se obtiene con la función MRCAntiColl(buff), esta función recupera los primeros cuatro bytes del bloque 3, sector 0 que es de solo lectura y contiene el numero serial único establecido por el fabricante conforme a la figura 7.

Fig7. Datos del fabricante, solo para lectura
 
Una vez registrado el numero de serie de la tarjeta en el vector buff, se enviara por el puerto UART un mensaje ID Card: 03505010 y la tarjeta sera seleccionada para la autenticacion mediante la función MRCSelectTag(buff), para el caso de una tarjeta MiFARE el valor a retornar debe ser 0x08 para que el procedimiento inicie la autenticacion pasando al quinto estado (case 4), caso contrario se pasa el cuarto estado (case 3) que espera un tiempo de dos segundos para repetir nuevamente el proceso.

El ultimo estado del procedimiento es para la autenticacion de la tarjeta en el bloque 2 con la función MRCAuth(PICC_AUTHENT1A, 2, tagkey, buff), utilizando la llave A establecida por defecto, entonces dependiendo del resultado y el estado del pulsador SW1, se llevaran a cabo las siguientes operaciones: 

Si el pulsador SW1 se encuentra presionado, entonces se realizara la escritura del bloque 2 con la función MRCWrite(2, buff), donde los 16 bytes del bloque estarán codificados en un formato de Valor(ValueBlock) que se lleva a cabo utilizando el procedimiento MRCEncodeValue(5, 2, buff), es valor que se escribe es un numero 5 que representa en nuestro ejemplo la recarga de un valor de crédito a una tarjeta.

Si el pulsado SW1 no esta presionado, entonces se decrementa un uno al valor del bloque 2, con la función MRCUpdateValue(2, PICC_DECREMENT, 1) y luego se realizara la lectura del bloque con MRCRead(2, buff), para luego recuperar el Valor con el procedimiento MRCDecodeValue(buff).

El propósito de este ultimo estado es recargar una tarjeta con crédito y usar la operación para decremento unitario de forma automática cada vez que se realiza la lectura de la tarjeta, entonces una luz verde se activara siempre que la tarjeta tenga crédito y una luz roja cuando ya no cuente con saldo.

Seguidamente se describe el procedimiento de configuracion del PIC y la rutina de interrupción ISR.

void setupMCU(void) //Procedimiento para configurar el PIC
{
    OSCCONbits.IRCF = 0b111;
//Oscilador Interno 8MHz
    while(OSCCONbits.HTS == 0);
    ANSEL = 0;
//Desactiva pines ADC AN0 al AN7
    ANSELH = 0;
//Desactiva pines ADC AN8 al AN13
    TRISEbits.TRISE2 = 0;
//Salida Pin LED
    PORTEbits.RE2 = 0;
//Apaga el LED
    TRISDbits.TRISD0 = 0;
//Salida DS2 Verde
    TRISDbits.TRISD1 = 0;
//Sal;da DS1 Rojo
    PORTD = 0;
//Salidas en nivel bajo
    OPTION_REGbits.nRBPU = 0;
//Activa las pull-ups PORTB
  
  /* CONFIGURACION TIMER0 1MS a Fosc=8MHz*/
    OPTION_REGbits.T0CS = 0;
//Modo Termporizador
    OPTION_REGbits.PSA = 0;
//Con prescala
    OPTION_REGbits.PS = 0b011;
//Prescala 1:16
    TMR0 = 131;
//256-(time/((pre)*(4/Fosc))) time=0.001 seg
    INTCONbits.T0IF = 0;
//Limpia bandera
    INTCONbits.T0IE = 1;
//Activa interrupción del TMR0
   
/* CONFIGURACION UART 9600 BPS*/
    BAUDCTLbits.BRG16 = 0;
//8-bit BaudGen
    TXSTAbits.BRGH = 1;
//Modo High Speed
    TXSTAbits.TXEN = 1;
//Habilita el transmisor UART
    //RCSTAbits.CREN = 1;
//Habilita el receptor UART
    RCSTAbits.SPEN = 1;
//Activa el modulo UART
    SPBRG = 51;
//Formula [8M/(16 * 9600)] - 1
   
/* CONFIGURACION MSSP MODO SPI 500KHz 8MHz*/
    TRISCbits.TRISC5 = 0;
//SDO Salida en modo master
    TRISCbits.TRISC3 = 0;
//SCK Salida en modo master
    SSPCONbits.SSPM = 0b0001;
//Master SPI(Fosc/16)=500KHz
    SSPSTATbits.SMP = 1;
//Data sample at end SMP = 1    
    SSPCONbits.CKP = 0;
//Clock Idle low level SCK=0
    SSPSTATbits.CKE = 1;
//Positive edge clock enable CKE = 1;
    SSPCONbits.SSPEN = 1;
//Enable SSP Module
    INTCONbits.GIE = 1;
//Habilitador global ISR
}

void __interrupt() isr(void)
{
    uint8_t res;
    if(INTCONbits.T0IF)
    {
        INTCONbits.T0IF = 0;
        TMR0 += 131;
        tickms = 1;
    }
}

Pruebas de funcionamiento
Antes de abrir el proyecto en MPLABX, asegúrate de tener instalado la versión del compilador XC8, una vez confirmado procedemos a abrir el proyecto, y luego de llevar a cabo la exploración de los archivos, procedemos a compilar el programa, si no hay errores procedemos a cargar el código al microcontrolador y llevar a cabo las pruebas de funcionamiento. 

En primer lugar debe notar que el led de indicación destella una vez por segundo, esto nos indica la correcta ejecución del programa, restando únicamente acercar una tarjeta RFID para que se lleve a cabo la identificación y se muestre un mensaje a través del puerto serie. Para poder visualizar este mensaje utilizaremos un complemento disponible para MPLABX, accediendo a menu->tools->plugins, en esta ventana nos aseguramos de activar la instalación de Simple Serial Port Terminal

Fig8. Instalación de Simple Serial Port Terminal

Una vez instalado Simple Serial Port Terminal se debe configurar el puerto COM asignado al convertidor UART-USB, en windows la asignación tiene nombre COMxx, mientras que en linux es un acceso a ruta /dev/ttyUSBx.

Finalizada la configuracion del terminal, cada vez que se aproxime una tarjeta MiFARE, veremos un mensaje con el numero serial de la tarjeta y la cantidad de crédito que dispone como se observa en la Figura 9. Recuerde que para hacer una recarga es necesario aproximar la tarjeta manteniendo el pulsador SW1 presionado. 

Fig9. Mensajes recibidos en el Puerto Serie
 La figura 10, muestra el circuito utilizado para el proyecto, donde se puede observar una placa de pruebas PIC16F, circuito de conversion de nivel, modulo lector RC522 y llavero con chip MiFARE Classic.

Fig10. Circuito PIC utilizado para el programa

Conclusiones y Recomendaciones.
Verificado el funcionamiento adecuado de la comunicación entre el PIC16F y el modulo RC522 como parte del objetivo de esta publicación, si bien las características de las tarjetas MiFARE posibilitan mayores prestaciones de seguridad, tales como la Autenticación por bloque, y el almacenamiento de variables en memoria con operaciones de auto incremento o decremento. En muchas situaciones solo basta con llevar a cabo la lectura del numero serial, puesto que la identificación de la tarjeta posibilita asociarlo con un elemento u objeto sujeto al control, por ejemplo se puede consultar un base de datos del personal autorizado en base al numero de serie en su tarjeta de control.

Aquí dejo los enlaces para que puedas descargar el proyecto, se encuentra en formato gzip, debes descomprimir y abrir con MPLABX  
Si quieres ver como compilar e implementar los proyectos de este blog, elaborados con MPLABX mira este vídeo.<Compilando proyectos MPLABX>
 
Sin mas que mencionar agradezco tu visita al blog y espero que el ejemplo visto pueda ser útil en tu formación y el proyecto que desarrollas.

Atentamente, Pablo Zárate Arancibia  email: pablinza@me.com / pablinzte@gmail.com, @pablinzar

Santa Cruz de la Sierra - Bolivia

No hay comentarios.:

Publicar un comentario