Low level driver for NFCT using HAL NFC library only.

Dear All,

We need to develop a custom low-level driver for NFC communication using the Hardware Abstraction Layer (HAL) library exclusively. We are currently unable to use the recommended NFC library because we only have access to its binary distribution, which prevents us from implementing a necessary customization (e.g., configuring the WQTX repetition count beyond 5).

Our initial setup, utilizing only the NFC HAL library (<hal/nrf_nfct.h>), is functional regarding field detection (present/lost) and anti-collision.

The primary issue occurs immediately upon **frame reception**. The **reported EasyDMA data length is consistently 8190 bits** (a known corruption value). This length is clearly incorrect and indicates a fundamental configuration or synchronization issue at the driver level. Crucially, data reception works correctly when using the recommended, full-stack NFC library, which confirms the integrity of the underlying hardware platform (nRF5340). Moreover, no consistent data are received.

We suspect the solution involves an **undocumented hardware/software detail** or a **specific synchronization sequence** required to properly clean or restart the EasyDMA register or any other issue related to NFCT peripheral.

We are therefore requesting a **sample code implementation, utilizing the NFC HAL library exclusively**, that successfully **receives and correctly reports the data length of the first frame (RATS/ISO-DEP block) after the anti-collision phase**.

Best regards,
Alain

Parents
  • Hi Alain,

    We do not have any sample code for using the NFC HAL The closest thing we have is looking at the nrfx NFC driver implementation as an example of how the HAL can be used if you want to use that directly. There is no example of using the nrfx driver either though, as this is handled by the NFC libraries which are the only approach we support in the SDK.

    Regarding the error, I agree that receiving 8190 bits is not possible (it is larger than the maximum Rx buffer size and what can be conveyed in the RXD.AMOUNT register. Can you share more details on your implementation and where this number comes from?

    Br,

    Einar

  • Hello,

    Please find below my sample code.
    The size is retrieved from the event structure, and it matches the value of the RXD.AMOUNT NFCT register when the NRFX_NFCT_EVT_RX_FRAMEEND event occurs.

    Best regards,

    A.

    #define USE_WORKAROUND_FOR_ANOMALY_190
    #include <nrfx.h>
    #include <nrfx_nfct.h>
    #include <hal/nrf_nfct.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include <stddef.h>
    #include "t4t_handler.h" // Interface towards the T=1/APDU layer
    #include <nfc_pool_frame_generic.h>
    #include <nfc_transport.h>

    #include <nfc/tnep/poller.h>
    #include "system_event.h"
    #include <zephyr/logging/log.h>
    #include <zephyr/kernel.h> // Required for k_work_delayable
    #include <zephyr/cache.h>
    LOG_MODULE_REGISTER(low_level_nfc, LOG_LEVEL_DBG);

    // ====================================================================
    // --- 1. LOW LEVEL PROTOCOL CONSTANTS (ISO 14443-3/4) ---
    // ====================================================================

    #define NFC_CMD_RATS 0xE0 ///< ISO 14443-4 command: Request for Answer To Select.

    #define NFC_PCB_BLOCK_MASK 0xC0 ///< Mask to identify the block type (I, S, R) in the PCB field.
    #define NFC_PCB_I_BLOCK_MASK 0x00 ///< Mask value for I-blocks (Information).
    #define NFC_PCB_S_BLOCK_MASK 0xC0 ///< Mask value for S-blocks (Supervisory).

    #define MAX_FRAME_SIZE 256 ///< Maximum size of ISO-DEP frames (based on FSD/FSC of layer 3).
    #define NFC_FRAME_DELAY_MODE_DEFAULT NRF_NFCT_FRAME_DELAY_MODE_WINDOWGRID ///< Default frame delay mode (Free Run) for transmission.
    #define NFC_RX_STATUS_OK 0 ///< Reception status indicating success without error.
    #define NFC_ACTIVATION_DELAY_MS 10 ///< Delay before activation for field stabilisation
    #define NFC_ALL_EVENTS \
    (NRF_NFCT_EVENT_READY | \
    NRF_NFCT_EVENT_FIELDDETECTED | \
    NRF_NFCT_EVENT_FIELDLOST | \
    NRF_NFCT_EVENT_TXFRAMESTART | \
    NRF_NFCT_EVENT_TXFRAMEEND | \
    NRF_NFCT_EVENT_RXFRAMESTART | \
    NRF_NFCT_EVENT_RXFRAMEEND | \
    NRF_NFCT_EVENT_ERROR | \
    NRF_NFCT_EVENT_RXERROR | \
    NRF_NFCT_EVENT_ENDRX | \
    NRF_NFCT_EVENT_ENDTX | \
    NRF_NFCT_EVENT_AUTOCOLRESSTARTED | \
    NRF_NFCT_EVENT_COLLISION | \
    NRF_NFCT_EVENT_SELECTED)

    /**
    * @brief NFCTT macro sel event types, passed to the upper-layer callback function
    * provided during the initialization.
    */
    typedef enum
    {
    NFCT_STATE_DISABLED,
    NFCT_STATE_RAMPUP2RAMPUP=2,
    NFCT_STATE_IDLE3IDLE,
    NFCT_STATE_RECEIVE4RECEIVE,
    NFCT_STATE_FRAMEDELAY5FRAMEDELAY,
    NFCT_STATE_TRANSMIT6TRANSMIT
    } nrfx_nfct_tag_state_id_t;

    /**
    * @brief Macro to calculate the TB1 byte of the ATS (Answer To Select).
    * @param FWI_VAL FWI (Frame Waiting Integer) value.
    * @param SFGI_VAL SFGI (Start-up Frame Guard Integer) value.
    */
    #define CALCULATE_TB1(FWI_VAL, SFGI_VAL) \
    ((((uint8_t)(FWI_VAL) & 0x0F) << 4) | ((uint8_t)(SFGI_VAL) & 0x0F))

    #ifndef T1_FWI
    #define T1_FWI 0 ///< Default Frame Waiting Integer (FWI) for ATS response (if not defined).
    #endif
    #ifndef T1_SFGI
    #define T1_SFGI 0 ///< Default Start-up Frame Guard Integer (SFGI) (if not defined).
    #endif

    // ====================================================================
    // --- 2. STRUCTURES AND STATE CONTEXTS (Static) ---
    // ====================================================================

    /** @brief Enumeration of low-level NFC protocol states. */
    typedef enum {
    STATE_IDLE, ///< Waiting for field detection or selection.
    STATE_SELECTED,///< The tag is selected (after RATS).
    STATE_DEINIT ///< NFC peripheral uninitialized.
    } nfc_protocol_state_t;

    /** @brief Current NFC protocol state. */
    static nfc_protocol_state_t current_protocol_state = STATE_IDLE;
    /** @brief Unique tag identifier (NFCID1) - 4 bytes. */
    static const uint8_t UID[4] = { 0x12, 0x34, 0x56, 0x74 };

    // --- Buffers and Descriptors (EasyDMA accessible)
    // Place buffers in a non-cacheable, 4-byte aligned section for EasyDMA
    /** @brief Receive buffer for NFC data (EasyDMA access). */
    __attribute__((section(".non_cacheable"))) __aligned(4) static uint8_t nfc_buffer[MAX_FRAME_SIZE];

    /** @brief Receive data descriptor for the nrfx_nfct driver. */
    static nrfx_nfct_data_desc_t rx_data_desc = {
    .p_data = nfc_buffer,
    .data_size = MAX_FRAME_SIZE
    };

    /** @brief Mutex to ensure exclusive access to the NFCT/NFC peripheral. */
    K_MUTEX_DEFINE(nfct_access_mutex);
    /** @brief RF field status indicator (true if the field is present). */
    static bool field_on;

    /** @brief Handler for the deferred work item to force NFCT into the ACTIVATED state. */
    static void nfc_activate_work_handler(struct k_work *work);

    /** @brief Work queue item for deferred NFCT activation (to satisfy Anomaly 190 delay). */
    K_WORK_DELAYABLE_DEFINE(nfc_activation_work, nfc_activate_work_handler);


    // ====================================================================
    // --- 3. STATIC PROTOTYPES ---
    // ====================================================================
    /**
    * @brief Performs a full hardware reset, state cleanup, and re-arms RX.
    *
    * @details This is the necessary workaround for persistent NFCT errors
    * like Anomaly 190 (status 0x2) and DMA corruption (RX errors).
    *
    * @param[in] context String indicating where the reset was triggered for logging.
    */
    static void nfc_full_reset_and_rearm(const char* context);

    /**
    * @brief Prepares the TX buffer and initiates NFC transmission.
    *
    * @details Copies the data to the EasyDMA-compatible buffer and calls
    * the nrfx_nfct driver's send function. In case of transmission failure,
    * re-arms the RX listener.
    *
    * @param[in] data Pointer to the data to send.
    * @param[in] len Length of the data.
    */
    static void prepare_and_send(const uint8_t *data, size_t len);

    /**
    * @brief Main event handler for the NFCT peripheral.
    *
    * @details Processes low-level events such as field detection,
    * end of reception/transmission, selection by the reader, and errors.
    *
    * @param[in] p_event Pointer to the nfct event structure.
    */
    static void nfct_event_handler(nrfx_nfct_evt_t const *p_event);

    /**
    * @brief Handles the reception of a RATS command (Request for Answer To Select).
    *
    * @details Sends the ATS response (Answer To Select) to the reader.
    */
    static void handle_rats(void);

    /**
    * @brief Handles ISO 14443-4 data frames (APDU/T=1).
    *
    * @details Extracts the PCB and information data, passes them to the
    * T=1 protocol handler (`t1_process_frame`) and sends the T=1/APDU response.
    *
    * @param[in] data Pointeur vers le début de la trame reçue (incluant le PCB).
    * @param[in] len Longueur totale de la trame reçue.
    */
    static void handle_iso14443_4_data(const uint8_t *data, size_t len);

    /**
    * @brief Handles the end of reception of a low-level NFC-A frame.
    *
    * @details Checks the status and length of the received frame. If the frame is valid,
    * it determines whether it is a RATS command or an ISO 14443-4 frame and
    * calls the appropriate handling function.
    *
    * @param[in] p_event Pointer to the NRFX_NFCT_EVT_RX_FRAMEEND event structure.
    */
    static void handle_iso14443a_frame(nrfx_nfct_evt_t const *p_event);

    // ====================================================================
    // --- 4. NFCT FUNCTION IMPLEMENTATION (Low Level) ---
    // ====================================================================

    /** @brief ATS response (Answer To Select) sent after a RATS. */
    static const uint8_t ATS_RESPONSE[] = {
    0x04, // TL = number of subsequent bytes
    0x70, // T0
    0x80, // TA(1)
    CALCULATE_TB1(T1_FWI, T1_SFGI), // TB(1)
    0x02 // TC(1): CID supported
    };

    /**
    * @brief Asynchronously prepares and transmits an S-Block WTX-REQUEST.
    *
    * @details This function is intended to be called from the application thread
    * to request an extension of the PCD's Frame Waiting Time (FWT).
    * It acquires a mutex to ensure exclusive access to the NFC driver
    * before initiating transmission.
    *
    * @return int 0 on success, or an error code if transmission failed
    * or the mutex could not be acquired.
    */
    int t1_send_wtx_request(void) {
    uint8_t tx_frame_buffer[4];
    size_t tx_len = 0;
    int ret = 0;

    if (k_mutex_lock(&nfct_access_mutex, K_FOREVER) != 0) {
    return -1;
    }

    tx_frame_buffer[tx_len++] = NFC_PCB_S_BLOCK_MASK | T1_PCB_WTX_REQ;
    tx_frame_buffer[tx_len++] = t1_get_wti();

    nrfx_nfct_data_desc_t tx_data_desc = {
    .p_data = tx_frame_buffer,
    .data_size = tx_len
    };

    nrfx_err_t err = nrfx_nfct_tx(&tx_data_desc, NFC_FRAME_DELAY_MODE_DEFAULT);
    if (err != NRFX_SUCCESS) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    }

    k_mutex_unlock(&nfct_access_mutex);
    return ret;
    }

    /**
    * @brief Prepares the TX buffer and initiates NFC transmission.
    *
    * @details Copies the data to the EasyDMA-compatible buffer and calls
    * the nrfx_nfct driver's send function. In case of transmission failure,
    * re-arms the RX listener.
    *
    * @param[in] data Pointer to the data to send.
    * @param[in] len Length of the data.
    */
    static void prepare_and_send(const uint8_t *data, size_t len) {
    if (len == 0 || len > MAX_FRAME_SIZE) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    return;
    }

    memcpy(nfc_buffer, data, len);

    nrfx_nfct_data_desc_t tx_data_desc = {
    .p_data = nfc_buffer,
    .data_size = len
    };

    nrfx_err_t err = nrfx_nfct_tx(&tx_data_desc, NFC_FRAME_DELAY_MODE_DEFAULT);

    if (err != NRFX_SUCCESS) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }
    LOG_HEXDUMP_INF(nfc_buffer, len, "NFC TX--> ");
    }

    /**
    * @brief Handles the reception of a RATS command (Request for Answer To Select).
    *
    * @details Sends the ATS response (Answer To Select) to the reader.
    */
    static void handle_rats(void) {
    prepare_and_send(ATS_RESPONSE, sizeof(ATS_RESPONSE));
    }

    /**
    * @brief Handles ISO 14443-4 data frames (APDU/T=1).
    *
    * @details Extracts the PCB and information data, passes them to the
    * T=1 protocol handler (`t1_process_frame`) and sends the T=1/APDU response.
    *
    * @param[in] data Pointer to the start of the received frame (including the PCB).
    * @param[in] len Total length of the received frame.
    */
    static void handle_iso14443_4_data(const uint8_t *data, size_t len) {
    nrfx_err_t err;
    if (len == 0) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    return;
    }

    uint8_t pcb = data[0];
    const uint8_t *inf_data = &data[1];
    size_t inf_len = (len > 1) ? (len - 1) : 0;

    uint8_t out_pcb;
    uint8_t out_inf_buffer[MAX_FRAME_SIZE];
    size_t out_inf_len = 0;

    if (t1_process_frame(pcb, inf_data, inf_len, out_inf_buffer, &out_inf_len, &out_pcb)) {
    nfc_buffer[0] = out_pcb;
    memcpy(&nfc_buffer[1], out_inf_buffer, out_inf_len);
    prepare_and_send(nfc_buffer, out_inf_len + 1);
    } else {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }
    }

    /**
    * @brief Performs a full hardware reset, state cleanup, and re-arms RX.
    *
    * @details This is the necessary workaround for persistent NFCT errors
    * like Anomaly 190 (status 0x2) and DMA corruption (RX errors).
    *
    * @param[in] context String indicating where the reset was triggered for logging.
    */
    static void nfc_full_reset_and_rearm(const char* context) {
    nrfx_err_t err;
    LOG_ERR("Applying full reset from %s context.", context);
    nrf_nfct_task_trigger(NRF_NFCT, NRF_NFCT_TASK_GOIDLE);
    // 2. Désactiver le périphérique (met l'alimentation en mode faible)
    nrf_nfct_task_trigger(NRF_NFCT, NRF_NFCT_TASK_DISABLE);
    // Clear error flags
    uint32_t err_status = nrf_nfct_error_status_get(NRF_NFCT);
    nrf_nfct_error_status_clear(NRF_NFCT, err_status);
    // Vider TOUS les événements pour un état propre
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    // Step 1: Disable Auto-Collision Resolution before reset
    nrfx_nfct_autocolres_disable();
    // // Step 2: Full hardware reset (EasyDMA Workaround)
    nrfx_nfct_disable();
    nrfx_nfct_enable();
    // // Step 3: Re-enable Auto-Collision Resolution
    nrfx_nfct_autocolres_enable();
    // Step 4: Force SENSING state
    nrfx_nfct_state_force(NRFX_NFCT_STATE_SENSING);

    // Step 5: Reset protocol state and field flag
    field_on = false;
    current_protocol_state = STATE_IDLE;
    k_work_cancel_delayable(&nfc_activation_work);

    // Step 6: Re-arm RX
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }

    /**
    * @brief Handles the end of reception of a low-level NFC-A frame.
    *
    * @details Checks the status and length of the received frame. If the frame is valid,
    * it determines whether it is a RATS command or an ISO 14443-4 frame and
    * calls the appropriate handling function.
    *
    * @param[in] p_event Pointeur vers la structure d'événement NRFX_NFCT_EVT_RX_FRAMEEND.
    */
    static void handle_iso14443a_frame(nrfx_nfct_evt_t const *p_event) {
    nrfx_err_t err;
    uint32_t status = p_event->params.rx_frameend.rx_status;
    uint32_t rx_len = p_event->params.rx_frameend.rx_data.data_size;
    const uint8_t *p_buffer = p_event->params.rx_frameend.rx_data.p_data;

    // Check for RX errors and abnormally large frames (indicating DMA corruption)
    if (status != NFC_RX_STATUS_OK || rx_len == 0 || rx_len > MAX_FRAME_SIZE) {
    LOG_ERR("RX Error: status=0x%X, len=%zu (max=%d). Possible DMA corruption.", status, rx_len, MAX_FRAME_SIZE);
    // Call full reset to clear potential DMA state corruption
    nfc_full_reset_and_rearm("handle_iso14443a_frame (DMA Corruption)");
    return;
    }

    const uint8_t command = p_buffer[0];
    LOG_HEXDUMP_INF(p_buffer, rx_len, "NFC RX--> ");

    if (current_protocol_state == STATE_SELECTED) {
    if (command == NFC_CMD_RATS) {
    LOG_INF("RATS received (len=%u)", rx_len);
    handle_rats();
    // Do not re-arm here: TX_FRAMEEND will re-arm
    } else if ((command & NFC_PCB_BLOCK_MASK) == NFC_PCB_I_BLOCK_MASK ||
    (command & NFC_PCB_BLOCK_MASK) == NFC_PCB_S_BLOCK_MASK) {
    handle_iso14443_4_data(p_buffer, rx_len);
    // If handle_iso14443_4_data generates a TX, TX_FRAMEEND will re-arm;
    // otherwise it re-arms itself.
    } else {
    LOG_WRN("Unexpected RX cmd: 0x%02X", command);
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }
    } else {
    LOG_WRN("RX in non-SELECTED state: %d", current_protocol_state);
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    }
    }

    /**
    * @brief Handler for the deferred work item to force NFCT into the ACTIVATED state.
    *
    * @details This function is executed after a short delay (e.g., 15ms) to respect
    * Anomaly 190.
    */
    static void nfc_activate_work_handler(struct k_work *work) {
    if (field_on) {
    LOG_INF("Deferred activation triggered by work queue (forcing ACTIVATED).");
    nrfx_nfct_state_force(NRFX_NFCT_STATE_ACTIVATED);
    } else {
    LOG_DBG("Deferred activation cancelled (Field lost before activation).");
    }
    }

    /**
    * @brief Main event handler for the NFCT peripheral.
    *
    * @details Processes low-level events such as field detection,
    * end of reception/transmission, selection by the reader, and errors.
    *
    * @param[in] p_event Pointer to the nfct event structure.
    */
    static void nfct_event_handler(nrfx_nfct_evt_t const *p_event) {
    nrfx_err_t err;
    nrf_nfct_tag_state_t state = nrf_nfct_tag_state_get(NRF_NFCT);
    LOG_DBG("Event: %d - HW State: %d - Protocol: %d", p_event->evt_id, state, current_protocol_state);

    switch (p_event->evt_id) {
    case NRFX_NFCT_EVT_FIELD_DETECTED:
    LOG_DBG("Field detected");
    field_on = true;
    // Increased delay to K_MSEC(NFC_ACTIVATION_DELAY_MS) for maximum robustness against Anomaly 190
    // and persistent status 0x2 errors, based on the failure of NFC_ACTIVATION_DELAY_MS ms.
    k_work_schedule(&nfc_activation_work, K_MSEC(NFC_ACTIVATION_DELAY_MS));
    //nrfx_nfct_state_force(NRFX_NFCT_STATE_ACTIVATED);
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_FIELDLOST);
    break;

    case NRFX_NFCT_EVT_FIELD_LOST:
    LOG_DBG("Field lost");
    // Cancel any pending activation work
    k_work_cancel_delayable(&nfc_activation_work);
    nrfx_nfct_state_force(NRFX_NFCT_STATE_SENSING);
    current_protocol_state = STATE_IDLE;
    field_on = false;
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_FIELDDETECTED);
    break;

    case NRFX_NFCT_EVT_SELECTED:
    LOG_DBG("Selected");
    nrf_nfct_error_status_clear(NRF_NFCT, nrf_nfct_error_status_get(NRF_NFCT));
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    if (field_on) {
    current_protocol_state = STATE_SELECTED;
    }
    // Always arm RX here
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    nrf_nfct_error_status_clear(NRF_NFCT, nrf_nfct_error_status_get(NRF_NFCT));
    // Vider TOUS les événements pour un état propre
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    }
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_SELECTED);
    break;

    case NRFX_NFCT_EVT_RX_FRAMEEND:
    LOG_DBG("RX end (status: %d, size: %d)", p_event->params.rx_frameend.rx_status, p_event->params.rx_frameend.rx_data.data_size);
    // No strict state check: handle if OK
    if (p_event->params.rx_frameend.rx_status == NFC_RX_STATUS_OK) {
    handle_iso14443a_frame(p_event);
    } else {
    // If status != OK, handle is inside handle_iso14443a_frame, but for robustness,
    // we call a reset here if handle_iso14443a_frame did not catch a DMA error.
    // In this case, we rely on the full reset from NRFX_NFCT_EVT_ERROR if the error
    // is severe enough to set the error flag. For typical ISO14443 errors (CRC, etc.),
    // re-arming RX is enough if not handled in handle_iso14443a_frame, but full reset
    // is more resilient. We rely on the reset in the error case.
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_ENDRX);
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    uint32_t err_status = nrf_nfct_error_status_get(NRF_NFCT);
    nrf_nfct_error_status_clear(NRF_NFCT, err_status);
    // Vider TOUS les événements pour un état propre
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    }
    }
    break;

    case NRFX_NFCT_EVT_TX_FRAMEEND:
    LOG_DBG("TX end");
    // Re-arm RX
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }

    break;

    case NRFX_NFCT_EVT_ERROR:
    uint32_t err_status = nrf_nfct_error_status_get(NRF_NFCT); // Detail error
    LOG_ERR("Error: reason %d, status 0x%X, State: %d", p_event->params.error.reason, err_status, state);
    // Full hardware reset and re-initialization to clear persistent errors
    nfc_full_reset_and_rearm("nfct_event_handler (NRFX_NFCT_EVT_ERROR)");
    break;

    default:
    LOG_DBG("Unknown: %d", p_event->evt_id);
    break;
    }
    }

    /**
    * @brief This function gets the NFC field status.
    * @details
    * This function returns true if the field is on.
    *
    * @param none
    * @returns true if the field is on
    */
    bool NFC_is_field_on() {
    return field_on;
    }

    /**
    * @brief This function sets the NFC field status.
    * @details
    * This function returns none.
    *
    * @param status: true if the field is on.
    * @returns none
    */
    void NFC_set_field_status(bool status) {
    field_on = status;
    }


    /**
    * @brief Initializes the custom NFC-A T4T driver.
    *
    * @details This is the only function exposed to the rest of the system.
    */
    int nfc_setup(void) {
    nrfx_nfct_config_t config = {
    .rxtx_int_mask = NRFX_NFCT_EVT_FIELD_DETECTED | NRFX_NFCT_EVT_FIELD_LOST | NRFX_NFCT_EVT_RX_FRAMEEND | NRFX_NFCT_EVT_TX_FRAMEEND | NRFX_NFCT_EVT_ERROR | NRFX_NFCT_EVT_SELECTED,
    .irq_priority = 2,
    .cb = nfct_event_handler,
    };

    nrfx_err_t err = nrfx_nfct_init(&config);
    if (err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    return -1;
    }
    nrfx_nfct_param_t fwt_param = {
    .id = NRFX_NFCT_PARAM_ID_FDT,
    // FWT est un entier de 16 bits (uint16_t)
    // 256 (0x0100) est la valeur la plus courante pour un FWI de 8,
    // représentant le temps d'attente maximum de 4.8 ms.
    .data.fdt = 0x0100 // 256 cycles FWT
    };
    nrfx_nfct_parameter_set(&fwt_param);

    nrfx_nfct_param_t nfcid1_param = {
    .id = NRFX_NFCT_PARAM_ID_NFCID1,
    .data.nfcid1 = {
    .p_id = UID,
    .id_size = sizeof(UID)
    }
    };
    nrfx_nfct_parameter_set(&nfcid1_param);

    nrfx_nfct_param_t sel_res_param = {
    .id = NRFX_NFCT_PARAM_ID_SEL_RES,
    .data.sel_res_protocol = NRF_NFCT_SELRES_PROTOCOL_NFCDEP
    };
    nrfx_nfct_parameter_set(&sel_res_param);

    // Initialization of data pools (assuming these functions are defined elsewhere)
    nfc_pool_init(&buffer_iso_reception,ISO_FRAME_SIZE, ISO_LIST_SIZE);
    nfc_pool_init(&buffer_iso_emission,ISO_FRAME_SIZE, ISO_LIST_SIZE);
    nfc_pool_init(&buffer_smp_emission,SMP_FRAME_SIZE, SMP_LIST_SIZE);


    nrfx_nfct_autocolres_enable();

    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);

    nrfx_nfct_enable();
    nrfx_nfct_state_force(NRFX_NFCT_STATE_SENSING);

    current_protocol_state = STATE_IDLE;
    LOG_INF("SMP NFC initialized with Auto Collision Resolution\n");
    return 0;
    }

    /**
    * @brief Deinitializes the custom NFC-A T4T driver and shuts down the NFCT hardware.
    * @details Disables the NFCT peripheral, stops sensing/transmission, and uninitializes the driver,
    * releasing associated resources.
    * @return int Returns 0 on success.
    */
    int nfc_teardown(void) {
    LOG_INF("NFC Teardown initiated.");
    // Cancel any pending activation work
    k_work_cancel_delayable(&nfc_activation_work);
    nrfx_nfct_disable();
    nrfx_nfct_uninit();

    current_protocol_state = STATE_DEINIT;
    t4t_deinit();
    LOG_INF("SMP NFC hardware and driver uninitialized.");
    return 0;
    }
Reply
  • Hello,

    Please find below my sample code.
    The size is retrieved from the event structure, and it matches the value of the RXD.AMOUNT NFCT register when the NRFX_NFCT_EVT_RX_FRAMEEND event occurs.

    Best regards,

    A.

    #define USE_WORKAROUND_FOR_ANOMALY_190
    #include <nrfx.h>
    #include <nrfx_nfct.h>
    #include <hal/nrf_nfct.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include <stddef.h>
    #include "t4t_handler.h" // Interface towards the T=1/APDU layer
    #include <nfc_pool_frame_generic.h>
    #include <nfc_transport.h>

    #include <nfc/tnep/poller.h>
    #include "system_event.h"
    #include <zephyr/logging/log.h>
    #include <zephyr/kernel.h> // Required for k_work_delayable
    #include <zephyr/cache.h>
    LOG_MODULE_REGISTER(low_level_nfc, LOG_LEVEL_DBG);

    // ====================================================================
    // --- 1. LOW LEVEL PROTOCOL CONSTANTS (ISO 14443-3/4) ---
    // ====================================================================

    #define NFC_CMD_RATS 0xE0 ///< ISO 14443-4 command: Request for Answer To Select.

    #define NFC_PCB_BLOCK_MASK 0xC0 ///< Mask to identify the block type (I, S, R) in the PCB field.
    #define NFC_PCB_I_BLOCK_MASK 0x00 ///< Mask value for I-blocks (Information).
    #define NFC_PCB_S_BLOCK_MASK 0xC0 ///< Mask value for S-blocks (Supervisory).

    #define MAX_FRAME_SIZE 256 ///< Maximum size of ISO-DEP frames (based on FSD/FSC of layer 3).
    #define NFC_FRAME_DELAY_MODE_DEFAULT NRF_NFCT_FRAME_DELAY_MODE_WINDOWGRID ///< Default frame delay mode (Free Run) for transmission.
    #define NFC_RX_STATUS_OK 0 ///< Reception status indicating success without error.
    #define NFC_ACTIVATION_DELAY_MS 10 ///< Delay before activation for field stabilisation
    #define NFC_ALL_EVENTS \
    (NRF_NFCT_EVENT_READY | \
    NRF_NFCT_EVENT_FIELDDETECTED | \
    NRF_NFCT_EVENT_FIELDLOST | \
    NRF_NFCT_EVENT_TXFRAMESTART | \
    NRF_NFCT_EVENT_TXFRAMEEND | \
    NRF_NFCT_EVENT_RXFRAMESTART | \
    NRF_NFCT_EVENT_RXFRAMEEND | \
    NRF_NFCT_EVENT_ERROR | \
    NRF_NFCT_EVENT_RXERROR | \
    NRF_NFCT_EVENT_ENDRX | \
    NRF_NFCT_EVENT_ENDTX | \
    NRF_NFCT_EVENT_AUTOCOLRESSTARTED | \
    NRF_NFCT_EVENT_COLLISION | \
    NRF_NFCT_EVENT_SELECTED)

    /**
    * @brief NFCTT macro sel event types, passed to the upper-layer callback function
    * provided during the initialization.
    */
    typedef enum
    {
    NFCT_STATE_DISABLED,
    NFCT_STATE_RAMPUP2RAMPUP=2,
    NFCT_STATE_IDLE3IDLE,
    NFCT_STATE_RECEIVE4RECEIVE,
    NFCT_STATE_FRAMEDELAY5FRAMEDELAY,
    NFCT_STATE_TRANSMIT6TRANSMIT
    } nrfx_nfct_tag_state_id_t;

    /**
    * @brief Macro to calculate the TB1 byte of the ATS (Answer To Select).
    * @param FWI_VAL FWI (Frame Waiting Integer) value.
    * @param SFGI_VAL SFGI (Start-up Frame Guard Integer) value.
    */
    #define CALCULATE_TB1(FWI_VAL, SFGI_VAL) \
    ((((uint8_t)(FWI_VAL) & 0x0F) << 4) | ((uint8_t)(SFGI_VAL) & 0x0F))

    #ifndef T1_FWI
    #define T1_FWI 0 ///< Default Frame Waiting Integer (FWI) for ATS response (if not defined).
    #endif
    #ifndef T1_SFGI
    #define T1_SFGI 0 ///< Default Start-up Frame Guard Integer (SFGI) (if not defined).
    #endif

    // ====================================================================
    // --- 2. STRUCTURES AND STATE CONTEXTS (Static) ---
    // ====================================================================

    /** @brief Enumeration of low-level NFC protocol states. */
    typedef enum {
    STATE_IDLE, ///< Waiting for field detection or selection.
    STATE_SELECTED,///< The tag is selected (after RATS).
    STATE_DEINIT ///< NFC peripheral uninitialized.
    } nfc_protocol_state_t;

    /** @brief Current NFC protocol state. */
    static nfc_protocol_state_t current_protocol_state = STATE_IDLE;
    /** @brief Unique tag identifier (NFCID1) - 4 bytes. */
    static const uint8_t UID[4] = { 0x12, 0x34, 0x56, 0x74 };

    // --- Buffers and Descriptors (EasyDMA accessible)
    // Place buffers in a non-cacheable, 4-byte aligned section for EasyDMA
    /** @brief Receive buffer for NFC data (EasyDMA access). */
    __attribute__((section(".non_cacheable"))) __aligned(4) static uint8_t nfc_buffer[MAX_FRAME_SIZE];

    /** @brief Receive data descriptor for the nrfx_nfct driver. */
    static nrfx_nfct_data_desc_t rx_data_desc = {
    .p_data = nfc_buffer,
    .data_size = MAX_FRAME_SIZE
    };

    /** @brief Mutex to ensure exclusive access to the NFCT/NFC peripheral. */
    K_MUTEX_DEFINE(nfct_access_mutex);
    /** @brief RF field status indicator (true if the field is present). */
    static bool field_on;

    /** @brief Handler for the deferred work item to force NFCT into the ACTIVATED state. */
    static void nfc_activate_work_handler(struct k_work *work);

    /** @brief Work queue item for deferred NFCT activation (to satisfy Anomaly 190 delay). */
    K_WORK_DELAYABLE_DEFINE(nfc_activation_work, nfc_activate_work_handler);


    // ====================================================================
    // --- 3. STATIC PROTOTYPES ---
    // ====================================================================
    /**
    * @brief Performs a full hardware reset, state cleanup, and re-arms RX.
    *
    * @details This is the necessary workaround for persistent NFCT errors
    * like Anomaly 190 (status 0x2) and DMA corruption (RX errors).
    *
    * @param[in] context String indicating where the reset was triggered for logging.
    */
    static void nfc_full_reset_and_rearm(const char* context);

    /**
    * @brief Prepares the TX buffer and initiates NFC transmission.
    *
    * @details Copies the data to the EasyDMA-compatible buffer and calls
    * the nrfx_nfct driver's send function. In case of transmission failure,
    * re-arms the RX listener.
    *
    * @param[in] data Pointer to the data to send.
    * @param[in] len Length of the data.
    */
    static void prepare_and_send(const uint8_t *data, size_t len);

    /**
    * @brief Main event handler for the NFCT peripheral.
    *
    * @details Processes low-level events such as field detection,
    * end of reception/transmission, selection by the reader, and errors.
    *
    * @param[in] p_event Pointer to the nfct event structure.
    */
    static void nfct_event_handler(nrfx_nfct_evt_t const *p_event);

    /**
    * @brief Handles the reception of a RATS command (Request for Answer To Select).
    *
    * @details Sends the ATS response (Answer To Select) to the reader.
    */
    static void handle_rats(void);

    /**
    * @brief Handles ISO 14443-4 data frames (APDU/T=1).
    *
    * @details Extracts the PCB and information data, passes them to the
    * T=1 protocol handler (`t1_process_frame`) and sends the T=1/APDU response.
    *
    * @param[in] data Pointeur vers le début de la trame reçue (incluant le PCB).
    * @param[in] len Longueur totale de la trame reçue.
    */
    static void handle_iso14443_4_data(const uint8_t *data, size_t len);

    /**
    * @brief Handles the end of reception of a low-level NFC-A frame.
    *
    * @details Checks the status and length of the received frame. If the frame is valid,
    * it determines whether it is a RATS command or an ISO 14443-4 frame and
    * calls the appropriate handling function.
    *
    * @param[in] p_event Pointer to the NRFX_NFCT_EVT_RX_FRAMEEND event structure.
    */
    static void handle_iso14443a_frame(nrfx_nfct_evt_t const *p_event);

    // ====================================================================
    // --- 4. NFCT FUNCTION IMPLEMENTATION (Low Level) ---
    // ====================================================================

    /** @brief ATS response (Answer To Select) sent after a RATS. */
    static const uint8_t ATS_RESPONSE[] = {
    0x04, // TL = number of subsequent bytes
    0x70, // T0
    0x80, // TA(1)
    CALCULATE_TB1(T1_FWI, T1_SFGI), // TB(1)
    0x02 // TC(1): CID supported
    };

    /**
    * @brief Asynchronously prepares and transmits an S-Block WTX-REQUEST.
    *
    * @details This function is intended to be called from the application thread
    * to request an extension of the PCD's Frame Waiting Time (FWT).
    * It acquires a mutex to ensure exclusive access to the NFC driver
    * before initiating transmission.
    *
    * @return int 0 on success, or an error code if transmission failed
    * or the mutex could not be acquired.
    */
    int t1_send_wtx_request(void) {
    uint8_t tx_frame_buffer[4];
    size_t tx_len = 0;
    int ret = 0;

    if (k_mutex_lock(&nfct_access_mutex, K_FOREVER) != 0) {
    return -1;
    }

    tx_frame_buffer[tx_len++] = NFC_PCB_S_BLOCK_MASK | T1_PCB_WTX_REQ;
    tx_frame_buffer[tx_len++] = t1_get_wti();

    nrfx_nfct_data_desc_t tx_data_desc = {
    .p_data = tx_frame_buffer,
    .data_size = tx_len
    };

    nrfx_err_t err = nrfx_nfct_tx(&tx_data_desc, NFC_FRAME_DELAY_MODE_DEFAULT);
    if (err != NRFX_SUCCESS) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    }

    k_mutex_unlock(&nfct_access_mutex);
    return ret;
    }

    /**
    * @brief Prepares the TX buffer and initiates NFC transmission.
    *
    * @details Copies the data to the EasyDMA-compatible buffer and calls
    * the nrfx_nfct driver's send function. In case of transmission failure,
    * re-arms the RX listener.
    *
    * @param[in] data Pointer to the data to send.
    * @param[in] len Length of the data.
    */
    static void prepare_and_send(const uint8_t *data, size_t len) {
    if (len == 0 || len > MAX_FRAME_SIZE) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    return;
    }

    memcpy(nfc_buffer, data, len);

    nrfx_nfct_data_desc_t tx_data_desc = {
    .p_data = nfc_buffer,
    .data_size = len
    };

    nrfx_err_t err = nrfx_nfct_tx(&tx_data_desc, NFC_FRAME_DELAY_MODE_DEFAULT);

    if (err != NRFX_SUCCESS) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }
    LOG_HEXDUMP_INF(nfc_buffer, len, "NFC TX--> ");
    }

    /**
    * @brief Handles the reception of a RATS command (Request for Answer To Select).
    *
    * @details Sends the ATS response (Answer To Select) to the reader.
    */
    static void handle_rats(void) {
    prepare_and_send(ATS_RESPONSE, sizeof(ATS_RESPONSE));
    }

    /**
    * @brief Handles ISO 14443-4 data frames (APDU/T=1).
    *
    * @details Extracts the PCB and information data, passes them to the
    * T=1 protocol handler (`t1_process_frame`) and sends the T=1/APDU response.
    *
    * @param[in] data Pointer to the start of the received frame (including the PCB).
    * @param[in] len Total length of the received frame.
    */
    static void handle_iso14443_4_data(const uint8_t *data, size_t len) {
    nrfx_err_t err;
    if (len == 0) {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    return;
    }

    uint8_t pcb = data[0];
    const uint8_t *inf_data = &data[1];
    size_t inf_len = (len > 1) ? (len - 1) : 0;

    uint8_t out_pcb;
    uint8_t out_inf_buffer[MAX_FRAME_SIZE];
    size_t out_inf_len = 0;

    if (t1_process_frame(pcb, inf_data, inf_len, out_inf_buffer, &out_inf_len, &out_pcb)) {
    nfc_buffer[0] = out_pcb;
    memcpy(&nfc_buffer[1], out_inf_buffer, out_inf_len);
    prepare_and_send(nfc_buffer, out_inf_len + 1);
    } else {
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }
    }

    /**
    * @brief Performs a full hardware reset, state cleanup, and re-arms RX.
    *
    * @details This is the necessary workaround for persistent NFCT errors
    * like Anomaly 190 (status 0x2) and DMA corruption (RX errors).
    *
    * @param[in] context String indicating where the reset was triggered for logging.
    */
    static void nfc_full_reset_and_rearm(const char* context) {
    nrfx_err_t err;
    LOG_ERR("Applying full reset from %s context.", context);
    nrf_nfct_task_trigger(NRF_NFCT, NRF_NFCT_TASK_GOIDLE);
    // 2. Désactiver le périphérique (met l'alimentation en mode faible)
    nrf_nfct_task_trigger(NRF_NFCT, NRF_NFCT_TASK_DISABLE);
    // Clear error flags
    uint32_t err_status = nrf_nfct_error_status_get(NRF_NFCT);
    nrf_nfct_error_status_clear(NRF_NFCT, err_status);
    // Vider TOUS les événements pour un état propre
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    // Step 1: Disable Auto-Collision Resolution before reset
    nrfx_nfct_autocolres_disable();
    // // Step 2: Full hardware reset (EasyDMA Workaround)
    nrfx_nfct_disable();
    nrfx_nfct_enable();
    // // Step 3: Re-enable Auto-Collision Resolution
    nrfx_nfct_autocolres_enable();
    // Step 4: Force SENSING state
    nrfx_nfct_state_force(NRFX_NFCT_STATE_SENSING);

    // Step 5: Reset protocol state and field flag
    field_on = false;
    current_protocol_state = STATE_IDLE;
    k_work_cancel_delayable(&nfc_activation_work);

    // Step 6: Re-arm RX
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }

    /**
    * @brief Handles the end of reception of a low-level NFC-A frame.
    *
    * @details Checks the status and length of the received frame. If the frame is valid,
    * it determines whether it is a RATS command or an ISO 14443-4 frame and
    * calls the appropriate handling function.
    *
    * @param[in] p_event Pointeur vers la structure d'événement NRFX_NFCT_EVT_RX_FRAMEEND.
    */
    static void handle_iso14443a_frame(nrfx_nfct_evt_t const *p_event) {
    nrfx_err_t err;
    uint32_t status = p_event->params.rx_frameend.rx_status;
    uint32_t rx_len = p_event->params.rx_frameend.rx_data.data_size;
    const uint8_t *p_buffer = p_event->params.rx_frameend.rx_data.p_data;

    // Check for RX errors and abnormally large frames (indicating DMA corruption)
    if (status != NFC_RX_STATUS_OK || rx_len == 0 || rx_len > MAX_FRAME_SIZE) {
    LOG_ERR("RX Error: status=0x%X, len=%zu (max=%d). Possible DMA corruption.", status, rx_len, MAX_FRAME_SIZE);
    // Call full reset to clear potential DMA state corruption
    nfc_full_reset_and_rearm("handle_iso14443a_frame (DMA Corruption)");
    return;
    }

    const uint8_t command = p_buffer[0];
    LOG_HEXDUMP_INF(p_buffer, rx_len, "NFC RX--> ");

    if (current_protocol_state == STATE_SELECTED) {
    if (command == NFC_CMD_RATS) {
    LOG_INF("RATS received (len=%u)", rx_len);
    handle_rats();
    // Do not re-arm here: TX_FRAMEEND will re-arm
    } else if ((command & NFC_PCB_BLOCK_MASK) == NFC_PCB_I_BLOCK_MASK ||
    (command & NFC_PCB_BLOCK_MASK) == NFC_PCB_S_BLOCK_MASK) {
    handle_iso14443_4_data(p_buffer, rx_len);
    // If handle_iso14443_4_data generates a TX, TX_FRAMEEND will re-arm;
    // otherwise it re-arms itself.
    } else {
    LOG_WRN("Unexpected RX cmd: 0x%02X", command);
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }
    }
    } else {
    LOG_WRN("RX in non-SELECTED state: %d", current_protocol_state);
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);
    }
    }

    /**
    * @brief Handler for the deferred work item to force NFCT into the ACTIVATED state.
    *
    * @details This function is executed after a short delay (e.g., 15ms) to respect
    * Anomaly 190.
    */
    static void nfc_activate_work_handler(struct k_work *work) {
    if (field_on) {
    LOG_INF("Deferred activation triggered by work queue (forcing ACTIVATED).");
    nrfx_nfct_state_force(NRFX_NFCT_STATE_ACTIVATED);
    } else {
    LOG_DBG("Deferred activation cancelled (Field lost before activation).");
    }
    }

    /**
    * @brief Main event handler for the NFCT peripheral.
    *
    * @details Processes low-level events such as field detection,
    * end of reception/transmission, selection by the reader, and errors.
    *
    * @param[in] p_event Pointer to the nfct event structure.
    */
    static void nfct_event_handler(nrfx_nfct_evt_t const *p_event) {
    nrfx_err_t err;
    nrf_nfct_tag_state_t state = nrf_nfct_tag_state_get(NRF_NFCT);
    LOG_DBG("Event: %d - HW State: %d - Protocol: %d", p_event->evt_id, state, current_protocol_state);

    switch (p_event->evt_id) {
    case NRFX_NFCT_EVT_FIELD_DETECTED:
    LOG_DBG("Field detected");
    field_on = true;
    // Increased delay to K_MSEC(NFC_ACTIVATION_DELAY_MS) for maximum robustness against Anomaly 190
    // and persistent status 0x2 errors, based on the failure of NFC_ACTIVATION_DELAY_MS ms.
    k_work_schedule(&nfc_activation_work, K_MSEC(NFC_ACTIVATION_DELAY_MS));
    //nrfx_nfct_state_force(NRFX_NFCT_STATE_ACTIVATED);
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_FIELDLOST);
    break;

    case NRFX_NFCT_EVT_FIELD_LOST:
    LOG_DBG("Field lost");
    // Cancel any pending activation work
    k_work_cancel_delayable(&nfc_activation_work);
    nrfx_nfct_state_force(NRFX_NFCT_STATE_SENSING);
    current_protocol_state = STATE_IDLE;
    field_on = false;
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_FIELDDETECTED);
    break;

    case NRFX_NFCT_EVT_SELECTED:
    LOG_DBG("Selected");
    nrf_nfct_error_status_clear(NRF_NFCT, nrf_nfct_error_status_get(NRF_NFCT));
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    if (field_on) {
    current_protocol_state = STATE_SELECTED;
    }
    // Always arm RX here
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    nrf_nfct_error_status_clear(NRF_NFCT, nrf_nfct_error_status_get(NRF_NFCT));
    // Vider TOUS les événements pour un état propre
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    }
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_SELECTED);
    break;

    case NRFX_NFCT_EVT_RX_FRAMEEND:
    LOG_DBG("RX end (status: %d, size: %d)", p_event->params.rx_frameend.rx_status, p_event->params.rx_frameend.rx_data.data_size);
    // No strict state check: handle if OK
    if (p_event->params.rx_frameend.rx_status == NFC_RX_STATUS_OK) {
    handle_iso14443a_frame(p_event);
    } else {
    // If status != OK, handle is inside handle_iso14443a_frame, but for robustness,
    // we call a reset here if handle_iso14443a_frame did not catch a DMA error.
    // In this case, we rely on the full reset from NRFX_NFCT_EVT_ERROR if the error
    // is severe enough to set the error flag. For typical ISO14443 errors (CRC, etc.),
    // re-arming RX is enough if not handled in handle_iso14443a_frame, but full reset
    // is more resilient. We rely on the reset in the error case.
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrf_nfct_event_clear(NRF_NFCT, NRF_NFCT_EVENT_ENDRX);
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    uint32_t err_status = nrf_nfct_error_status_get(NRF_NFCT);
    nrf_nfct_error_status_clear(NRF_NFCT, err_status);
    // Vider TOUS les événements pour un état propre
    nrf_nfct_event_clear(NRF_NFCT, NFC_ALL_EVENTS);
    }
    }
    break;

    case NRFX_NFCT_EVT_TX_FRAMEEND:
    LOG_DBG("TX end");
    // Re-arm RX
    rx_data_desc.data_size = MAX_FRAME_SIZE;
    err =nrfx_nfct_rx(&rx_data_desc);
    if ( err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    }

    break;

    case NRFX_NFCT_EVT_ERROR:
    uint32_t err_status = nrf_nfct_error_status_get(NRF_NFCT); // Detail error
    LOG_ERR("Error: reason %d, status 0x%X, State: %d", p_event->params.error.reason, err_status, state);
    // Full hardware reset and re-initialization to clear persistent errors
    nfc_full_reset_and_rearm("nfct_event_handler (NRFX_NFCT_EVT_ERROR)");
    break;

    default:
    LOG_DBG("Unknown: %d", p_event->evt_id);
    break;
    }
    }

    /**
    * @brief This function gets the NFC field status.
    * @details
    * This function returns true if the field is on.
    *
    * @param none
    * @returns true if the field is on
    */
    bool NFC_is_field_on() {
    return field_on;
    }

    /**
    * @brief This function sets the NFC field status.
    * @details
    * This function returns none.
    *
    * @param status: true if the field is on.
    * @returns none
    */
    void NFC_set_field_status(bool status) {
    field_on = status;
    }


    /**
    * @brief Initializes the custom NFC-A T4T driver.
    *
    * @details This is the only function exposed to the rest of the system.
    */
    int nfc_setup(void) {
    nrfx_nfct_config_t config = {
    .rxtx_int_mask = NRFX_NFCT_EVT_FIELD_DETECTED | NRFX_NFCT_EVT_FIELD_LOST | NRFX_NFCT_EVT_RX_FRAMEEND | NRFX_NFCT_EVT_TX_FRAMEEND | NRFX_NFCT_EVT_ERROR | NRFX_NFCT_EVT_SELECTED,
    .irq_priority = 2,
    .cb = nfct_event_handler,
    };

    nrfx_err_t err = nrfx_nfct_init(&config);
    if (err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_nfct_init failed: 0x%X", err);
    return -1;
    }
    nrfx_nfct_param_t fwt_param = {
    .id = NRFX_NFCT_PARAM_ID_FDT,
    // FWT est un entier de 16 bits (uint16_t)
    // 256 (0x0100) est la valeur la plus courante pour un FWI de 8,
    // représentant le temps d'attente maximum de 4.8 ms.
    .data.fdt = 0x0100 // 256 cycles FWT
    };
    nrfx_nfct_parameter_set(&fwt_param);

    nrfx_nfct_param_t nfcid1_param = {
    .id = NRFX_NFCT_PARAM_ID_NFCID1,
    .data.nfcid1 = {
    .p_id = UID,
    .id_size = sizeof(UID)
    }
    };
    nrfx_nfct_parameter_set(&nfcid1_param);

    nrfx_nfct_param_t sel_res_param = {
    .id = NRFX_NFCT_PARAM_ID_SEL_RES,
    .data.sel_res_protocol = NRF_NFCT_SELRES_PROTOCOL_NFCDEP
    };
    nrfx_nfct_parameter_set(&sel_res_param);

    // Initialization of data pools (assuming these functions are defined elsewhere)
    nfc_pool_init(&buffer_iso_reception,ISO_FRAME_SIZE, ISO_LIST_SIZE);
    nfc_pool_init(&buffer_iso_emission,ISO_FRAME_SIZE, ISO_LIST_SIZE);
    nfc_pool_init(&buffer_smp_emission,SMP_FRAME_SIZE, SMP_LIST_SIZE);


    nrfx_nfct_autocolres_enable();

    rx_data_desc.data_size = MAX_FRAME_SIZE;
    nrfx_nfct_rx(&rx_data_desc);

    nrfx_nfct_enable();
    nrfx_nfct_state_force(NRFX_NFCT_STATE_SENSING);

    current_protocol_state = STATE_IDLE;
    LOG_INF("SMP NFC initialized with Auto Collision Resolution\n");
    return 0;
    }

    /**
    * @brief Deinitializes the custom NFC-A T4T driver and shuts down the NFCT hardware.
    * @details Disables the NFCT peripheral, stops sensing/transmission, and uninitializes the driver,
    * releasing associated resources.
    * @return int Returns 0 on success.
    */
    int nfc_teardown(void) {
    LOG_INF("NFC Teardown initiated.");
    // Cancel any pending activation work
    k_work_cancel_delayable(&nfc_activation_work);
    nrfx_nfct_disable();
    nrfx_nfct_uninit();

    current_protocol_state = STATE_DEINIT;
    t4t_deinit();
    LOG_INF("SMP NFC hardware and driver uninitialized.");
    return 0;
    }
Children
  • Hi,

    Where in this code is it you see the wrong size? Is it in you handle_iso14443a_frame() where you read p_event->params.rx_frameend.rx_data.data_size? Can you specify exactly? Also, do you have logs from when you run this (I see you have logging) and I also see you use the nrfx  nfct driver which also has loggging, so I suggest you enabel debug logging there.

    I am confused about the number though. You still see 8190 bits(?), and also see this in RXD.AMOUNT if you check with a debugger? Is my understand correct? However, so high values should not be possible there. Can you let me know which raw value you read form a debugger as well as what you see in code when you debug?

  • Hello,

    I will provide you with a trace. In short, after anti-collision, I receive a frame reception event. The HAL does not report any errors, but the length of the data received by the controller via EasyDMA indicates 8190 bits both via the HAL layer and when reading the RXD.AMOUNT register directly via a debugger (Ozone). The frame is not transferred to the receive buffer.
    BR/

    A.

  • eI see. I do believe you, but I do not see how you can get 8190 in the RXD.AMOUNT. Can you share the exact raw value you read form RXD.AMOUNT a debugger in form of a screenshot or something similar? The reason is that this consists of two parts, adding up bytes and bits, and I do not see how you get get this value.

  • Hi,

    I got an explanation about this meaningless value (8190). After the ISO 14443 selection, the PCD is supposed to send RATS, but we received 7 bits. In the HAL handler, there is the computation of the received length using the following function:

    NRF_STATIC_INLINE uint16_t nrf_nfct_rx_bits_get(NRF_NFCT_Type const * p_reg, bool crc_excluded) { uint16_t rx_bits = p_reg->RXD.AMOUNT & (NFCT_RXD_AMOUNT_RXDATABITS_Msk | NFCT_RXD_AMOUNT_RXDATABYTES_Msk); return (uint16_t)(rx_bits - (crc_excluded ? (8u * NRF_NFCT_CRC_SIZE) : 0)); }

    The RATS is supposed to have a CRC, so the content of the NFCT register should be 0x18 (3 bytes = 24 bits).

    Nevertheless, the HAL code is not protected: if the bit count is 7, then the number of bytes is 0. 0 - 2 (CRC size) = -2, which becomes 0x1FFE (8190) due to uint16_t underflow.

    The problem is still open, why I cannot receive the RATS?

    Best regards, Alain



Related