2025-08-06 4:07 PM
I am trying to get UIDs returned for an PICOPASS ISO15693 iClass card. I am using the default analogConfig.h generated for a ST25R3916B (though am using a ST2525R3917B). I can read ISO14443A/B and non-picopass ISO15693 cards without any issues, but am unable to read this iClass card. My understanding is that iClass cards use PICOPASS, which requires me to send:
In order to do this, I am doing the following to initialize RFAL in PICOPASS:
ReturnCode err;
err = rfalSetMode(RFAL_MODE_POLL_PICOPASS, RFAL_BR_26p48, RFAL_BR_26p48);
if (err != ERR_NONE) {
LOG_ERR("Failed to set mode for NFC PICO-15693 (err %d)", err);
return -EIO;
}
rfalSetErrorHandling(RFAL_ERRORHANDLING_NONE);
rfalSetGT(RFAL_GT_PICOPASS);
rfalSetFDTListen(RFAL_FDT_LISTEN_PICOPASS_POLLER);
rfalSetFDTPoll(RFAL_FDT_POLL_PICOPASS_POLLER);
I will then turn on my RF field and try to POLL for the UID using:
static int st25r_nfc_pico15693_poll(const struct device *dev, struct nfc_id *device_ids,
uint8_t count)
{
int ret;
LOG_DBG("Polling for NFC PICO-15693");
uint8_t tx_buff[16] = {0};
uint8_t rx_buff[16] = {0};
uint16_t rx_len = 0;
uint32_t start_time = k_uptime_get_32();
do {
tx_buff[0] = 0x0A;
ret = st25r_nfc_pico15693_rxtx(dev, tx_buff, nfc_byte2bit(1), rx_buff,
sizeof(rx_buff), &rx_len);
if (ret) {
LOG_ERR("Failed to send ACTALL command (err %d)", ret);
}
tx_buff[0] = 0x0C;
ret = st25r_nfc_pico15693_rxtx(dev, tx_buff, nfc_byte2bit(1), rx_buff,
sizeof(rx_buff), &rx_len);
if (ret) {
LOG_ERR("Failed to send IDENTIFY command (err %d)", ret);
ret = 0;
continue;
}
tx_buff[0] = 0x81;
memcpy(&tx_buff[1], rx_buff, 8); /* load the hashed UID */
ret = st25r_nfc_pico15693_rxtx(dev, tx_buff, nfc_byte2bit(9), rx_buff,
sizeof(rx_buff), &rx_len);
if (ret) {
LOG_ERR("Failed to send SELECT command (err %d)", ret);
return 0;
} else {
break;
}
} while (k_uptime_get_32() - start_time < 500);
return 1;
}
/**
* @brief NFC PICO-15693 poller @p rxtx API function
* See @ref nfc_poller_api for poller API
*/
static int st25r_nfc_pico15693_rxtx(const struct device *dev, uint8_t *tx_data, uint16_t tx_bitlen,
uint8_t *rx_data, uint16_t rx_size, uint16_t *rx_bitlen)
{
ReturnCode err;
rfalTransceiveContext ctx = {
.txBuf = tx_data,
.txBufLen = tx_bitlen,
.rxBuf = rx_data,
.rxBufLen = nfc_byte2bit(rx_size),
.rxRcvdLen = rx_bitlen,
/* Flags get overriden in rfalStartTransceive() because in PICOPASS mode */
.flags = RFAL_TXRX_FLAGS_DEFAULT,
.fwt = rfalConvUsTo1fc(NFC_ST25R_DATA(dev)->timeout_us),
};
err = rfalStartTransceive(&ctx);
if (err != ERR_NONE) {
LOG_ERR("Failed to start transceive (err %d)", err);
return -EIO;
}
do {
rfalWorker();
err = rfalGetTransceiveStatus();
} while (rfalIsTransceiveInTx() && (err == ERR_BUSY));
if (rfalIsTransceiveInRx()) {
err = RFAL_ERR_NONE;
}
if (err != RFAL_ERR_NONE) {
LOG_ERR("Failed to transceive Tx data (err %d)", err);
return err;
}
/* Block until Rx is complete */
err = rfalTransceiveBlockingRx();
if (err == RFAL_ERR_CRC) {
LOG_WRN("CRC check failed");
/* continue */
} else if ((err >= RFAL_ERR_INCOMPLETE_BYTE) && (err <= RFAL_ERR_INCOMPLETE_BYTE_07)) {
LOG_WRN("Incomplete byte received");
/* continue */
} else if (err) {
LOG_ERR("Failed to receive Rx data (err %d)", err);
return -EIO;
}
LOG_INF("Received %d bits", *rx_bitlen);
LOG_HEXDUMP_INF(rx_data, nfc_bit2byte(*rx_bitlen), "Received data:");
return 0;
}
I've also tried adjusting the register manually prior to entering the do/while loop where I'm tried variations of:
err = st25r3916ChangeRegisterBits(ST25R3916_REG_MODE, ST25R3916_REG_MODE_tr_am,
ST25R3916_REG_MODE_tr_am_am);
if (err != ERR_NONE) {
LOG_ERR("Failed to set mode to AM for NFC PICO-15693 (err %d)", err);
return -EIO;
}
err = st25r3916ChangeRegisterBits(ST25R3916_REG_TX_DRIVER,
ST25R3916_REG_TX_DRIVER_am_mod_mask,
ST25R3916_REG_TX_DRIVER_am_mod_10percent);
if (err != ERR_NONE) {
LOG_ERR("Failed to set AM modulation to 10%% for NFC PICO-15693 (err %d)", err);
return -EIO;
}
I'm pretty confused how to go about this. The only documentation I have to go off is this from Proxmark which I'm not 100% sure is even for the same card.
For context, I am process of upgrading our architecture from using the ams AG 3911 library by Christian Eisendle (I'm just going based on the headers here, I don't know who actually released this) to using the RFAL library for the 3917B. I'm unsure what I'm missing here and am hoping ST can provide me some additional context or notes about possible register changes needed...