Pokusy s čtečkou RFID karet MFRC522. Čtečka komunikuje pomocí SPI sběrnice.
Piny

| MFRC522 pin | Pico pin |
|---|---|
SDA |
GPIO17, SPI0 CSn |
SCK |
GPIO18, SPI0 SCK |
MOSI |
GPIO19, SPI0 TX |
MISO |
GPIO16, SPI0 RX |
IRQ |
nezapojeno |
GND |
GND |
RST |
GPIO20 |
3.3V |
3.3V fyzický pin 36 |
Zdrojové kódy
pico-mfrc522/CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(rfid-rc522)
pico_sdk_init()
add_executable(rfid-rc522
example.c
mfrc522.c
)
target_link_libraries(rfid-rc522 pico_stdlib hardware_spi)
# create map/bin/hex file etc.
pico_add_extra_outputs(rfid-rc522)
pico_enable_stdio_usb(rfid-rc522 1)
pico_enable_stdio_uart(rfid-rc522 0)
pico-mfrc522/example.c
#include "mfrc522.h"
void main() {
stdio_init_all();
// Declare card UID's
uint8_t tag2[] = {0x19, 0x09, 0x29, 0x3e};
uint8_t tag1[] = {0x5a, 0x23, 0xc8, 0x4f};
MFRC522Ptr_t mfrc = MFRC522_Init();
PCD_Init(mfrc, spi0);
sleep_ms(5000);
// char test_result = PCD_SelfTest(mfrc);
// if (test_result == 0) {
// printf("Self Test: SUCCESS");
// } else {
// printf("Self Test: FAILED");
// }
while(1) {
//Wait for new card
printf("Waiting for card\n\r");
while(!PICC_IsNewCardPresent(mfrc));
//Select the card
printf("Selecting card\n\r");
PICC_ReadCardSerial(mfrc);
//Show UID on serial monitor
printf("PICC dump: \n\r");
PICC_DumpToSerial(mfrc, &(mfrc->uid));
//Authorization with uid
printf("Uid is: ");
for(int i = 0; i < 4; i++) {
printf("%x ", mfrc->uid.uidByte[i]);
} printf("\n\r");
if(memcmp(mfrc->uid.uidByte, tag1, 4) == 0 || memcmp(mfrc->uid.uidByte, tag2, 4) == 0 ) {
printf("Authentication Success\n\r");
} else {
printf("Authentication Failed\n\r");
}
}
}
pico-mfrc522/mfrc522.c
/**
* mfrc522 library for pi pico c/c++ sdk
* NOTE: Please also check the comments in MFRC522.h - they provide useful hints
* and background information.
*/
#include "mfrc522.h"
// ADT object allocation counter
static int MFRC_Instance_Counter = 0;
/**
* Set up the data structures of an MFRC522 ADT object and return a pointer
*/
MFRC522Ptr_t MFRC522_Init() {
// allocate instance struct array
static struct MFRC522_T mfrc_Instances[MFRC_MAX_INSTANCES];
// static Chip_SSP_DATA_SETUP_T dataSetup_Instances[MFRC_MAX_INSTANCES];
// struct MFRC522_T mfrc_struct;
// Chip_SSP_DATA_SETUP_T data_setup;
// initialize fields
uint16_t i;
for (i = 0; i < BUFFER_SIZE; i++) {
mfrc_Instances[MFRC_Instance_Counter].Rx_Buf[i] = 0;
mfrc_Instances[MFRC_Instance_Counter].Tx_Buf[i] = 0;
}
mfrc_Instances[MFRC_Instance_Counter]._chipSelectPin = cs_pin;
// update instance counter
MFRC_Instance_Counter++;
return &(mfrc_Instances[MFRC_Instance_Counter - 1]);
}
/*******************************************************************************
* Basic interface functions for communicating with the MFRC522
*******************************************************************************/
/**
* Writes a uint8_t to the specified register in the MFRC522 chip.
* The interface is described in the datasheet section 8.1.2.
*/
void PCD_WriteRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t value) {
uint8_t msg[2];
msg[0] = 0x00 | reg;
msg[1] = value;
cs_select(mfrc->_chipSelectPin);
spi_write_blocking(mfrc->spi, msg, 2);
cs_deselect(mfrc->_chipSelectPin);
}
/**
* Writes a number of uint8_ts to the specified register in the MFRC522 chip.
* The interface is described in the datasheet section 8.1.2.
*/
void PCD_WriteNRegister(
MFRC522Ptr_t mfrc,
uint8_t reg, ///< The register to write to. One of the PCD_Register enums.
uint8_t count, ///< The number of uint8_ts to write to the register
uint8_t *values ///< The values to write. uint8_t array.
) {
uint8_t msg[count + 1]; //Values and address
msg[0] = 0x00 | reg;
uint8_t i;
for(i = 0; i < count; i++) {
msg[i + 1] = values[i];
}
cs_select(mfrc->_chipSelectPin);
spi_write_blocking(mfrc->spi, msg, count + 1);
cs_deselect(mfrc->_chipSelectPin);
}
/**
* Reads a uint8_t from the specified register in the MFRC522 chip.
* The interface is described in the datasheet section 8.1.2.
*/
uint8_t PCD_ReadRegister(
MFRC522Ptr_t mfrc,
uint8_t reg ///< The register to read from. One of the PCD_Register enums
) {
uint8_t buf = 0;
const uint8_t msg = 0x80 | reg;
cs_select(mfrc->_chipSelectPin);
spi_write_blocking(mfrc->spi, &msg, 1);
spi_read_blocking(mfrc->spi, 0, &buf, 1);
cs_deselect(mfrc->_chipSelectPin);
return buf;
}
/**
* Reads a number of uint8_ts from the specified register in the MFRC522 chip.
* The interface is described in the datasheet section 8.1.2.
*/
void PCD_ReadNRegister(
MFRC522Ptr_t mfrc,
uint8_t reg, ///< The register to read from. One of the PCD_Register enums.
uint8_t count, ///< The number of uint8_ts to read
uint8_t *values, ///< uint8_t array to store the values in.
uint8_t rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated.
) {
uint8_t buf = 0;
const uint8_t msg = 0x80 | reg;
cs_select(mfrc->_chipSelectPin);
// uint8_t i;
// for(i = 0; i < count; i++) {
// uint8_t value = 0;
// spi_write_blocking(mfrc->spi, &msg, 1);
// spi_read_blocking(mfrc->spi, 0, &value, 1);
// values[i] = value;
// }
uint8_t i;
for(i = 0; i < count; i++) {
values[i] = PCD_ReadRegister(mfrc, msg);
}
cs_deselect(mfrc->_chipSelectPin);
}
/**
* Sets the bits given in mask in register reg.
*/
void PCD_SetRegisterBitMask(
MFRC522Ptr_t mfrc,
uint8_t reg, ///< The register to update. One of the PCD_Register enums.
uint8_t mask ///< The bits to set.
) {
uint8_t tmp;
tmp = PCD_ReadRegister(mfrc, reg);
PCD_WriteRegister(mfrc, reg, tmp | mask); // set bit mask
} // End PCD_SetRegisterBitMask()
/**
* Clears the bits given in mask from register reg.
*/
void PCD_ClearRegisterBitMask(
MFRC522Ptr_t mfrc,
uint8_t reg, ///< The register to update. One of the PCD_Register enums.
uint8_t mask ///< The bits to clear.
) {
uint8_t tmp;
tmp = PCD_ReadRegister(mfrc, reg);
PCD_WriteRegister(mfrc, reg, tmp & (~mask)); // clear bit mask
} // End PCD_ClearRegisterBitMask()
/**
* Use the CRC coprocessor in the MFRC522 to calculate a CRC_A.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode
PCD_CalculateCRC(MFRC522Ptr_t mfrc,
uint8_t *data, ///< In: Pointer to the data to transfer to the
///FIFO for CRC calculation.
uint8_t length, ///< In: The number of uint8_ts to transfer.
uint8_t *result ///< Out: Pointer to result buffer. Result is
///written to result[0..1], low uint8_t first.
) {
PCD_WriteRegister(mfrc, CommandReg, PCD_Idle); // Stop any active command.
PCD_WriteRegister(mfrc, DivIrqReg,
0x04); // Clear the CRCIRq interrupt request bit
PCD_SetRegisterBitMask(mfrc, FIFOLevelReg,
0x80); // FlushBuffer = 1, FIFO initialization
PCD_WriteNRegister(mfrc, FIFODataReg, length,
data); // Write data to the FIFO
PCD_WriteRegister(mfrc, CommandReg, PCD_CalcCRC); // Start the calculation
// Wait for the CRC calculation to complete. Each iteration of the
// while-loop takes 17.73�s.
uint16_t i = 5000;
uint8_t n;
while (1) {
n = PCD_ReadRegister(mfrc, DivIrqReg); // DivIrqReg[7..0] bits are: Set2
// reserved reserved MfinActIRq
// reserved CRCIRq reserved
// reserved
if (n & 0x04) { // CRCIRq bit set - calculation done
break;
}
if (--i == 0) { // The emergency break. We will eventually terminate on
// this one after 89ms. Communication with the MFRC522
// might be down.
return STATUS_TIMEOUT;
}
}
PCD_WriteRegister(
mfrc, CommandReg,
PCD_Idle); // Stop calculating CRC for new content in the FIFO.
// Transfer the result from the registers to the result buffer
result[0] = PCD_ReadRegister(mfrc, CRCResultRegL);
result[1] = PCD_ReadRegister(mfrc, CRCResultRegH);
return STATUS_OK;
} // End PCD_CalculateCRC()
/*******************************************************************************
* Functions for manipulating the MFRC522
*******************************************************************************/
/**
* Initializes the MFRC522 chip.
*/
void PCD_Init(MFRC522Ptr_t mfrc, spi_inst_t *spi) {
mfrc->spi = spi0;
gpio_put(RESET_PIN, 0);
sleep_ms(1000);
gpio_put(RESET_PIN, 1);
sleep_ms(50);
gpio_init(cs_pin);
gpio_set_dir(cs_pin, GPIO_OUT);
gpio_put(cs_pin, 1);
spi_init(spi0, 1000000);
spi_set_format(spi0, 8, 0, 0, SPI_MSB_FIRST);
gpio_set_function(sck_pin, GPIO_FUNC_SPI);
gpio_set_function(mosi_pin, GPIO_FUNC_SPI);
gpio_set_function(miso_pin, GPIO_FUNC_SPI);
PCD_WriteRegister(mfrc, CommandReg, PCD_SoftReset);
// When communicating with a PICC we need a timeout if something goes wrong.
// f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler =
// [TPrescaler_Hi:TPrescaler_Lo].
// TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is
// TPrescalerReg.
PCD_WriteRegister(mfrc, TModeReg, 0x80); // TAuto=1; timer starts
// automatically at the end of the
// transmission in all
// communication modes at all
// speeds
PCD_WriteRegister(mfrc, TPrescalerReg,
0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie
// 0x0A9 = 169 => f_timer=40kHz, ie a timer period
// of 25�s.
PCD_WriteRegister(
mfrc, TReloadRegH,
0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout.
PCD_WriteRegister(mfrc, TReloadRegL, 0xE8);
PCD_WriteRegister(mfrc, TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK
// modulation independent of the
// ModGsPReg register setting
PCD_WriteRegister(mfrc, ModeReg, 0x3D); // Default 0x3F. Set the preset
// value for the CRC coprocessor for
// the CalcCRC command to 0x6363
// (ISO 14443-3 part 6.2.4)
PCD_AntennaOn(mfrc); // Enable the antenna driver pins TX1 and TX2 (they
// were disabled by the reset)
} // End PCD_Init()
/**
* Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
*/
void PCD_Reset(MFRC522Ptr_t mfrc) {
PCD_WriteRegister(mfrc, CommandReg,
PCD_SoftReset); // Issue the SoftReset command.
// The datasheet does not mention how long the SoftRest command takes to
// complete.
// But the MFRC522 might have been in soft power-down mode (triggered by bit
// 4 of CommandReg)
// Section 8.8.2 in the datasheet says the oscillator start-up time is the
// start up time of the crystal + 37,74�s. Let us be generous: 50ms.
//SysTick_Init();
sleep_ms(50);
// Wait for the PowerDown bit in CommandReg to be cleared
while (PCD_ReadRegister(mfrc, CommandReg) & (1 << 4)) {
// PCD still restarting - unlikely after waiting 50ms, but better safe
// than sorry.
}
} // End PCD_Reset()
/**
* Turns the antenna on by enabling pins TX1 and TX2.
* After a reset these pins are disabled.
*/
void PCD_AntennaOn(MFRC522Ptr_t mfrc) {
uint8_t value = PCD_ReadRegister(mfrc, TxControlReg);
if ((value & 0x03) != 0x03) {
PCD_WriteRegister(mfrc, TxControlReg, value | 0x03);
}
} // End PCD_AntennaOn()
/**
* Turns the antenna off by disabling pins TX1 and TX2.
*/
void PCD_AntennaOff(MFRC522Ptr_t mfrc) {
PCD_ClearRegisterBitMask(mfrc, TxControlReg, 0x03);
} // End PCD_AntennaOff()
/**
* Get the current MFRC522 Receiver Gain (RxGain[2:0]) value.
* See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf
* NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use
*reserved bits.
*
* @return Value of the RxGain, scrubbed to the 3 bits used.
*/
uint8_t PCD_GetAntennaGain(MFRC522Ptr_t mfrc) {
return PCD_ReadRegister(mfrc, RFCfgReg) & (0x07 << 4);
} // End PCD_GetAntennaGain()
/**
* Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask.
* See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf
* NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use
* reserved bits.
*/
void PCD_SetAntennaGain(MFRC522Ptr_t mfrc, uint8_t mask) {
if (PCD_GetAntennaGain(mfrc) != mask) { // only bother if there is a change
PCD_ClearRegisterBitMask(
mfrc, RFCfgReg, (0x07 << 4)); // clear needed to allow 000 pattern
PCD_SetRegisterBitMask(mfrc, RFCfgReg,
mask & (0x07 << 4)); // only set RxGain[2:0] bits
}
} // End PCD_SetAntennaGain()
/**
* Performs the MFRC522 self test described in 16.1.1
* Returns 0 if test is ok. Check what version of MFRC522 and put the buffer values in SELF_TEST_BYTES[]
*/
uint8_t PCD_SelfTest(MFRC522Ptr_t mfrc) {
//Perform a soft reset
PCD_WriteRegister(mfrc, CommandReg, PCD_SoftReset);
sleep_ms(50); //Allow the chip to reset
//printf("Soft reset complete\n\r");
//Clear the internal buffer by writing 25 bytes of 00h (and implement the config command)??.
PCD_WriteRegister(mfrc, FIFOLevelReg, 0x80); //FIFOLevel indicates nbr of bytes in FIFO buffer. MSB = 1 -> Flush
for (int i = 0; i < 25; i++) {
PCD_WriteRegister(mfrc, FIFODataReg, 0x00);
}
PCD_WriteRegister(mfrc, CommandReg, PCD_Mem); //Transfer the 25 bytes from FIFO to internal buffer
//printf("Clearing of internal buffer complete\n\r");
//Enable the self test by writing 09h to the AutoTestReg register.
PCD_WriteRegister(mfrc, AutoTestReg, PCD_EnableSelfTest);
//printf("Self test enable complete\n\r");
//Write 00h to the FIFO buffer.
PCD_WriteRegister(mfrc, FIFODataReg, 0x00);
//printf("Written 0x00 to FIFO buffer\n\r");
//Start the self test with the CalcCRC command.
PCD_WriteRegister(mfrc, CommandReg, PCD_CalcCRC);
//printf("Started self test with the CRC command\n\r");
//The self test is initiated. Wait for completion
bool self_test_complete = false;
while (!self_test_complete) {
uint8_t buf = PCD_ReadRegister(mfrc, FIFOLevelReg);
//printf("%x\n\r", buf);
if (buf >= 64) {
self_test_complete = true;
}
}
//printf("Self test completed\n\r");
//Stop calculating CRC with idle command???
//When the self test has completed, the FIFO buffer contains the version bytes.
uint8_t result[64];
for (int i = 0; i < 64; i++) {
uint8_t buf = PCD_ReadRegister(mfrc, FIFODataReg);
result[i] = buf;
}
//printf("Bytes copied to result array\n\r");
//Check if the received bytes correspond to the versions.
PCD_WriteRegister(mfrc, AutoTestReg, 0x00); //Disable self-test
//printf("Disabled self test\n\r");
/*printf("Self test result:\n\r");
for (uint8_t i = 0; i < 64; i++) {
printf("%x, ", result[i]);
if (i % 10 == 0) printf("\n\r");
} printf("\n\r"); */
for (uint8_t i = 0; i < 64; i++) {
if (result[i] != SELF_TEST_BYTES[i]) {
return -1;
}
}
return 0;
}
/*******************************************************************************
* Functions for communicating with PICCs
*******************************************************************************/
/**
* Executes the Transceive command.
* CRC validation can only be done if backData and backLen are specified.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PCD_TransceiveData(
MFRC522Ptr_t mfrc,
uint8_t *sendData, ///< Pointer to the data to transfer to the FIFO.
uint8_t sendLen, ///< Number of uint8_ts to transfer to the FIFO.
uint8_t *backData, ///< NULL or pointer to buffer if data should be read
///back after executing the command.
uint8_t *backLen, ///< In: Max number of uint8_ts to write to *backData.
///Out: The number of uint8_ts returned.
uint8_t *validBits, ///< In/Out: The number of valid bits in the last
///uint8_t. 0 for 8 valid bits. Default NULL.
uint8_t rxAlign, ///< In: Defines the bit position in backData[0] for the
///first bit received. Default 0.
bool checkCRC ///< In: True => The last two uint8_ts of the response is
///assumed to be a CRC_A that must be validated.
) {
// validBits=NULL;
// rxAlign=0;
// checkCRC=false;
uint8_t waitIRq = 0x30; // RxIRq and IdleIRq
return PCD_CommunicateWithPICC(mfrc, PCD_Transceive, waitIRq, sendData,
sendLen, backData, backLen, validBits,
rxAlign, checkCRC);
} // End PCD_TransceiveData()
/**
* Transfers data to the MFRC522 FIFO, executes a command, waits for completion
*and transfers data back from the FIFO.
* CRC validation can only be done if backData and backLen are specified.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PCD_CommunicateWithPICC(
MFRC522Ptr_t mfrc,
uint8_t command, ///< The command to execute. One of the PCD_Command enums.
uint8_t waitIRq, ///< The bits in the ComIrqReg register that signals
///successful completion of the command.
uint8_t *sendData, ///< Pointer to the data to transfer to the FIFO.
uint8_t sendLen, ///< Number of uint8_ts to transfer to the FIFO.
uint8_t *backData, ///< NULL or pointer to buffer if data should be read
///back after executing the command.
uint8_t *backLen, ///< In: Max number of uint8_ts to write to *backData.
///Out: The number of uint8_ts returned.
uint8_t *validBits, ///< In/Out: The number of valid bits in the last
///uint8_t. 0 for 8 valid bits.
uint8_t rxAlign, ///< In: Defines the bit position in backData[0] for the
///first bit received. Default 0.
bool checkCRC ///< In: True => The last two uint8_ts of the response is
///assumed to be a CRC_A that must be validated.
) {
// backData=NULL;
// backLen=NULL;
// validBits=NULL;
// rxAlign=0;
// checkCRC=false;
uint8_t n, _validBits;
unsigned int i;
// Prepare values for BitFramingReg
uint8_t txLastBits = validBits ? *validBits : 0;
uint8_t bitFraming =
(rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4].
// TxLastBits = BitFramingReg[2..0]
PCD_WriteRegister(mfrc, CommandReg, PCD_Idle); // Stop any active command.
PCD_WriteRegister(mfrc, ComIrqReg,
0x7F); // Clear all seven interrupt request bits
PCD_SetRegisterBitMask(mfrc, FIFOLevelReg,
0x80); // FlushBuffer = 1, FIFO initialization
PCD_WriteNRegister(mfrc, FIFODataReg, sendLen,
sendData); // Write sendData to the FIFO
PCD_WriteRegister(mfrc, BitFramingReg, bitFraming); // Bit adjustments
PCD_WriteRegister(mfrc, CommandReg, command); // Execute the command
if (command == PCD_Transceive) {
PCD_SetRegisterBitMask(
mfrc, BitFramingReg,
0x80); // StartSend=1, transmission of data starts
}
// Wait for the command to complete.
// In PCD_Init() we set the TAuto flag in TModeReg. This means the timer
// automatically starts when the PCD stops transmitting.
// Each iteration of the do-while-loop takes 17.86�s.
i = 2000;
while (1) {
n = PCD_ReadRegister(mfrc, ComIrqReg); // ComIrqReg[7..0] bits are: Set1
// TxIRq RxIRq IdleIRq HiAlertIRq
// LoAlertIRq ErrIRq TimerIRq
if (n & waitIRq) { // One of the interrupts that signal success has been
// set.
break;
}
if (n & 0x01) { // Timer interrupt - nothing received in 25ms
return STATUS_TIMEOUT;
}
if (--i == 0) { // The emergency break. If all other conditions fail we
// will eventually terminate on this one after 35.7ms.
// Communication with the MFRC522 might be down.
return STATUS_TIMEOUT;
}
}
// Stop now if any errors except collisions were detected.
uint8_t errorRegValue =
PCD_ReadRegister(mfrc, ErrorReg); // ErrorReg[7..0] bits are: WrErr
// TempErr reserved BufferOvfl CollErr
// CRCErr ParityErr ProtocolErr
if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr
return STATUS_ERROR;
}
// If the caller wants data back, get it from the MFRC522.
if (backData && backLen) {
n = PCD_ReadRegister(mfrc,
FIFOLevelReg); // Number of uint8_ts in the FIFO
if (n > *backLen) {
return STATUS_NO_ROOM;
}
*backLen = n; // Number of uint8_ts returned
PCD_ReadNRegister(mfrc, FIFODataReg, n, backData,
rxAlign); // Get received data from FIFO
_validBits = PCD_ReadRegister(mfrc, ControlReg) &
0x07; // RxLastBits[2:0] indicates the number of valid bits
// in the last received uint8_t. If this value is
// 000b, the whole uint8_t is valid.
if (validBits) {
*validBits = _validBits;
}
}
// Tell about collisions
if (errorRegValue & 0x08) { // CollErr
return STATUS_COLLISION;
}
// Perform CRC_A validation if requested.
if (backData && backLen && checkCRC) {
// In this case a MIFARE Classic NAK is not OK.
if (*backLen == 1 && _validBits == 4) {
return STATUS_MIFARE_NACK;
}
// We need at least the CRC_A value and all 8 bits of the last uint8_t
// must be received.
if (*backLen < 2 || _validBits != 0) {
return STATUS_CRC_WRONG;
}
// Verify CRC_A - do our own calculation and store the control in
// controlBuffer.
uint8_t controlBuffer[2];
StatusCode status = PCD_CalculateCRC(mfrc, &backData[0], *backLen - 2,
&controlBuffer[0]);
if (status != STATUS_OK) {
return status;
}
if ((backData[*backLen - 2] != controlBuffer[0]) ||
(backData[*backLen - 1] != controlBuffer[1])) {
return STATUS_CRC_WRONG;
}
}
return STATUS_OK;
} // End PCD_CommunicateWithPICC()
/**
* Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to
*READY and prepare for anticollision or selection. 7 bit frame.
* Beware: When two PICCs are in the field at the same time I often get
*STATUS_TIMEOUT - probably due do bad antenna design.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PICC_RequestA(
MFRC522Ptr_t mfrc,
uint8_t *
bufferATQA, ///< The buffer to store the ATQA (Answer to request) in
uint8_t *bufferSize ///< Buffer size, at least two uint8_ts. Also number of
///uint8_ts returned if STATUS_OK.
) {
return PICC_REQA_or_WUPA(mfrc, PICC_CMD_REQA, bufferATQA, bufferSize);
} // End PICC_RequestA()
/**
* Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to
*go to READY(*) and prepare for anticollision or selection. 7 bit frame.
* Beware: When two PICCs are in the field at the same time I often get
*STATUS_TIMEOUT - probably due do bad antenna design.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PICC_WakeupA(
MFRC522Ptr_t mfrc,
uint8_t *
bufferATQA, ///< The buffer to store the ATQA (Answer to request) in
uint8_t *bufferSize ///< Buffer size, at least two uint8_ts. Also number of
///uint8_ts returned if STATUS_OK.
) {
return PICC_REQA_or_WUPA(mfrc, PICC_CMD_WUPA, bufferATQA, bufferSize);
} // End PICC_WakeupA()
/**
* Transmits REQA or WUPA commands.
* Beware: When two PICCs are in the field at the same time I often get
*STATUS_TIMEOUT - probably due do bad antenna design.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PICC_REQA_or_WUPA(
MFRC522Ptr_t mfrc,
uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
uint8_t *
bufferATQA, ///< The buffer to store the ATQA (Answer to request) in
uint8_t *bufferSize ///< Buffer size, at least two uint8_ts. Also number of
///uint8_ts returned if STATUS_OK.
) {
uint8_t validBits;
StatusCode status;
if (bufferATQA == NULL ||
*bufferSize < 2) { // The ATQA response is 2 uint8_ts long.
return STATUS_NO_ROOM;
}
PCD_ClearRegisterBitMask(mfrc, CollReg, 0x80); // ValuesAfterColl=1 => Bits
// received after collision
// are cleared.
validBits = 7; // For REQA and WUPA we need the short frame format -
// transmit only 7 bits of the last (and only) uint8_t.
// TxLastBits = BitFramingReg[2..0]
status = PCD_TransceiveData(mfrc, &command, 1, bufferATQA, bufferSize,
&validBits, 0, false);
if (status != STATUS_OK) {
return status;
}
if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits.
return STATUS_ERROR;
}
return STATUS_OK;
} // End PICC_REQA_or_WUPA()
/**
* Transmits SELECT/ANTICOLLISION commands to select a single PICC.
* Before calling this function the PICCs must be placed in the READY(*) state
*by calling PICC_RequestA() or PICC_WakeupA().
* On success:
* - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned
*to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.)
* - The UID size and value of the chosen PICC is returned in *uid along with
*the SAK.
*
* A PICC UID consists of 4, 7 or 10 uint8_ts.
* Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs
*two or three iterations are used:
* UID size Number of UID uint8_ts Cascade levels Example of
*PICC
* ======== =================== ============== ===============
* single 4 1 MIFARE
*Classic
* double 7 2 MIFARE
*Ultralight
* triple 10 3 Not currently in
*use?
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PICC_Select(MFRC522Ptr_t mfrc,
Uid *uid, ///< Pointer to Uid struct. Normally output,
///but can also be used to supply a known UID.
uint8_t validBits ///< The number of known UID bits
///supplied in *uid. Normally 0. If set
///you must also supply uid->size.
) {
// validBits=0;
bool uidComplete;
bool selectDone;
bool useCascadeTag;
uint8_t cascadeLevel = 1;
StatusCode result;
uint8_t count;
uint8_t index;
uint8_t uidIndex; // The first index in uid->uiduint8_t[] that is used in
// the current Cascade Level.
int8_t currentLevelKnownBits; // The number of known UID bits in the current
// Cascade Level.
uint8_t buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 uint8_t
// standard frame + 2 uint8_ts CRC_A
uint8_t bufferUsed; // The number of uint8_ts used in the buffer, ie the
// number of uint8_ts to transfer to the FIFO.
uint8_t rxAlign; // Used in BitFramingReg. Defines the bit position for the
// first bit received.
uint8_t txLastBits; // Used in BitFramingReg. The number of valid bits in
// the last transmitted uint8_t.
uint8_t *responseBuffer;
uint8_t responseLength;
// Description of buffer structure:
// uint8_t 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1,
//PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
// uint8_t 1: NVB Number of Valid Bits (in complete command, not
//just the UID): High nibble: complete uint8_ts, Low nibble: Extra bits.
// uint8_t 2: UID-data or CT See explanation below. CT means Cascade
//Tag.
// uint8_t 3: UID-data
// uint8_t 4: UID-data
// uint8_t 5: UID-data
// uint8_t 6: BCC Block Check Character - XOR of uint8_ts
//2-5
// uint8_t 7: CRC_A
// uint8_t 8: CRC_A
// The BCC and CRC_A are only transmitted if we know all the UID bits of the
// current Cascade Level.
//
// Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft:
// UID contents and cascade levels)
// UID size Cascade level uint8_t2 uint8_t3 uint8_t4
//uint8_t5
// ======== ============= ===== ===== ===== =====
// 4 uint8_ts 1 uid0 uid1 uid2 uid3
// 7 uint8_ts 1 CT uid0 uid1 uid2
// 2 uid3 uid4 uid5 uid6
// 10 uint8_ts 1 CT uid0 uid1 uid2
// 2 CT uid3 uid4 uid5
// 3 uid6 uid7 uid8 uid9
// Sanity checks
if (validBits > 80) {
return STATUS_INVALID;
}
// Prepare MFRC522
PCD_ClearRegisterBitMask(mfrc, CollReg, 0x80); // ValuesAfterColl=1 => Bits
// received after collision
// are cleared.
// Repeat Cascade Level loop until we have a complete UID.
uidComplete = false;
while (!uidComplete) {
// Set the Cascade Level in the SEL uint8_t, find out if we need to use
// the Cascade Tag in uint8_t 2.
switch (cascadeLevel) {
case 1:
buffer[0] = PICC_CMD_SEL_CL1;
uidIndex = 0;
useCascadeTag =
validBits &&
uid->size >
4; // When we know that the UID has more than 4 uint8_ts
break;
case 2:
buffer[0] = PICC_CMD_SEL_CL2;
uidIndex = 3;
useCascadeTag =
validBits &&
uid->size >
7; // When we know that the UID has more than 7 uint8_ts
break;
case 3:
buffer[0] = PICC_CMD_SEL_CL3;
uidIndex = 6;
useCascadeTag = false; // Never used in CL3.
break;
default:
return STATUS_INTERNAL_ERROR;
break;
}
// How many UID bits are known in this Cascade Level?
currentLevelKnownBits = validBits - (8 * uidIndex);
if (currentLevelKnownBits < 0) {
currentLevelKnownBits = 0;
}
// Copy the known bits from uid->uiduint8_t[] to buffer[]
index = 2; // destination index in buffer[]
if (useCascadeTag) {
buffer[index++] = PICC_CMD_CT;
}
uint8_t uint8_tsToCopy =
currentLevelKnownBits / 8 +
(currentLevelKnownBits % 8 ? 1 : 0); // The number of uint8_ts
// needed to represent the
// known bits for this level.
if (uint8_tsToCopy) {
uint8_t maxuint8_ts =
useCascadeTag ? 3 : 4; // Max 4 uint8_ts in each Cascade Level.
// Only 3 left if we use the Cascade Tag
if (uint8_tsToCopy > maxuint8_ts) {
uint8_tsToCopy = maxuint8_ts;
}
for (count = 0; count < uint8_tsToCopy; count++) {
buffer[index++] = uid->uidByte[uidIndex + count];
}
}
// Now that the data has been copied we need to include the 8 bits in CT
// in currentLevelKnownBits
if (useCascadeTag) {
currentLevelKnownBits += 8;
}
// Repeat anti collision loop until we can transmit all UID bits + BCC
// and receive a SAK - max 32 iterations.
selectDone = false;
while (!selectDone) {
// Find out how many bits and uint8_ts to send and receive.
if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade
// Level are known. This is a
// SELECT.
// printf(F("SELECT: currentLevelKnownBits="));
// printf(currentLevelKnownBits, DEC);
buffer[1] =
0x70; // NVB - Number of Valid Bits: Seven whole uint8_ts
// Calculate BCC - Block Check Character
buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5];
// Calculate CRC_A
result = PCD_CalculateCRC(mfrc, buffer, 7, &buffer[7]);
if (result != STATUS_OK) {
return result;
}
txLastBits = 0; // 0 => All 8 bits are valid.
bufferUsed = 9;
// Store response in the last 3 uint8_ts of buffer (BCC and
// CRC_A - not needed after tx)
responseBuffer = &buffer[6];
responseLength = 3;
} else { // This is an ANTICOLLISION.
// printf(F("ANTICOLLISION: currentLevelKnownBits="));
// printf(currentLevelKnownBits, DEC);
txLastBits = currentLevelKnownBits % 8;
count = currentLevelKnownBits /
8; // Number of whole uint8_ts in the UID part.
index = 2 + count; // Number of whole uint8_ts: SEL + NVB + UIDs
buffer[1] =
(index << 4) + txLastBits; // NVB - Number of Valid Bits
bufferUsed = index + (txLastBits ? 1 : 0);
// Store response in the unused part of buffer
responseBuffer = &buffer[index];
responseLength = sizeof(buffer) - index;
}
// Set bit adjustments
rxAlign = txLastBits; // Having a separate variable is overkill. But
// it makes the next line easier to read.
PCD_WriteRegister(mfrc, BitFramingReg,
(rxAlign << 4) +
txLastBits); // RxAlign = BitFramingReg[6..4].
// TxLastBits =
// BitFramingReg[2..0]
// Transmit the buffer and receive the response.
result = PCD_TransceiveData(mfrc, buffer, bufferUsed,
responseBuffer, &responseLength,
&txLastBits, rxAlign, false);
if (result == STATUS_COLLISION) { // More than one PICC in the field
// => collision.
uint8_t valueOfCollReg = PCD_ReadRegister(
mfrc, CollReg); // CollReg[7..0] bits are: ValuesAfterColl
// reserved CollPosNotValid CollPos[4:0]
if (valueOfCollReg & 0x20) { // CollPosNotValid
return STATUS_COLLISION; // Without a valid collision
// position we cannot continue
}
uint8_t collisionPos =
valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32.
if (collisionPos == 0) {
collisionPos = 32;
}
if (collisionPos <=
currentLevelKnownBits) { // No progress - should not happen
return STATUS_INTERNAL_ERROR;
}
// Choose the PICC with the bit set.
currentLevelKnownBits = collisionPos;
count = (currentLevelKnownBits - 1) % 8; // The bit to modify
index = 1 + (currentLevelKnownBits / 8) +
(count ? 1 : 0); // First uint8_t is index 0.
buffer[index] |= (1 << count);
} else if (result != STATUS_OK) {
return result;
} else { // STATUS_OK
if (currentLevelKnownBits >= 32) { // This was a SELECT.
selectDone = true; // No more anticollision
// We continue below outside the while.
} else { // This was an ANTICOLLISION.
// We now have all 32 bits of the UID in this Cascade Level
currentLevelKnownBits = 32;
// Run loop again to do the SELECT.
}
}
} // End of while (!selectDone)
// We do not check the CBB - it was constructed by us above.
// Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[]
index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[]
uint8_tsToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4;
for (count = 0; count < uint8_tsToCopy; count++) {
uid->uidByte[uidIndex + count] = buffer[index++];
}
// Check response SAK (Select Acknowledge)
if (responseLength != 3 ||
txLastBits !=
0) { // SAK must be exactly 24 bits (1 uint8_t + CRC_A).
return STATUS_ERROR;
}
// Verify CRC_A - do our own calculation and store the control in
// buffer[2..3] - those uint8_ts are not needed anymore.
result = PCD_CalculateCRC(mfrc, responseBuffer, 1, &buffer[2]);
if (result != STATUS_OK) {
return result;
}
if ((buffer[2] != responseBuffer[1]) ||
(buffer[3] != responseBuffer[2])) {
return STATUS_CRC_WRONG;
}
if (responseBuffer[0] &
0x04) { // Cascade bit set - UID not complete yes
cascadeLevel++;
} else {
uidComplete = true;
uid->sak = responseBuffer[0];
}
} // End of while (!uidComplete)
// Set correct uid->size
uid->size = 3 * cascadeLevel + 1;
return STATUS_OK;
} // End PICC_Select()
/**
* Instructs a PICC in state ACTIVE(*) to go to state HALT.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PICC_HaltA(MFRC522Ptr_t mfrc) {
StatusCode result;
uint8_t buffer[4];
// Build command buffer
buffer[0] = PICC_CMD_HLTA;
buffer[1] = 0;
// Calculate CRC_A
result = PCD_CalculateCRC(mfrc, buffer, 2, &buffer[2]);
if (result != STATUS_OK) {
return result;
}
// Send the command.
// The standard says:
// If the PICC responds with any modulation during a period of 1 ms after
//the end of the frame containing the
// HLTA command, this response shall be interpreted as 'not
//acknowledge'.
// We interpret that this way: Only STATUS_TIMEOUT is a success.
result = PCD_TransceiveData(mfrc, buffer, sizeof(buffer), NULL, 0, NULL, 0,
false);
if (result == STATUS_TIMEOUT) {
return STATUS_OK;
}
if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-)
return STATUS_ERROR;
}
return result;
} // End PICC_HaltA()
/*******************************************************************************
*Functions for communicating with MIFARE PICCs
*******************************************************************************/
/**
* Executes the MFRC522 MFAuthent command.
* This command manages MIFARE authentication to enable a secure communication
*to any MIFARE Mini, MIFARE 1K and MIFARE 4K card.
* The authentication is described in the MFRC522 datasheet section 10.3.1.9 and
*http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1.
* For use with MIFARE Classic PICCs.
* The PICC must be selected - ie in state ACTIVE(*) - before calling this
*function.
* Remember to call PCD_StopCrypto1() after communicating with the authenticated
*PICC - otherwise no new communications can start.
*
* All keys are set to FFFFFFFFFFFFh at chip delivery.
*
* @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT
*if you supply the wrong key.
*/
StatusCode PCD_Authenticate(
MFRC522Ptr_t mfrc,
uint8_t command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B
uint8_t blockAddr, ///< The block number. See numbering in the comments in
///the .h file.
MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 uint8_ts)
Uid *
uid ///< Pointer to Uid struct. The first 4 uint8_ts of the UID is used.
) {
uint8_t waitIRq = 0x10; // IdleIRq
// Build command buffer
uint8_t sendData[12];
sendData[0] = command;
sendData[1] = blockAddr;
for (uint8_t i = 0; i < MF_KEY_SIZE; i++) { // 6 key uint8_ts
sendData[2 + i] = key->keybyte[i];
}
for (uint8_t i = 0; i < 4; i++) { // The first 4 uint8_ts of the UID
sendData[8 + i] = uid->uidByte[i];
}
// Start the authentication.
return PCD_CommunicateWithPICC(mfrc, PCD_MFAuthent, waitIRq, &sendData[0],
sizeof(sendData), NULL, 0, 0, 0, false);
} // End PCD_Authenticate()
/**
* Used to exit the PCD from its authenticated state.
* Remember to call this function after communicating with an authenticated PICC
* - otherwise no new communications can start.
*/
void PCD_StopCrypto1(MFRC522Ptr_t mfrc) {
// Clear MFCrypto1On bit
PCD_ClearRegisterBitMask(mfrc, Status2Reg, 0x08); // Status2Reg[7..0] bits
// are: TempSensClear
// I2CForceHS reserved
// reserved MFCrypto1On
// ModemState[2:0]
} // End PCD_StopCrypto1()
/**
* Reads 16 uint8_ts (+ 2 uint8_ts CRC_A) from the active PICC.
*
* For MIFARE Classic the sector containing the block must be authenticated
*before calling this function.
*
* For MIFARE Ultralight only addresses 00h to 0Fh are decoded.
* The MF0ICU1 returns a NAK for higher addresses.
* The MF0ICU1 responds to the READ command by sending 16 uint8_ts starting from
*the page address defined by the command argument.
* For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned.
* A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages
*0Eh, 0Fh, 00h and 01h are returned.
*
* The buffer must be at least 18 uint8_ts because a CRC_A is also returned.
* Checks the CRC_A before returning STATUS_OK.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_Read(MFRC522Ptr_t mfrc,
uint8_t blockAddr, ///< MIFARE Classic: The block
///(0-0xff) number. MIFARE Ultralight:
///The first page to return data from.
uint8_t *buffer, ///< The buffer to store the data in
uint8_t *bufferSize ///< Buffer size, at least 18
///uint8_ts. Also number of uint8_ts
///returned if STATUS_OK.
) {
StatusCode result;
// Sanity check
if (buffer == NULL || *bufferSize < 18) {
return STATUS_NO_ROOM;
}
// Build command buffer
buffer[0] = PICC_CMD_MF_READ;
buffer[1] = blockAddr;
// Calculate CRC_A
result = PCD_CalculateCRC(mfrc, buffer, 2, &buffer[2]);
if (result != STATUS_OK) {
return result;
}
// Transmit the buffer and receive the response, validate CRC_A.
return PCD_TransceiveData(mfrc, buffer, 4, buffer, bufferSize, NULL, 0,
true);
} // End MIFARE_Read()
/**
* Writes 16 uint8_ts to the active PICC.
*
* For MIFARE Classic the sector containing the block must be authenticated
*before calling this function.
*
* For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE".
* Even though 16 uint8_ts are transferred to the Ultralight PICC, only the
*least significant 4 uint8_ts (uint8_ts 0 to 3)
* are written to the specified address. It is recommended to set the remaining
*uint8_ts 04h to 0Fh to all logic 0.
* *
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode
MIFARE_Write(MFRC522Ptr_t mfrc,
uint8_t blockAddr, ///< MIFARE Classic: The block (0-0xff) number.
///MIFARE Ultralight: The page (2-15) to write
///to.
uint8_t *buffer, ///< The 16 uint8_ts to write to the PICC
uint8_t bufferSize ///< Buffer size, must be at least 16 uint8_ts.
///Exactly 16 uint8_ts are written.
) {
StatusCode result;
// Sanity check
if (buffer == NULL || bufferSize < 16) {
return STATUS_INVALID;
}
// Mifare Classic protocol requires two communications to perform a write.
// Step 1: Tell the PICC we want to write to block blockAddr.
uint8_t cmdBuffer[2];
cmdBuffer[0] = PICC_CMD_MF_WRITE;
cmdBuffer[1] = blockAddr;
result = PCD_MIFARE_Transceive(
mfrc, cmdBuffer, 2,
false); // Adds CRC_A and checks that the response is MF_ACK.
if (result != STATUS_OK) {
return result;
}
// Step 2: Transfer the data
result = PCD_MIFARE_Transceive(
mfrc, buffer, bufferSize,
false); // Adds CRC_A and checks that the response is MF_ACK.
if (result != STATUS_OK) {
return result;
}
return STATUS_OK;
} // End MIFARE_Write()
/**
* Writes a 4 uint8_t page to the active MIFARE Ultralight PICC.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_Ultralight_Write(
MFRC522Ptr_t mfrc,
uint8_t page, ///< The page (2-15) to write to.
uint8_t *buffer, ///< The 4 uint8_ts to write to the PICC
uint8_t bufferSize ///< Buffer size, must be at least 4 uint8_ts. Exactly 4
///uint8_ts are written.
) {
StatusCode result;
// Sanity check
if (buffer == NULL || bufferSize < 4) {
return STATUS_INVALID;
}
// Build commmand buffer
uint8_t cmdBuffer[6];
cmdBuffer[0] = PICC_CMD_UL_WRITE;
cmdBuffer[1] = page;
memcpy(&cmdBuffer[2], buffer, 4);
// Perform the write
result = PCD_MIFARE_Transceive(
mfrc, cmdBuffer, 6,
false); // Adds CRC_A and checks that the response is MF_ACK.
if (result != STATUS_OK) {
return result;
}
return STATUS_OK;
} // End MIFARE_Ultralight_Write()
/**
* MIFARE Decrement subtracts the delta from the value of the addressed block,
*and stores the result in a volatile memory.
* For MIFARE Classic only. The sector containing the block must be
*authenticated before calling this function.
* Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
*or [001].
* Use MIFARE_Transfer() to store the result in a block.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_Decrement(
MFRC522Ptr_t mfrc,
uint8_t blockAddr, ///< The block (0-0xff) number.
long delta ///< This number is subtracted from the value of block blockAddr.
) {
return MIFARE_TwoStepHelper(mfrc, PICC_CMD_MF_DECREMENT, blockAddr, delta);
} // End MIFARE_Decrement()
/**
* MIFARE Increment adds the delta to the value of the addressed block, and
*stores the result in a volatile memory.
* For MIFARE Classic only. The sector containing the block must be
*authenticated before calling this function.
* Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
*or [001].
* Use MIFARE_Transfer() to store the result in a block.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_Increment(
MFRC522Ptr_t mfrc,
uint8_t blockAddr, ///< The block (0-0xff) number.
long delta ///< This number is added to the value of block blockAddr.
) {
return MIFARE_TwoStepHelper(mfrc, PICC_CMD_MF_INCREMENT, blockAddr, delta);
} // End MIFARE_Increment()
/**
* MIFARE Restore copies the value of the addressed block into a volatile
*memory.
* For MIFARE Classic only. The sector containing the block must be
*authenticated before calling this function.
* Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
*or [001].
* Use MIFARE_Transfer() to store the result in a block.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_Restore(MFRC522Ptr_t mfrc,
uint8_t blockAddr ///< The block (0-0xff) number.
) {
// The datasheet describes Restore as a two step operation, but does not
// explain what data to transfer in step 2.
// Doing only a single step does not work, so I chose to transfer 0L in step
// two.
return MIFARE_TwoStepHelper(mfrc, PICC_CMD_MF_RESTORE, blockAddr, 0L);
} // End MIFARE_Restore()
/**
* Helper function for the two-step MIFARE Classic protocol operations
*Decrement, Increment and Restore.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode
MIFARE_TwoStepHelper(MFRC522Ptr_t mfrc,
uint8_t command, ///< The command to use
uint8_t blockAddr, ///< The block (0-0xff) number.
long data ///< The data to transfer in step 2
) {
StatusCode result;
uint8_t cmdBuffer[2]; // We only need room for 2 uint8_ts.
// Step 1: Tell the PICC the command and block address
cmdBuffer[0] = command;
cmdBuffer[1] = blockAddr;
result = PCD_MIFARE_Transceive(
mfrc, cmdBuffer, 2,
false); // Adds CRC_A and checks that the response is MF_ACK.
if (result != STATUS_OK) {
return result;
}
// Step 2: Transfer the data
result = PCD_MIFARE_Transceive(
mfrc, (uint8_t *)&data, 4,
true); // Adds CRC_A and accept timeout as success.
if (result != STATUS_OK) {
return result;
}
return STATUS_OK;
} // End MIFARE_TwoStepHelper()
/**
* MIFARE Transfer writes the value stored in the volatile memory into one
*MIFARE Classic block.
* For MIFARE Classic only. The sector containing the block must be
*authenticated before calling this function.
* Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
*or [001].
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_Transfer(MFRC522Ptr_t mfrc,
uint8_t blockAddr ///< The block (0-0xff) number.
) {
StatusCode result;
uint8_t cmdBuffer[2]; // We only need room for 2 uint8_ts.
// Tell the PICC we want to transfer the result into block blockAddr.
cmdBuffer[0] = PICC_CMD_MF_TRANSFER;
cmdBuffer[1] = blockAddr;
result = PCD_MIFARE_Transceive(
mfrc, cmdBuffer, 2,
false); // Adds CRC_A and checks that the response is MF_ACK.
if (result != STATUS_OK) {
return result;
}
return STATUS_OK;
} // End MIFARE_Transfer()
/**
* Helper routine to read the current value from a Value Block.
*
* Only for MIFARE Classic and only for blocks in "value block" mode, that
* is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing
* the block must be authenticated before calling this function.
*
* @param[in] blockAddr The block (0x00-0xff) number.
* @param[out] value Current value of the Value Block.
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_GetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long *value) {
StatusCode status;
uint8_t buffer[18];
uint8_t size = sizeof(buffer);
// Read the block
status = MIFARE_Read(mfrc, blockAddr, buffer, &size);
if (status == STATUS_OK) {
// Extract the value
// *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) |
//(long(buffer[1])<<8) | long(buffer[0]);
// long l_0 = buffer[0];
// long l_8 = buffer[1]<<8;
// long l_16 = buffer[2]<<16;
// long l_24 = buffer[3]<<24;
// *value =l_24 | l_16 | l_8 | l_0;
*value = ((long)(buffer[3]) << 24) | ((long)(buffer[2]) << 16) |
((long)(buffer[1]) << 8) | (long)(buffer[0]);
}
return status;
} // End MIFARE_GetValue()
/**
* Helper routine to write a specific value into a Value Block.
*
* Only for MIFARE Classic and only for blocks in "value block" mode, that
* is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing
* the block must be authenticated before calling this function.
*
* @param[in] blockAddr The block (0x00-0xff) number.
* @param[in] value New value of the Value Block.
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode MIFARE_SetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long value) {
uint8_t buffer[18];
// Translate the long into 4 uint8_ts; repeated 2x in value block
buffer[0] = buffer[8] = (value & 0xFF);
buffer[1] = buffer[9] = (value & 0xFF00) >> 8;
buffer[2] = buffer[10] = (value & 0xFF0000) >> 16;
buffer[3] = buffer[11] = (value & 0xFF000000) >> 24;
// Inverse 4 uint8_ts also found in value block
buffer[4] = ~buffer[0];
buffer[5] = ~buffer[1];
buffer[6] = ~buffer[2];
buffer[7] = ~buffer[3];
// Address 2x with inverse address 2x
buffer[12] = buffer[14] = blockAddr;
buffer[13] = buffer[15] = ~blockAddr;
// Write the whole data block
return MIFARE_Write(mfrc, blockAddr, buffer, 16);
} // End MIFARE_SetValue()
/**
* Authenticate with a NTAG216.
*
* Only for NTAG216. First implemented by Gargantuanman.
*
* @param[in] passWord password.
* @param[in] pACK result success???.
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode PCD_NTAG216_AUTH(MFRC522Ptr_t mfrc, uint8_t *passWord,
uint8_t pACK[]) // Authenticate with 32bit password
{
StatusCode result;
uint8_t cmdBuffer[18]; // We need room for 16 uint8_ts data and 2 uint8_ts
// CRC_A.
cmdBuffer[0] = 0x1B; // Comando de autentificacion
for (uint8_t i = 0; i < 4; i++)
cmdBuffer[i + 1] = passWord[i];
result = PCD_CalculateCRC(mfrc, cmdBuffer, 5, &cmdBuffer[5]);
if (result != STATUS_OK) {
return result;
}
// Transceive the data, store the reply in cmdBuffer[]
uint8_t waitIRq = 0x30; // RxIRq and IdleIRq
uint8_t cmdBufferSize = sizeof(cmdBuffer);
uint8_t validBits = 0;
uint8_t rxlength = 5;
result =
PCD_CommunicateWithPICC(mfrc, PCD_Transceive, waitIRq, cmdBuffer, 7,
cmdBuffer, &rxlength, &validBits, 0, false);
pACK[0] = cmdBuffer[0];
pACK[1] = cmdBuffer[1];
if (result != STATUS_OK) {
return result;
}
return STATUS_OK;
} // End PCD_NTAG216_AUTH()
/*******************************************************************************
* Support functions
*******************************************************************************/
/**
* Wrapper for MIFARE protocol communication.
* Adds CRC_A, executes the Transceive command and checks that the response is
*MF_ACK or a timeout.
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
StatusCode
PCD_MIFARE_Transceive(MFRC522Ptr_t mfrc,
uint8_t *sendData, ///< Pointer to the data to transfer to
///the FIFO. Do NOT include the CRC_A.
uint8_t sendLen, ///< Number of uint8_ts in sendData.
bool acceptTimeout ///< True => A timeout is also success
) {
// acceptTimeout = false;
StatusCode result;
uint8_t cmdBuffer[18]; // We need room for 16 uint8_ts data and 2 uint8_ts
// CRC_A.
// Sanity check
if (sendData == NULL || sendLen > 16) {
return STATUS_INVALID;
}
// Copy sendData[] to cmdBuffer[] and add CRC_A
memcpy(cmdBuffer, sendData, sendLen);
result = PCD_CalculateCRC(mfrc, cmdBuffer, sendLen, &cmdBuffer[sendLen]);
if (result != STATUS_OK) {
return result;
}
sendLen += 2;
// Transceive the data, store the reply in cmdBuffer[]
uint8_t waitIRq = 0x30; // RxIRq and IdleIRq
uint8_t cmdBufferSize = sizeof(cmdBuffer);
uint8_t validBits = 0;
result = PCD_CommunicateWithPICC(mfrc, PCD_Transceive, waitIRq, cmdBuffer,
sendLen, cmdBuffer, &cmdBufferSize,
&validBits, 0, false);
if (acceptTimeout && result == STATUS_TIMEOUT) {
return STATUS_OK;
}
if (result != STATUS_OK) {
return result;
}
// The PICC must reply with a 4 bit ACK
if (cmdBufferSize != 1 || validBits != 4) {
return STATUS_ERROR;
}
if (cmdBuffer[0] != MF_ACK) {
return STATUS_MIFARE_NACK;
}
return STATUS_OK;
} // End PCD_MIFARE_Transceive()
/**
* Returns const char pointer to a status code name.
*
* @return const char *
*/
const char *GetStatusCodeName(StatusCode code ///< One of theStatusCode enums.
) {
switch (code) {
case STATUS_OK:
return "Success.";
case STATUS_ERROR:
return "Error in communication.";
case STATUS_COLLISION:
return "Collision detected.";
case STATUS_TIMEOUT:
return "Timeout in communication.";
case STATUS_NO_ROOM:
return "A buffer is not big enough.";
case STATUS_INTERNAL_ERROR:
return "Internal error in the code. Should not happen.";
case STATUS_INVALID:
return "Invalid argument.";
case STATUS_CRC_WRONG:
return "The CRC_A does not match.";
case STATUS_MIFARE_NACK:
return "A MIFARE PICC responded with NAK.";
default:
return "Unknown error";
}
} // End GetStatusCodeName()
/**
* Translates the SAK (Select Acknowledge) to a PICC type.
*
* @return PICC_Type
*/
PICC_Type
PICC_GetType(uint8_t sak ///< The SAK uint8_t returned from PICC_Select().
) {
// http://www.nxp.com/documents/application_note/AN10833.pdf
// 3.2 Coding of Select Acknowledge (SAK)
// ignore 8-bit (iso14443 starts with LSBit = bit 1)
// fixes wrong type for manufacturer Infineon
// (http://nfc-tools.org/index.php?title=ISO14443A)
sak &= 0x7F;
switch (sak) {
case 0x04:
return PICC_TYPE_NOT_COMPLETE; // UID not complete
case 0x09:
return PICC_TYPE_MIFARE_MINI;
case 0x08:
return PICC_TYPE_MIFARE_1K;
case 0x18:
return PICC_TYPE_MIFARE_4K;
case 0x00:
return PICC_TYPE_MIFARE_UL;
case 0x10:
case 0x11:
return PICC_TYPE_MIFARE_PLUS;
case 0x01:
return PICC_TYPE_TNP3XXX;
case 0x20:
return PICC_TYPE_ISO_14443_4;
case 0x40:
return PICC_TYPE_ISO_18092;
default:
return PICC_TYPE_UNKNOWN;
}
} // End PICC_GetType()
/**
* Returns const char pointer to the PICC type name.
*
* @return const char *
*/
const char *PICC_GetTypeName(PICC_Type piccType ///< One of the PICC_Type enums.
) {
switch (piccType) {
case PICC_TYPE_ISO_14443_4:
return "PICC compliant with ISO/IEC 14443-4";
case PICC_TYPE_ISO_18092:
return "PICC compliant with ISO/IEC 18092 (NFC)";
case PICC_TYPE_MIFARE_MINI:
return "MIFARE Mini, 320 bytes";
case PICC_TYPE_MIFARE_1K:
return "MIFARE 1KB";
case PICC_TYPE_MIFARE_4K:
return "MIFARE 4KB";
case PICC_TYPE_MIFARE_UL:
return "MIFARE Ultralight or Ultralight C";
case PICC_TYPE_MIFARE_PLUS:
return "MIFARE Plus";
case PICC_TYPE_TNP3XXX:
return "MIFARE TNP3XXX";
case PICC_TYPE_NOT_COMPLETE:
return "SAK indicates UID is not complete.";
case PICC_TYPE_UNKNOWN:
default:
return "Unknown type";
}
} // End PICC_GetTypeName()
/**
* Dumps debug info about the connected PCD to Serial.
* Shows all known firmware versions
*/
void PCD_DumpVersionToSerial(MFRC522Ptr_t mfrc) {
char string[2];
// Get the MFRC522 firmware version
uint8_t v = PCD_ReadRegister(mfrc, VersionReg);
printf("Firmware Version: 0x");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)v);
printf(string);
// Lookup which version
switch (v) {
case 0x88:
printf(" = (clone)\r\n");
break;
case 0x90:
printf(" = v0.0\r\n");
break;
case 0x91:
printf(" = v1.0\r\n");
break;
case 0x92:
printf(" = v2.0\r\n");
break;
default:
printf(" = (unknown)\r\n");
}
// When 0x00 or 0xFF is returned, communication probably failed
if ((v == 0x00) || (v == 0xFF))
printf("WARNING: Communication failure, is the MFRC522 properly "
"connected?\r\n");
} // End PCD_DumpVersionToSerial()
/**
* Dumps debug info about the selected PICC to Serial.
* On success the PICC is halted after dumping the data.
* For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried.
*/
void PICC_DumpToSerial(MFRC522Ptr_t mfrc, Uid *uid ///< Pointer to Uid struct
///returned from a successful
///PICC_Select().
) {
MIFARE_Key key;
// Dump UID, SAK and Type
PICC_DumpDetailsToSerial(uid);
// Dump contents
PICC_Type piccType = PICC_GetType(uid->sak);
switch (piccType) {
case PICC_TYPE_MIFARE_MINI:
case PICC_TYPE_MIFARE_1K:
case PICC_TYPE_MIFARE_4K:
// All keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
for (uint8_t i = 0; i < 6; i++) {
key.keybyte[i] = 0xFF;
}
PICC_DumpMifareClassicToSerial(mfrc, uid, piccType, &key);
break;
case PICC_TYPE_MIFARE_UL:
PICC_DumpMifareUltralightToSerial(mfrc);
break;
case PICC_TYPE_ISO_14443_4:
case PICC_TYPE_ISO_18092:
case PICC_TYPE_MIFARE_PLUS:
case PICC_TYPE_TNP3XXX:
printf(
"Dumping memory contents not implemented for that PICC type.\r\n");
break;
case PICC_TYPE_UNKNOWN:
case PICC_TYPE_NOT_COMPLETE:
default:
break; // No memory dump here
}
printf("\r\n");
PICC_HaltA(mfrc); // Already done if it was a MIFARE Classic PICC.
} // End PICC_DumpToSerial()
/**
* Dumps card info (UID,SAK,Type) about the selected PICC to Serial.
*/
void PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from
///a successful PICC_Select().
) {
char string[2];
// UID
printf("Card UID:");
for (uint8_t i = 0; i < uid->size; i++) {
if (uid->uidByte[i] < 0x10)
printf(" 0");
else
printf(" ");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)uid->uidByte[i]);
printf(string);
}
printf("\r\n");
// SAK
printf("Card SAK: ");
if (uid->sak < 0x10)
printf("0");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)uid->sak);
printf(string);
// (suggested) PICC type
PICC_Type piccType = PICC_GetType(uid->sak);
printf(" PICC type: ");
printf(PICC_GetTypeName(piccType));
} // End PICC_DumpDetailsToSerial()
/**
* Dumps memory contents of a MIFARE Classic PICC.
* On success the PICC is halted after dumping the data.
*/
void PICC_DumpMifareClassicToSerial(
MFRC522Ptr_t mfrc,
Uid *uid, ///< Pointer to Uid struct returned from a successful
///PICC_Select().
PICC_Type piccType, ///< One of the PICC_Type enums.
MIFARE_Key *key ///< Key A used for all sectors.
) {
uint8_t no_of_sectors = 0;
switch (piccType) {
case PICC_TYPE_MIFARE_MINI:
// Has 5 sectors * 4 blocks/sector * 16 uint8_ts/block = 320 uint8_ts.
no_of_sectors = 5;
break;
case PICC_TYPE_MIFARE_1K:
// Has 16 sectors * 4 blocks/sector * 16 uint8_ts/block = 1024 uint8_ts.
no_of_sectors = 16;
break;
case PICC_TYPE_MIFARE_4K:
// Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) *
// 16 uint8_ts/block = 4096 uint8_ts.
no_of_sectors = 40;
break;
default: // Should not happen. Ignore.
break;
}
// Dump sectors, highest address first.
if (no_of_sectors) {
printf("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 "
"14 15 AccessBits\r\n");
for (int8_t i = no_of_sectors - 1; i >= 0; i--) {
PICC_DumpMifareClassicSectorToSerial(mfrc, uid, key, i);
}
}
PICC_HaltA(mfrc); // Halt the PICC before stopping the encrypted session.
PCD_StopCrypto1(mfrc);
} // End PICC_DumpMifareClassicToSerial()
/**
* Dumps memory contents of a sector of a MIFARE Classic PICC.
* Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1.
* Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the
* sector trailer access bits.
*/
void PICC_DumpMifareClassicSectorToSerial(
MFRC522Ptr_t mfrc,
Uid *uid, ///< Pointer to Uid struct returned from a successful
///PICC_Select().
MIFARE_Key *key, ///< Key A for the sector.
uint8_t sector ///< The sector to dump, 0..39.
) {
char string[2];
uint8_t acces_bit;
StatusCode status;
uint8_t firstBlock; // Address of lowest address to dump actually last block
// dumped)
uint8_t no_of_blocks; // Number of blocks in sector
bool isSectorTrailer; // Set to true while handling the "last" (ie highest
// address) in the sector.
// The access bits are stored in a peculiar fashion.
// There are four groups:
// g[3] Access bits for the sector trailer, block 3 (for sectors 0-31)
//or block 15 (for sectors 32-39)
// g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for
//sectors 32-39)
// g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for
//sectors 32-39)
// g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for
//sectors 32-39)
// Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is
// LSB.
// The four CX bits are stored together in a nible cx and an inverted nible
// cx_.
uint8_t c1, c2, c3; // Nibbles
uint8_t c1_, c2_, c3_; // Inverted nibbles
bool invertedError; // True if one of the inverted nibbles did not match
uint8_t g[4]; // Access bits for each of the four groups.
uint8_t group; // 0-3 - active group for access bits
bool firstInGroup; // True for the first block dumped in the group
// Determine position and size of sector.
if (sector < 32) { // Sectors 0..31 has 4 blocks each
no_of_blocks = 4;
firstBlock = sector * no_of_blocks;
} else if (sector < 40) { // Sectors 32-39 has 16 blocks each
no_of_blocks = 16;
firstBlock = 128 + (sector - 32) * no_of_blocks;
} else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors.
return;
}
// Dump blocks, highest address first.
uint8_t uint8_tCount;
uint8_t buffer[18];
uint8_t blockAddr;
isSectorTrailer = true;
for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0;
blockOffset--) {
blockAddr = firstBlock + blockOffset;
// Sector number - only on first line
if (isSectorTrailer) {
if (sector < 10)
printf(" "); // Pad with spaces
else
printf(" "); // Pad with spaces
sprintf(string, "%u", sector);
printf(string);
printf(" ");
} else {
printf(" ");
}
// Block number
if (blockAddr < 10)
printf(" "); // Pad with spaces
else {
if (blockAddr < 100)
printf(" "); // Pad with spaces
else
printf(" "); // Pad with spaces
}
sprintf(string, "%u", blockAddr);
printf(string);
printf(" ");
// Establish encrypted communications before reading the first block
if (isSectorTrailer) {
status = PCD_Authenticate(mfrc, PICC_CMD_MF_AUTH_KEY_A, firstBlock,
key, uid);
if (status != STATUS_OK) {
printf("PCD_Authenticate() failed: ");
printf(GetStatusCodeName(status));
printf("\n");
return;
}
}
// Read block
uint8_tCount = sizeof(buffer);
status = MIFARE_Read(mfrc, blockAddr, buffer, &uint8_tCount);
if (status != STATUS_OK) {
printf("MIFARE_Read() failed: ");
printf(GetStatusCodeName(status));
continue;
}
// Dump data
for (uint8_t index = 0; index < 16; index++) {
if (buffer[index] < 0x10)
printf(" 0");
else
printf(" ");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)buffer[index]);
printf(string);
if ((index % 4) == 3) {
printf(" ");
}
}
// Parse sector trailer data
if (isSectorTrailer) {
c1 = buffer[7] >> 4;
c2 = buffer[8] & 0xF;
c3 = buffer[8] >> 4;
c1_ = buffer[6] & 0xF;
c2_ = buffer[6] >> 4;
c3_ = buffer[7] & 0xF;
invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) ||
(c3 != (~c3_ & 0xF));
g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0);
g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1);
g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2);
g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3);
isSectorTrailer = false;
}
// Which access group is this block in?
if (no_of_blocks == 4) {
group = blockOffset;
firstInGroup = true;
} else {
group = blockOffset / 5;
firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5);
}
if (firstInGroup) {
// Print access bits
printf(" [ ");
acces_bit = (g[group] >> 2) & 1;
sprintf(string, "%u", acces_bit);
printf(string);
printf(" ");
acces_bit = (g[group] >> 1) & 1;
sprintf(string, "%u", acces_bit);
printf(string);
printf(" ");
acces_bit = (g[group] >> 0) & 1;
sprintf(string, "%u", acces_bit);
printf(string);
printf(" ] ");
if (invertedError) {
printf(" Inverted access bits did not match! ");
}
}
if (group != 3 &&
(g[group] == 1 ||
g[group] == 6)) { // Not a sector trailer, a value block
long value = ((long)(buffer[3]) << 24) | ((long)(buffer[2]) << 16) |
((long)(buffer[1]) << 8) | (long)(buffer[0]);
printf(" Value=0x");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)value);
printf(string);
printf(" Adr=0x");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)buffer[12]);
printf(string);
}
printf("\r\n");
}
return;
} // End PICC_DumpMifareClassicSectorToSerial()
/**
* Dumps memory contents of a MIFARE Ultralight PICC.
*/
void PICC_DumpMifareUltralightToSerial(MFRC522Ptr_t mfrc) {
char string[2];
StatusCode status;
uint8_t uint8_tCount;
uint8_t buffer[18];
uint8_t i;
printf("Page 0 1 2 3\r\n");
// Try the mpages of the original Ultralight. Ultralight C has more pages.
for (uint8_t page = 0; page < 16;
page += 4) { // Read returns data for 4 pages at a time.
// Read pages
uint8_tCount = sizeof(buffer);
status = MIFARE_Read(mfrc, page, buffer, &uint8_tCount);
if (status != STATUS_OK) {
printf("MIFARE_Read() failed: ");
printf(GetStatusCodeName(status));
break;
}
// Dump data
for (uint8_t offset = 0; offset < 4; offset++) {
i = page + offset;
if (i < 10)
printf(" "); // Pad with spaces
else
printf(" "); // Pad with spaces
sprintf(string, "%u", i);
printf(string);
printf(" ");
for (uint8_t index = 0; index < 4; index++) {
i = 4 * offset + index;
if (buffer[i] < 0x10)
printf(" 0");
else
printf(" ");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)buffer[i]);
printf(string);
}
printf("\r\n");
}
}
} // End PICC_DumpMifareUltralightToSerial()
/**
* Calculates the bit pattern needed for the specified access bits. In the [C1
* C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1).
*/
void MIFARE_SetAccessBits(
uint8_t *accessBitBuffer, ///< Pointer to uint8_t 6, 7 and 8 in the sector
///trailer. uint8_ts [0..2] will be set.
uint8_t g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or
///blocks 0-4 (for sectors 32-39)
uint8_t g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or
///blocks 5-9 (for sectors 32-39)
uint8_t g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or
///blocks 10-14 (for sectors 32-39)
uint8_t g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for
///sectors 0-31) or block 15 (for sectors 32-39)
) {
uint8_t c1 =
((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2);
uint8_t c2 =
((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1);
uint8_t c3 =
((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0);
accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF);
accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF);
accessBitBuffer[2] = c3 << 4 | c2;
} // End MIFARE_SetAccessBits()
/**
* Performs the "magic sequence" needed to get Chinese UID changeable
* Mifare cards to allow writing to sector 0, where the card UID is stored.
*
* Note that you do not need to have selected the card through REQA or WUPA,
* this sequence works immediately when the card is in the reader vicinity.
* This means you can use this method even on "bricked" cards that your reader
*does
* not recognise anymore (see MIFARE_UnbrickUidSector).
*
* Of course with non-bricked devices, you're free to select them before calling
*this function.
*/
bool MIFARE_OpenUidBackdoor(MFRC522Ptr_t mfrc, bool logErrors) {
char string[2];
// Magic sequence:
// > 50 00 57 CD (HALT + CRC)
// > 40 (7 bits only)
// < A (4 bits only)
// > 43
// < A (4 bits only)
// Then you can write to sector 0 without authenticating
PICC_HaltA(mfrc); // 50 00 57 CD
uint8_t cmd = 0x40;
uint8_t validBits =
7; /* Our command is only 7 bits. After receiving card response,
this will contain amount of valid response bits. */
uint8_t response[32]; // Card's response is written here
uint8_t received;
StatusCode status =
PCD_TransceiveData(mfrc, &cmd, (uint8_t)1, response, &received,
&validBits, (uint8_t)0, false); // 40
if (status != STATUS_OK) {
if (logErrors) {
printf("Card did not respond to 0x40 after HALT command. Are you "
"sure it is a UID changeable one?\r\n");
printf("Error name: ");
printf(GetStatusCodeName(status));
}
return false;
}
if (received != 1 || response[0] != 0x0A) {
if (logErrors) {
printf("Got bad response on backdoor 0x40 command: ");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)response[0]);
printf(string);
printf(" (");
sprintf(string, "%u", validBits);
printf(string);
printf(" valid bits)\r\n");
}
return false;
}
cmd = 0x43;
validBits = 8;
status = PCD_TransceiveData(mfrc, &cmd, (uint8_t)1, response, &received,
&validBits, (uint8_t)0, false); // 43
if (status != STATUS_OK) {
if (logErrors) {
printf("Error in communication at command 0x43, after "
"successfully executing 0x40\r\n");
printf("Error name: ");
printf(GetStatusCodeName(status));
}
return false;
}
if (received != 1 || response[0] != 0x0A) {
if (logErrors) {
printf("Got bad response on backdoor 0x43 command: ");
// print the hexa value of a unsigned char
sprintf(string, "%02X", (char)response[0]);
printf(string);
printf(" (");
sprintf(string, "%u", validBits);
printf(string);
printf(" valid bits)\r\n");
}
return false;
}
// You can now write to sector 0 without authenticating!
return true;
} // End MIFARE_OpenUidBackdoor()
/**
* Reads entire block 0, including all manufacturer data, and overwrites
* that block with the new UID, a freshly calculated BCC, and the original
* manufacturer data.
*
* It assumes a default KEY A of 0xFFFFFFFFFFFF.
* Make sure to have selected the card before this function is called.
*/
bool MIFARE_SetUid(MFRC522Ptr_t mfrc, uint8_t *newUid, uint8_t uidSize,
bool logErrors) {
// UID + BCC uint8_t can not be larger than 16 together
if (!newUid || !uidSize || uidSize > 15) {
if (logErrors) {
printf("New UID buffer empty, size 0, or size > 15 given\r\n");
}
return false;
}
// Authenticate for reading
MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
StatusCode status = PCD_Authenticate(mfrc, PICC_CMD_MF_AUTH_KEY_A,
(uint8_t)1, &key, &(mfrc->uid));
if (status != STATUS_OK) {
if (status == STATUS_TIMEOUT) {
// We get a read timeout if no card is selected yet, so let's select
// one
// Wake the card up again if sleeping
// uint8_t atqa_answer[2];
// uint8_t atqa_size = 2;
// PICC_WakeupA(atqa_answer, &atqa_size);
if (!PICC_IsNewCardPresent(mfrc) || !PICC_ReadCardSerial(mfrc)) {
printf("No card was previously selected, and none are "
"available. Failed to set UID.\r\n");
return false;
}
status = PCD_Authenticate(mfrc, PICC_CMD_MF_AUTH_KEY_A, (uint8_t)1,
&key, &(mfrc->uid));
if (status != STATUS_OK) {
// We tried, time to give up
if (logErrors) {
printf("Failed to authenticate to card for reading, "
"could not set UID: \r\n");
printf(GetStatusCodeName(status));
}
return false;
}
} else {
if (logErrors) {
printf("PCD_Authenticate() failed: ");
printf(GetStatusCodeName(status));
printf("\n");
}
return false;
}
}
// Read block 0
uint8_t block0_buffer[18];
uint8_t uint8_tCount = sizeof(block0_buffer);
status = MIFARE_Read(mfrc, (uint8_t)0, block0_buffer, &uint8_tCount);
if (status != STATUS_OK) {
if (logErrors) {
printf("MIFARE_Read() failed: ");
printf(GetStatusCodeName(status));
printf(
"Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?\r\n");
}
return false;
}
// Write new UID to the data we just read, and calculate BCC uint8_t
uint8_t bcc = 0;
for (int i = 0; i < uidSize; i++) {
block0_buffer[i] = newUid[i];
bcc ^= newUid[i];
}
// Write BCC uint8_t to buffer
block0_buffer[uidSize] = bcc;
// Stop encrypted traffic so we can send raw uint8_ts
PCD_StopCrypto1(mfrc);
// Activate UID backdoor
if (!MIFARE_OpenUidBackdoor(mfrc, logErrors)) {
if (logErrors) {
printf("Activating the UID backdoor failed.\r\n");
}
return false;
}
// Write modified block 0 back to card
status = MIFARE_Write(mfrc, (uint8_t)0, block0_buffer, (uint8_t)16);
if (status != STATUS_OK) {
if (logErrors) {
printf("MIFARE_Write() failed: ");
printf(GetStatusCodeName(status));
}
return false;
}
// Wake the card up again
uint8_t atqa_answer[2];
uint8_t atqa_size = 2;
PICC_WakeupA(mfrc, atqa_answer, &atqa_size);
return true;
}
/**
* Resets entire sector 0 to zeroes, so the card can be read again by readers.
*/
bool MIFARE_UnbrickUidSector(MFRC522Ptr_t mfrc, bool logErrors) {
MIFARE_OpenUidBackdoor(mfrc, logErrors);
uint8_t block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Write modified block 0 back to card
StatusCode status =
MIFARE_Write(mfrc, (uint8_t)0, block0_buffer, (uint8_t)16);
if (status != STATUS_OK) {
if (logErrors) {
printf("MIFARE_Write() failed: ");
printf(GetStatusCodeName(status));
}
return false;
}
return true;
}
/*******************************************************************************
* Convenience functions - does not add extra functionality
*******************************************************************************/
/**
* Returns true if a PICC responds to PICC_CMD_REQA.
* Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are
*ignored.
*
* @return bool
*/
bool PICC_IsNewCardPresent(MFRC522Ptr_t mfrc) {
uint8_t bufferATQA[2];
uint8_t bufferSize = sizeof(bufferATQA);
StatusCode result = PICC_RequestA(mfrc, bufferATQA, &bufferSize);
return (result == STATUS_OK || result == STATUS_COLLISION);
} // End PICC_IsNewCardPresent()
/**
* Simple wrapper around PICC_Select.
* Returns true if a UID could be read.
* Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA()
*first.
* The read UID is available in the class variable uid.
*
* @return bool
*/
bool PICC_ReadCardSerial(MFRC522Ptr_t mfrc) {
StatusCode result = PICC_Select(mfrc, &(mfrc->uid), 0);
return (result == STATUS_OK);
} // End
static inline void cs_select(const uint cs) {
asm volatile("nop \n nop \n nop");
gpio_put(cs, 0); // Active low
asm volatile("nop \n nop \n nop");
}
static inline void cs_deselect(const uint cs) {
asm volatile("nop \n nop \n nop");
gpio_put(cs, 1);
asm volatile("nop \n nop \n nop");
}
Nastavení pinů je zde:
pico-mfrc522/mfrc522.h
/*
* mfrc522.h
*
* mfrc522 library for pi pico c/c++ sdk
*
* Created: 2023
* Author: Benjamin Modica
*
* This is an adaptation of a c library for PLCOpen made by:
* https://github.com/luisfg30/rfid Which is an adaptation of the C++ Arduino library , available at:
* https://github.com/miguelbalboa/rfid.git
* The original library is released to the public domain.
*
*/
#ifndef MFRC522_h
#define MFRC522_h
#include <stdint.h>
#include <stdio.h>
#include <string.h> //some functions need NULL to be defined
#include "pico/stdlib.h"
#include "hardware/spi.h"
/*******************************************************************************
* Types/enumerations/variables
******************************************************************************/
// send only one byte per transfer, see WriteRegister functions
#define BUFFER_SIZE 1
// Defined as 4MHz in the original library
#define MFRC522_BIT_RATE 4000000
// Used for ADT object allocation
#define MFRC_MAX_INSTANCES 2
// Reset pin to MFRC522
#define RESET_PIN 20
static const uint8_t FIFO_SIZE = 64; // Size of the MFRC522 FIFO
static const uint cs_pin = 17;
static const uint sck_pin = 18;
static const uint mosi_pin = 19;
static const uint miso_pin = 16;
static const uint8_t SELF_TEST_BYTES[] = {
0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95,
0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE,
0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82,
0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49,
0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81,
0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9,
0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D,
0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F
};
//0x2F
/**
* MFRC522 registers. Described in chapter 9 of the datasheet.
* When using SPI all addresses are shifted one bit left in the "SPI address
* (section 8.1.2.3)
*/
enum PCD_Register {
// Page 0: Command and status
// 0x00 // reserved for future use
CommandReg = 0x01 << 1, // starts and stops command execution
ComIEnReg = 0x02 << 1, // enable and disable interrupt request control bits
DivIEnReg = 0x03 << 1, // enable and disable interrupt request control bits
ComIrqReg = 0x04 << 1, // interrupt request bits
DivIrqReg = 0x05 << 1, // interrupt request bits
ErrorReg = 0x06 << 1, // error bits showing the error status of the last
// command executed
Status1Reg = 0x07 << 1, // communication status bits
Status2Reg = 0x08 << 1, // receiver and transmitter status bits
FIFODataReg = 0x09 << 1, // input and output of 64 byte FIFO buffer
FIFOLevelReg = 0x0A << 1, // number of bytes stored in the FIFO buffer
WaterLevelReg = 0x0B << 1, // level for FIFO underflow and overflow warning
ControlReg = 0x0C << 1, // miscellaneous control registers
BitFramingReg = 0x0D << 1, // adjustments for bit-oriented frames
CollReg = 0x0E << 1, // bit position of the first bit-collision detected on
// the RF interface
// 0x0F // reserved for future use
// Page 1: Command
// 0x10 // reserved for future use
ModeReg = 0x11 << 1, // defines general modes for transmitting and receiving
TxModeReg = 0x12 << 1, // defines transmission data rate and framing
RxModeReg = 0x13 << 1, // defines reception data rate and framing
TxControlReg = 0x14 << 1, // controls the logical behavior of the antenna
// driver pins TX1 and TX2
TxASKReg = 0x15 << 1, // controls the setting of the transmission modulation
TxSelReg = 0x16 << 1, // selects the internal sources for the antenna driver
RxSelReg = 0x17 << 1, // selects internal receiver settings
RxThresholdReg = 0x18 << 1, // selects thresholds for the bit decoder
DemodReg = 0x19 << 1, // defines demodulator settings
// 0x1A // reserved for future use
// 0x1B // reserved for future use
MfTxReg =
0x1C << 1, // controls some MIFARE communication transmit parameters
MfRxReg =
0x1D << 1, // controls some MIFARE communication receive parameters
// 0x1E // reserved for future use
SerialSpeedReg =
0x1F << 1, // selects the speed of the serial UART interface
// Page 2: Configuration
// 0x20 // reserved for future use
CRCResultRegH =
0x21 << 1, // shows the MSB and LSB values of the CRC calculation
CRCResultRegL = 0x22 << 1,
// 0x23 // reserved for future use
ModWidthReg = 0x24 << 1, // controls the ModWidth setting?
// 0x25 // reserved for future use
RFCfgReg = 0x26 << 1, // configures the receiver gain
GsNReg = 0x27 << 1, // selects the conductance of the antenna driver pins
// TX1 and TX2 for modulation
CWGsPReg = 0x28 << 1, // defines the conductance of the p-driver output
// during periods of no modulation
ModGsPReg = 0x29 << 1, // defines the conductance of the p-driver output
// during periods of modulation
TModeReg = 0x2A << 1, // defines settings for the internal timer
TPrescalerReg = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The
// 4 high bits are in TModeReg.
TReloadRegH = 0x2C << 1, // defines the 16-bit timer reload value
TReloadRegL = 0x2D << 1,
TCounterValueRegH = 0x2E << 1, // shows the 16-bit timer value
TCounterValueRegL = 0x2F << 1,
// Page 3: Test Registers
// 0x30 // reserved for future use
TestSel1Reg = 0x31 << 1, // general test signal configuration
TestSel2Reg = 0x32 << 1, // general test signal configuration
TestPinEnReg = 0x33 << 1, // enables pin output driver on pins D1 to D7
TestPinValueReg =
0x34
<< 1, // defines the values for D1 to D7 when it is used as an I/O bus
TestBusReg = 0x35 << 1, // shows the status of the internal test bus
AutoTestReg = 0x36 << 1, // controls the digital self-test
VersionReg = 0x37 << 1, // shows the software version
AnalogTestReg = 0x38 << 1, // controls the pins AUX1 and AUX2
TestDAC1Reg = 0x39 << 1, // defines the test value for TestDAC1
TestDAC2Reg = 0x3A << 1, // defines the test value for TestDAC2
TestADCReg = 0x3B << 1 // shows the value of ADC I and Q channels
// 0x3C // reserved for production tests
// 0x3D // reserved for production tests
// 0x3E // reserved for production tests
// 0x3F // reserved for production tests
};
/**
* MFRC522 commands. Described in chapter 10 of the datasheet.
*/
typedef enum _PCD_Command {
PCD_Idle = 0x00, // no action, cancels current command execution
PCD_Mem = 0x01, // stores 25 bytes into the internal buffer
PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number
PCD_CalcCRC = 0x03, // activates the CRC co-processor or performs a self-test
PCD_Transmit = 0x04, // transmits data from the FIFO buffer
PCD_NoCmdChange = 0x07, // no command change, can be used to modify the
// CommandReg register bits without affecting the
// command, for example, the PowerDown bit
PCD_Receive = 0x08, // activates the receiver circuits
PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and
// automatically activates the receiver after
// transmission
PCD_MFAuthent =
0x0E, // performs the MIFARE standard authentication as a reader
PCD_SoftReset = 0x0F, // resets the MFRC522
PCD_EnableSelfTest = 0x09
} PCD_Command;
/**
* MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor
* (on the PCD).
* Described in 9.3.3.6 / table 98 of the datasheet.
*/
typedef enum _PCD_RxGain {
RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum
RxGain_23dB = 0x01 << 4, // 001b - 23 dB
RxGain_18dB_2 =
0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b
RxGain_23dB_2 =
0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b
RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default
RxGain_38dB = 0x05 << 4, // 101b - 38 dB
RxGain_43dB = 0x06 << 4, // 110b - 43 dB
RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum
RxGain_min =
0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB
RxGain_avg =
0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB
RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB
} PCD_RxGain;
/**
* Commands sent to the PICC.
*/
typedef enum _PICC_Command {
// The commands used by the PCD to manage communication with several PICCs
// (ISO 14443-3, Type A, section 6.4)
PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state
// IDLE to go to READY and prepare for anti collision
// or selection. 7 bit frame.
PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state
// IDLE and HALT to go to READY(*) and prepare for
// anti collision or selection. 7 bit frame.
PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during
// anti collision.
PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1
PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2
PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3
PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to
// go to state HALT.
// The commands used for MIFARE Classic (from
// http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9)
// Use PCD_MFAuthent to authenticate access to a sector, then use these
// commands to read/write/modify the blocks on the sector.
// The read/write commands can also be used for MIFARE Ultralight.
PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A
PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B
PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the
// authenticated sector of the PICC. Also used for
// MIFARE Ultralight.
PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the
// authenticated sector of the PICC. Called
// "COMPATIBILITY WRITE" for MIFARE Ultralight.
PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and
// stores the result in the internal data
// register.
PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and
// stores the result in the internal data
// register.
PICC_CMD_MF_RESTORE =
0xC2, // Reads the contents of a block into the internal data register.
PICC_CMD_MF_TRANSFER =
0xB0, // Writes the contents of the internal data register to a block.
// The commands used for MIFARE Ultralight (from
// http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
// The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE
// Ultralight.
PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC.
} PICC_Command;
// MIFARE constants that does not fit anywhere else
typedef enum _MIFARE_Misc {
MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value
// than 0xA is NAK.
MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes.
} MIFARE_Misc;
// PICC types we can detect. Remember to update PICC_GetTypeName() if you add
// more.
// last value set to 0xff, then compiler uses less ram, it seems some
// optimizations are triggered
typedef enum _PICC_Type {
PICC_TYPE_UNKNOWN,
PICC_TYPE_ISO_14443_4, // PICC compliant with ISO/IEC 14443-4
PICC_TYPE_ISO_18092, // PICC compliant with ISO/IEC 18092 (NFC)
PICC_TYPE_MIFARE_MINI, // MIFARE Classic protocol, 320 bytes
PICC_TYPE_MIFARE_1K, // MIFARE Classic protocol, 1KB
PICC_TYPE_MIFARE_4K, // MIFARE Classic protocol, 4KB
PICC_TYPE_MIFARE_UL, // MIFARE Ultralight or Ultralight C
PICC_TYPE_MIFARE_PLUS, // MIFARE Plus
PICC_TYPE_TNP3XXX, // Only mentioned in NXP AN 10833 MIFARE Type
// Identification Procedure
PICC_TYPE_NOT_COMPLETE = 0xff // SAK indicates UID is not complete.
} PICC_Type;
// Return codes from the functions in this class. Remember to update
// GetStatusCodeName() if you add more.
// last value set to 0xff, then compiler uses less ram, it seems some
// optimizations are triggered
typedef enum _StatusCode {
STATUS_OK, // Success
STATUS_ERROR, // Error in communication
STATUS_COLLISION, // Collision detected
STATUS_TIMEOUT, // Timeout in communication.
STATUS_NO_ROOM, // A buffer is not big enough.
STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-)
STATUS_INVALID, // Invalid argument.
STATUS_CRC_WRONG, // The CRC_A does not match
STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK.
} StatusCode;
// A struct used for passing the UID of a PICC.
typedef struct {
uint8_t size; // Number of bytes in the UID. 4, 7 or 10.
uint8_t uidByte[10];
uint8_t sak; // The SAK (Select acknowledge) byte returned from the PICC
// after successful selection.
} Uid;
// A struct used for passing a MIFARE Crypto1 key
typedef struct { uint8_t keybyte[MF_KEY_SIZE]; } MIFARE_Key;
// A struct used to set GPIO pins of LPCXpresso4337
typedef struct {
uint8_t port;
uint8_t pin;
} io_port_t;
// A struct used to define a MFRC522 ADT object, useful when using more than one
struct MFRC522_T {
Uid uid; // Used by PICC_ReadCardSerial().
// Variables used in the SSP(SPI) peripheral of the board
spi_inst_t *spi; // Select SSP0 or SSP1
uint _chipSelectPin; // = {1, 8}; // As default example use GPIO1[8]= P1_5
uint8_t Tx_Buf[BUFFER_SIZE];
uint8_t Rx_Buf[BUFFER_SIZE];
};
// Pointer to a MFRC5222 ADT object
typedef struct MFRC522_T *MFRC522Ptr_t;
/**
* Function to setup a MFRC522 ADT object
* @return an initialized ADT object
*/
MFRC522Ptr_t MFRC522_Init();
/*******************************************************************************
* Basic interface functions for communicating with the MFRC522
*******************************************************************************/
void PCD_WriteRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t value);
void PCD_WriteNRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t count,
uint8_t *values);
uint8_t PCD_ReadRegister(MFRC522Ptr_t mfrc, uint8_t reg);
void PCD_ReadNRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t count,
uint8_t *values, uint8_t rxAlign);
void setBitMask(unsigned char reg, unsigned char mask);
void PCD_SetRegisterBitMask(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t mask);
void PCD_ClearRegisterBitMask(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t mask);
StatusCode PCD_CalculateCRC(MFRC522Ptr_t mfrc, uint8_t *data, uint8_t length,
uint8_t *result);
//Chip select for pi pico SPI
static inline void cs_select(const uint cs);
static inline void cs_deselect(const uint cs);
/*******************************************************************************
* Functions for manipulating the MFRC522
*******************************************************************************/
void PCD_Init(MFRC522Ptr_t mfrc, spi_inst_t *spi);
void PCD_Reset(MFRC522Ptr_t mfrc);
void PCD_AntennaOn(MFRC522Ptr_t mfrc);
void PCD_AntennaOff(MFRC522Ptr_t mfrc);
uint8_t PCD_GetAntennaGain(MFRC522Ptr_t mfrc);
void PCD_SetAntennaGain(MFRC522Ptr_t mfrc, uint8_t mask);
uint8_t PCD_SelfTest(MFRC522Ptr_t mfrc);
/*******************************************************************************
* Functions for communicating with PICCs
*******************************************************************************/
StatusCode PCD_TransceiveData(MFRC522Ptr_t mfrc, uint8_t *sendData,
uint8_t sendLen, uint8_t *backData,
uint8_t *backLen, uint8_t *validBits,
uint8_t rxAlign, bool checkCRC);
StatusCode PCD_CommunicateWithPICC(MFRC522Ptr_t mfrc, uint8_t command,
uint8_t waitIRq, uint8_t *sendData,
uint8_t sendLen, uint8_t *backData,
uint8_t *backLen, uint8_t *validBits,
uint8_t rxAlign, bool checkCRC);
StatusCode PICC_RequestA(MFRC522Ptr_t mfrc, uint8_t *bufferATQA,
uint8_t *bufferSize);
StatusCode PICC_WakeupA(MFRC522Ptr_t mfrc, uint8_t *bufferATQA,
uint8_t *bufferSize);
StatusCode PICC_REQA_or_WUPA(MFRC522Ptr_t mfrc, uint8_t command,
uint8_t *bufferATQA, uint8_t *bufferSize);
StatusCode PICC_Select(MFRC522Ptr_t mfrc, Uid *uid, uint8_t validBits);
StatusCode PICC_HaltA(MFRC522Ptr_t mfrc);
/*******************************************************************************
*Functions for communicating with MIFARE PICCs
*******************************************************************************/
StatusCode PCD_Authenticate(MFRC522Ptr_t mfrc, uint8_t command,
uint8_t blockAddr, MIFARE_Key *key, Uid *uid);
void PCD_StopCrypto1(MFRC522Ptr_t mfrc);
StatusCode MIFARE_Read(MFRC522Ptr_t mfrc, uint8_t blockAddr, uint8_t *buffer,
uint8_t *bufferSize);
StatusCode MIFARE_Write(MFRC522Ptr_t mfrc, uint8_t blockAddr, uint8_t *buffer,
uint8_t bufferSize);
StatusCode MIFARE_Ultralight_Write(MFRC522Ptr_t mfrc, uint8_t page,
uint8_t *buffer, uint8_t bufferSize);
StatusCode MIFARE_Decrement(MFRC522Ptr_t mfrc, uint8_t blockAddr, long delta);
StatusCode MIFARE_Increment(MFRC522Ptr_t mfrc, uint8_t blockAddr, long delta);
StatusCode MIFARE_Restore(MFRC522Ptr_t mfrc, uint8_t blockAddr);
StatusCode MIFARE_Transfer(MFRC522Ptr_t mfrc, uint8_t blockAddr);
StatusCode MIFARE_GetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long *value);
StatusCode MIFARE_SetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long value);
StatusCode PCD_NTAG216_AUTH(MFRC522Ptr_t mfrc, uint8_t *passWord,
uint8_t pACK[]);
/*******************************************************************************
* Support functions
*******************************************************************************/
StatusCode PCD_MIFARE_Transceive(MFRC522Ptr_t mfrc, uint8_t *sendData,
uint8_t sendLen, bool acceptTimeout);
const char *GetStatusCodeName(StatusCode code);
const char *PICC_GetTypeName(PICC_Type type);
StatusCode MIFARE_TwoStepHelper(MFRC522Ptr_t mfrc, uint8_t command,
uint8_t blockAddr, long data);
// Support functions for debugging
void PCD_DumpVersionToSerial(MFRC522Ptr_t mfrc);
void PICC_DumpToSerial(MFRC522Ptr_t mfrc, Uid *uid);
void PICC_DumpDetailsToSerial(Uid *uid);
void PICC_DumpMifareClassicToSerial(MFRC522Ptr_t mfrc, Uid *uid,
PICC_Type piccType, MIFARE_Key *key);
void PICC_DumpMifareClassicSectorToSerial(MFRC522Ptr_t mfrc, Uid *uid,
MIFARE_Key *key, uint8_t sector);
void PICC_DumpMifareUltralightToSerial(MFRC522Ptr_t mfrc);
// Advanced functions for MIFARE
void MIFARE_SetAccessBits(uint8_t *accessBitBuffer, uint8_t g0, uint8_t g1,
uint8_t g2, uint8_t g3);
bool MIFARE_OpenUidBackdoor(MFRC522Ptr_t mfrc, bool logErrors);
bool MIFARE_SetUid(MFRC522Ptr_t mfrc, uint8_t *newUid, uint8_t uidSize,
bool logErrors);
bool MIFARE_UnbrickUidSector(MFRC522Ptr_t mfrc, bool logErrors);
/*******************************************************************************
* Convenience functions - does not add extra functionality
*******************************************************************************/
bool PICC_IsNewCardPresent(MFRC522Ptr_t mfrc);
bool PICC_ReadCardSerial(MFRC522Ptr_t mfrc);
#endif
Čtení karty
Selecting card
PICC dump:
Card UID: 19 009 29 3E
Card SAK: 008 PICC type: MIFARE 1KBSector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits
15 63 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
62 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
61 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
60 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
14 59 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
58 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
57 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
56 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
13 55 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
54 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
53 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
52 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
12 51 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
50 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
49 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
48 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
11 47 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
46 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
45 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
44 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
10 43 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
42 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
41 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
40 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
9 39 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
38 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
37 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
36 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
8 35 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
34 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
33 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
32 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
7 31 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
30 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
29 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
28 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
6 27 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
26 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
25 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
24 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
5 23 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
22 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
21 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
20 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
4 19 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
18 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
17 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
16 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
3 15 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
14 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
13 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
12 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
2 11 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
10 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
9 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
8 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
1 7 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
6 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
5 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
4 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
0 3 000 000 000 000 000 000 FF 007 80 69 FF FF FF FF FF FF [ 0 0 1 ]
2 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
1 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 [ 0 0 0 ]
0 19 009 29 3E 007 008 004 000 62 63 64 65 66 67 68 69 [ 0 0 0 ]
Uid is: 19 9 29 3e
Authentication Success
Waiting for card