0,0 → 1,481 |
#include "defines.h" |
#include "nfc_PN532.h" |
#include "i2c.h" |
#include <string.h> |
#include <delays.h> |
|
static NFC_DATA nfc_data; |
static NFC_DATA *nfc_data_p = &nfc_data; |
|
// Const value arrays for comparison use |
static char pn532response_firmwarevers[] = {0x01, 0x00, 0x00, 0xFF, 0x06, 0xFA, 0xD5, 0x03}; |
static char pn532ack[] = {0x01, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}; |
|
void NFC_Init() { |
NFC_IRQ_TRIS = 1; // IRQ Pin is RC5 |
|
/* NFC reset is disabled due to lack of pins */ |
// NFC_RESET_TRIS = 0; // Reset Pin is RC2 |
// |
// // Reset the PN532 |
// NFC_RESET_LAT = 1; |
// NFC_RESET_LAT = 0; |
// Delay10TCYx(1); |
// NFC_RESET_LAT = 1; |
} |
|
// Configures the SAM (Secure Access Module) |
unsigned char NFC_SAMConfig() { |
nfc_data_p->packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; |
nfc_data_p->packetbuffer[1] = 0x01; // Normal mode |
nfc_data_p->packetbuffer[2] = 0x14; // Timeout 50ms * 20 = 1s |
nfc_data_p->packetbuffer[3] = 0x01; // Use IRQ pin |
|
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 4)) |
return 0; |
|
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 8); |
|
return (nfc_data_p->packetbuffer[7] == 0x15); |
} |
|
// Checks the firmware version of the PN5xx chip |
NFC_FIRMWARE_VERSION NFC_Get_Firmware_Version(void) { |
NFC_FIRMWARE_VERSION response = {0, 0, 0, 0}; |
|
// Create and send command |
nfc_data_p->packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION; |
|
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 1)) |
return response; |
|
// Read back data from the PN532 |
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 12); |
|
// Compare and check returned values |
if (strncmp((char *) nfc_data_p->packetbuffer, (char *) pn532response_firmwarevers, 8) != 0) |
return response; |
|
// Save and return info |
response.IC = nfc_data_p->packetbuffer[8]; |
response.Ver = nfc_data_p->packetbuffer[9]; |
response.Rev = nfc_data_p->packetbuffer[10]; |
response.Support = nfc_data_p->packetbuffer[11]; |
|
return response; |
} |
|
// Sends a command and waits a specified period for the ACK |
unsigned char NFC_Send_Command_Check_Ack(unsigned char *cmd, unsigned char cmdlen) { |
unsigned int timer = 0; |
|
// Write the command |
NFC_I2C_Write_Cmd(cmd, cmdlen); |
|
// Wait for chip to be ready |
while (NFC_I2C_Read_Status() != PN532_I2C_READY) { |
if (PN532_TIMEOUT != 0) { |
timer += 1; |
if (timer > PN532_TIMEOUT) |
return 0; |
} |
Delay10TCYx(1); |
} |
|
// Check ACK |
if (!NFC_I2C_Read_ACK()) { |
return 0; |
} |
|
return 1; |
} |
|
// Passive polling, waits for an ISO14443A target to enter the field |
unsigned char NFC_Read_Passive_Target_ID(NFC_TargetDataMiFare *cardData) { |
|
nfc_data_p->packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; |
nfc_data_p->packetbuffer[1] = 2; // Max 2 cards at once |
nfc_data_p->packetbuffer[2] = PN532_MIFARE_ISO14443A; // Mifare only |
|
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 3)) |
return 0; |
|
// Wait for IRQ line |
while (NFC_I2C_Read_Status() != PN532_I2C_READY); |
|
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 35); |
|
/* InListPassiveTarget response should be in the following format: |
* Byte Description |
* ---------- ------------------ |
* b0 Data ACK |
* b1..7 Frame header and preamble |
* b8 Tags found |
* b9..N NFC_TargetDataMiFare[2] |
* bN+1..N+2 Checksum + postamble |
*/ |
|
// Check # of tags found |
if (!nfc_data_p->packetbuffer[8]) |
return 0; |
|
// Save data from first card |
if (nfc_data_p->packetbuffer[13] == 4) { |
memcpy((char *)&cardData[0], (const char *)&nfc_data_p->packetbuffer[9], 9); |
} else { |
memcpy((char *)&cardData[0], (const char *)&nfc_data_p->packetbuffer[9], 12); |
} |
|
// Save data from second card |
if (nfc_data_p->packetbuffer[8] == 2) { |
// Offset will vary depending on length of first card |
if (nfc_data_p->packetbuffer[13] == 4) { |
if (nfc_data_p->packetbuffer[22] == 4) { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[18], 9); |
} else { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[18], 12); |
} |
} else { // Length of first UID is 7 |
if (nfc_data_p->packetbuffer[25] == 4) { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[21], 9); |
} else { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[21], 12); |
} |
} |
} |
|
// Return the number of cards detected |
return nfc_data_p->packetbuffer[8]; |
} |
|
// Active polling, returns number of cards in the field |
unsigned char NFC_Poll_Targets(unsigned char number, unsigned char period, NFC_TargetDataMiFare *cardData) { |
|
nfc_data_p->packetbuffer[0] = PN532_COMMAND_INAUTOPOLL; |
nfc_data_p->packetbuffer[1] = number; // Number of polling |
nfc_data_p->packetbuffer[2] = period; // Polling period in units of 150ms |
nfc_data_p->packetbuffer[3] = 0x10; // Check for Mifare cards only |
|
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 4)) |
return 0; |
|
// Wait for IRQ line |
while (NFC_I2C_Read_Status() != PN532_I2C_READY); |
|
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 37); |
|
/* InAutoPoll response should be in the following format: |
* Byte Description |
* ---------- ------------------ |
* b0 Data ACK |
* b1..7 Frame header and preamble |
* b6 Tags found |
* b7 Polled target type (should be 0x10 Mifare) |
* b8 TargetData length (1/2) |
* b9..N NFC_TargetDataMiFare[1/2] |
* bN+1..N+2 Checksum + postamble |
*/ |
|
// Check # of tags found |
if (!nfc_data_p->packetbuffer[8]) |
return 0; |
|
// Save data from first card |
if (nfc_data_p->packetbuffer[15] == 4) { |
memcpy((char *)&cardData[0], (const char *)&nfc_data_p->packetbuffer[11], 9); |
} else { |
memcpy((char *)&cardData[0], (const char *)&nfc_data_p->packetbuffer[11], 12); |
} |
|
// Save data from second card |
if (nfc_data_p->packetbuffer[8] == 2) { |
// Offset will vary depending on length of first card |
if (nfc_data_p->packetbuffer[15] == 4) { |
if (nfc_data_p->packetbuffer[26] == 4) { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[22], 9); |
} else { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[22], 12); |
} |
} else { |
if (nfc_data_p->packetbuffer[29] == 4) { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[25], 9); |
} else { |
memcpy((char *)&cardData[1], (const char *)&nfc_data_p->packetbuffer[25], 12); |
} |
} |
} |
|
// Return the number of cards detected |
return nfc_data_p->packetbuffer[8]; |
} |
|
// Indicates whether the specified block number is the first block |
// in the sector (block 0 relative to the current sector) |
unsigned char NFC_mifareclassic_IsFirstBlock(unsigned long uiBlock) { |
// Test if we are in the small or big sectors |
if (uiBlock < 128) |
return ((uiBlock) % 4 == 0); |
else |
return ((uiBlock) % 16 == 0); |
} |
|
// Indicates whether the specified block number is the sector trailer |
unsigned char NFC_mifareclassic_IsTrailerBlock(unsigned long uiBlock) { |
// Test if we are in the small or big sectors |
if (uiBlock < 128) |
return ((uiBlock + 1) % 4 == 0); |
else |
return ((uiBlock + 1) % 16 == 0); |
} |
|
// Tries to authenticate a block of memory on a MIFARE card using the INDATAEXCHANGE command |
unsigned char NFC_mifareclassic_AuthenticateBlock(unsigned char *uid, unsigned char uidLen, unsigned long blockNumber, unsigned char keyNumber, unsigned char *keyData) { |
// See section 7.3.8 of the PN532 User Manual |
// blockNumber = The block number to authenticate. (0..63 for 1KB cards, and 0..255 for 4KB cards)\ |
// keyNumber = Which key type to use during authentication (0 = MIFARE_CMD_AUTH_A, 1 = MIFARE_CMD_AUTH_B) |
// keyData = Pointer to a byte array containing the 6 byte key value |
|
unsigned char i; |
|
// Assemble frame data |
nfc_data_p->packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ |
nfc_data_p->packetbuffer[1] = 1; /* Max card numbers */ |
nfc_data_p->packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_A : MIFARE_CMD_AUTH_B; |
nfc_data_p->packetbuffer[3] = blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ |
for (i = 0; i < 6; i++) { |
nfc_data_p->packetbuffer[4 + i] = keyData[i]; |
} |
for (i = 0; i < uidLen; i++) { |
nfc_data_p->packetbuffer[10 + i] = uid[i]; |
} |
|
// Send frame and check for ACK |
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 10 + uidLen)) |
return 0; |
|
// Read response from PN532 |
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 12); |
|
return 1; |
} |
|
// Tries to read an entire 16-byte data block at the specified block address |
unsigned char NFC_mifareclassic_ReadDataBlock(unsigned char blockNumber, unsigned char *data) { |
unsigned char i; |
|
// Assemble frame data |
nfc_data_p->packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; |
nfc_data_p->packetbuffer[1] = 1; /* Card number */ |
nfc_data_p->packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ |
nfc_data_p->packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ |
|
// Send frame and check for ACK |
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 4)) |
return 0; |
|
// Read reponse |
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 26); |
|
// If byte 9 isnt 0x00 we probably have and error |
if (nfc_data_p->packetbuffer[8] != 0x00) { |
return 0; |
} |
|
// Copy the 16 data bytes into the data buffer |
// Block contents starts at byte 10 of a valid response |
for (i = 0; i < 16; i++) { |
data[i] = nfc_data_p->packetbuffer[9 + i]; |
} |
|
return 1; |
} |
|
// Tries to write an entire 16-byte data block at the specified block address |
unsigned char NFC_mifareclassic_WriteDataBlock(unsigned char blockNumber, unsigned char *data) { |
unsigned char i; |
|
// Assemble frame data |
nfc_data_p->packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; |
nfc_data_p->packetbuffer[1] = 1; /* Card number */ |
nfc_data_p->packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */ |
nfc_data_p->packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ |
for (i = 0; i < 16; i++) { /* Data Payload */ |
nfc_data_p->packetbuffer[4 + i] = data[i]; |
} |
|
// Send frame and check for ACK |
if (!NFC_Send_Command_Check_Ack(nfc_data_p->packetbuffer, 20)) |
return 0; |
|
// Read response |
NFC_I2C_Read_Data(nfc_data_p->packetbuffer, 26); |
|
return 1; |
} |
|
// Formats a Mifare Classic card to store NDEF Records |
unsigned char NFC_mifareclassic_FormatNDEF(void) { |
unsigned char sectorbuffer1[16] = {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; |
unsigned char sectorbuffer2[16] = {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; |
unsigned char sectorbuffer3[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
|
// Write blocks 1 and 2 |
if (!NFC_mifareclassic_WriteDataBlock(1, sectorbuffer1)) |
return 0; |
if (!NFC_mifareclassic_WriteDataBlock(2, sectorbuffer2)) |
return 0; |
// Write key A and access rights |
if (!NFC_mifareclassic_WriteDataBlock(3, sectorbuffer3)) |
return 0; |
|
return 1; |
} |
|
// Writes an NDEF URI Record to the specified sector (1..15) |
/* Note that this function assumes that the Mifare Classic card is |
already formatted to work as an "NFC Forum Tag" and uses a MAD1 |
file system. You can use the NXP TagWriter app on Android to |
properly format cards for this. */ |
unsigned char NFC_mifareclassic_WriteNDEFURI(unsigned char sectorNumber, unsigned char uriIdentifier, const char * url) { |
// uriIdentifier = The uri identifier code (0 = none, 0x01 = "http://www.", etc.) |
// url = The uri text to write (max 38 characters) |
|
// Figure out how long the string is |
unsigned char len = strlen(url); |
|
unsigned char sectorbuffer1[16] = {0x00, 0x00, 0x03, len + 5, 0xD1, 0x01, len + 1, 0x55, uriIdentifier, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
unsigned char sectorbuffer2[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
unsigned char sectorbuffer3[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
unsigned char sectorbuffer4[16] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
|
// Make sure we're within a 1K limit for the sector number |
if ((sectorNumber < 1) || (sectorNumber > 15)) |
return 0; |
|
// Make sure the URI payload is between 1 and 38 chars |
if ((len < 1) || (len > 38)) |
return 0; |
|
if (len <= 6) { |
// Unlikely we'll get a url this short, but why not ... |
memcpy(sectorbuffer1 + 9, url, len); |
sectorbuffer1[len + 9] = 0xFE; |
} else if (len == 7) { |
// 0xFE needs to be wrapped around to next block |
memcpy(sectorbuffer1 + 9, url, len); |
sectorbuffer2[0] = 0xFE; |
} else if ((len > 7) || (len <= 22)) { |
// Url fits in two blocks |
memcpy(sectorbuffer1 + 9, url, 7); |
memcpy(sectorbuffer2, url + 7, len - 7); |
sectorbuffer2[len - 7] = 0xFE; |
} else if (len == 23) { |
// 0xFE needs to be wrapped around to final block |
memcpy(sectorbuffer1 + 9, url, 7); |
memcpy(sectorbuffer2, url + 7, len - 7); |
sectorbuffer3[0] = 0xFE; |
} else { |
// Url fits in three blocks |
memcpy(sectorbuffer1 + 9, url, 7); |
memcpy(sectorbuffer2, url + 7, 16); |
memcpy(sectorbuffer3, url + 23, len - 24); |
sectorbuffer3[len - 22] = 0xFE; |
} |
|
// Now write all three blocks back to the card |
if (!(NFC_mifareclassic_WriteDataBlock(sectorNumber * 4, sectorbuffer1))) |
return 0; |
if (!(NFC_mifareclassic_WriteDataBlock((sectorNumber * 4) + 1, sectorbuffer2))) |
return 0; |
if (!(NFC_mifareclassic_WriteDataBlock((sectorNumber * 4) + 2, sectorbuffer3))) |
return 0; |
if (!(NFC_mifareclassic_WriteDataBlock((sectorNumber * 4) + 3, sectorbuffer4))) |
return 0; |
|
return 1; |
} |
|
// Reads and checks for the ACK signal |
unsigned char NFC_I2C_Read_ACK() { |
unsigned char buffer[7]; |
|
// Check ACK |
NFC_I2C_Read_Data(buffer, 6); |
|
// Return if the 7 bytes matches the ACK pattern |
return (strncmp((char *) buffer, (char *) pn532ack, 7) == 0); |
} |
|
// Checks the IRQ pin to know if the PN532 is ready |
unsigned char NFC_I2C_Read_Status() { |
if (NFC_IRQ_PORT == 1) { |
return PN532_I2C_BUSY; |
} else { |
return PN532_I2C_READY; |
} |
} |
|
// Reads n bytes of data from the PN532 via I2C |
void NFC_I2C_Read_Data(unsigned char *buffer, unsigned char length) { |
unsigned char result; |
|
// Wait for IRQ to go low |
while (NFC_I2C_Read_Status() != PN532_I2C_READY); |
|
// Read bytes from PN532 into buffer |
I2C_Master_Recv(PN532_I2C_ADDRESS, length + 2); |
result = I2C_Get_Status(); |
while (!result) { |
result = I2C_Get_Status(); |
} |
I2C_Read_Buffer((char *) buffer); |
|
/* Remaining packet byte layout is as follows: |
Byte Description |
----- ---------------------- |
* 0 Data ready ACK |
* 1 Preamble (0x00) |
* 2-3 Start code (0x00,0xFF) |
* 4 Length (TFI to N) |
* 5 Length Checksum (Length + LCS = 0x00) |
* 6 TFI (Frame identifier) |
* 0xD4 - Host to PN532 |
* 0xD5 - PN532 to Host |
* 7-N Data (Length - 1 bytes) |
* N+1 Data checksum (TFI + Data~N + DCS = 0x00) |
* N+2 Postamble (0x00) */ |
} |
|
// Writes a command to the PN532, automatically inserting the preamble and required frame details (checksum, len, etc.) |
void NFC_I2C_Write_Cmd(unsigned char* cmd, unsigned char cmdlen) { |
int i; |
unsigned char checksum; |
unsigned char buffer[PN532_PACKBUFFSIZ + 8]; |
unsigned char buffer_ind = 6; |
cmdlen++; |
|
checksum = PN532_PREAMBLE + PN532_PREAMBLE + PN532_STARTCODE2 + PN532_HOSTTOPN532; |
|
// Fill out required frame fields |
buffer[0] = PN532_PREAMBLE; |
buffer[1] = PN532_PREAMBLE; |
buffer[2] = PN532_STARTCODE2; |
buffer[3] = cmdlen; |
buffer[4] = ~cmdlen + 1; |
buffer[5] = PN532_HOSTTOPN532; |
|
|
// Copy cmd to be sent |
for (i = 0; i < cmdlen - 1; i++) { |
checksum += cmd[i]; |
buffer[buffer_ind] = cmd[i]; |
buffer_ind++; |
} |
|
buffer[buffer_ind] = ~checksum; |
buffer_ind++; |
buffer[buffer_ind] = PN532_POSTAMBLE; |
buffer_ind++; |
|
I2C_Master_Send(PN532_I2C_ADDRESS, buffer_ind, buffer); |
} |