RC522 and PN532 RFID Basics

34K2144

Intro: RC522 and PN532 RFID Basics

NOTE: I now have Instructables that offer Arduino code for the RC522 and PN532.

Some time ago I bought three different RFID modules for experimenting. In a previous project I detailed how to use a simple 125-kHz module to do a basic security function. Modules like that use read-only tags so the process is scan for the ID, store if desired, and compare against stored ID’s. The other modules I bought operate at 13.56-MHz and use tags that can be both read and written so it’s kind of a waste to simply use them for basic security. The two common modules use either the RC522 chip or the PN532 chip – both made by NXP.

If you’ve read any of my other projects you know that I like to use cheap PIC microcontrollers and program in assembly language. So what I was looking for was a sequence of steps required to talk to the modules and to the RFID tags. While there are lots of example programs online for the modules, most of them are written in ‘C’ software for the Arduino and use the SPI interface. Also, the manuals for the chips and for the Mifare tags take a bit of deciphering. This post is primarily about the information I wish I had when I started the project. I also include PIC assembly software programs for performing the basic commands required by each module. Even if you don’t use a PIC and/or assembly language, the source code should at least provide you with a good idea of the specific commands required to perform each step.

STEP 1: Serial Interfaces

Both of the chips used on these modules are capable of interfacing via SPI, I2C, or UART (HSSP). The PN532 module has a DIP switch that is used to select the desired interface but the MFRC522 module is hardwired for the SPI interface. I prefer to use the built-in UART of the PIC, so I hunted online to see if there was a way to get the MFRC522 module into the UART mode. What I found was that cutting one trace on the board would do the trick. The cut effectively removes 3.3 volts from the EA pin of the chip. Technically the EA pin should then be connected to ground but not many people can pull off that soldering feat given the chip pin density. Not to worry, though, because the EA pin does not have an internal pull-up and doesn’t “float” like the old TTL logic inputs do. Refer to the chip diagram and the board section picture for the spot to cut. Make sure that you only cut the short trace going directly to the EA pin.

STEP 2: Hardware

The hardware connections for UART communications are shown in the diagram above. The UART connections for the MFRC522 are not marked on the board but, as shown in the schematic, the SDA pin receives UART data and the MISO pin transmits UART data. The PN532 module has the UART markings on the bottom side of the board.

Both modules run on 3.3 volts and the 5-volt logic level from the PIC TX pin also needs to be limited. The LCD connection is the standard 4-bit setup that has been used in a number of my previous projects. The default format for all of the messages is set for the standard 1602 LCD (16 characters by 2 lines). I also have a 40 character by 2 line LCD that I use for raw data dumps during debugging so I included a define in the software that allows me to take advantage of the extra display space.

STEP 3: Data Blocks

The Mifare Classic 1k tags used for this project are configured as 16 sectors, four data blocks per sector, 16 bytes per data block. Of the 64 data blocks, only 47 are actually usable. Data block 0 contains manufacturer data and blocks 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, and 63 are called Trailer blocks. The Trailer blocks are the last one in each sector and they contain two keys and the block access bits. The keys and block access bits apply to just the data blocks in that sector so you could have different keys and access rules for each sector. The default keys are set to “FF FF FF FF FFh”. For this basic project I use just one data block and keep the default keys and access bits. There are lots of documents related to these cards so just do an online search for “Mifare” or visit the NXP website if you want to explore them in more depth.

STEP 4: General Operation

While both modules are unique in the way that they are accessed and the way they access the tags, there is a general process that is required to get the job done. For this project we assume that the tags are the Mifare Classic 1k type and that we are only allowing one tag at a time in the antenna field. The basic steps are defined below.

· Initialize the module: In general this requires things such as writing values to registers in the chip, sending “wakeup” commands, and turning power on to the antenna. In a battery operated application you would want to be able to turn the antenna power on and off to save the battery but for this simple application we turn it on once and then leave it on.

· Clear the crypto flag (522 only): When a tag is authenticated a flag gets set to let the user know that communications with the tag will be encrypted. This flag needs to be cleared by the user prior to the next scan, even if the tag being scanned is the same one.

· Scan for a tag: The module basically asks “Is anyone out there?” and the tag responds “I’m here”. If the module doesn’t get a quick response it stops listening. That means that we need to repeatedly send scan commands to the module until it finds a tag.

· Get the tag User Identification number (UID): The tag will respond to the scan request with some limited information such as the type of tag it is. That means that we may need to send another command to get its UID. The UID is four bytes for the Mifare Classic 1k tags. If may be longer for other tags but this project doesn’t address them.

· Select the tag (522 only): The UID is used to select the tag that the user wants to authenticate for reads and writes. This is based on the possibility that there may be more than one tag in the antenna field. That is not the case for our simple application but we need to select the tag anyway.

· Authenticate the tag: This step is required if we want to do any reading or writing of the tag. If all we want to do is to differentiate between tags for a simple security application then the UID is enough. Authentication requires that we know the UID and that we know the crypto key for the data sector of the tag we want to access. For this project we stick with the default keys but my follow-on project changes the keys so that the tag can be used as an electronic wallet.

· Read or write the tag: Reads always return all 16 bytes of the Data Block requested. Writes require that all 16 bytes be written at the same time. If you want to read or write another block in the same data sector the tag does not need to be authenticated again. If you want to read or write a block in a different data sector then the tag needs to be authenticated again using the key for that sector.

STEP 5: MFRC522 Module Access Sequence

The startup routine includes these basic steps found in most of the applications I looked at:

· Send dummy data byte (see the next paragraph)

· Soft reset

· Set RF receiver gain (if something other than the default is desired)

· Set ASK modulation percentage to 100%

· Set seed value for CRC calculations

· Turn on the antenna

· Get firmware version (not required)

For some unexplained reason my module powers up and thinks that it has received a write command without the data byte. I don’t know if this is just an issue with my module but I haven’t seen any references to it elsewhere. I experimented with both hardware and software resets and neither fixed the problem. My solution was to add a dummy read call to register “0” (undefined) at the start of the module initialization routine. If the module sees this as data for the unknown write command there doesn’t appear to be any ill effects. If it sees it as a read command, then nothing useful happens. It bothers me that I can’t fully define the issue, especially given that a hardware reset of just the module doesn’t fix the problem.

The RC522 chip is composed of a number of registers, most of which are both read and write. To perform a write, the register number is sent to the module followed by the value to write. To perform a read, the register number has 0x80 added to it and that is sent to the module. The response to a write command is an echo of the register accessed. The response to a read command is the contents of the register. The software takes advantage of that knowledge to verify that the command was properly executed.

STEP 6: PN532 Module Access Sequence

The startup routine includes these required steps:

· Send an initialization string: This is specific to the UART interface. The manual states that the UART interface will wake-up on the fifth rising edge detected on the interface. It recommends sending 0x55, 0x55, 0x00, 0x00, 0x00, 0x00. For the most part, there just needs to be a sufficient number of characters with rising edges and they must not look like a command preamble (00 00 FF).

· Wake up the module: Buried in the user manual it shows that the module initializes into a sort of sleep state called “LowVbat”. To exit this state we need to send a “SAMConfiguration” command.

The PN532 expects commands to be sent in a defined message format that includes a preamble, the message, and a postamble. The response messages follow the same format. The command and response messages both include a TFI (Frame Identifier) and a command version. The command uses a TFI of 0xD4 and the response uses 0xD5. The command versions vary but the response will always increment the command version and return it in the byte following the TFI. That consistency allows for the response messages to be easily scanned for the relevant information.

Each command message (following the preamble) consists of the message length, the 2’s complement of the message length, TFI, command, data, checksum, and postamble. The software builds the individual commands and then calls a routine that calculates the checksum and appends the postamble.

The message format for the response is similar to that of the command. A typical response will include an ACK (00 00 FF 00 FF 00) followed by the specific response to the command. Each command response begins with a preamble of 00 00 FF. The response should also have a TFI byte of D5 followed by the command number incremented by 1. For our “SAMConfiguration” command (14) that would be 15. The “SAMConfiguration” command gets this response: 00 00 FF 00 FF 00 00 00 FF 02 FE D5 15 16 00.

There are other module-specific commands that can be sent but they aren’t needed for this application. I did, however, include a routine that can be called to retrieve the firmware version number. A typical response (after the ACK and preamble) would be: 06 FA D5 03 32 01 06 07 E8 00. The “01 06 07” indicates firmware version number 1.6.7.

STEP 7: Tag Access Sequence

After the module gets ready, we can send commands specific to the tags. In order to read or write tag data we need to have its identification number (UID). The UID and key will then be used to authorize a specific tag data sector for reads/writes. Tag data reads/writes are always done on all 16 bytes in a specified data block. That means that the typical application will read the data block, modify the data as desired, and then a write the new data back to the tag.

STEP 8: Software

The interrupt handler software gets called whenever the PIC UART receives a byte of data. In some of my previous UART projects I was able to just poll the RX interrupt flag instead of having to use an interrupt handler. That is not the case for this software, especially for the PN532 which communicates at a much higher baud rate than the RC522. The UART interface of the RC522 is limited to 9600 baud while the default for the PN532 is 115k and can be set as high as 1.288M baud. The received bytes are stored in a buffer area and the main part of the software retrieves them as needed.

The New_Msg flag indicates that bytes have been received and Byte_Count indicates how many. I’ve included a “Disp_Buff” routine in the software that can be called to display the contents of the receive buffer during debugging. Some of the return messages will overflow a typical 1602 display but I have a 40 character by 2 line LCD that I found at an online surplus electronics site. The “Max_Line” define can be set for your LCD size. If “Max_Line” is reached, the “Disp_Buff” routine continues by writing to the second line. You could add a little code to that routine to continue onto lines three and four if you have a 4-line LCD. For the PN532 there is a flag that can be set so that the routine either dumps all received bytes or just dumps the 16 data bytes from a read response.

There isn’t a need to clear the receive buffer or Byte_Count because clearing the New_Msg flag will cause Byte_Count to get cleared by the interrupt handler and that is what is used as the index into the buffer. New_Msg usually gets cleared before each command step so that the results specific to that command can be easily located and verified. In the RC522 that means that the receive buffer usually has only 1 to 4 bytes. In some cases, such as data block reads, the Read_FIFO command must be issued multiple times in order to move the bytes from the FIFO into the receive buffer. All command results for the PN532 end up in the receive buffer so a scan procedure is performed to locate the specific bytes needed.

The main loop in the software scans for a tag and then authenticates the tag for reads/writes. For the test software included here the variable Junk_Num is modified each time through the main loop and is used during the write to the tag. The values written alternate between the value of Junk_Num and the 1’s complement of Junk_Num. Finally, the 16 written values are read and displayed. There are display messages for each step with delay routine calls to allow time to read each message. Error messages are also provided but should normally only occur if the tag is removed during an operation.

Part of the software initialization is a section of code that is only executed on power up and is skipped if a software reset is detected. The error messages generally terminate with a software reset as a way to exit the main loop. The reset happens in the “Tilt” routine which simply enables the Watchdog Timer and then goes into an infinite loop waiting for the timeout.

STEP 9: MFRC522 Unique Software

The RC522 chip requires more low-level instructions than the PN532 chip to accomplish communications with tags. It’s kind of like programming in assembly language versus programming in “C”. Another significant difference is that the RC522 requires that communications with the tag get funneled through a FIFO buffer. The routines “Write_FIFO” and “Read_FIFO” handle those tasks. The MFRC522 software includes a section for many of the lower-level commands from which the main functions are built.

The tag command checksum calculation for the RC522 is very different than for the PN532. After the tag command is built in the FIFO, a module command is sent to calculate the checksum. The 16-bit result is not automatically appended to the tag command but is available for reading from two 8-bit registers. The checksum calculation wipes out the data in the FIFO so the required sequence is as follows:

· Build the command in the FIFO

· Command a checksum calculation

· Build the command in the FIFO again

· Read the CRC registers and write the checksum bytes to the FIFO

· Send either a Transceive or Authenticate command

The Transceive command will transmit the FIFO buffer and then automatically switch to receive mode to wait for the response from the tag. The Transceive command must be followed by the setting of the StartSend bit in the BitFramingRegister in order to actually transmit the data. The Authenticate command doesn’t have that requirement.

In general, the Arduino “C” code applications available online use the interrupt flag registers and the timeout register to ensure that the correct response is received in a timely fashion. In my opinion that is overkill for this non-time critical application. Instead, I use short software timeouts to wait for the response and then verify that it is correct. The manual for the Mifare tags details the timing for the various transactions and time is also allowed for the expected number of bytes to be received. These time delays are built into most of the low-level command subroutines.

STEP 10: PN532 Unique Software

After the module is initialized, the steps needed to find and authenticate the tag are accomplished by writing the appropriate command followed by the necessary data. The scan command returns the UID which is then used for the authentication. After that, reads and writes of the tag send or return the 16-bytes for the addressed data block.

The initialize sequence was detailed earlier and the same software routine also sends the SAMConfiguration command to get the module out of the “LowVbat” state. The rest of the basic commands, such as Scan, Authenticate, Read/Write Tag, are just built sequentially in the applicable routines. The checksum is calculated by just adding up the command bytes, doing a complement, and then adding 1 to make it a 2’s complement. The 8-bit result is appended to the command string just before the postamble.

There is no FIFO like in the RC522 so the complete response messages are received automatically. The “Find_Response” routine scans the receive data buffer for the TFI (0xD5). The routine takes advantage of knowing what the expected messages should be and ignores simple ACK responses that don’t include data. Once the TFI is found, the desired responses are a known offset from it. The command echo and command status bytes are saved off by the “Read_Buff” routine for later verification.

That’s it for this post. Check out my other electronics projects at: www.boomerrules.wordpress.com

35 Comments

Does either of the PN532 or RC522 support ISO 14443-4 compatible dual mode card Fudan FM1280? I am using ESP32 with PN532 module from SeeedStudio in HSU and Chinese RC522 in SPI and they both read Mifare Classic 1K cards UID ok, but do not notice the FM1280 card at all. The ReadPassiveTarget just retuns with timeout as if no cards are presented.

My mobile phone reads Fudan FM1280 card ok as ISO 14443-4 NXP Semiconductors IsoDep, NfcA, ATQA: 0x1203, SAK: 0x20. So the card should work ok I guess.
With second read on Android NFC Tools the Fudan FM1280 is identified as
NXP - Mifare DESFire
IsoDep, NfcA, NdefFormatable
ATQA: 0x0344
SAK: 0x20

Why it does not work with PN532 or RC522 or are there some settings that would need to be enabled. I just need to read the UID and not the data.
I do not know anything about the Fudan card.
I do not know anything about the Fudan card.
Hello, do you have the code in C language for "CCS C Compiler"... I would appreciate it.
I do have an Instructable with an Arduino version of the software. That is closer to C code. You should be able to find C code on the internet for these devices.
Hello! Great project! I read carefully the explanations about all the operating steps but still some doubts were not clear:

(a) Are all the operating instructions of the system (communication with the user, response after reading the TAG, etc.) shown through the LCD display?

(b) Does this system (PIC & RFID module) go into "hibernating" mode automatically after a few seconds or minutes without being used? In other words, does it have a low enough power consumption level for the power batteries to last for several weeks or months? Thank you in advance for the answer to these questions!
If you read the software there is a main routine called "Scan_Loop" that shows what is displayed. When a tag is detected the UID is displayed and the PIC sends commands to read the tag. There are some debug lines that show that the data read from the tag is displayed. That is all that is displayed because this is a proof of concept project. The "Electronic Wallet" project goes a little further. The PIC is not sent to sleep mode but it could be and then it could wake up when serial port data is received from a tag being read. I do not know what the power consumption would be for that mode.
Hi again! Could you please let me know what type and version of assembly editor/compiler the code was written in? I am trying to include a "sleep" instruction and recompile, using MPLAB IDE v8.0 but when I try to compile the process is aborted by the MPASM compiler and several syntax problems are accused, and I believe that due to differences between the originally used compiler and the compiler I'm working on right now. Thank you in advance for your kindness in answering me and helping me with this question.
Hi again! just one more question...when you say that "The PIC is not sent to sleep mode, but it can be" I should understand that in this present code the PIC does not go into hibernation mode but this could be changed, by including a few lines of code in this routine?
The PIC has a "SLEEP" instruction that could be added to the start of the "Scan_Loop" routine. If the RFID module starts sending serial port data when it detects a tag, then that should wake up the PIC and allow it to run the loop one time before it goes back to sleep.
good to know this! I'll try to include this command! thank you very much for your help and for your attention!


Hey, thanks for the guide. I'm currently strugling with my RC522 Module and UART. I've cut the trace as stated, but somehow the TX-pin of the RC522 is always low. It does not react to anything I'm sending on the RX-pin. It is also the same behaviour with two different boards. Does anyone know a fix to this?
Thanks in advance :)
See the conversation string with "Giels_Cal" below. Maybe something there will help.
Hi! I'm trying to make my RC522 work with an LPC1769. I'm a student learning about this so I'm probably doing something wrong here, I just don't know what. I'm using UART to communicate with the module so I cut the trace as indicated. The problem is that no matter what command I send, all I receive is either 0x80 or nothing at all.
For example, I send 0x80 (dummy data byte), then 0x01 and 0x0F (Soft reset), then 0x15 and 0x40 (Set ASK modulation percentage) and at this point if I check for responses I get 0x80 instead of the expected 0x15. I'm delaying about 10ms between each command and response check, just to be safe. I'd really appreciate any help.
I'm not familiar with the LPC1769. Are you using C as your programming language? If so, look at my Arduino version Instructable. It kind of looks like you are getting the Dummy Byte as your return. If you send just the Dummy Byte you should get either 0x00 as a return or nothing. There was an earlier commenter who had a problem with the particular module he bought. Read that exchange to see if yours might be the same type.
Thanks for the answer, I'll check the Arduino project out, didn't know about it.
I am getting no response (clock stretching) from my red PN532 module on I2C interface. I have 3.3v on the module and use 3.3V TTL I2C bus signals. The NXP user manual says that the I2C address of 0x48 is the only address allowed. I have tried interface dip switches all the 3 ways shown in the modules marked table just in case. Any ideas why no response/clock stretching?

THEANSWER WAS: The I2C slave address of the board had been set to 0x24. If I ever need to do this again I will write code to loop thru all 7 bit address values, looking for a response. As it was, I had to get it attached to an Arduino and run their sketch, then capture the I2C bus data with a logic analyzer in order to notice that the Arduino sketch was sending out slave address of 0x24 :(
More Comments