NHS3152 Passive Measurement

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

NHS3152 Passive Measurement

688 Views
JamesH_Scion
Contributor II

Hi everyone

I have an NHS3152 on a custom PCB. The PCB is very simple, with an SWD connector for my MCU-Link Pro and some traces to a connector to attach to a screen-printed antenna. For power stability, there are 2x 100nF capacitors in parallel for both pin 3 & pin 7 (4 capacitors total).

I am having an issue regarding the resistive measurements. I have two channels for resistive measurements (channel0 = pins 0,1; channel1 = pins 4,5;). When I perform a resistive measurement when the board is running passively (no power from the debugger), my NFC scanner (Adafruit PN532 shield for Arduino Uno) does not receive any data. However, it does receive the correct data from the measurements when the NHS3152 is receiving power from my debugger.

If I disable the sections of the code relating to taking resistive measurements, I can successfully read the NFC contents, so I suspect the ADC is consuming too much power?

 

Any help on this is greatly appreciated.

 

IDE: MCUXpresso v24.9.25

SDK: release_mra2_12_6_nhs3152

 

Output from scanner on unsuccessful read (running in passive mode):

=================================================
Tag Found and Activated! Preparing to read data.
UID Length: 7 bytes
UID Value: 0x04 0x8E 0xE6 0x00 0xA8 0x00 0x10
----------------------------------------
Reading data from pages 8-12
Failed to read page 8

=================================================
Tag lost or communication failed. Resetting...
=================================================

 

Output from scanner when NHS3152 is powered via debugger:

=================================================
Tag Found and Activated! Preparing to read data.
UID Length: 7 bytes
UID Value: 0x04 0x8E 0xE6 0x00 0xA8 0x00 0x10
----------------------------------------
Reading data from pages 8-12
Page 08: 6E 52 31 3A nR1:
Page 09: 34 2C 52 32 4,R2
Page 10: 3A 2D 31 FE :-1
Page 11: 86 56 4D 3D VM=
Page 12: 84 28 57 0C (W.
Data read successfully. Tag is still present.
----------------------------------------

 

NHS3152 

 

main.c:

 

/*

* James Harold, 2025, Scion

*

* This program measures resistance on two separate analog channels

* and writes the results to an NFC tag for wireless reading.

*

*/

 

// 1. Library Imports

#include "board.h"

#include "ndeft2t/ndeft2t.h"

#include <string.h>

#include "pmu_nss.h"

#include "gpio_nss.h"

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

// 2. Defines

#define CHANNEL_0 0 // Capacitive & resistive channel

#define CHANNEL_1 1 // Resistive channel

#define NUM_CHANNELS 2 // Number of total measurement channels

 

// 3. Global Variables

 

// Buffers for NDEF message creation (for NFC)

static uint8_t g_ndeft2tInstanceBuffer[NDEFT2T_INSTANCE_SIZE] __attribute__((aligned (4)));

static uint8_t g_nfcMessageBuffer[NFC_SHARED_MEM_BYTE_SIZE] __attribute__((aligned (4)));

 

// Array to store the results for each measurement channel

volatile int32_t measuredResistance[NUM_CHANNELS] = {0, 0};

 

// 4. NFC Callbacks (Required by the library, but unused)

void NDEFT2T_FieldStatus_Cb(bool status) { (void)status; }

void NDEFT2T_MsgAvailable_Cb(void) { /* Unused */ }

 

// 5. Function Prototypes

void SetupPowerStabilisation(void);

void SetupResistiveMeasurement(int channel);

int32_t PerformAndCalculateResistance(int channel);

void WriteMeasurementsToNFC(int32_t res1, int32_t res2);

void WriteHelloMessageToNFC(void);

 

// 6. Main Code

int main(void) {

 

// Step 1: Perform minimal board and power setup

Board_Init();

SetupPowerStabilisation(); // Must be performed early for energy harvesting from NFC tag

 

// --- Enable Clocks and Power for peripherals that will be used ---

Chip_Clock_Peripheral_EnableClock(CLOCK_PERIPHERAL_ADCDAC | CLOCK_PERIPHERAL_I2D);

Chip_SysCon_Peripheral_EnablePower(SYSCON_PERIPHERAL_POWER_ADCDAC | SYSCON_PERIPHERAL_POWER_I2D);

 

// A small delay for power and clocks to stabilise

Chip_Clock_System_BusyWait_us(100);

 

// --- Resistance Measurement Initialisation ---

// Initialise the peripherals and configure the analog pins

Chip_ADCDAC_Init(NSS_ADCDAC0);

Chip_I2D_Init(NSS_I2D);

 

// Configure analog pins for Channel 0 (ANA0_0, ANA0_1)

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_ANA0_0, IOCON_FUNC_1);

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_ANA0_1, IOCON_FUNC_1);

 

// Configure analog pins for Channel 1 (ANA0_4, ANA0_5)

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_ANA0_4, IOCON_FUNC_1);

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_ANA0_5, IOCON_FUNC_1);

 

// Set the ADC to a narrow voltage range (1.0V)

Chip_ADCDAC_SetInputRangeADC(NSS_ADCDAC0, ADCDAC_INPUTRANGE_NARROW);

 

// Step 2: Initialise NFC communication

Chip_NFC_Init(NSS_NFC);

NDEFT2T_Init();

 

// Step 3: Perform resistive measurements

SetupResistiveMeasurement(CHANNEL_0);

measuredResistance[CHANNEL_0] = PerformAndCalculateResistance(CHANNEL_0);

 

SetupResistiveMeasurement(CHANNEL_1);

measuredResistance[CHANNEL_1] = PerformAndCalculateResistance(CHANNEL_1);

 

// Step 4: Write measurements to the NFC tag

WriteMeasurementsToNFC(measuredResistance[CHANNEL_0], measuredResistance[CHANNEL_1]);

//WriteHelloMessageToNFC();

 

// --- Power down peripherals after use to save energy ---

Chip_SysCon_Peripheral_DisablePower(SYSCON_PERIPHERAL_POWER_ADCDAC | SYSCON_PERIPHERAL_POWER_I2D);

Chip_Clock_Peripheral_DisableClock(CLOCK_PERIPHERAL_ADCDAC | CLOCK_PERIPHERAL_I2D);

 

// Step 5: Wait for NFC peripheral interrupt

while(1) {

__WFI();

}

 

return 0; // Should never be reached

}

 

// 7. Function Declarations

 

/**

* @brief Configures hardware for a resistive measurement on a specific channel.

* @param channel : The measurement channel to set up (CHANNEL_0 or CHANNEL_1)

* @return Nothing

*/

void SetupResistiveMeasurement(int channel) {

ADCDAC_IO_T dac_pin;

I2D_INPUT_T i2d_pin;

 

if (channel == CHANNEL_1) {

dac_pin = ADCDAC_IO_ANA0_4;

i2d_pin = I2D_INPUT_ANA0_5;

}

else {

dac_pin = ADCDAC_IO_ANA0_0;

i2d_pin = I2D_INPUT_ANA0_1;

}

 

Chip_ADCDAC_SetMuxDAC(NSS_ADCDAC0, dac_pin);

Chip_ADCDAC_WriteOutputDAC(NSS_ADCDAC0, 0xFFF); // Changed from 0xFFF to 0x800 to lower DAC output voltage

Chip_I2D_SetMuxInput(NSS_I2D, i2d_pin);

 

if (channel == CHANNEL_0) {

Chip_I2D_Setup(NSS_I2D, I2D_SINGLE_SHOT, I2D_SCALER_GAIN_100_1, I2D_CONVERTER_GAIN_LOW, 10); // Changed I2D conversion period from 100 to 10 (power save)

}

else {

Chip_I2D_Setup(NSS_I2D, I2D_SINGLE_SHOT, I2D_SCALER_GAIN_100_1, I2D_CONVERTER_GAIN_LOW, 10); // Changed I2D conversion period from 100 to 10 (power save)

}

 

// Wait a moment for the voltage and current to stabilise.

Chip_Clock_System_BusyWait_ms(1);

}

 

/**

* @brief Initialises GPIOs to enable the capacitor bank for power stability.

* @return Nothing

* @note This is the most critical initialisation for passive operation.

*/

void SetupPowerStabilisation(void) {

 

Chip_IOCON_Init(NSS_IOCON);

Chip_GPIO_Init(NSS_GPIO);

 

// --- Phase 1: PRECHARGE ---

// Configure pins as INPUTS with internal pull-up resistors enabled.

// This allows the external caps to charge slowly without causing a voltage drop.

 

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_PIO0_3, IOCON_FUNC_0 | IOCON_RMODE_PULLUP); // Configure pin 3 as GPIO and enable pull-up

Chip_GPIO_SetPinDIRInput(NSS_GPIO, 0, 3); // Configure pin 3 as input

 

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_PIO0_7, IOCON_FUNC_0 | IOCON_RMODE_PULLUP); // Configure pin 7 as GPIO and enable pull-up

Chip_GPIO_SetPinDIRInput(NSS_GPIO, 0, 7); // Configure pin 7 as input

 

// Wait for a short time to allow the capacitors to charge.

Chip_Clock_System_BusyWait_ms(60); // Approx. 60ms for 4 time constants (each pin has 200nF attached)

 

// --- Phase 2: ENGAGE ---

// Now that the caps are charged, we can safely connect them to the power rail

// by setting the pins to OUTPUT and driving them HIGH.

 

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_PIO0_3, IOCON_FUNC_0 | IOCON_RMODE_INACT); // Configure pin 3 as GPIO and disable pull-up

Chip_GPIO_SetPinDIROutput(NSS_GPIO, 0, 3); // Configure pin 3 as output

Chip_GPIO_SetPinOutHigh(NSS_GPIO, 0, 3); // Connect pin 3 to VDD rail by setting it high

 

Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_PIO0_7, IOCON_FUNC_0 | IOCON_RMODE_INACT); // Configure pin 7 as GPIO and disable pull-up

Chip_GPIO_SetPinDIROutput(NSS_GPIO, 0, 7); // Configure pin 7 as output

Chip_GPIO_SetPinOutHigh(NSS_GPIO, 0, 7); // Connect pin 7 to VDD rail by setting it high

}

 

/**

* @brief Creates an NDEF message with the two resistance values and writes it to memory.

* @param res1 : The raw resistance value from the first channel.

* @param res2 : The raw resistance value from the second channel.

* @return Nothing

*/

void WriteMeasurementsToNFC(int32_t res1, int32_t res2) {

char payloadText[64];

NDEFT2T_CREATE_RECORD_INFO_T createRecordInfo;

uint8_t locale[] = "en";

 

snprintf(payloadText, sizeof(payloadText), "R1:%ld,R2:%ld", res1, res2);

 

NDEFT2T_CreateMessage(g_ndeft2tInstanceBuffer, g_nfcMessageBuffer,

NFC_SHARED_MEM_BYTE_SIZE, true);

 

createRecordInfo.shortRecord = 1;

createRecordInfo.pString = locale;

 

if (NDEFT2T_CreateTextRecord(g_ndeft2tInstanceBuffer, &createRecordInfo)) {

if (NDEFT2T_WriteRecordPayload(g_ndeft2tInstanceBuffer, (uint8_t*)payloadText,

strlen(payloadText))) {

NDEFT2T_CommitRecord(g_ndeft2tInstanceBuffer);

}

}

 

NDEFT2T_CommitMessage(g_ndeft2tInstanceBuffer);

}

 

/**

* @brief Creates a simple NDEF text message "hello" and writes it to NFC memory.

* @return Nothing

*/

void WriteHelloMessageToNFC(void) {

char payloadText[] = "bananas";

NDEFT2T_CREATE_RECORD_INFO_T createRecordInfo;

uint8_t locale[] = "en";

 

// 1. Start creating a new NDEF message in our buffer.

NDEFT2T_CreateMessage(g_ndeft2tInstanceBuffer, g_nfcMessageBuffer,

NFC_SHARED_MEM_BYTE_SIZE, true /* isFirstMessage */);

 

// 2. Prepare information for a new NDEF Text Record.

createRecordInfo.shortRecord = 1;

createRecordInfo.pString = locale;

 

// 3. Create the Text Record structure within the message.

if (NDEFT2T_CreateTextRecord(g_ndeft2tInstanceBuffer, &createRecordInfo)) {

 

// 4. Write the actual "hello" string into the record's payload.

if (NDEFT2T_WriteRecordPayload(g_ndeft2tInstanceBuffer, (uint8_t*)payloadText,

strlen(payloadText))) {

 

// 5. Finalise this specific record.

NDEFT2T_CommitRecord(g_ndeft2tInstanceBuffer);

}

}

 

// 6. Finalize the entire message, making it available to the NFC hardware.

NDEFT2T_CommitMessage(g_ndeft2tInstanceBuffer);

}

 

/**

* @brief Performs ADC and I2D conversions and calculates the resistance for a specific channel.

* @param channel : The measurement channel to measure (CHANNEL_0 or CHANNEL_1)

* @return A raw, scaled resistance value, or -1 if an error occurs (e.g., open circuit).

* @note Uses low-power __WFE() to wait for conversions, essential for passive mode.

*/

int32_t PerformAndCalculateResistance(int channel) {

int32_t v_drive, v_sense, i2d_val, adc_diff, resistance_result;

ADCDAC_IO_T drive_pin_adc, sense_pin_adc;

 

/*

* NOTE: Peripheral clocks and power are assumed to be enabled in main().

* If this function were called from multiple places, you might enable/disable

* them here instead. For this application, enabling in main() is more efficient.

*/

 

if (channel == CHANNEL_1) {

drive_pin_adc = ADCDAC_IO_ANA0_4;

sense_pin_adc = ADCDAC_IO_ANA0_5;

}

else {

drive_pin_adc = ADCDAC_IO_ANA0_0;

sense_pin_adc = ADCDAC_IO_ANA0_1;

}

 

// --- V_DRIVE ADC Measurement ---

Chip_ADCDAC_SetMuxADC(NSS_ADCDAC0, drive_pin_adc);

Chip_ADCDAC_StartADC(NSS_ADCDAC0);

// NEW, LOW-POWER WAY: Sleep until the ADC conversion is done.

while (!(Chip_ADCDAC_ReadStatus(NSS_ADCDAC0) & ADCDAC_STATUS_ADC_DONE)) {

{ /* wait */ }

}

v_drive = Chip_ADCDAC_GetValueADC(NSS_ADCDAC0);

 

// --- V_SENSE ADC Measurement ---

Chip_ADCDAC_SetMuxADC(NSS_ADCDAC0, sense_pin_adc);

Chip_ADCDAC_StartADC(NSS_ADCDAC0);

// Sleep until the ADC conversion is done.

while (!(Chip_ADCDAC_ReadStatus(NSS_ADCDAC0) & ADCDAC_STATUS_ADC_DONE)) {

{ /* wait */ }

}

v_sense = Chip_ADCDAC_GetValueADC(NSS_ADCDAC0);

 

// --- I2D Measurement ---

Chip_I2D_Start(NSS_I2D);

// Sleep until the I2D conversion is done.

while (!(Chip_I2D_ReadStatus(NSS_I2D) & I2D_STATUS_CONVERSION_DONE)) {

{ /* wait */ } // Wait For Event - wakes up on ADC_DONE event --> Replaced __WFE() with busy waits {}

}

i2d_val = Chip_I2D_GetValue(NSS_I2D);

 

/*

* NOTE: Peripherals are left powered on for the next measurement.

* They will be powered down at the end of main().

*/

 

// --- Calculation ---

adc_diff = v_drive - v_sense;

if (adc_diff < 1) {

adc_diff = 1;

}

 

if (i2d_val > 0) {

resistance_result = ((uint32_t)adc_diff * 10000) / i2d_val;

}

else {

resistance_result = -1; // Indicates open circuit or error

}

 

return resistance_result;

}

 

ntag2xx_read.ino:

 

/**************************************************************************/
/*!
    @file     readntag_continuous_power_and_data.ino
    @author   KTOWN (Adafruit Industries) / Modified for continuous power and data read
    @license  BSD (see license.txt)

    This version keeps a passive NFC tag continuously powered, and on each
    loop, it reads and displays the data from a specific range of pages (8-13).
    It resets when the tag is removed.
*/
/**************************************************************************/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>

// If using the breakout with SPI, define the pins for SPI communication.
#define PN532_SCK  (SCK)
#define PN532_MISO (MISO)
#define PN532_MOSI (MOSI)
#define PN532_SS   (10) // Or whatever SPI CS pin you're using

// If using the breakout or shield with I2C, define the pins for I2C
// and comment out the SPI definitions.
#define PN532_IRQ   (2)
#define PN532_RESET (3)

// Use this line for I2C
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);

// Or use this line for SPI
// Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);


// Global state variables to track if a tag is currently selected
bool tagIsPresent = false;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
uint8_t uidLength;
int pageStart = 8;
int pageEnd = 12;

void setup(void) {
  Serial.begin(115200);
  while (!Serial) delay(10); // For Leonardo/Micro/Zero

  nfc.begin();
  uint32_t versiondata = nfc.getFirmwareVersion();
  if (!versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
  }
  Serial.print("Found chip PN5"); Serial.print((versiondata >> 24) & 0xFF, HEX);
  Serial.print(", Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC);
  Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC);

  // Configure the board to listen for ISO14443A cards
  nfc.SAMConfig();

  Serial.println("Waiting for an ISO14443A Card to power and read...");
}

void loop(void) {
  // === STATE 1: NO TAG IS SELECTED ===
  // If we don't have a tag, try to find one.
  if (!tagIsPresent) {
    Serial.print("."); // Print a dot to show we are waiting

    // Wait for a card to enter the field. This also provides power.
    bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500);

    if (success) {
      tagIsPresent = true;
      Serial.println("\n=================================================");
      Serial.println("Tag Found and Activated! Preparing to read data.");
      Serial.print("  UID Length: "); Serial.print(uidLength, DEC); Serial.println(" bytes");
      Serial.print("  UID Value: ");
      nfc.PrintHex(uid, uidLength);
     
      // OPTIONAL DELAY: If your tag is a sensor that needs time to
      // perform a measurement after power-up, add a delay here.
      // 200ms is a good starting point for many sensor tags.
      delay(200);
    }
    // If it fails or times out, the loop will just restart and try again.
    return;
  }

  // === STATE 2: TAG IS SELECTED AND POWERED ===
  // If we have a tag, attempt to read the data from pages 8 to 13.
  if (tagIsPresent) {
    Serial.println("----------------------------------------");
    Serial.print("Reading data from pages ");
    Serial.print(pageStart);
    Serial.print("-");
    Serial.print(pageEnd);
    Serial.println();
   
    bool all_reads_succeeded = true;
   
    for (int page = pageStart; page <= pageEnd; page++) {
      uint8_t page_data[4]; // NTAG pages are 4 bytes long
     
      // Attempt to read the current page
      if (nfc.ntag2xx_ReadPage(page, page_data)) {
        // Success! Print the data.
        Serial.print("  Page ");
        if (page < 10) Serial.print("0"); // for nice alignment
        Serial.print(page);
        Serial.print(": ");
        // nfc.PrintHexChar is a great helper function that prints hex and ASCII
        nfc.PrintHexChar(page_data, 4);
      } else {
        // FAILED TO READ! This is our signal that the tag has been removed.
        Serial.print("  Failed to read page "); Serial.println(page);
        all_reads_succeeded = false;
        break; // Exit the for loop immediately
      }
    }

    if (all_reads_succeeded) {
      Serial.println("Data read successfully. Tag is still present.");
    } else {
      // If any read failed, we reset the system state.
      Serial.println("\n=================================================");
      Serial.println("Tag lost or communication failed. Resetting...");
      Serial.println("=================================================");
      tagIsPresent = false; // Reset the state
      nfc.SAMConfig(); // Re-configure the reader to listen for a new tag
    }
   
    // Wait a bit before the next read cycle.
    delay(2000);
  }
}
0 Kudos
Reply
3 Replies

644 Views
jimmychan
NXP TechSupport
NXP TechSupport

when the board running passively, the power may not stable enough for the ADC operation.

0 Kudos
Reply

639 Views
JamesH_Scion
Contributor II
Hi Jimmy, thank you for replying. Do you think adding more capacitance to the capacitor bank could possibly enable ADC operation in passive mode? If so, what would be a safe amount of total capacitance to have to perform these operations.
0 Kudos
Reply

629 Views
jimmychan
NXP TechSupport
NXP TechSupport
0 Kudos
Reply