Many of you may be familiar with the RFID module MFRC522, but I’m willing to bet that most of you have only interfaced this module with an Arduino and the Arduino IDE environment. However, if you need to use the RFID module with other MCUs, you may find yourself at a loss for how to proceed. If this is the case, you’ve come to the right place. In this blog, I will show you how to create a device driver for the RFID Module MFRC522, allowing you to interface it with any Host MCU. If you’re not sure what we mean by interfacing with a Host MCU or why it’s necessary, be sure to check out our blog for more information.
Continuing with the MFRC522 RFID reader interfacing to host MCU, Objective would be to interface this module with Host MCU’s like of NXP Semiconductors, STMicroelectronics or other vendors MCU’s. Will make the driver to interface the RFID Reader with any MCU, not unlike just with Arduino and Arduino IDE environment. You just need to change 2-3 low level API’s for running it on different MCU’s, would be telling about it in below sections. In this blog we are going to write the driver in c++ language. Before proceeding further would recommend viewers go through, the following set of blogs and videos to have a better understanding.
RFID Reader MFRC522: Overview and Datasheet Explanation( Highly recommended to go through at first)

MIFRAME RFID Tags: Overview and Datasheet Explanation
Hardware Connection of MFRC522 Reader module
MFRC522 Module has 8 pins exposed out, which can be categorised into 3 parts: Communication pins, Power Supply Pins and Additional Pins. As explained below

4 pins are communication pins, that would be connected to Host MCU either using SPI, I2C, or UART.

We would be making the connection using the SPI peripheral. Here I am referencing out hardware connection with 2 microcontrollers: NXP Semiconductors S32K144 MCU and STMicroelectronics STM32F103 MCU.
STMicroelectronics STM32F103 would be using SPI-1 Instance and S32K144 would be using LPSPI-0 Instance. 
2 pins are for Power Supply Connection, which would be used for powering the RFID reader. One can power the MFRC522 Reader via Host MCU. Connect the VCC and GND pins with the Host MCU Power pins. Make sure, you supply MFRC522 with 3.3 V, don’t power it with 5V.

There are 2 additional pins on MFRC522: IRQ and RST pins.
IRQ pin is an interrupt pin, that is used for alerting the HOST MCU when an RFID tag is in the vicinity. Read about the IRQ pin and interrupts in MFRC522 from here.
RST pin would be not used for this project.
These connections are for debugging and understanding purposes. By connecting the logic analyzers, we would be able to see how literally SPI communication and what commands we are sending, and what responses we are getting in bit and byte levels. Would recommend doing this step, as it makes the understanding clear at the root level. It would hardly take a couple of minutes to setup this.
For connecting the logic analyzer, connect the Channel1,3,5,7 of the logic analyzer with communication pins.
I am using the Salae logic analyzer, which is readily available. viewers can refer to this video on Gettobyte Youtube channel on how to set up logic analyzer connections, hardware, and software.

MFRC522 Functional Description API's
MFRC522 has a set of functional descriptions, on which the whole of the working of MFRC522 depends. To write the driver of MFRC522, it’s important to have an understanding of those functional blocks. Reading from the datasheet could be tiering, hence viewers can read and understand from here.
MFRC522 Host Interface API’s
We are going to connect MFRC522 via the SPI interface to the host MCU.
SPI configurations:
- MSB is sent first
- 8 Bits per transfer
- The clock is Low when inactive(CPOL=0)
- Data is valid on Clock Leading Edge(CPHA=0)
- Enable line is Active LOW
SPI Address Byte
These address bytes are of 6 bits. When sending the address byte, MSB should tell whether we have to perform a read/write operation on that address. LSB is always set to logic 0 when sending the address byte.
Thus you would find in the below driver that MFRC522 registers which are defined in GB_MFRC522.h have been left shifted 1 bit so that the MSB bit can be configured whether to perform a Read or write operation on that register address.
SPI Read Data
To read the data, Host MCU will send the register address at the MOSI line with LSB as 1 and then in MISO, it would get the data.
This would be performed via a low-level function, that is reading one single byte from the address GB_reg which is sent in its argument.
uint8_t GB_MFRC522_ReadRegister(PCD_Register GB_reg);
For e,g we have to read the version of the MFRC522, which can be done via VersionReg(ox37): uint8_t v = GB_MFRC522_ReadRegister(VersionReg);
- So it would be left shifted first: 0x37<<1 = 0x6E(So that can configure MSB for read and Write operation).

- And then as we want to perform a read operation at this register, so need to write MSB with 1. We will Or above value with 0x80( See definition of uint8_t GB_MFRC522_ReadRegister(PCD_Register GB_reg): 0x6E | 0x80 = 0xEE.
As per the datasheet, reading this register would give either 0x92 or 0x91.(Refer the datasheet for in-depth-description of this register). In logic analyzer reading from the MFRC522 would look like this:

SPI Write Data
To write the data, Host MCU will send the register address at the MOSI line with LSB as 0 and then will write bytes that have to be written at that address register.
This would be performed via a low-level function, that is writing one single byte value at the address GB_reg .
void GB_MFRC522_WriteRegister(PCD_Register GB_reg, uint8_t value)
For e.g, we have to write data on the CommandReg register(0x02), which is used to specify which command MFRC522 has to send to PICC(refer to MFRC522 Command set of previous blog): GB_MFRC522_WriteRegister(CommandReg, command);
- So it would be left shifted first: 0x02<<1 = 0x12(So that can configure MSB for read and Write operation).

- And then as we want to perform a write operation at this register, so need to write MSB with 0. That would be done by default. We are sending a Transcive command(0b1100/0xC)
In the logic analyzer writing on MFRC522 would look like this
MFRC522 FIFO API’s
MFRC522 has an Internal FIFO of 64 bytes. Broadly FIFO of MFRC522 can be controlled via 3 registers: FIFODataReg(0x09), FIFOLevelReg(0x0A), and WaterLevelReg(0x0B). These registers would be read and written directly using the Low-Level Host Interface API.
- FIFODataReg(0x09): Accessing this register, we can write or read data on the FIFO buffer.
- GB_MFRC522_WriteRegister(FIFODataReg, sendLen, sendData);
0x09 is left shifted by 1 byte, which becomes 0x12. Thus you can see below pic 0x12 is transmitted from the MOSI line and then in FIFO we are writing just 1 byte which is the REQA command(0x26)

- GB_MFRC522_ReadRegister(FIFODataReg, n, backdata, rxalign);
0x09 is left shifted by 1 byte, which becomes 0x012. And then further we OR it with Ox80 to read the FIFO buffer, which becomes 0x12 | 0x80 = 0x92. Thus you can see below pic HOST MCU is transmitting 0x92 and in return, it reads the FIFO buffer, which is basically ATQA( response of REQA which is transmitted above)
2. FIFOLevelReg(0x0A): Accessing this register, we can read how many bytes are there on the FIFO buffer.(0x0A<<1 ) | 0x80 = 0x94.
Reading the number of bytes received in FIFO after the REQA command is sent. Would be getting 2 bytes in FIFO. Hence on transmitting 0x94 on MOSI, we would be getting 0x02 at MISO.

MFRC522 CRC API’s
- gb_MFRC522_statusCodes PCD_CalculateCRC( byte *data, byte length, byte *result): Is the API that would be used for the calculation of CRC.( API defination see below driver source file)
- For using the CRC we have to configure its preset value. That can be done via the ModeReg register. In the ModeReg register, bits CRCPreset[1:0] are used for configuring the Preset value.
GB_MFRC522_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC co processor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)

We would be setting the preset to 6363h. Thus would be setting the CRCPreset bits to 01.
gb_MFRC522_statusCodes PCD_CalculateCRC( byte *data, byte length, byte *result): Is the API that would be used for calculation of CRC
MFRC522 Interrupts API’s
MFRC522 can trigger the interrupts when a certain event occurs. There are 4 registers for configuring the interrupts of MFRC522: ComIrqReg(0x04) & DivIrqReg(0x03) for indicating which interrupt has been triggered. ComIEnReg (0x02) & DivIEnReg(0x03) for configuring the behavior of the IRQ pin. These registers would be read and written directly using the Low-Level Host Interface API.
MFRC522 Time Unit API’s
MFRC522 has a Timer Unit, that is used for configuring the internal clock and analog interface. The timer unit has the following registers: TModeReg(0x2A), TPrescalerReg(0x2B),TReloadReg(0x2C and 0x2D) and TCounterVAlReg(0x2E & 0x2F). These registers would be directly written and read by Low-Level Host Interface API.
- GB_MFRC522_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds

- GB_MFRC522_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs.

- GB_MFRC522_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout.

- GB_MFRC522_WriteRegister(TReloadRegL, 0xE8);

MFRC522 Command Set API’s
MFRC522 operation is determined by certain commands. According to these commands, correspondingly MFRC522 would be performing some action. Host MCU will write the command code to the CommandReg Register(0x01). This Register would be read and written directly using the Low-Level Host Interface API.
Following is the table of the commands, that can be sent.
So say, we have to send Idle Command to MFRC522.
- GB_MFRC522_WriteRegister(CommandReg, PCD_Idle);

Host MCU sending Idlle command to MFRC522 - GB_MFRC522_WriteRegister(CommandReg, PCD_Transceive);

Host MCU sending Transcieve command to MFRC522 - GB_MFRC522_WriteRegister(CommandReg, PCD_CalcCRC);

Host MCU sending CRC command to MFRC522
MFRC522 State Machines
State Machines for doing various operations with MFRC522.
In this would talk about different state machines for the working of MFRC522.
- MFRC522 init state machine
- MFRC522 scanning PICC state machine
- MFRC522 Get UID PICC state machine.
- MFRC522 CRC
MFRC522 PICC Scanning State Machine
For scanning PCD would emit the REQA Command, it will emit the REQA command via short frame format(specified in ISO14443-3 doc). So Host MCU will configure the PCD FIFO to send the REQA command and then FIFO buffer would be transmitted. If PICC is in energizing field, it will start listening for valid REQ command and transmits its ATQA (Answer to Request) as a response into the PCD FIFO. This response would be used to check whether some PICC is scanned or not. When PICC is scanned it will be in ready state, if PICC is not scanned by PCD then it would be in idle state. 


MFRC522 Get UID PICC State Machine
PICC that are compatible with MFRC522 are MF1S503x, these are easily avaible in online stores and normally comes along with the MFRC522 modules. Below is the state machine to get the UID of PICC. For its programming implementation refer the GB_PICC_ReadCardSerial API() and GB_PICC_Select API(), which are specified below. Also so as to understand in bit and byte level communication for UID refer to PICC Command Set section of previous blog.





