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.
----------------------------------------
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
#include "pmu_nss.h"
#include "gpio_nss.h"
#include
#include
#include
// 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:
FYI.
NHS3152 power only from NFC field and load Capacitor - NXP Community
when the board running passively, the power may not stable enough for the ADC operation.