T4T tag callback blocks

Hello,

I am trying to develop an application where I can turn on/off the NFC T4T tag emulation using interrupt-driven buttons.

However, after the first read of the NFC tag, only NFC callback events can be run, while blocking any other code to run(Including button interrupts, and logging).

I couldn't solve the issue by myself, and I would appreciate it if you can help.

I attached my code below.

/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>

#include <nfc_t4t_lib.h>
#include <nfc/t4t/ndef_file.h>
#include <nfc/ndef/msg.h>
#include <nfc/ndef/text_rec.h>

#include <dk_buttons_and_leds.h>

#define MAX_REC_COUNT		1
#define NDEF_MSG_BUF_SIZE	128

#define NFC_FIELD_LED		DK_LED1
#define NFC_SW_BUTTON 		DK_BTN1_MSK

int nfc_state;

/* Text message in English with its language code. */
static const uint8_t en_payload[] = {
	'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'
};
static const uint8_t en_code[] = {'e', 'n'};

/* Buffer used to hold an NFC NDEF message. */
static uint8_t ndef_msg_buf[NDEF_MSG_BUF_SIZE];


static void nfc_callback(void *context,
			 nfc_t4t_event_t event,
			 const uint8_t *data,
			 size_t data_length)
{
	ARG_UNUSED(context);
	ARG_UNUSED(data);
	ARG_UNUSED(data_length);

	switch (event) {
	case NFC_T4T_EVENT_FIELD_ON:
		dk_set_led_on(NFC_FIELD_LED);
		break;
	case NFC_T4T_EVENT_FIELD_OFF:
		dk_set_led_off(NFC_FIELD_LED);
		break;
	default:
		break;
	}
}


/**
 * @brief Function for encoding the NDEF text message.
 */
static int welcome_msg_encode(uint8_t *buffer, uint32_t *len)
{
	int err;

	/* Create NFC NDEF text record description in English */
	NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_en_text_rec,
				      UTF_8,
				      en_code,
				      sizeof(en_code),
				      en_payload,
				      sizeof(en_payload));

	/* Create NFC NDEF message description, capacity - MAX_REC_COUNT
	 * records
	 */
	NFC_NDEF_MSG_DEF(nfc_text_msg, MAX_REC_COUNT);

	/* Add text records to NDEF text message */
	err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
				   &NFC_NDEF_TEXT_RECORD_DESC(nfc_en_text_rec));
	if (err < 0) {
		printk("Cannot add first record!\n");
		return err;
	}
	
	err = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_text_msg),
				      buffer,
				      len);
	if (err < 0) {
		printk("Cannot encode message!\n");
	}

	return err;
}

static void button_changed(uint32_t button_state, uint32_t has_changed)
{
	int err;
	if (has_changed & NFC_SW_BUTTON) {
		uint32_t user_button_state = button_state & NFC_SW_BUTTON;

		if (user_button_state == 1) {
			if (nfc_state == 1) {
				err = nfc_t4t_emulation_stop();
				if (err) {
					printk("Cannot stop emulation\n");
				} else {
					printk("Emulation stopped\n");
					nfc_state = 0;
				}
			} else {
				err = nfc_t4t_emulation_start();
				if (err) {
					printk("Cannot start emulation\n");
				} else {
					printk("Emulation started\n");
					nfc_state = 1;
				}
			}
		}
	}
}

int main(void)
{
	uint32_t len = nfc_t4t_ndef_file_msg_size_get(sizeof(ndef_msg_buf));

	printk("Starting NFC Text Record example\n");

	/* Configure LED-pins as outputs */
	if (dk_leds_init() < 0) {
		printk("Cannot init LEDs!\n");
		goto fail;
	}

	if (dk_buttons_init(button_changed) < 0) {
		printk("Cannot init buttons!\n");
		goto fail;
	}


	/* Set up NFC */
	if (nfc_t4t_setup(nfc_callback, NULL) < 0) {
		printk("Cannot setup NFC T2T library!\n");
		goto fail;
	}


	/* Encode welcome message */
	if (welcome_msg_encode(nfc_t4t_ndef_file_msg_get(ndef_msg_buf), &len) < 0) {
		printk("Cannot encode message!\n");
		goto fail;
	}

	nfc_t4t_ndef_file_encode(ndef_msg_buf, &len);

	/* Set created message as the NFC payload */
	if (nfc_t4t_ndef_rwpayload_set(ndef_msg_buf, sizeof(ndef_msg_buf)) < 0) {
		printk("Cannot set payload!\n");
		goto fail;
	}


	/* Start sensing NFC field */
	if (nfc_t4t_emulation_start() < 0) {
		printk("Cannot start emulation!\n");
		goto fail;
	}
	nfc_state = 1;

	printk("NFC configuration done\n");

	return 0;

fail:
#if CONFIG_REBOOT
	sys_reboot(SYS_REBOOT_COLD);
#endif /* CONFIG_REBOOT */

	return -EIO;
}

Parents
  • Hello,

    What version of NCS are you using? Is it possible to zip your entire application folder, including all the .conf files and upload them here, so that I will have the same setup that you are testing on?

    Best regards,

    Edvin

  • Hi Edvin, 

    Thanks for reaching out. I use NCS latest version 2.3.0. I attached the zip file below.

    Also, while trying to debug, I noticed that after tag is read and right before blocking, callback receives "Field detected" event although the polling device (phone) is taken away.

    writable_ndef_msg.zip 

  • Hello again, 

    I have an update on the issue.

    I have acquired more nRF52 DKs to test, and on new hardware the issue appears much more rare than on initial ones. Also, on new hardware the program started occasionally giving ASSERTION FAIL @ WEST_TOPDIR/zephyr/include/zephyr/spinlock.h:148 when I read the tag. 

    So, I guess the problem occurs when spinlock is already locked by one thread and the interrupt tries to lock it again(thus entering deadlock state). But I don't get why one some hardware it invokes Assertion Fail, while on other hardware silently entering deadlock condition.

    After experimenting a little, I found that program consistently enters the same ASSERTION FAIL on unmodified "writable_ndef_msg" sample when reading the tag multiple times. Maybe you could try running it to see if the issue is reproducible there.

    Let me know if that could be the origin of the problem.

    Best regards,

    Zhama

  • Hi Edvin,

    After reading this ticket, I have found that the CONFIG_NFC_ZERO_LATENCY_IRQ was causing the problem. If I understand correctly that config was enabling fast NFC interrupt execution. However, it was causing above-mentioned assertion fail(or deadlock) when the tag is read/written by Samsung/Android devices. For iOS it works fine.

    So the workaround for Samsung/Android devices can be to disable CONFIG_NFC_ZERO_LATENCY_IRQ or CONFIG_NFC_THREAD_CALLBACK Kconfig parameters.

    But, I am curious why the application behaves differently for different polling devices. Could that be because of driver incompatibility between nrf52832 and Samsung NFC?

    Best regards,

    Zhama

  • Hello,

    I am sorry, again, for the late reply. Glad to hear that you found a workaround. 

    I am not sure exactly what the difference is in the two polling devices, but I guess they are timing related. When I tested with a Samsung and an iPhone, I saw from time to time that the log from the Samsung phone wa cropped (missing linefeeds, and sometimes only half a line before the next one began). This never occured on the iPhone. This suggests that there are timing differences between the polling events in the two devices. I guess these same timing differences are the cause of the issue with the sample as well. The phone is not doing something wrong/illegal, but the nRF's NFC is missing an event (field lost), and then, when it is stuck in this state, it doesn't see any other events. I didn't get to digging into how this is implemented, but I guess you could find something if you check where CONFIG_NFC_ZERO_LATENCY_IRW is being used. Perhaps it disables the button GPIOs IRQ to focus on the NFC being detected, or perhaps the interrupt priority of the NFC event which it doesn't return from is higher than the button interrupts. I guess one of the two. 

    Best regards,

    Edvin

  • Hi, 

    Thank you for the clarification.

    If the issue is the event timings, do you think it is possible to tune NFC T4T parameters to make it work correctly for both Android and iOS polling devices?

    If yes, where can I find exhaustive information about those parameters and their values?

    Best regards,

    Zhama

  • I believe the queue is in platform_internal_thread.c somewhere. I see that depending on whether CONFIG_NFC_SWI_NUMBER is set or not, it will either set up a software interrupt, or call the cb_work handler directly. My guess is that the SWI interrupt are happening too close to one another, so that one event is lost somhow, while using the scheduler, and cb_work, it has SW to handle these kind of errors, such as a timeout or something.

    BR,

    Edvin

Reply
  • I believe the queue is in platform_internal_thread.c somewhere. I see that depending on whether CONFIG_NFC_SWI_NUMBER is set or not, it will either set up a software interrupt, or call the cb_work handler directly. My guess is that the SWI interrupt are happening too close to one another, so that one event is lost somhow, while using the scheduler, and cb_work, it has SW to handle these kind of errors, such as a timeout or something.

    BR,

    Edvin

Children
No Data
Related