diff --git a/Debug.cpp b/Debug.cpp index ea42e42..13225a1 100644 --- a/Debug.cpp +++ b/Debug.cpp @@ -16,6 +16,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // +//#define DEBUG 1 + #include #include "Debug.h" diff --git a/Debug.h b/Debug.h index 475d872..57cbe6c 100644 --- a/Debug.h +++ b/Debug.h @@ -19,16 +19,6 @@ #ifndef DEBUG_H #define DEBUG_H -#ifdef DEBUG -extern uint8_t _pn5180_debugIndent; -extern uint8_t _pn5180_debugIndentN; -extern bool _pn5180_debugNL; -extern uint8_t _pn5180_debugSilent; - -// These macros are helper macros to make the see the debug macros like function calls so they do not alter any current code block structures when the macro contains if/else/break, etc. -#define __DEBUG_BEGIN__ do{if(DEBUG){ -#define __DEBUG_END__ }}while(0) - // DEBUG print with indention macros: // // USAGE: @@ -83,6 +73,17 @@ extern uint8_t _pn5180_debugSilent; // | IRQ-Status=0x00000004 // ---------------------------------- +#if defined(DEBUG) || defined(DEBUG_ERROR) +extern uint8_t _pn5180_debugIndent; +extern uint8_t _pn5180_debugIndentN; +extern bool _pn5180_debugNL; +extern uint8_t _pn5180_debugSilent; + +#ifdef DEBUG +// These macros are helper macros to make c compiler treat the debug macros like function calls so they do not alter any current code block structures when the macro contains if/else/break, etc. +#define __DEBUG_BEGIN__ do{if(DEBUG){ +#define __DEBUG_END__ }}while(0) + #define PN5180DEBUG_OFF __DEBUG_BEGIN__ ++_pn5180_debugSilent; __DEBUG_END__ #define PN5180DEBUG_ON __DEBUG_BEGIN__ _pn5180_debugSilent-=((_pn5180_debugSilent>0)?1:0); __DEBUG_END__ #define PN5180DEBUG_ENTER __DEBUG_BEGIN__ ++_pn5180_debugIndent; __DEBUG_END__ @@ -92,7 +93,24 @@ extern uint8_t _pn5180_debugSilent; #define PN5180DEBUG_PRINTF(...) __DEBUG_BEGIN__ if (!_pn5180_debugSilent) { if (_pn5180_debugNL) PN5180DEBUG_INDENT; Serial.printf(__VA_ARGS__); }; __DEBUG_END__ #define PN5180DEBUG_PRINT(...) __DEBUG_BEGIN__ if (!_pn5180_debugSilent) { if (_pn5180_debugNL) PN5180DEBUG_INDENT; Serial.print(__VA_ARGS__); }; __DEBUG_END__ #define PN5180DEBUG(msg) PN5180DEBUG_PRINT(msg) -#else +#endif /* DEBUG */ + +#ifdef DEBUG_ERROR +// These macros are helper macros to make c compiler treat the debug macros like function calls so they do not alter any current code block structures when the macro contains if/else/break, etc. +#define __ERROR_BEGIN__ do{if(DEBUG_ERROR){ +#define __ERROR_END__ }}while(0) + +#define PN5180ERROR(...) __ERROR_BEGIN__ if (!_pn5180_debugSilent) { if (!_pn5180_debugNL) Serial.print(F("\n"));; Serial.print(F("*** ERROR: ")); Serial.printf(__VA_ARGS__); Serial.print(F("\n"));}; __ERROR_END__ +#endif /* DEBUG_ERROR */ + +extern char * formatHex(const uint8_t val); +extern char * formatHex(const uint16_t val); +extern char * formatHex(const uint32_t val); + +#else /* defined(DEBUG) || defined(DEBUG_ERROR) */ +#endif /* defined(DEBUG) || defined(DEBUG_ERROR) */ + +#ifndef DEBUG #define PN5180DEBUG_OFF #define PN5180DEBUG_ON #define PN5180DEBUG_ENTER @@ -104,10 +122,8 @@ extern uint8_t _pn5180_debugSilent; #define PN5180DEBUG(msg) #endif -#ifdef DEBUG -extern char * formatHex(const uint8_t val); -extern char * formatHex(const uint16_t val); -extern char * formatHex(const uint32_t val); +#ifndef DEBUG_ERROR +#define PN5180ERROR(...) #endif #endif /* DEBUG_H */ diff --git a/PN5180.cpp b/PN5180.cpp index 886167c..154b06d 100644 --- a/PN5180.cpp +++ b/PN5180.cpp @@ -17,26 +17,41 @@ // Lesser General Public License for more details. // //#define DEBUG 1 +//#define DEBUG_ERROR 1 #include #include "PN5180.h" #include "Debug.h" // PN5180 1-Byte Direct Commands -// see 11.4.3.3 Host Interface Command List -#define PN5180_WRITE_REGISTER (0x00) -#define PN5180_WRITE_REGISTER_OR_MASK (0x01) -#define PN5180_WRITE_REGISTER_AND_MASK (0x02) -#define PN5180_READ_REGISTER (0x04) -#define PN5180_WRITE_EEPROM (0x06) -#define PN5180_READ_EEPROM (0x07) -#define PN5180_SEND_DATA (0x09) -#define PN5180_READ_DATA (0x0A) -#define PN5180_SWITCH_MODE (0x0B) -#define PN5180_MIFARE_AUTHENTICATE (0x0C) -#define PN5180_LOAD_RF_CONFIG (0x11) -#define PN5180_RF_ON (0x16) -#define PN5180_RF_OFF (0x17) +// see 11.4.3.3 Host Interface Command List (https://www.nxp.com/docs/en/data-sheet/PN5180A0XX_C3_C4.pdf, page 20) +#define PN5180_WRITE_REGISTER (0x00) // Write one 32bit register value +#define PN5180_WRITE_REGISTER_OR_MASK (0x01) // Sets one 32bit register value using a 32 bit OR mask +#define PN5180_WRITE_REGISTER_AND_MASK (0x02) // Sets one 32bit register value using a 32 bit AND mask +//#define PN5180_WRITE_REGISTER_MULTIPLE (0x03) // Processes an array of register addresses in random order and performs the defined action on these addresses. +#define PN5180_READ_REGISTER (0x04) // Reads one 32bit register value +//#define PN5180_READ_REGISTER_MULTIPLE (0x05) // Reads from an array of max.18 register addresses in random order +#define PN5180_WRITE_EEPROM (0x06) // Processes an array of EEPROM addresses in random order and writes the value to these addresses +#define PN5180_READ_EEPROM (0x07) // Processes an array of EEPROM addresses from a start address and reads the values from these addresses +//#define PN5180_WRITE_TX_DATA (0x08) // Write data into the transmission buffer +#define PN5180_SEND_DATA (0x09) // Write data into the transmission buffer, the START_SEND bit is automatically set +#define PN5180_READ_DATA (0x0A) // Read data from reception buffer, after successful reception +#define PN5180_SWITCH_MODE (0x0B) // Switch the mode. It is only possible to switch from NormalMode to standby, LPCD or Autocoll +#define PN5180_MIFARE_AUTHENTICATE (0x0C) // Perform a MIFARE Classic Authentication on an activated card +//#define PN5180_EPC_INVENTORY (0x0D) // Perform an inventory of ISO18000-3M3 tags +//#define PN5180_EPC_RESUME_INVENTORY (0x0E) // Resume the inventory algorithm in case it is paused +//#define PN5180_EPC_RETRIEVE_INVENTORY_RESULT_SIZE (0x0F) // Retrieve the size of the inventory result +//#define PN5180_EPC_RETRIEVE_INVENTORY_RESULT (0x10) // Retrieve the result of a preceding EPC_INVENTORY or EPC_RESUME_INVENTORY instruction +#define PN5180_LOAD_RF_CONFIG (0x11) // Load the RF configuration from EEPROM into the configuration registers +//#define UPDATE_RF_CONFIG (0x12) // Update the RF configuration within EEPROM. +//#define RETRIEVE_RF_CONFIG_SIZE (0x13) // Retrieve the number of registers for a selected RF configuration +//#define RETRIEVE_RF_CONFIG (0x14) // Read out an RF configuration. The register address-value-pairs are available in the response +//#define RFU (0x15) // RFU (reserved for future use) +#define PN5180_RF_ON (0x16) // Switch on the RF Field +#define PN5180_RF_OFF (0x17) // Switch off the RF Field + +#define SETRF_ON_TIMEOUT (500) +#define SETRF_OFF_TIMEOUT (500) uint8_t PN5180::readBufferStatic16[16]; @@ -140,8 +155,13 @@ bool PN5180::writeRegister(uint8_t reg, uint32_t value) { */ uint8_t cmd[] = { PN5180_WRITE_REGISTER, reg, p[0], p[1], p[2], p[3] }; - transceiveCommand(cmd, sizeof(cmd)); - + // transceiveCommand(cmd, sizeof(cmd)); + if (!transceiveCommand(cmd, sizeof(cmd))) { + PN5180ERROR(F("writeRegister() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } + PN5180DEBUG_EXIT; return true; } @@ -172,14 +192,19 @@ bool PN5180::writeRegisterWithOrMask(uint8_t reg, uint32_t mask) { uint8_t cmd[] = { PN5180_WRITE_REGISTER_OR_MASK, reg, p[0], p[1], p[2], p[3] }; - transceiveCommand(cmd, sizeof(cmd)); + // transceiveCommand(cmd, sizeof(cmd)); + if (!transceiveCommand(cmd, sizeof(cmd))) { + PN5180ERROR(F("writeRegisterWithOrMask() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } PN5180DEBUG_EXIT; return true; } /* - * WRITE _REGISTER_AND_MASK - 0x02 + * WRITE_REGISTER_AND_MASK - 0x02 * This command modifies the content of a register using a logical AND operation. The * content of the register is read and a logical AND operation is performed with the provided * mask. The modified content is written back to the register. @@ -204,7 +229,12 @@ bool PN5180::writeRegisterWithAndMask(uint8_t reg, uint32_t mask) { uint8_t cmd[] = { PN5180_WRITE_REGISTER_AND_MASK, reg, p[0], p[1], p[2], p[3] }; - transceiveCommand(cmd, sizeof(cmd)); + // transceiveCommand(cmd, sizeof(cmd)); + if (!transceiveCommand(cmd, sizeof(cmd))) { + PN5180ERROR(F("writeRegisterWithAndMask() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } PN5180DEBUG_EXIT; return true; @@ -224,7 +254,12 @@ bool PN5180::readRegister(uint8_t reg, uint32_t *value) { uint8_t cmd[] = { PN5180_READ_REGISTER, reg }; - transceiveCommand(cmd, sizeof(cmd), (uint8_t*)value, 4); + // transceiveCommand(cmd, sizeof(cmd), (uint8_t*)value, 4); + if (!transceiveCommand(cmd, sizeof(cmd), (uint8_t*)value, 4)) { + PN5180ERROR(F("readRegister() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } PN5180DEBUG(F("Register value=0x")); PN5180DEBUG(formatHex(*value)); @@ -241,11 +276,19 @@ bool PN5180::writeEEprom(uint8_t addr, const uint8_t *buffer, uint8_t len) { PN5180DEBUG_PRINTF(F("PN5180::writeEEprom(addr=%s, *buffer, len=%d)"), formatHex(addr), len); PN5180DEBUG_PRINTLN(); PN5180DEBUG_ENTER; + uint8_t cmd[len + 2]; cmd[0] = PN5180_WRITE_EEPROM; cmd[1] = addr; for (int i = 0; i < len; i++) cmd[2 + i] = buffer[i]; - transceiveCommand(cmd, len + 2); + + // transceiveCommand(cmd, len + 2); + if (!transceiveCommand(cmd, len + 2)) { + PN5180ERROR(F("writeEEprom() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } + PN5180DEBUG_EXIT; return true; } @@ -265,8 +308,9 @@ bool PN5180::readEEprom(uint8_t addr, uint8_t *buffer, int len) { PN5180DEBUG_PRINTF(F("PN5180::readEEprom(addr=%s, *buffer, len=%d)"), formatHex(addr), len); PN5180DEBUG_PRINTLN(); PN5180DEBUG_ENTER; + if ((addr > 254) || ((addr+len) > 254)) { - PN5180DEBUG_PRINTLN(F("ERROR: Reading beyond addr 254!")); + PN5180ERROR(F("readEEprom() failed: Reading beyond addr 254!")); PN5180DEBUG_EXIT; return false; } @@ -279,7 +323,12 @@ bool PN5180::readEEprom(uint8_t addr, uint8_t *buffer, int len) { uint8_t cmd[] = { PN5180_READ_EEPROM, addr, uint8_t(len) }; - transceiveCommand(cmd, sizeof(cmd), buffer, len); + // transceiveCommand(cmd, sizeof(cmd), buffer, len); + if (!transceiveCommand(cmd, sizeof(cmd), buffer, len)) { + PN5180ERROR(F("readEEprom() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } #ifdef DEBUG PN5180DEBUG(F("EEPROM values: ")); @@ -294,8 +343,13 @@ bool PN5180::readEEprom(uint8_t addr, uint8_t *buffer, int len) { return true; } - /* + * This function: + * 1. checks parameters + * 2. sets SYSTEM_CONFIG Idle/StopCom Command + * 3. sets SYSTEM_CONFIG Transceive Command + * 4. calls cmd_SendData() which in turn calls transceiveCommand() to send the command over SPI to the PN5180 chip + * * SEND_DATA - 0x09 * This command writes data to the RF transmission buffer and starts the RF transmission. * The parameter ‘Number of valid bits in last Byte’ indicates the exact number of bits to be @@ -314,8 +368,9 @@ bool PN5180::sendData(const uint8_t *data, int len, uint8_t validBits) { PN5180DEBUG_PRINTF(F("PN5180::sendData(*data, len=%d, validBits=%d)"), len, validBits); PN5180DEBUG_PRINTLN(); PN5180DEBUG_ENTER; + if (len > 260) { - PN5180DEBUG_PRINTLN(F("ERROR: sendData with more than 260 bytes is not supported!")); + PN5180ERROR(F("sendData() failed: more than 260 bytes is not supported!")); PN5180DEBUG_EXIT; return false; } @@ -331,15 +386,20 @@ bool PN5180::sendData(const uint8_t *data, int len, uint8_t validBits) { PN5180DEBUG_PRINTLN(); #endif - uint8_t buffer[len+2]; - buffer[0] = PN5180_SEND_DATA; - buffer[1] = validBits; // number of valid bits of last byte are transmitted (0 = all bits are transmitted) - for (int i=0; i 508) { - Serial.println(F("*** FATAL: Reading more than 508 bytes is not supported!")); + Serial.println(F("readData() failed: Reading more than 508 bytes is not supported!")); PN5180DEBUG_EXIT; return 0L; } @@ -397,14 +462,20 @@ uint8_t * PN5180::readData(int len) { if (!readBufferDynamic508) { readBufferDynamic508 = (uint8_t *) malloc(508); if (!readBufferDynamic508) { - PN5180DEBUG(F("Cannot allocate the read buffer of 508 Bytes!")); + PN5180ERROR(F("readData() failed: Cannot allocate the read buffer of 508 Bytes!")); PN5180DEBUG_EXIT; return 0; } } readBuffer = readBufferDynamic508; } - transceiveCommand(cmd, sizeof(cmd), readBuffer, len); + + // transceiveCommand(cmd, sizeof(cmd), readBuffer, len); + if (!transceiveCommand(cmd, sizeof(cmd), readBuffer, len)) { + PN5180ERROR(F("readData() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return 0L; + } #ifdef DEBUG PN5180DEBUG(F("Data read: ")); @@ -429,9 +500,16 @@ bool PN5180::readData(int len, uint8_t *buffer) { return false; } uint8_t cmd[] = { PN5180_READ_DATA, 0x00 }; - bool ret = transceiveCommand(cmd, sizeof(cmd), buffer, len); + + //bool ret = transceiveCommand(cmd, sizeof(cmd), buffer, len); + if (!transceiveCommand(cmd, sizeof(cmd), buffer, len)) { + PN5180ERROR(F("sendData() failed at writeRegisterWithAndMask() Idle/StopCom Command")); + PN5180DEBUG_EXIT; + return false; + } + PN5180DEBUG_EXIT; - return ret; + return true; } /* prepare LPCD registers (Low Power Card Detection) */ @@ -513,7 +591,7 @@ bool PN5180::switchToLPCD(uint16_t wakeupCounterInMs) { */ int16_t PN5180::mifareAuthenticate(uint8_t blockNo, const uint8_t *key, uint8_t keyType, const uint8_t *uid) { if (keyType != 0x60 && keyType != 0x61){ - PN5180DEBUG_PRINTLN(F("*** ERROR: invalid key type supplied!")); + PN5180ERROR(F("invalid key type supplied!")); return -2; } @@ -529,10 +607,10 @@ int16_t PN5180::mifareAuthenticate(uint8_t blockNo, const uint8_t *key, uint8_t cmdBuffer[9+i] = uid[i]; } - bool retval = transceiveCommand(cmdBuffer, 13, rcvBuffer, 1); - - if (!retval){ - PN5180DEBUG_PRINTLN(F("*** ERROR: sending command failed!")); + //bool retval = transceiveCommand(cmdBuffer, 13, rcvBuffer, 1); + if (!transceiveCommand(cmdBuffer, 13, rcvBuffer, 1)) { + PN5180ERROR(F("mifareAuthenticate() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; return -3; } @@ -568,7 +646,12 @@ bool PN5180::loadRFConfig(uint8_t txConf, uint8_t rxConf) { uint8_t cmd[] = { PN5180_LOAD_RF_CONFIG, txConf, rxConf }; - transceiveCommand(cmd, sizeof(cmd)); + // transceiveCommand(cmd, sizeof(cmd)); + if (!transceiveCommand(cmd, sizeof(cmd))) { + PN5180ERROR(F("loadRFConfig() failed at transceiveCommand()")); + PN5180DEBUG_EXIT; + return false; + } PN5180DEBUG_EXIT; return true; @@ -580,28 +663,54 @@ bool PN5180::loadRFConfig(uint8_t txConf, uint8_t rxConf) { * set after the field is switched on. */ bool PN5180::setRF_on() { - PN5180DEBUG_PRINTLN(F("Set RF ON")); + PN5180DEBUG_PRINTLN(F("PN5180::setRF_on()")); PN5180DEBUG_ENTER; - uint8_t cmd[] = { PN5180_RF_ON, 0x00 }; - - transceiveCommand(cmd, sizeof(cmd)); + //uint8_t cmd[] = { PN5180_RF_ON, 0x00 }; + //transceiveCommand(cmd, sizeof(cmd)); + if (!cmd_RfOn(0)) { + PN5180ERROR(F("setRF_on() failed at cmd_RfOn()")); + PN5180DEBUG_EXIT; + return false; + } - unsigned long startedWaiting = millis(); - - PN5180DEBUG_PRINTLN(F("wait for RF field to set up (max 500ms)")); + PN5180DEBUG_PRINTF(F("wait for RF field to set up (max %d ms)"), SETRF_ON_TIMEOUT); + PN5180DEBUG_PRINTLN(); PN5180DEBUG_OFF; - while (0 == (TX_RFON_IRQ_STAT & getIRQStatus())) { // wait for RF field to set up (max 500ms) - if (millis() - startedWaiting > 500) { + //while (0 == (TX_RFON_IRQ_STAT & getIRQStatus())) { // wait for RF field to set up (max 500ms) + // if (millis() - startedWaiting > SETRF_ON_TIMEOUT) { + // PN5180DEBUG_ON; + // PN5180ERROR(F("setRF_on() timeout waiting for TX_RFON_IRQ_STAT")); + // PN5180DEBUG_EXIT; + // return false; + // } + //}; + unsigned long startedWaiting = millis(); + uint32_t irqStatus; + while (1) { // wait for RF field to shut down + if (!readRegister(IRQ_STATUS, &irqStatus)) { PN5180DEBUG_ON; - PN5180DEBUG_PRINTLN(F("*** ERROR: Set RF ON timeout")); + PN5180ERROR(F("setRF_off() failed at readRegister()")); + PN5180DEBUG_EXIT; + return false; + } + if (0 != (TX_RFON_IRQ_STAT & irqStatus)) break; + if (millis() - startedWaiting > SETRF_ON_TIMEOUT) { + PN5180DEBUG_ON; + PN5180ERROR(F("setRF_on() timeout failed waiting for SETRF_ON_TIMEOUT")); PN5180DEBUG_EXIT; return false; } - }; + } PN5180DEBUG_ON; - clearIRQStatus(TX_RFON_IRQ_STAT); + // clearIRQStatus(TX_RFON_IRQ_STAT); + if (!clearIRQStatus(TX_RFON_IRQ_STAT)) { + PN5180ERROR(F("setRF_on() failed at clearIRQStatus()")); + PN5180DEBUG_EXIT; + return false; + } + PN5180DEBUG_EXIT; return true; } @@ -612,27 +721,43 @@ bool PN5180::setRF_on() { * is set after the field is switched off. */ bool PN5180::setRF_off() { - PN5180DEBUG_PRINTLN(F("Set RF OFF")); + PN5180DEBUG_PRINTLN(F("PN5180::setRF_off()")); PN5180DEBUG_ENTER; - uint8_t cmd[] { PN5180_RF_OFF, 0x00 }; - - transceiveCommand(cmd, sizeof(cmd)); + if (!cmd_RfOff(0)) { + PN5180ERROR(F("setRF_off() failed at cmd_RfOff()")); + PN5180DEBUG_EXIT; + return false; + } - unsigned long startedWaiting = millis(); - PN5180DEBUG_PRINTLN(F("wait for RF field to shut down (max 500ms)")); + PN5180DEBUG_PRINTF(F("wait for RF field to shut down (max %d ms)"), SETRF_OFF_TIMEOUT); + PN5180DEBUG_PRINTLN(); PN5180DEBUG_OFF; - while (0 == (TX_RFOFF_IRQ_STAT & getIRQStatus())) { // wait for RF field to shut down - if (millis() - startedWaiting > 500) { + unsigned long startedWaiting = millis(); + uint32_t irqStatus; + while (1) { // wait for RF field to shut down + if (!readRegister(IRQ_STATUS, &irqStatus)) { + PN5180DEBUG_ON; + PN5180ERROR(F("setRF_off() failed at readRegister()")); + PN5180DEBUG_EXIT; + return false; + } + if (0 != (TX_RFOFF_IRQ_STAT & irqStatus)) break; + if (millis() - startedWaiting > SETRF_OFF_TIMEOUT) { PN5180DEBUG_ON; - PN5180DEBUG_PRINTLN(F("*** ERROR: Set RF OFF timeout")); + PN5180ERROR(F("setRF_off() timeout failed waiting for TX_RFOFF_IRQ_STAT")); PN5180DEBUG_EXIT; return false; } - }; - PN5180DEBUG_ON; - - clearIRQStatus(TX_RFOFF_IRQ_STAT); + } + + // clearIRQStatus(TX_RFOFF_IRQ_STAT); + if (!clearIRQStatus(TX_RFOFF_IRQ_STAT)) { + PN5180ERROR(F("setRF_on() failed at clearIRQStatus()")); + PN5180DEBUG_EXIT; + return false; + } + PN5180DEBUG_EXIT; return true; } @@ -679,6 +804,7 @@ bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_ PN5180DEBUG_PRINTF(F("PN5180::transceiveCommand(*sendBuffer, sendBufferLen=%d, *recvBuffer, recvBufferLen=%d)"), sendBufferLen, recvBufferLen); PN5180DEBUG_PRINTLN(); PN5180DEBUG_ENTER; + PN5180_SPI.beginTransaction(SPI_SETTINGS); #ifdef DEBUG PN5180DEBUG(F("Sending SPI frame: '")); @@ -693,7 +819,7 @@ bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_ unsigned long startedWaiting = millis(); while (LOW != digitalRead(PN5180_BUSY)) { if (millis() - startedWaiting > commandTimeout) { - PN5180DEBUG("*** ERROR: transceiveCommand timeout (send/0)"); + PN5180ERROR("transceiveCommand() timeout waiting for BUSY=LOW (send/0)"); PN5180_SPI.endTransaction(); digitalWrite(PN5180_NSS, HIGH); PN5180DEBUG_EXIT; @@ -708,7 +834,7 @@ bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_ startedWaiting = millis(); while (HIGH != digitalRead(PN5180_BUSY)) { if (millis() - startedWaiting > commandTimeout) { - PN5180DEBUG("*** ERROR: transceiveCommand timeout (send/3)"); + PN5180ERROR("transceiveCommand() timeout waiting for BUSY=HIGH (send/3)"); PN5180_SPI.endTransaction(); digitalWrite(PN5180_NSS, HIGH); PN5180DEBUG_EXIT; @@ -721,7 +847,7 @@ bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_ startedWaiting = millis(); while (LOW != digitalRead(PN5180_BUSY)) { if (millis() - startedWaiting > commandTimeout) { - PN5180DEBUG("*** ERROR: transceiveCommand timeout (send/5)"); + PN5180ERROR("transceiveCommand() timeout waiting for BUSY=LOW (send/5)"); PN5180_SPI.endTransaction(); digitalWrite(PN5180_NSS, HIGH); PN5180DEBUG_EXIT; @@ -747,7 +873,7 @@ bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_ startedWaiting = millis(); //delay(1); while (HIGH != digitalRead(PN5180_BUSY)) { if (millis() - startedWaiting > commandTimeout) { - PN5180DEBUG("*** ERROR: transceiveCommand timeout (receive/3)"); + PN5180ERROR("transceiveCommand() timeout waiting for BUSY=HIGH (receive/3)"); PN5180_SPI.endTransaction(); digitalWrite(PN5180_NSS, HIGH); PN5180DEBUG_EXIT; @@ -760,7 +886,7 @@ bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_ startedWaiting = millis(); while (LOW != digitalRead(PN5180_BUSY)) { if (millis() - startedWaiting > commandTimeout) { - PN5180DEBUG("*** ERROR: transceiveCommand timeout (receive/5)"); + PN5180ERROR("transceiveCommand() timeout waiting for BUSY=LOW (receive/5)"); PN5180_SPI.endTransaction(); digitalWrite(PN5180_NSS, HIGH); PN5180DEBUG_EXIT; @@ -799,7 +925,7 @@ void PN5180::reset() { while (0 == (IDLE_IRQ_STAT & getIRQStatus())) { // wait for system to start up (with timeout) if (millis() - startedWaiting > commandTimeout) { PN5180DEBUG_ON; - PN5180DEBUG_PRINTLN(F("*** ERROR: reset failed (timeout)!!!")); + PN5180ERROR(F("reset() timeout waiting for IDLE_IRQ_STAT")); // try again with larger time digitalWrite(PN5180_RST, LOW); delay(10); @@ -823,7 +949,13 @@ uint32_t PN5180::getIRQStatus() { PN5180DEBUG_PRINTLN(F("Read IRQ-Status register...")); uint32_t irqStatus; - readRegister(IRQ_STATUS, &irqStatus); + + // readRegister(IRQ_STATUS, &irqStatus); + if (!readRegister(IRQ_STATUS, &irqStatus)) { + PN5180ERROR(F("getIRQStatus() failed at readRegister()")); + PN5180DEBUG_EXIT; + return 0L; + } PN5180DEBUG(F("IRQ-Status=0x")); PN5180DEBUG(formatHex(irqStatus)); @@ -839,19 +971,26 @@ bool PN5180::clearIRQStatus(uint32_t irqMask) { PN5180DEBUG_ENTER; PN5180DEBUG_PRINTLN(F("Clear IRQ-Status with mask")); - bool ret = writeRegister(IRQ_CLEAR, irqMask); + + //bool ret = writeRegister(IRQ_CLEAR, irqMask); + if (!writeRegister(IRQ_CLEAR, irqMask)) { + PN5180ERROR(F("clearIRQStatus() failed at writeRegister()")); + PN5180DEBUG_EXIT; + return false; + } PN5180DEBUG_EXIT; - return ret; + return true; } -/* - * Get TRANSCEIVE_STATE from RF_STATUS register - */ #ifdef DEBUG extern void showIRQStatus(uint32_t); #endif + +/* + * Get TRANSCEIVE_STATE from RF_STATUS register + */ PN5180TransceiveStat PN5180::getTransceiveState() { PN5180DEBUG_PRINT(F("PN5180::getTransceiveState()")); PN5180DEBUG_PRINTLN(); @@ -865,7 +1004,7 @@ PN5180TransceiveStat PN5180::getTransceiveState() { #ifdef DEBUG showIRQStatus(getIRQStatus()); #endif - PN5180DEBUG_PRINTLN(F("ERROR reading RF_STATUS register.")); + PN5180ERROR(F("getTransceiveState() failed reading RF_STATUS register.")); ret = PN5180TransceiveStat(0); PN5180DEBUG_EXIT; return ret; @@ -891,3 +1030,118 @@ PN5180TransceiveStat PN5180::getTransceiveState() { PN5180DEBUG_EXIT; return ret; } + +/* + * SEND_DATA - 0x09 + * This instruction is used to write data into the transmission buffer, the START_SEND bit is automatically set. + * + * Payload Length(byte) Value/Description + * Command code 1 0x09 + * Parameter 1 Number of valid bits in last Byte + * 1-260 Array of up to 260 elements {Transmit data} + * 1 Byte Transmit Data + * Response: - - + * + * This command writes data to the RF transmission buffer and starts the RF transmission. + * The parameter ‘Number of valid bits in last Byte’ indicates the exact number of bits to be + * transmitted for the last byte (for non-byte aligned frames). + * + * Precondition: Host shall configure the Transceiver by setting the register + * SYSTEM_CONFIG.COMMAND to 0x3 before using the SEND_DATA command, as + * the command SEND_DATA is only writing data to the transmission buffer and starts the + * transmission but does not perform any configuration. + * + * Parameter: 'valid bits in last byte' + * + * Note: When the command terminates, the transmission might still be ongoing, i.e. the command starts the + * transmission but does not wait for the end of transmission. + * + * Condition: The size of ‘Tx Data’ field must be in the range from 0 to 260, inclusive (the 0 byte length + * allows a symbol only transmission when the TX_DATA_ENABLE is cleared).‘Number of + * valid bits in last Byte’ field must be in the range from 0 to 7. The command must not be + * called during an ongoing RF transmission. Transceiver must be in ‘WaitTransmit’ state + * with ‘Transceive’ command set. If the condition is not fulfilled, an exception is raised. + * + * Returns: true on success + */ +bool PN5180::cmd_SendData(const uint8_t *data, int len, uint8_t validBits) { + PN5180DEBUG_PRINTF(F("PN5180::cmd_sendData(*data, len=%d, validBits=%d)"), len, validBits); + PN5180DEBUG_PRINTLN(); + + uint8_t buffer[len+2]; + buffer[0] = PN5180_SEND_DATA; + buffer[1] = validBits; // number of valid bits of last byte are transmitted (0 = all bits are transmitted) + for (int i=0; i -// PN5180 Registers -#define SYSTEM_CONFIG (0x00) -#define IRQ_ENABLE (0x01) -#define IRQ_STATUS (0x02) -#define IRQ_CLEAR (0x03) -#define TRANSCEIVE_CONTROL (0x04) -#define TIMER1_RELOAD (0x0c) -#define TIMER1_CONFIG (0x0f) -#define RX_WAIT_CONFIG (0x11) -#define CRC_RX_CONFIG (0x12) -#define RX_STATUS (0x13) -#define TX_WAIT_CONFIG (0x17) -#define TX_CONFIG (0x18) -#define CRC_TX_CONFIG (0x19) -#define RF_STATUS (0x1d) -#define SYSTEM_STATUS (0x24) -#define TEMP_CONTROL (0x25) -#define AGC_REF_CONFIG (0x26) - +// PN5180 Registers (https://www.nxp.com/docs/en/data-sheet/PN5180A0XX-C1-C2.pdf, pp 87-88) +#define SYSTEM_CONFIG (0x00) +#define IRQ_ENABLE (0x01) +#define IRQ_STATUS (0x02) +#define IRQ_CLEAR (0x03) +#define TRANSCEIVE_CONTROL (0x04) +//#define PADCONFIG (0x05) +//#define RFU (0x06) +//#define PADOUT (0x07) +//#define TIMER0_STATUS (0x08) +//#define TIMER1_STATUS (0x09) +//#define TIMER2_STATUS (0x0a) +//#define TIMER0_RELOAD (0x0b) +#define TIMER1_RELOAD (0x0c) +//#define TIMER2_RELOAD (0x0d) +//#define TIMER0_CONFIG (0x0e) +#define TIMER1_CONFIG (0x0f) +//#define TIMER2_CONFIG (0x10) +#define RX_WAIT_CONFIG (0x11) +#define CRC_RX_CONFIG (0x12) +#define RX_STATUS (0x13) +//#define TX_UNDERSHOOT_CONFIG (0x14) +//#define TX_OVERSHOOT_CONFIG (0x15) +//#define TX_DATA_MOD (0x16) +#define TX_WAIT_CONFIG (0x17) +#define TX_CONFIG (0x18) +#define CRC_TX_CONFIG (0x19) +//#define SIGPRO_CONFIG (0x1a) +//#define SIGPRO_CM_CONFIG (0x1b) +//#define SIGPRO_RM_CONFIG (0x1c) +#define RF_STATUS (0x1d) +//#define AGC_CONFIG (0x1e) +//#define AGC_VALUE (0x1f) +//#define RF_CONTROL_TX (0x20) +//#define RF_CONTROL_TX_CLK (0x21) +//#define RF_CONTROL_RX (0x22) +#define LD_CONTROL (0x23) +#define SYSTEM_STATUS (0x24) +#define TEMP_CONTROL (0x25) +#define AGC_REF_CONFIG (0x26) +//#define DPC_CONFIG (0x27) +//#define EMD_CONTROL (0x28) +//#define ANT_CONTROL (0x29) +//#define SIGPRO_RM_CONFIG_EXTENSION (0x39) // requires FW 3.8 or later +// PN5180 Registers - additional registers(https://www.nxp.com/docs/en/data-sheet/PN5180A0XX_C3_C4.pdf, pp 80-81) +//#define TX_CONTROL (0x36) +//#define FELICA_EMD_CONTROL (0x43) // requires FW 4.1 or later // PN5180 EEPROM Addresses #define DIE_IDENTIFIER (0x00) @@ -69,17 +97,28 @@ enum PN5180TransceiveStat { PN5180_TS_RESERVED = 7 }; -// PN5180 IRQ_STATUS -#define RX_IRQ_STAT (1<<0) // End of RF receiption IRQ -#define TX_IRQ_STAT (1<<1) // End of RF transmission IRQ -#define IDLE_IRQ_STAT (1<<2) // IDLE IRQ -#define RFOFF_DET_IRQ_STAT (1<<6) // RF Field OFF detection IRQ -#define RFON_DET_IRQ_STAT (1<<7) // RF Field ON detection IRQ -#define TX_RFOFF_IRQ_STAT (1<<8) // RF Field OFF in PCD IRQ -#define TX_RFON_IRQ_STAT (1<<9) // RF Field ON in PCD IRQ -#define RX_SOF_DET_IRQ_STAT (1<<14) // RF SOF Detection IRQ -#define GENERAL_ERROR_IRQ_STAT (1<<17) // General error IRQ -#define LPCD_IRQ_STAT (1<<19) // LPCD Detection IRQ +// PN5180 IRQ_STATUS register (https://www.nxp.com/docs/en/data-sheet/PN5180A0XX_C3_C4.pdf, pages 83-84) +#define RX_IRQ_STAT (1<<0) // End of RF receiption IRQ +#define TX_IRQ_STAT (1<<1) // End of RF transmission IRQ +#define IDLE_IRQ_STAT (1<<2) // IDLE IRQ +#define MODE_DETECTED_IRQ_STAT (1<<3) // External modulation scheme detection IRQ +#define CARD_ACTIVATED_IRQ_STAT (1<<4) // Activated as a Card IRQ +#define STATE_CHANGE_IRQ_STAT (1<<5) // State Change in the transceive state machine IRQ +#define RFOFF_DET_IRQ_STAT (1<<6) // RF Field OFF detection IRQ +#define RFON_DET_IRQ_STAT (1<<7) // RF Field ON detection IRQ +#define TX_RFOFF_IRQ_STAT (1<<8) // RF Field OFF in PCD IRQ +#define TX_RFON_IRQ_STAT (1<<9) // RF Field ON in PCD IRQ +#define RF_ACTIVE_ERROR_IRQ_STAT (1<<10) // RF active error IRQ +#define TIMER0_IRQ_STAT (1<<11) // Timer0 IRQ +#define TIMER1_IRQ_STAT (1<<12) // Timer1 IRQ +#define TIMER2_IRQ_STAT (1<<13) // Timer2 IRQ +#define RX_SOF_DET_IRQ_STAT (1<<14) // RF SOF Detection IRQ +#define RX_SC_DET_IRQ_STAT (1<<15) // RX Subcarrier Detection IRQ +#define TEMPSENS_ERROR_IRQ_STAT (1<<16) // Temperature Sensor IRQ +#define GENERAL_ERROR_IRQ_STAT (1<<17) // General error IRQ +#define HV_ERROR_IRQ_STAT (1<<18) // EEPROM Failure during Programming IRQ +#define LPCD_IRQ_STAT (1<<19) // LPCD Detection IRQ +// Bits 20-31 RFU (Reserved for Future Use) #define MIFARE_CLASSIC_KEYA 0x60 // Mifare Classic key A #define MIFARE_CLASSIC_KEYB 0x61 // Mifare Classic key B @@ -104,9 +143,28 @@ class PN5180 { void begin(int8_t sck=-1, int8_t miso=-1, int8_t mosi=-1, int8_t SSpin=-1); void end(); + +public: + + /* - * PN5180 direct commands with host interface + * PN5180 Direct Commands + * + * These functions are the low level interface to the device: + * 1. Verify command's parameters + * 2. Prepare the transmission frame + * 3. Call transceiveCommand() to send the command + * 4. No state checking */ +public: + /* 0x09 - SEND_DATA */ + bool cmd_SendData(const uint8_t *data, int len, uint8_t validBits); + /* 0x16 - RF_ON */ + bool cmd_RfOn(uint8_t parameter); + /* 0x17 - RF_OFF */ + bool cmd_RfOff(uint8_t parameter); + + public: /* cmd 0x00 */ bool writeRegister(uint8_t reg, uint32_t value); @@ -150,8 +208,9 @@ class PN5180 { public: void reset(); - uint16_t commandTimeout = 500; + uint16_t commandTimeout = 5000; uint32_t getIRQStatus(); + bool waitIRQ(uint8_t irq); bool clearIRQStatus(uint32_t irqMask); PN5180TransceiveStat getTransceiveState(); diff --git a/PN5180ISO14443.cpp b/PN5180ISO14443.cpp index 340653b..505c819 100644 --- a/PN5180ISO14443.cpp +++ b/PN5180ISO14443.cpp @@ -17,6 +17,7 @@ // Lesser General Public License for more details. // //#define DEBUG 1 +//#define DEBUG_ERROR 1 #include #include "PN5180ISO14443.h" @@ -95,7 +96,7 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { // Load standard TypeA protocol already done in reset() if (!loadRFConfig(0x0, 0x80)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Load standard TypeA protocol failed!")); + PN5180ERROR(F("Load standard TypeA protocol failed!")); PN5180DEBUG_EXIT; return -1; } @@ -107,33 +108,33 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { // OFF Crypto if (!writeRegisterWithAndMask(SYSTEM_CONFIG, 0xFFFFFFBF)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: OFF Crypto failed!")); + PN5180ERROR(F("OFF Crypto failed!")); PN5180DEBUG_EXIT; return -1; } // clear RX CRC if (!writeRegisterWithAndMask(CRC_RX_CONFIG, 0xFFFFFFFE)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Clear RX CRC failed!")); + PN5180ERROR(F("Clear RX CRC failed!")); PN5180DEBUG_EXIT; return -1; } // clear TX CRC if (!writeRegisterWithAndMask(CRC_TX_CONFIG, 0xFFFFFFFE)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Clear TX CRC failed!")); + PN5180ERROR(F("Clear TX CRC failed!")); PN5180DEBUG_EXIT; return -1; } // set the PN5180 into IDLE state if (!writeRegisterWithAndMask(SYSTEM_CONFIG, 0xFFFFFFF8)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: set IDLE state failed!")); + PN5180ERROR(F("Set IDLE state failed!")); PN5180DEBUG_EXIT; return -1; } // activate TRANSCEIVE routine if (!writeRegisterWithOrMask(SYSTEM_CONFIG, 0x00000003)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Activates TRANSCEIVE routine failed!")); + PN5180ERROR(F("Activate TRANSCEIVE routine failed!")); PN5180DEBUG_EXIT; return -1; } @@ -141,7 +142,7 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { // wait for wait-transmit state PN5180TransceiveStat transceiveState = getTransceiveState(); if (PN5180_TS_WaitTransmit != transceiveState) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Transceiver not in state WaitTransmit!?")); + PN5180ERROR(F("Transceiver not in state WaitTransmit!?")); PN5180DEBUG_EXIT; return -1; } @@ -158,7 +159,7 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { //Send REQA/WUPA, 7 bits in last byte cmd[0] = (kind == 0) ? 0x26 : 0x52; if (!sendData(cmd, 1, 0x07)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Send REQA/WUPA failed!")); + PN5180ERROR(F("Send REQA/WUPA failed!")); PN5180DEBUG_EXIT; return 0; } @@ -168,7 +169,7 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { // READ 2 bytes ATQA into buffer if (!readData(2, buffer)) { - PN5180DEBUG(F("*** ERROR: READ 2 bytes ATQA failed!\n")); + PN5180ERROR(F("READ 2 bytes ATQA failed!\n")); PN5180DEBUG_EXIT; return 0; } @@ -180,10 +181,10 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { while (PN5180_TS_WaitTransmit != getTransceiveState()) { if (millis() - startedWaiting > 200) { PN5180DEBUG_ON; - PN5180DEBUG_PRINTLN(F("*** ERROR: timeout in PN5180_TS_WaitTransmit!")); + PN5180ERROR(F("Timeout in PN5180_TS_WaitTransmit!")); PN5180DEBUG_EXIT; return -1; - } + } } PN5180DEBUG_ON; @@ -194,7 +195,7 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { cmd[0] = 0x93; cmd[1] = 0x20; if (!sendData(cmd, 2, 0x00)) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Send Anti collision 1 failed!")); + PN5180ERROR(F("Send Anti collision 1 failed!")); PN5180DEBUG_EXIT; return -2; } @@ -204,13 +205,13 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { uint8_t numBytes = rxBytesReceived(); if (numBytes != 5) { - PN5180DEBUG_PRINTLN(F("*** ERROR: Read 5 bytes sak failed!")); + PN5180ERROR(F("Read 5 bytes sak failed!")); PN5180DEBUG_EXIT; return -2; }; // read 5 bytes sak, we will store at offset 2 for later usage if (!readData(5, cmd+2)) { - Serial.println("Read 5 bytes failed!"); + PN5180ERROR("Read 5 bytes failed!"); PN5180DEBUG_EXIT; return -2; } @@ -220,11 +221,13 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { //Enable RX CRC calculation if (!writeRegisterWithOrMask(CRC_RX_CONFIG, 0x01)) { + PN5180ERROR("Enable RX CRC failed!"); PN5180DEBUG_EXIT; return -2; } //Enable TX CRC calculation if (!writeRegisterWithOrMask(CRC_TX_CONFIG, 0x01)) { + PN5180ERROR("Enable TX CRC failed!"); PN5180DEBUG_EXIT; return -2; } @@ -239,6 +242,7 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { } //Read 1 byte SAK into buffer[2] if (!readData(1, buffer+2)) { + PN5180ERROR("Read 1 byte SAK into buffer failed!"); PN5180DEBUG_EXIT; return -2; } @@ -258,11 +262,13 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { for (int i = 0; i < 3; i++) buffer[3+i] = cmd[3 + i]; // Clear RX CRC if (!writeRegisterWithAndMask(CRC_RX_CONFIG, 0xFFFFFFFE)) { + PN5180ERROR(F("Clear RX CRC Failed!")); PN5180DEBUG_EXIT; return -2; } // Clear TX CRC if (!writeRegisterWithAndMask(CRC_TX_CONFIG, 0xFFFFFFFE)) { + PN5180ERROR(F("Clear TX CRC Failed!")); PN5180DEBUG_EXIT; return -2; } @@ -270,11 +276,13 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { cmd[0] = 0x95; cmd[1] = 0x20; if (!sendData(cmd, 2, 0x00)) { + PN5180ERROR(F("Do anti collision 2 Failed!")); PN5180DEBUG_EXIT; return -2; } //Read 5 bytes. we will store at offset 2 for later use if (!readData(5, cmd+2)) { + PN5180ERROR(F("Read 5 bytes Failed!")); PN5180DEBUG_EXIT; return -2; } @@ -284,11 +292,13 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { } //Enable RX CRC calculation if (!writeRegisterWithOrMask(CRC_RX_CONFIG, 0x01)) { + PN5180ERROR(F("Enable RX CRC calculation Failed!")); PN5180DEBUG_EXIT; return -2; } //Enable TX CRC calculation if (!writeRegisterWithOrMask(CRC_TX_CONFIG, 0x01)) { + PN5180ERROR(F("Enable TX CRC calculation Failed!")); PN5180DEBUG_EXIT; return -2; } @@ -296,11 +306,13 @@ int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { cmd[0] = 0x95; cmd[1] = 0x70; if (!sendData(cmd, 7, 0x00)) { + PN5180ERROR(F("Send Select anti collision 2 Failed!")); PN5180DEBUG_EXIT; return -2; } //Read 1 byte SAK into buffer[2] if (!readData(1, buffer + 2)) { + PN5180ERROR(F("Read 1 byte SAK into buffer[2] Failed!")); PN5180DEBUG_EXIT; return -2; } diff --git a/PN5180ISO15693.cpp b/PN5180ISO15693.cpp index 01ef748..bcbe2d8 100644 --- a/PN5180ISO15693.cpp +++ b/PN5180ISO15693.cpp @@ -17,11 +17,55 @@ // Lesser General Public License for more details. // //#define DEBUG 1 +//#define DEBUG_ERROR 1 #include #include "PN5180ISO15693.h" #include "Debug.h" +// ISO15693-3 (1999) Request Flags Table 3 (http://olmicrowaves.com/menucontents/designsupport/rfid/ISO15693-3.pdf, page 9) +#define ISO15693_REQ_FLAG_SUBCARRIER (1<<0) // Subcarrier (0:ASK, 1:FSK) +#define ISO15693_REQ_FLAG_DATARATE (1<<1) // Uplink data rate (0:Low, 1:High) +#define ISO15693_REQ_FLAG_INVENTORY (1<<2) // Inventory (0: Bits 5-8 are table 4 flags, 1: Bits 5-8 are table 5 (inventory) flags) +#define ISO15693_REQ_FLAG_PROTOCOL (1<<3) // Protocol extention (always 0) + +// ISO15693-3 (1999) Request Flags Table 4 (http://olmicrowaves.com/menucontents/designsupport/rfid/ISO15693-3.pdf, page 10) +#define ISO15693_REQ_FLAG_SELECT (1<<4) // Select (0: if Address flag set, use address mode, 1: Use select mode) +#define ISO15693_REQ_FLAG_ADDRESS (1<<5) // Address (0: Don't use address more, 1: Use address mode) +#define ISO15693_REQ_FLAG_OPTION (1<<6) // Option (0: Default value, 1: Must be set for write operations) +#define ISO15693_REQ_FLAG_RFU (1<<7) // RFU (always 0) + +// ISO15693-3 (1999) Request Flags Table 5 (http://olmicrowaves.com/menucontents/designsupport/rfid/ISO15693-3.pdf, page 10) +#define ISO15693_REQ_FLAG_AFI (1<<4) // Inventory AFI (0: Don't use AFI, 1: Use AFI) +#define ISO15693_REQ_FLAG_NBSLOTS (1<<5) // Inventory Slots (0: 16-slots, 1: 1-slot) +#define ISO15693_REQ_FLAG_OPTION (1<<6) // Inventory Option (0: Default value, 1: Must be set for write operations) +#define ISO15693_REQ_FLAG_RFU (1<<7) // Inventory RFU (always 0) + +// ISO15693-3 (1999) Response Flags Table 6 (http://olmicrowaves.com/menucontents/designsupport/rfid/ISO15693-3.pdf, page 11) +#define ISO15693_RESP_FLAG_ERROR (1<<0) // Error: (0: no error, 1: error) +#define ISO15693_RESP_FLAG_EXTENSION (1<<3) // Extension: (0: no protocol format extension, 1: protocol format is extended) + +// ISO15693-3 (1999) Response Error Codes Table 7 (http://olmicrowaves.com/menucontents/designsupport/rfid/ISO15693-3.pdf, page 12) +// (See enum ISO15693ErrorCode in PN5180ISO15693.h) + +// ISO15693-3 (1999) Commands (http://olmicrowaves.com/menucontents/designsupport/rfid/ISO15693-3.pdf, pages 21) +#define ISO15693_CMD_INVENTORY (0x01) +#define ISO15693_CMD_STAYQUIET (0x02) +// 0x03 to 0x1F RFU +#define ISO15693_CMD_READSINGLEBLOCK (0x20) +#define ISO15693_CMD_WRITESINGLEBLOCK (0x21) +#define ISO15693_CMD_LOCKBLOCK (0x22) +#define ISO15693_CMD_READMULTIPLEBLOCKS (0x23) +#define ISO15693_CMD_WRITEMULTIPLEBLOCKS (0x24) +#define ISO15693_CMD_SELECT (0x25) +#define ISO15693_CMD_RESETTOREADY (0x26) +#define ISO15693_CMD_WRITEAFI (0x27) +#define ISO15693_CMD_LOCKAFI (0x28) +#define ISO15693_CMD_WRITEDSFID (0x29) +#define ISO15693_CMD_LOCKDSFID (0x2A) +#define ISO15693_CMD_GETSYSTEMINFORMATION (0x2B) +#define ISO15693_CMD_GETMULTIPLEBLOCKSECURITYSTATUS (0x2C) + PN5180ISO15693::PN5180ISO15693(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi) : PN5180(SSpin, BUSYpin, RSTpin, spi) { } @@ -29,17 +73,27 @@ PN5180ISO15693::PN5180ISO15693(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, S /* * Inventory, code=01 * + * Inventory with flag for 16 time slots, code=01 + * https://www.nxp.com.cn/docs/en/application-note/AN12650.pdf * Request format: SOF, Req.Flags, Inventory, AFI (opt.), Mask len, Mask value, CRC16, EOF + * | | \_ Mask Length (defined by ISO 15693) + * | \_ 0x01: Inventory command (defined by ISO 15693) + * \_ 0x26: 0x02 high data rate | 0x04 inventory flags | 0x20 1 time slot (defined by ISO 15693) * Response format: SOF, Resp.Flags, DSFID, UID, CRC16, EOF - * */ ISO15693ErrorCode PN5180ISO15693::getInventory(uint8_t *uid) { - // Flags, CMD, maskLen - uint8_t inventory[] = { 0x26, 0x01, 0x00 }; - // |\- inventory flag + high data rate - // \-- 1 slot: only one card, no AFI field present - PN5180DEBUG(F("Get Inventory...\n")); + PN5180DEBUG_PRINTF("PN5180ISO15693::getInventory()"); + PN5180DEBUG_PRINTLN(); + PN5180DEBUG_ENTER; + + uint8_t cmd = ISO15693_CMD_INVENTORY; + // flags = 1 slot, only one card, no AFI field present, inventory flag + high data rate + uint8_t flags = ISO15693_REQ_FLAG_NBSLOTS | ISO15693_REQ_FLAG_INVENTORY | ISO15693_REQ_FLAG_DATARATE; + uint8_t maskLen = 0x00; + uint8_t inventory[] = { cmd, flags, maskLen }; + PN5180DEBUG(F("Get Inventory...")); + PN5180DEBUG_PRINTLN(); for (int i=0; i<8; i++) { uid[i] = 0; } @@ -47,25 +101,27 @@ ISO15693ErrorCode PN5180ISO15693::getInventory(uint8_t *uid) { uint8_t *readBuffer; ISO15693ErrorCode rc = issueISO15693Command(inventory, sizeof(inventory), &readBuffer); if (ISO15693_EC_OK != rc) { + PN5180ERROR(F("getInventory() failed at issueISO15693Command()")); + PN5180DEBUG_EXIT; return rc; } + for (int i=0; i<8; i++) { + uid[i] = readBuffer[2+i]; + } +#ifdef DEBUG PN5180DEBUG(F("Response flags: ")); PN5180DEBUG(formatHex(readBuffer[0])); PN5180DEBUG(F(", Data Storage Format ID: ")); PN5180DEBUG(formatHex(readBuffer[1])); PN5180DEBUG(F(", UID: ")); - for (int i=0; i<8; i++) { - uid[i] = readBuffer[2+i]; -#ifdef DEBUG PN5180DEBUG(formatHex(uid[7-i])); // LSB comes first if (i<2) PN5180DEBUG(":"); -#endif - } - PN5180DEBUG_PRINTLN(); +#endif + PN5180DEBUG_EXIT; return ISO15693_EC_OK; } @@ -73,18 +129,27 @@ ISO15693ErrorCode PN5180ISO15693::getInventory(uint8_t *uid) { * Inventory with flag set for 16 time slots, code=01 * https://www.nxp.com.cn/docs/en/application-note/AN12650.pdf * Request format: SOF, Req.Flags, Inventory, AFI (opt.), Mask len, Mask value, CRC16, EOF + * | | \- ? Mask Length (defined by ISO 15693) + * | \- 0x01: Inventory command (defined by ISO 15693) + * \- 0x26: 0x02 high data rate | 0x04 inventory flags | !(0x20) 16 time slots (defined by ISO 15693) * Response format: SOF, Resp.Flags, DSFID, UID, CRC16, EOF - * */ ISO15693ErrorCode PN5180ISO15693::getInventoryMultiple(uint8_t *uid, uint8_t maxTags, uint8_t *numCard) { PN5180DEBUG_PRINTF("PN5180ISO15693::getInventoryMultiple(maxTags=%d, numCard=%d)", maxTags, *numCard); PN5180DEBUG_PRINTLN(); PN5180DEBUG_ENTER; + uint16_t collision[maxTags]; *numCard = 0; uint8_t numCol = 0; - inventoryPoll(uid, maxTags, numCard, &numCol, collision); - PN5180DEBUG_PRINTF("*** Number of collisions=%d", numCol); + + if (ISO15693_EC_OK != inventoryPoll(uid, maxTags, numCard, &numCol, collision)) { + PN5180ERROR(F("getInventoryMultiple() failed at first inventoryPoll()")); + PN5180DEBUG_EXIT; + return ISO15693_EC_UNKNOWN_ERROR; + } + + PN5180DEBUG_PRINTF("Number of collisions=%d", numCol); PN5180DEBUG_PRINTLN(); while(numCol){ // 5+ Continue until no collisions detected @@ -93,16 +158,25 @@ ISO15693ErrorCode PN5180ISO15693::getInventoryMultiple(uint8_t *uid, uint8_t max PN5180DEBUG(formatHex(collision[0])); PN5180DEBUG_PRINTLN(); #endif - inventoryPoll(uid, maxTags, numCard, &numCol, collision); + + // inventoryPoll(uid, maxTags, numCard, &numCol, collision); + if (ISO15693_EC_OK != inventoryPoll(uid, maxTags, numCard, &numCol, collision)) { + PN5180ERROR(F("getInventoryMultiple() failed at collision resolution inventoryPoll()")); + PN5180DEBUG_EXIT; + return ISO15693_EC_UNKNOWN_ERROR; + } + numCol--; for(int i=0; i 0); } uint8_t *p = (uint8_t*)&(collision[0]); - // Flags, CMD, - const uint8_t inventory[7] = { 0x06, 0x01, uint8_t(maskLen*4), p[0], p[1], p[2], p[3] }; - // |\- inventory flag + high data rate - // \-- 16 slots: upto 16 cards, no AFI field present + // flags = 16 slots, up to 16 cards, no AFI field present, inventory flag + high data rate + uint8_t flags = ISO15693_REQ_FLAG_DATARATE | ISO15693_REQ_FLAG_INVENTORY; + const uint8_t inventory[7] = { flags, ISO15693_CMD_INVENTORY, uint8_t(maskLen*4), p[0], p[1], p[2], p[3] }; + // TODO: can p[] containing the collision mask bytes be more than 4 bytes long? + // TODO: Why is collision[] initialized to a length of maxTags, but only 4 p[] values in this call? uint8_t cmdLen = 3 + (maskLen/2) + (maskLen%2); #ifdef DEBUG PN5180DEBUG_PRINTF(F("mask=%d, maskLen=%d, cmdLen=%d"), p[0], maskLen, cmdLen); PN5180DEBUG_PRINTLN(); #endif - clearIRQStatus(0x000FFFFF); // 3. Clear all IRQ_STATUS flags - sendData(inventory, cmdLen, 0); // 4. 5. 6. Idle/StopCom Command, Transceive Command, Inventory command + + // 3. Clear all IRQ_STATUS flags + // clearIRQStatus(0x000FFFFF); + if (!clearIRQStatus(0x000FFFFF)) { + PN5180ERROR(F("inventoryPoll() failed at step 3. clearIRQStatus()")); + PN5180DEBUG_EXIT; + return ISO15693_EC_UNKNOWN_ERROR; + } + + uint8_t *readBuffer; + + // 4. 5. 6. Idle/StopCom Command, Transceive Command, Inventory command + // sendData(inventory, cmdLen, 0); + if (!sendData(inventory, cmdLen, 0)) { + PN5180ERROR(F("inventoryPoll() failed at step 4.5.6. sendData()")); + PN5180DEBUG_EXIT; + return ISO15693_EC_UNKNOWN_ERROR; + } - for(uint8_t slot=0; slot<16; slot++){ // 7. Loop to check 16 time slots for data + // 7. Loop to check 16 time slots for data + for(uint8_t slot=0; slot<16; slot++){ uint32_t rxStatus; uint32_t irqStatus = getIRQStatus(); - readRegister(RX_STATUS, &rxStatus); + + // readRegister(RX_STATUS, &rxStatus); + if (!readRegister(RX_STATUS, &rxStatus)) { + PN5180ERROR(F("inventoryPoll() failed at step 7. readRegister() for slot %d"), slot); + PN5180DEBUG_EXIT; + return ISO15693_EC_UNKNOWN_ERROR; + } + PN5180DEBUG(F("slot=")); PN5180DEBUG(formatHex(slot)); PN5180DEBUG(F(": ")); @@ -141,42 +240,49 @@ ISO15693ErrorCode PN5180ISO15693::inventoryPoll(uint8_t *uid, uint8_t maxTags, u PN5180DEBUG(F(", RX_STATUS=")); PN5180DEBUG(formatHex(rxStatus)); PN5180DEBUG(F(": ")); + + // 7+ Determine if a collision occurred uint16_t len = (uint16_t)(rxStatus & 0x000001ff); - if((rxStatus >> 18) & 0x01 && *numCol < maxTags){ // 7+ Determine if a collision occurred - if(maskLen > 0) collision[*numCol] = collision[0] | (slot << (maskLen * 2)); - else collision[*numCol] = slot << (maskLen * 2); // Yes, store position of collision + if((rxStatus >> 18) & 0x01 && *numCol < maxTags){ + if(maskLen > 0) collision[*numCol] = collision[0] | (slot << (maskLen * 4)); + else collision[*numCol] = slot << (maskLen * 4); // Yes, store position of collision *numCol = *numCol + 1; #ifdef DEBUG PN5180DEBUG_PRINTF("Collision detected for UIDs matching %X starting at LSB", collision[*numCol-1]); PN5180DEBUG_PRINTLN(); #endif } - else if(!(irqStatus & RX_IRQ_STAT) && !len){ // 8. Check if a card has responded + else + // 8. Check if a card has responded + if(!(irqStatus & RX_IRQ_STAT) && !len){ PN5180DEBUG(F("No card in this time slot.")); PN5180DEBUG_PRINTLN(); } else{ #ifdef DEBUG - PN5180DEBUG_PRINTF("slot=%d, irqStatus: %ld, RX_STATUS: %ld, Response length=%d", slot, irqStatus, rxStatus, len); + PN5180DEBUG_PRINTF("slot=%d, irqStatus: %ld, RX_STATUS: %ld, Response length=%d, ", slot, irqStatus, rxStatus, len); #endif uint8_t *readBuffer; - readBuffer = readData(len+1); // 9. Read reception buffer + + // 9. Read reception buffer + readBuffer = readData(len+1); if(0L == readBuffer){ - PN5180DEBUG(F("ERROR in readData!")); - PN5180DEBUG_PRINTLN(); + PN5180ERROR(F("inventoryPoll() failed at step 9. readBuffer() for slot %d"), slot); PN5180DEBUG_EXIT; return ISO15693_EC_UNKNOWN_ERROR; } + #ifdef DEBUG PN5180DEBUG(F("readBuffer=")); for(int i=0; i