Sparkfun NRF52840 Mini Breakout Board - Bricked after trying to use UICR register

Hi everyone!

I have a Sparkfun NRF52840 Mini Breakout Board. I am trying to use this for development of nrf82540. I am using Arduino as IDE with Adafruit_NRF52 Library.

I tried to use UICR register as internal storage for configuration. The code is as below:

/*
  A BLE test code for App development on BLE devices
*/

#define firmwareVersion "0.1.0"

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include <avr/dtostrf.h>
#include <bluefruit.h>
#include <Adafruit_ADS1X15.h>
#include <ArduinoJson.h>
#include <CRC32.h>

#define INTERPOLATE_F(x, xA, yA, i) (*(yA + i - 1) + (x - *(xA + i - 1)) * (*(yA + i) - *(yA + i - 1)) / (*(xA + i) - *(xA + i - 1)))

#define CUSTOM_UUID(val) \
  (const uint8_t[]) { \
    0x20, 0x70, 0x28, 0xEA, 0xDD, 0xF4, 0x19, 0x9B, \
      0x8A, 0x44, (uint8_t)(val & 0xff), (uint8_t)(val >> 8), 0x56, 0xAC, 0xE1, 0xDF \
  }

#define MAX_PRPH_CONNECTION 1
#define MAX_JSON_SIZE 100

BLEService custom_service(CUSTOM_UUID(0x0000));
BLECharacteristic temp1_chars(CUSTOM_UUID(0x0001));
BLECharacteristic measurement_interval_chars(CUSTOM_UUID(MEASUREMENT_INTERVAL));

uint8_t buffer[MAX_JSON_SIZE] = { 0 };

uint8_t connection_count = 0;
uint32_t measurement_interval = 30;

/* Use this for the 16-bit version */
Adafruit_ADS1115 ads;

// BLE Service
BLEDfu bledfu;    // OTA DFU service
BLEDis bledis;    // device information
BLEUart bleuart;  // uart over ble

/*
  Setup
*/
void setup() {
  Serial.begin(115200);

  // delay(25000);
  Serial.println("[Setup]: begin.");
  if (!ads.begin()) {
    Serial.println("[Setup]: Failed to initialize ADS.");
  }

  // Setup the BLE LED to be enabled on CONNECT
  // Note: This is actually the default behaviour, but provided
  // here in case you want to control this LED manually via PIN 19
  Bluefruit.autoConnLed(false);

  Bluefruit.getAddr();

  // Initialize Bluefruit with max concurrent connections as Peripheral = 2, Central = 0
  Bluefruit.begin(MAX_PRPH_CONNECTION, 0);

  char mac[18];
  // Get actual BLE address in case it is different from hardware register BLE address
  ble_gap_addr_t device_addr;  // 48-bit address, LSB format
  sd_ble_gap_addr_get(&device_addr);
  snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X",
           device_addr.addr[5], device_addr.addr[4], device_addr.addr[3], device_addr.addr[2], device_addr.addr[1], device_addr.addr[0]);

  Serial.println(mac);

  Bluefruit.setName(mac);
  Bluefruit.setTxPower(8);  // Check bluefruit.h for supported values

  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // To be consistent OTA DFU should be added first if it exists
  bledfu.begin();

  // Configure and Start Device Information Service
  bledis.setManufacturer("XXXX");
  bledis.setModel("YYYY");
  bledis.begin();

  // Configure and Start BLE Uart Service
  bleuart.begin();

  // Read Measurement Interval from Internal Register Storage (UICR)
  readMeasurementIntervalFromUICR();

  setupCustomService();

  // Set up and start advertising
  startAdv();
}

void loop() {
  static unsigned long time_start_ms = millis();

  receive_data();

  if ((measurement_interval * 1000 <= millis() - time_start_ms) && (connection_count)) {
    Serial.println("-----------------------------------------------------------");
    float temp1 = 20.49;  // S1
    if (temp1_chars.notifyEnabled()) {
      temp1_chars.notify32(temp1);
    }

    Serial.printf("[Data]: TEMP1: %0.2f\n", temp1);

    char snumby[20];
    sprintf(snumby, "%0.2f,%d", temp1, measurement_interval);

    bleuart.write(snumby);
    time_start_ms = millis();
  }
}

void readMeasurementIntervalFromUICR() {
  measurement_interval = NRF_UICR->CUSTOMER[0];

  Serial.print("measurement_interval: ");
  Serial.println(measurement_interval);

  if (measurement_interval == 0 || measurement_interval == 0xFFFFFFFF) {
    measurement_interval = 30;
    storeMeasurementIntervalInUICR();
  }
}

void storeMeasurementIntervalInUICR() {
	// Erase the UICR.
  while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een;
  NRF_NVMC->ERASEUICR = NVMC_ERASEUICR_ERASEUICR_Erase;

  // Enable write access to UICR
  while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;

  while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;

  // Write a value to an address in UICR.
  while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
  NRF_UICR->CUSTOMER[0] = measurement_interval;  // Writing to CUSTOMER[0], you can use CUSTOMER[1], CUSTOMER[2], etc. as needed

  while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;

  // // Write your data to the customer registers
  // NRF_UICR->CUSTOMER[0] = measurement_interval;  // Writing to CUSTOMER[0], you can use CUSTOMER[1], CUSTOMER[2], etc. as needed
  // while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};

  // // Disable write access to UICR
  // NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
  // while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}

void setMeasurementInterval(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len) {
  (void)conn_hdl;
  (void)chr;
  (void)data;
  (void)len;

  measurement_interval = measurement_interval_chars.read32();
  Serial.print("Received measurement_interval: ");
  Serial.println(measurement_interval);
  // measurement_interval_chars.write32(measurement_interval);
}

void setupCustomService(void) {
  custom_service.begin();

  temp1_chars.setProperties(CHR_PROPS_NOTIFY);
  temp1_chars.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  temp1_chars.setPresentationFormatDescriptor(BLE_GATT_CPF_FORMAT_FLOAT32, 0, UUID16_UNIT_THERMODYNAMIC_TEMPERATURE_DEGREE_CELSIUS);
  temp1_chars.setFixedLen(sizeof(float));
  temp1_chars.begin();

  measurement_interval_chars.setProperties(CHR_PROPS_WRITE | CHR_PROPS_READ);
  measurement_interval_chars.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  measurement_interval_chars.setPresentationFormatDescriptor(BLE_GATT_CPF_FORMAT_UINT32, 0, UUID16_UNIT_UNITLESS);
  measurement_interval_chars.setFixedLen(sizeof(uint32_t));
  measurement_interval_chars.setWriteCallback(setMeasurementInterval);
  measurement_interval_chars.begin();
  measurement_interval_chars.write32(measurement_interval);
}

void receive_data() {
  static unsigned long stop_uart_ms = millis();
  static unsigned int bufferIndex = 0;

  while (bleuart.available()) {
    buffer[bufferIndex] = (uint8_t)bleuart.read();
    bufferIndex++;
    // Serial.write(ch);
    stop_uart_ms = millis();
  }

  if (millis() - stop_uart_ms >= 250 && bufferIndex) {
    buffer[bufferIndex] = '\0';
    Serial.println("Received data!");
    Serial.println((char*)buffer);

    DynamicJsonDocument doc(MAX_JSON_SIZE);

    DeserializationError error = deserializeJson(doc, (char*)buffer);

    if (error) {
      Serial.println("Serialization error!");
    } else {
      int cmd = doc["cmd"];
      switch (cmd) {
        case WRITE_TIMEOUT:
          measurement_interval = doc["measurement_interval"];
          break;

        default:
          break;
      }
    }

    memclr(buffer, MAX_JSON_SIZE);
    bufferIndex = 0;  // Reset for next data
  }
}

// callback invoked when central connects
void connect_callback(uint16_t conn_handle) {
  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));

  Serial.print("Connected to ");
  Serial.println(central_name);

  connection_count++;
  Serial.print("Connection count: ");
  Serial.println(connection_count);

  // Keep advertising if not reaching max
  if (connection_count < MAX_PRPH_CONNECTION) {
    Serial.println("Keep advertising");
    Bluefruit.Advertising.start(0);
  }
}

/**
 * Callback invoked when a connection is dropped
 * @param conn_handle connection where this event happens
 * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
 */
void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
  (void)conn_handle;
  (void)reason;

  Serial.println();
  Serial.print("Disconnected, reason = 0x");
  Serial.println(reason, HEX);

  connection_count--;
}

void startAdv(void) {
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();

  // Include bleuart 128-bit uuid
  Bluefruit.Advertising.addService(bleuart);
  Bluefruit.Advertising.addService(custom_service);

  // Secondary Scan Response packet (optional)
  // Since there is no room for 'Name' in Advertising packet
  Bluefruit.ScanResponse.addName();

  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);  // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);    // number of seconds in fast mode
  Bluefruit.Advertising.start(0);              // 0 = Don't stop advertising after n seconds
}

/* 
  Read the resistance value from the analog pin A0.  
*/
float readThermistor(uint8_t pin) {
  int16_t adc = ads.readADC_SingleEnded(pin);  // Read raw ADC value from channel 0

  // Convert raw ADC value to voltage
  float voltage = ads.computeVolts(adc);  //adc * (VCC / 32767.0);
  R2 = (R1 * voltage) / (3.3 - voltage);  // R2 = (R1 x Vout) / (Vin - Vout)

  // Calculate temperature for the given resistance
  float targetTemperature = interpolate(R2, resistanceData, temperatureData, numDataPoints);

  // Print the result
  Serial.printf("[Temp]: Raw ADC: %0.2f,\tR2: %0.2f,\tVolt: %0.2f\tTemperature (°C): %0.2f\n", adc, R2, voltage, targetTemperature);

  return targetTemperature;
}

/*
  Calculate the resistance value to the corresponding temperature. 
*/
float interpolate(float x, float* xData, float* yData, int dataSize) {
  // Linear interpolation algorithm
  for (int i = 1; i < dataSize; i++) {
    if (x <= xData[i]) {
      return INTERPOLATE_F(x, xData, yData, i);
    }
  }
  return 0.0;  // Return 0 if x is outside the range of the data
}


But after flashing the code, the board bricked. USB is not detected. even i tried to enter Bootloader by double pressing reset button. (which normally shows as a usb drive with files in it). It is not even detecting USB in windows device manager. I tried to use JLink Commander. 



I dont know what to do now. Also I dont know How to check the below "Recover after an accidental UICR erase". The Power LED is the only LED that glows now. the bootloader LED is not blinking even after double pressing RESET button twice. 

Recover after an accidental UICR erase

You have two options for recovering when the REG0 (VDD) voltage is 1.8 V (typically after a UICR erase):

  • Either use a debug probe that adapts the logic levels to the target device (for instance a J-Link PRO). Program firmware that configures REGOUT0 for 3 V.
  • Or modify the dongle so that it can be supplied by an external supply and use the nRF52840 DK to program it.
    1. Adapt the dongle hardware as described under External regulated source in the nRF52840 Dongle documentation.
    2. Connect VDD from the nRF52840 DK to VDD OUT on the nRF52840 Dongle.



Please help! Thanks.

Parents
  • Hi

    The Sparkfun boards aren't our designs, so I don't know the specifics of it or what the bootloader entails, but since it has a USB bootloader I assume it's somewhat comparable to the nRF52840 Dongle. If you have written something to the UICR without knowing if it overrides the bootloader or not, that is likely what has happened, and why it seems bricked. 

    You most likely needs to reflash the bootloader somehow. I see the Adafruit bootloader source code is available online, so it should be possible. First you need access to VDD, GND, SWDIO and SWDCLK on the onboard nRF52840. See the HW specifications for how to do so. Then you need a J-Link programmer so you can communicate with the SoC and flash the bootloader back onto it.

    Best regards,

    Simon

Reply
  • Hi

    The Sparkfun boards aren't our designs, so I don't know the specifics of it or what the bootloader entails, but since it has a USB bootloader I assume it's somewhat comparable to the nRF52840 Dongle. If you have written something to the UICR without knowing if it overrides the bootloader or not, that is likely what has happened, and why it seems bricked. 

    You most likely needs to reflash the bootloader somehow. I see the Adafruit bootloader source code is available online, so it should be possible. First you need access to VDD, GND, SWDIO and SWDCLK on the onboard nRF52840. See the HW specifications for how to do so. Then you need a J-Link programmer so you can communicate with the SoC and flash the bootloader back onto it.

    Best regards,

    Simon

Children
Related