XIAO store data in QSPI flash memory (Mbed)

Hi everybody.
I’ve a Xiao BLE SENSE (nRF52840).

In the specifications I read that the board has 2MB flash to store data. Connected with QSPI.

Well… how can I use it with the mbed-enabled version?

I found the Adafruit SPIFlash library but obviously does not compile… but seems something hackable …

This board has so many things but is a pain to use it properly…

Agreed, If they had Solid Library and board support along the lines of Adafruit and Raspie stuff, this little footprint would rule the embedded & wearable world.

it’s a slog to get it all collected up and Tested. I’m wondering how long b4 the other two knock it off with a clone?
my .02
GL :wink:

1 Like

At this point I hope that Adafruit will make a similar board…

I’m having the same problem.
Is there a clue somewhere to use QSPI on-board flash memory?

Hi,

this may help …

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include “nrfx_qspi.h”
#include “app_util_platform.h”
#include “nrf_log.h”
#include “nrf_log_ctrl.h”
#include “nrf_log_default_backends.h”
#include “sdk_config.h”
#include “nrf_delay.h”

/* Strange parts of this code … Or things I don’t understand
*

  • After the first READ in Setup() it successfully reads the data (Returns 0 = NRFX_SUCCESS), but the status flag
  • has the top 8 bits set to 0xFF which causes nrfx_qspi_mem_busy_check() to show 17 (Returns 17 = NRFX_ERROR_BUSY).
  • However masking the STATUS register with 8 reveals the Ready Status = 1, QSPI is ready!
  • This was why I wrote the QSPI_IsReady().
  • nrf_qspi_phy_conf_t not visible as a structure if you try and set it like this :
  • QSPIConfig.phy_if {
  • .xxx = yyy,
    
  • .aaa = bbb
    
  • };
  • I don’t know what the significance of the 48ms Deep Power-down Mode (DPM) is.
  • Will it go into DPM if not used for 48ms and then take 48ms to wake up if instructed?
  • Hope you enjoy this little snippet of code! Feel free to butcher and use it.
  • Thanks to JM_Laird and Haakonish in Case ID: 224515.
  • And, yes, I could have made it neater, but really need to add some of the bits into a project and tidy them up there!
    */

// QSPI Settings
#define QSPI_STD_CMD_WRSR 0x01
#define QSPI_STD_CMD_RSTEN 0x66
#define QSPI_STD_CMD_RST 0x99
#define QSPI_DPM_ENTER 0x0003 // 3 x 256 x 62.5ns = 48ms
#define QSPI_DPM_EXIT 0x0003

static uint32_t QSPI_Status_Ptr = (uint32_t) 0x40029604; // Setup for the SEEED XIAO BLE - nRF52840
static nrfx_qspi_config_t QSPIConfig;
static nrf_qspi_cinstr_conf_t QSPICinstr_cfg;
static const uint32_t MemToUse = 64 * 1024; // Alter this to create larger read writes, 64Kb is the size of the Erase
static bool Debug_On = true;
static uint16_t pBuf[MemToUse / 2] = {0}; // 16bit used as that is what this memory is going to be used for
static bool QSPIWait = true;
static volatile bool QSPI_HasFinished = true;
// QSPI Settings Complete

static void qspi_handler(nrfx_qspi_evt_t event, void *p_context) {
// UNUSED_PARAMETER(p_context);
// Serial.println(“QSPI Interrupt”);
// if (event == NRFX_QSPI_EVENT_DONE) {
QSPI_HasFinished = true;
// }
}

static void QSPI_Status(char ASender[]) { // Prints the QSPI Status
Serial.print("(");
Serial.print(ASender);
Serial.print(") QSPI is busy/idle … Result = “);
Serial.println(nrfx_qspi_mem_busy_check() & 8);
Serial.print(”(");
Serial.print(ASender);
Serial.print(") QSPI Status flag = 0x");
Serial.print(NRF_QSPI->STATUS, HEX);
Serial.print(" (from NRF_QSPI) or 0x");
Serial.print(*QSPI_Status_Ptr, HEX);
Serial.print(" (from my QSPI_Status_Ptr Pointer)");
Serial.print(" QSPI_HasFinished = ");
if (QSPI_HasFinished) {
Serial.println(“True”);
} else {
Serial.println(“False”);
}
}

static void QSPI_PrintData(uint16_t *AnAddress, uint32_t AnAmount) {
uint32_t i;

Serial.print(“Data :”);
for (i = 0; i < AnAmount; i++) {
Serial.print(" 0x");
Serial.print(*(AnAddress + i), HEX);
}
Serial.println("");
}

static nrfx_err_t QSPI_IsReady() {
if (((*QSPI_Status_Ptr & 8) == 8) && (*QSPI_Status_Ptr & 0x01000000) == 0) {
return NRFX_SUCCESS;
} else {
return NRFX_ERROR_BUSY;
}
}

static nrfx_err_t QSPI_WaitForFinished() {
while (!QSPI_HasFinished) {}
return NRFX_SUCCESS;
}

static nrfx_err_t QSPI_WaitForReady() {
while (QSPI_IsReady() == NRFX_ERROR_BUSY) {
/*
Serial.print("*QSPI_Status_Ptr & 8 = “);
Serial.print(*QSPI_Status_Ptr & 8);
Serial.print(”, *QSPI_Status_Ptr & 0x01000000 = ");
Serial.println(*QSPI_Status_Ptr & 0x01000000);
QSPI_Status(“QSPI_WaitForReady”);
*/
}
return NRFX_SUCCESS;
}

static nrfx_err_t QSPI_Initialise() { // Initialises the QSPI and NRF LOG
uint32_t Error_Code;

NRF_LOG_INIT(NULL); // Initialise the NRF Log
NRF_LOG_DEFAULT_BACKENDS_INIT();
// QSPI Config
QSPIConfig.xip_offset = NRFX_QSPI_CONFIG_XIP_OFFSET;
QSPIConfig.pins = { // Setup for the SEEED XIAO BLE - nRF52840
.sck_pin = 21,
.csn_pin = 25,
.io0_pin = 20,
.io1_pin = 24,
.io2_pin = 22,
.io3_pin = 23,
};
QSPIConfig.irq_priority = (uint8_t)NRFX_QSPI_CONFIG_IRQ_PRIORITY;
QSPIConfig.prot_if = {
// .readoc = (nrf_qspi_readoc_t)NRFX_QSPI_CONFIG_READOC,
.readoc = (nrf_qspi_readoc_t)NRF_QSPI_READOC_READ4O,
// .writeoc = (nrf_qspi_writeoc_t)NRFX_QSPI_CONFIG_WRITEOC,
.writeoc = (nrf_qspi_writeoc_t)NRF_QSPI_WRITEOC_PP4O,
.addrmode = (nrf_qspi_addrmode_t)NRFX_QSPI_CONFIG_ADDRMODE,
.dpmconfig = false,
};
QSPIConfig.phy_if.sck_freq = (nrf_qspi_frequency_t)NRF_QSPI_FREQ_32MDIV1; // I had to do it this way as it complained about nrf_qspi_phy_conf_t not being visible
// QSPIConfig.phy_if.sck_freq = (nrf_qspi_frequency_t)NRFX_QSPI_CONFIG_FREQUENCY;
QSPIConfig.phy_if.spi_mode = (nrf_qspi_spi_mode_t)NRFX_QSPI_CONFIG_MODE;
QSPIConfig.phy_if.dpmen = false;
// QSPI Config Complete
// Setup QSPI to allow for DPM but with it turned off
QSPIConfig.prot_if.dpmconfig = true;
NRF_QSPI->DPMDUR = (QSPI_DPM_ENTER << 16) | QSPI_DPM_EXIT; // Found this on the Nordic Q&A pages, Sets the Deep power-down mode timer
Error_Code = 1;
while (Error_Code != 0) {
Error_Code = nrfx_qspi_init(&QSPIConfig, NULL, NULL);
if (Error_Code != NRFX_SUCCESS) {
if (Debug_On) {
Serial.print("(QSPI_Initialise) nrfx_qspi_init returned : “);
Serial.println(Error_Code);
}
} else {
if (Debug_On) {
Serial.println(”(QSPI_Initialise) nrfx_qspi_init successful");
}
}
}
QSPI_Status(“QSPI_Initialise (Before QSIP_Configure_Memory)”);
QSIP_Configure_Memory();
if (Debug_On) {
Serial.println("(QSPI_Initialise) Wait for QSPI to be ready …");
}
NRF_QSPI->EVENTS_READY = 0;
NRF_QSPI->TASKS_ACTIVATE = 1;
while(NRF_QSPI->EVENTS_READY == 0) {
if (Debug_On) {
Serial.print(“NRF_QSPI->STATUS = “);
Serial.println(NRF_QSPI->EVENTS_READY);
QSPI_Status(“QSPI_Initialise (After QSIP_Configure_Memory)”);
}
}
if (Debug_On) {
Serial.println(”(QSPI_Initialise) QSPI is ready”);
}
return QSPI_IsReady();
}

static void QSPI_Erase(uint32_t AStartAddress) {
uint32_t TimeTaken;
bool QSPIReady = false;
bool AlreadyPrinted = false;

if (Debug_On) {
Serial.println("(QSPI_Erase) Erasing memory");
}
while (!QSPIReady) {
if (QSPI_IsReady() != NRFX_SUCCESS) {
if (!AlreadyPrinted) {
QSPI_Status(“QSPI_Erase (Waiting)”);
AlreadyPrinted = true;
}
} else {
QSPIReady = true;
QSPI_Status(“QSPI_Erase (Waiting Loop Breakout)”);
}
}
if (Debug_On) {
QSPI_Status(“QSPI_Erase (Finished Waiting)”);
TimeTaken = millis();
}
if (nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_64KB, AStartAddress) != NRFX_SUCCESS) {
if (Debug_On) {
Serial.print("(QSPI_Initialise_Page) QSPI Address 0x");
Serial.print(AStartAddress, HEX);
Serial.println(" failed to erase!");
}
} else {
if (Debug_On) {
TimeTaken = millis() - TimeTaken;
Serial.print("(QSPI_Initialise_Page) QSPI took ");
Serial.print(TimeTaken);
Serial.println(“ms to erase a 64Kb page”);
}
}
}

static void QSIP_Configure_Memory() {
// uint8_t temporary = 0x40;
uint8_t temporary[] = {0x00, 0x02};
uint32_t Error_Code;

QSPICinstr_cfg = {
.opcode = QSPI_STD_CMD_RSTEN,
.length = NRF_QSPI_CINSTR_LEN_1B,
.io2_level = true,
.io3_level = true,
.wipwait = QSPIWait,
.wren = true
};
QSPI_WaitForReady();
if (nrfx_qspi_cinstr_xfer(&QSPICinstr_cfg, NULL, NULL) != NRFX_SUCCESS) { // Send reset enable
if (Debug_On) {
Serial.println("(QSIP_Configure_Memory) QSPI ‘Send reset enable’ failed!");
}
} else {
QSPICinstr_cfg.opcode = QSPI_STD_CMD_RST;
QSPI_WaitForReady();
if (nrfx_qspi_cinstr_xfer(&QSPICinstr_cfg, NULL, NULL) != NRFX_SUCCESS) { // Send reset command
if (Debug_On) {
Serial.println("(QSIP_Configure_Memory) QSPI Reset failed!");
}
} else {
QSPICinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
// QSPICinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
QSPICinstr_cfg.length = NRF_QSPI_CINSTR_LEN_3B;
QSPI_WaitForReady();
if (nrfx_qspi_cinstr_xfer(&QSPICinstr_cfg, &temporary, NULL) != NRFX_SUCCESS) { // Switch to qspi mode
if (Debug_On) {
Serial.println("(QSIP_Configure_Memory) QSPI failed to switch to QSPI mode!");
}
} else {
QSPI_Status(“QSIP_Configure_Memory”);
}
}
}
}

void setup() {
uint32_t Error_Code;
uint32_t TimeTaken;
uint16_t i;

delay(10000);
Serial.begin(9600);
while (!Serial) {}

if (Debug_On) {
Serial.println("(Setup) QSPI Initialising …");
}
if (QSPI_Initialise() != NRFX_SUCCESS) {
if (Debug_On) {
Serial.println("(Setup) QSPI Memory failed to start!");
}
} else {
if (Debug_On) {
Serial.println("(Setup) QSPI initialised and ready");
QSPI_Status(“Setup (After initialise)”);
}
}

if (Debug_On) {
Serial.print("(Setup) QSPI is about to be read and then erased. Current busy state is = ");
Serial.println(QSPI_IsReady());
}

// QSPI Speed Test
if (Debug_On) {
QSPI_Status(“Setup (Before read)”);
TimeTaken = millis();
}
QSPI_HasFinished = false;
Error_Code = nrfx_qspi_read(pBuf, MemToUse, 0x0);
if (Debug_On) {
TimeTaken = millis() - TimeTaken;
Serial.print("(Setup) QSPI took ");
Serial.print(TimeTaken);
Serial.print("ms to read “);
Serial.print(MemToUse / 1024);
Serial.print(“Kb … Read result = “);
Serial.println(Error_Code);
// QSPI_Status(“Setup (After read)”);
// QSPI_WaitForReady();
delay(1000);
QSPI_PrintData(&pBuf[0], 10);
}
if (Debug_On) {
Serial.println(“QSPI Erasing 64Kb of memory”);
}
QSPI_HasFinished = false;
QSPI_Erase(0);
// QSPI_WaitForReady();
delay(1000);
QSPI_HasFinished = false;
if (Debug_On) {
Serial.println(”(Setup) QSPI read after erase”);
TimeTaken = millis();
}
Error_Code = nrfx_qspi_read(pBuf, MemToUse, 0x0);
if (Debug_On) {
TimeTaken = millis() - TimeTaken;
Serial.print(”(Setup) QSPI took ");
Serial.print(TimeTaken);
Serial.print("ms to read “);
Serial.print(MemToUse / 1024);
Serial.print(“Kb … Read result = “);
Serial.println(Error_Code);
// QSPI_WaitForReady();
delay(1000);
QSPI_PrintData(&pBuf[0], 10);
}
for (i = 0; i < MemToUse / 2; i++) {
pBuf[i] = i * 2;
}
/*
for (i = 0; i < MemToUse / 2; i++) {
pBuf[i] = i * 2;
}
*/
// QSPI_WaitForReady();
delay(1000);
QSPI_HasFinished = false;
if (Debug_On) {
Serial.println(”(Setup) Just before QSPI write”);
TimeTaken = millis();
}
Error_Code = nrfx_qspi_write(pBuf, MemToUse, 0x0);
if (Debug_On) {
TimeTaken = millis() - TimeTaken;
Serial.print(”(Setup) QSPI took ");
Serial.print(TimeTaken);
Serial.print("ms to write “);
Serial.print(MemToUse / 1024);
Serial.print(“Kb … Write result = “);
Serial.println(Error_Code);
}
// QSPI_WaitForReady();
delay(1000);
QSPI_HasFinished = false;
if (Debug_On) {
Serial.println(”(Setup) Just before QSPI read”);
TimeTaken = millis();
}
Error_Code = nrfx_qspi_read(pBuf, MemToUse, 0x0);
if (Debug_On) {
TimeTaken = millis() - TimeTaken;
Serial.print(”(Setup) QSPI took ");
Serial.print(TimeTaken);
Serial.print("ms to read ");
Serial.print(MemToUse / 1024);
Serial.print("Kb … Read result = ");
Serial.println(Error_Code);
// QSPI_WaitForReady();
delay(1000);
QSPI_PrintData(&pBuf[0], 10);
}
// QSPI_WaitForReady();
delay(1000);
QSPI_Status(“Setup”);
// QSPI Speed Test Complete
}

void loop() {
/*
Serial.println(“Wait for QSPI to shutdown”);
uint32_t nConfig = NRF_QSPI->IFCONFIG1;
nConfig |= 1U << QSPI_IFCONFIG1_DPMEN_Pos;
NRF_QSPI->IFCONFIG1 = nConfig;

Serial.println("QSPI is shutting down");
//This never executes
NRF_LOG_INFO("done");

nrfx_qspi_uninit();

NRF_LOG_INFO("end");

Serial.println("All done!");
for (;;)
{
}

*/
}

Hi (again),

If anyone manages to get the “qspi_handler” interrupt working could you let me know.

Line 149 can be altered to …

Error_Code = nrfx_qspi_init(&QSPIConfig, qspi_handler, NULL);

This would make using the QSPI even better as code execution will continue whilst the memory is being written or read, would need to change the code a little. The lines …

QSPI_WaitForReady()

would change to …

QSPI_WaitForFinished()

Thanks

Paul

Hi Paul.
Thank you so much!!!
Your code works fine on my XIAO(I did have to restore the lost asterisks, though :upside_down_face:).
Thanks to you my project has taken a step forward :laughing:
Ryu

Hi R.Ikeda,
I am getting a compile error.
Where is “the lost asterisks”?

Hi msfujino

static uint32_t QSPI_Status_Ptr = (uint32_t) 0x40029604; // Setup for the SEEED XIAO BLE - nRF52840
//↓
static uint32_t *QSPI_Status_Ptr = (uint32_t* ) 0x40029604; // Setup for the SEEED XIAO BLE - nRF52840

Here it is.
The asterisk is recognized as an italic typeface.

Thanks for the information. I was able to compile it successfully.

Hi Ryu,

I am very happy this helped!

Paul

1 Like

Has anyone run Paul’s code with XIAO_BLE_nRF52840 (mbed 2.7.2)?
I get the following message on the serial monitor and it does not work.
Please advise?

(QSPI_Initialise (Before QSIP_Configure_Memory)) QSPI is busy/idle … Result = 0
(QSPI_Initialise (Before QSIP_Configure_Memory)) QSPI Status flag = 0xA (from NRF_QSPI) or 0xA (from my QSPI_Status_Ptr Pointer) QSPI_HasFinished = True
(QSIP_Configure_Memory) QSPI is busy/idle … Result = 0
(QSIP_Configure_Memory) QSPI Status flag = 0x300000A (from NRF_QSPI) or 0x300000A (from my QSPI_Status_Ptr Pointer) QSPI_HasFinished = True
(QSPI_Erase (Waiting Loop Breakout)) QSPI is busy/idle … Result = 0
(QSPI_Erase (Waiting Loop Breakout)) QSPI Status flag = 0xA (from NRF_QSPI) or 0xA (from my QSPI_Status_Ptr Pointer) QSPI_HasFinished = False
(Setup) QSPI is busy/idle … Result = 0
(Setup) QSPI Status flag = 0xA (from NRF_QSPI) or 0xA (from my QSPI_Status_Ptr Pointer) QSPI_HasFinished = False

Hi msfujino,

Does it just hang after showing those messages?
I run it on a XIAO_BLE_nRF52840 Sense.

Just looking at the code now …

Paul

Hi Paul,
Thanks for the reply.
Running on XIAO_BLE_nRF52840_Sence and hangs after this message.
I have a concern. You posted your sketch as text, so it contained a lot of characters that could not be compiled. I fixed a lot of places until the compile passed. Below is the sketch I am running. Is this the same as your sketch?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <nrfx_qspi.h>
#include <app_util_platform.h>
#include <nrf_log.h>
#include <nrf_log_ctrl.h>
#include <nrf_log_default_backends.h>
#include <sdk_config.h>
#include <nrf_delay.h>

// QSPI Settings
#define QSPI_STD_CMD_WRSR 0x01
#define QSPI_STD_CMD_RSTEN 0x66
#define QSPI_STD_CMD_RST 0x99
#define QSPI_DPM_ENTER 0x0003 // 3 x 256 x 62.5ns = 48ms
#define QSPI_DPM_EXIT 0x0003

static uint32_t *QSPI_Status_Ptr = (uint32_t*)0x40029604; // Setup for the SEEED XIAO BLE - nRF52840
static nrfx_qspi_config_t QSPIConfig;
static nrf_qspi_cinstr_conf_t QSPICinstr_cfg;
static const uint32_t MemToUse = 64 * 1024; // Alter this to create larger read writes, 64Kb is the size of the Erase
//static bool Debug_On = true;
static bool Debug_On = false;
static uint16_t pBuf[MemToUse / 2] = {0}; // 16bit used as that is what this memory is going to be used for
static bool QSPIWait = true;
static volatile bool QSPI_HasFinished = true;
// QSPI Settings Complete

static void qspi_handler(nrfx_qspi_evt_t event, void *p_context) {
// UNUSED_PARAMETER(p_context);
// Serial.println(“QSPI Interrupt”);
// if (event == NRFX_QSPI_EVENT_DONE) {
QSPI_HasFinished = true;
// }
}

static void QSPI_Status(char ASender[]) { // Prints the QSPI Status
	Serial.print("(");
	Serial.print(ASender);
	Serial.print(") QSPI is busy/idle … Result = ");
	Serial.println(nrfx_qspi_mem_busy_check() & 8);
	Serial.print("(");
	Serial.print(ASender);
	Serial.print(") QSPI Status flag = 0x");
	Serial.print(NRF_QSPI->STATUS, HEX);
	Serial.print(" (from NRF_QSPI) or 0x");
	Serial.print(*QSPI_Status_Ptr, HEX);
	Serial.print(" (from my QSPI_Status_Ptr Pointer)");
	Serial.print(" QSPI_HasFinished = ");
	
	if (QSPI_HasFinished) {
		Serial.println("True");
	}
	else {
		Serial.println("False");
	}
}

static void QSPI_PrintData(uint16_t *AnAddress, uint32_t AnAmount) {
	uint32_t i;

	Serial.print("Data :");
	for (i = 0; i < AnAmount; i++) {
		Serial.print(" 0x");
		Serial.print(*(AnAddress + i), HEX);
	}
		Serial.println("");
}

static nrfx_err_t QSPI_IsReady() {  
	if (((*QSPI_Status_Ptr & 8) == 8) && (*QSPI_Status_Ptr & 0x01000000) == 0) {
		return NRFX_SUCCESS;
	}
	else {
		return NRFX_ERROR_BUSY;
	}
}

static nrfx_err_t QSPI_WaitForFinished() {
	while (!QSPI_HasFinished) {}
	return NRFX_SUCCESS;
}

static nrfx_err_t QSPI_WaitForReady() {
	while (QSPI_IsReady() == NRFX_ERROR_BUSY) {
/*
Serial.print("*QSPI_Status_Ptr & 8 = “);
Serial.print(*QSPI_Status_Ptr & 8);
Serial.print(”, *QSPI_Status_Ptr & 0x01000000 = ");
Serial.println(*QSPI_Status_Ptr & 0x01000000);
QSPI_Status(“QSPI_WaitForReady”);
*/
	}
	return NRFX_SUCCESS;
}

static nrfx_err_t QSPI_Initialise() { // Initialises the QSPI and NRF LOG
	uint32_t Error_Code;

	NRF_LOG_INIT(NULL); // Initialise the NRF Log
	NRF_LOG_DEFAULT_BACKENDS_INIT();
	// QSPI Config
	QSPIConfig.xip_offset = NRFX_QSPI_CONFIG_XIP_OFFSET;
	QSPIConfig.pins = { // Setup for the SEEED XIAO BLE - nRF52840
	.sck_pin = 21,
	.csn_pin = 25,
	.io0_pin = 20,
	.io1_pin = 24,
	.io2_pin = 22,
	.io3_pin = 23,
	};
	
	QSPIConfig.irq_priority = (uint8_t)NRFX_QSPI_CONFIG_IRQ_PRIORITY;
	QSPIConfig.prot_if = {
	// .readoc = (nrf_qspi_readoc_t)NRFX_QSPI_CONFIG_READOC,
	.readoc = (nrf_qspi_readoc_t)NRF_QSPI_READOC_READ4O,
	// .writeoc = (nrf_qspi_writeoc_t)NRFX_QSPI_CONFIG_WRITEOC,
	.writeoc = (nrf_qspi_writeoc_t)NRF_QSPI_WRITEOC_PP4O,
	.addrmode = (nrf_qspi_addrmode_t)NRFX_QSPI_CONFIG_ADDRMODE,
	.dpmconfig = false,
	};

  // I had to do it this way as it complained about nrf_qspi_phy_conf_t not being visible
	QSPIConfig.phy_if.sck_freq = (nrf_qspi_frequency_t)NRF_QSPI_FREQ_32MDIV1; 
	// QSPIConfig.phy_if.sck_freq = (nrf_qspi_frequency_t)NRFX_QSPI_CONFIG_FREQUENCY;
	QSPIConfig.phy_if.spi_mode = (nrf_qspi_spi_mode_t)NRFX_QSPI_CONFIG_MODE;
	QSPIConfig.phy_if.dpmen = false;
	// QSPI Config Complete
	// Setup QSPI to allow for DPM but with it turned off
	QSPIConfig.prot_if.dpmconfig = true;
  // Found this on the Nordic Q&A pages, Sets the Deep power-down mode timer
	NRF_QSPI->DPMDUR = (QSPI_DPM_ENTER << 16) | QSPI_DPM_EXIT; 
	Error_Code = 1;
	
	while (Error_Code != 0) {
		Error_Code = nrfx_qspi_init(&QSPIConfig, NULL, NULL);
		if (Error_Code != NRFX_SUCCESS) {
			if (Debug_On) {
				Serial.print("(QSPI_Initialise) nrfx_qspi_init returned : ");
				Serial.println(Error_Code);
			}
		}
		else {
			if (Debug_On) {
				Serial.println("(QSPI_Initialise) nrfx_qspi_init successful");
			}
		}
	}
	
	QSPI_Status("QSPI_Initialise (Before QSIP_Configure_Memory)");
	QSIP_Configure_Memory();
	
	if (Debug_On) {
		Serial.println("(QSPI_Initialise) Wait for QSPI to be ready …");
	}
	
	NRF_QSPI->EVENTS_READY = 0;
	NRF_QSPI->TASKS_ACTIVATE = 1;
	
	while(NRF_QSPI->EVENTS_READY == 0) {
		if (Debug_On) {
			Serial.print("NRF_QSPI->STATUS = ");
			Serial.println(NRF_QSPI->EVENTS_READY);
			QSPI_Status("QSPI_Initialise (After QSIP_Configure_Memory)");
		}
	}
	if (Debug_On) {
		Serial.println("(QSPI_Initialise) QSPI is ready");
	}
	return QSPI_IsReady();
}

static void QSPI_Erase(uint32_t AStartAddress) {
	uint32_t TimeTaken;
	bool QSPIReady = false;
	bool AlreadyPrinted = false;

	if (Debug_On) {
		Serial.println("(QSPI_Erase) Erasing memory");
	}
	
	while (!QSPIReady) {
		if (QSPI_IsReady() != NRFX_SUCCESS) {
			if (!AlreadyPrinted) {
				QSPI_Status("QSPI_Erase (Waiting)");
				AlreadyPrinted = true;
			}
		} 
		else {
			QSPIReady = true;
			QSPI_Status("QSPI_Erase (Waiting Loop Breakout)");
		}
	}
	
	if (Debug_On) {
		QSPI_Status("QSPI_Erase (Finished Waiting)");
		TimeTaken = millis();
	}
	
	if (nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_64KB, AStartAddress) != NRFX_SUCCESS) {
		if (Debug_On) {
			Serial.print("(QSPI_Initialise_Page) QSPI Address 0x");
			Serial.print(AStartAddress, HEX);
			Serial.println(" failed to erase!");
		}
	} 
	else {
		if (Debug_On) {
			TimeTaken = millis() - TimeTaken;
			Serial.print("(QSPI_Initialise_Page) QSPI took ");
			Serial.print(TimeTaken);
			Serial.println("ms to erase a 64Kb page");
		}
	}
}

static void QSIP_Configure_Memory() {
	// uint8_t temporary = 0x40;
	uint8_t temporary[] = {0x00, 0x02};
	uint32_t Error_Code;

	QSPICinstr_cfg = {
	.opcode = QSPI_STD_CMD_RSTEN,
	.length = NRF_QSPI_CINSTR_LEN_1B,
	.io2_level = true,
	.io3_level = true,
	.wipwait = QSPIWait,
	.wren = true
	};
	
	QSPI_WaitForReady();
	if (nrfx_qspi_cinstr_xfer(&QSPICinstr_cfg, NULL, NULL) != NRFX_SUCCESS) { // Send reset enable
		if (Debug_On) {
			Serial.println("(QSIP_Configure_Memory) QSPI ‘Send reset enable’ failed!");
		}
	} 
	else {
		QSPICinstr_cfg.opcode = QSPI_STD_CMD_RST;
		QSPI_WaitForReady();
		
		if (nrfx_qspi_cinstr_xfer(&QSPICinstr_cfg, NULL, NULL) != NRFX_SUCCESS) { // Send reset command
			if (Debug_On) {
					Serial.println("(QSIP_Configure_Memory) QSPI Reset failed!");
			}
		} 
		else {
			QSPICinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
			// QSPICinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
			QSPICinstr_cfg.length = NRF_QSPI_CINSTR_LEN_3B;
			QSPI_WaitForReady();
			if (nrfx_qspi_cinstr_xfer(&QSPICinstr_cfg, &temporary, NULL) != NRFX_SUCCESS) { // Switch to qspi mode
				if (Debug_On) {
					Serial.println("(QSIP_Configure_Memory) QSPI failed to switch to QSPI mode!");
				}
			}
			else {
				QSPI_Status("QSIP_Configure_Memory");
			}
		}
	}
}


//***********************************************************************************************

void setup() {
	uint32_t Error_Code;
	uint32_t TimeTaken;
	uint16_t i;

	delay(10000);
	Serial.begin(115200);
	while (!Serial) {;}

	if (Debug_On) {
		Serial.println("(Setup) QSPI Initialising …");
	}
	
	if (QSPI_Initialise() != NRFX_SUCCESS) {
		if (Debug_On) {
			Serial.println("(Setup) QSPI Memory failed to start!");
		}
	}
	else {
		if (Debug_On) {
			Serial.println("(Setup) QSPI initialised and ready");
			QSPI_Status("Setup (After initialise)");
		}
	}

	if (Debug_On) {
		Serial.print("(Setup) QSPI is about to be read and then erased. Current busy state is = ");
		Serial.println(QSPI_IsReady());
	}

	// QSPI Speed Test
	if (Debug_On) {
		QSPI_Status("Setup (Before read)");
		TimeTaken = millis();
	}
	
	QSPI_HasFinished = false;
	Error_Code = nrfx_qspi_read(pBuf, MemToUse, 0x0);
	
	if (Debug_On) {
		TimeTaken = millis() - TimeTaken;
		Serial.print("(Setup) QSPI took ");
		Serial.print(TimeTaken);
		Serial.print("ms to read ");
		Serial.print(MemToUse / 1024);
		Serial.print("Kb … Read result = ");
		Serial.println(Error_Code);
		// QSPI_Status("Setup (After read)");
		// QSPI_WaitForReady();
		delay(1000);
		QSPI_PrintData(&pBuf[0], 10);
	}
	
	if (Debug_On) {
		Serial.println("QSPI Erasing 64Kb of memory");
	}
	
	QSPI_HasFinished = false;
	QSPI_Erase(0);
	// QSPI_WaitForReady();
	delay(1000);
	QSPI_HasFinished = false;
	
	if (Debug_On) {
		Serial.println("(Setup) QSPI read after erase");
		TimeTaken = millis();
	}
	
	Error_Code = nrfx_qspi_read(pBuf, MemToUse, 0x0);
	
	if (Debug_On) {
		TimeTaken = millis() - TimeTaken;
		Serial.print("(Setup) QSPI took ");
		Serial.print(TimeTaken);
		Serial.print("ms to read ");
		Serial.print(MemToUse / 1024);
		Serial.print("Kb … Read result = ");
		Serial.println(Error_Code);
		// QSPI_WaitForReady();
		delay(1000);
		QSPI_PrintData(&pBuf[0], 10);
	}
	
	for (i = 0; i < MemToUse / 2; i++) {
		pBuf[i] = i * 2;
	}
/*
	for (i = 0; i < MemToUse / 2; i++) {
		pBuf[i] = i * 2;
	}
*/
	// QSPI_WaitForReady();
	delay(1000);
	QSPI_HasFinished = false;
	
	if (Debug_On) {
		Serial.println("(Setup) Just before QSPI write");
		TimeTaken = millis();
	}
	
	Error_Code = nrfx_qspi_write(pBuf, MemToUse, 0x0);
	
	if (Debug_On) {
		TimeTaken = millis() - TimeTaken;
		Serial.print("(Setup) QSPI took ");
		Serial.print(TimeTaken);
		Serial.print("ms to write ");
		Serial.print(MemToUse / 1024);
		Serial.print("Kb … Write result = ");
		Serial.println(Error_Code);
	}
	
	// QSPI_WaitForReady();
	delay(1000);
	QSPI_HasFinished = false;
	
	if (Debug_On) {
		Serial.println("(Setup) Just before QSPI read");
		TimeTaken = millis();
	}
	
	Error_Code = nrfx_qspi_read(pBuf, MemToUse, 0x0);
	
	if (Debug_On) {
		TimeTaken = millis() - TimeTaken;
		Serial.print("(Setup) QSPI took ");
		Serial.print(TimeTaken);
		Serial.print("ms to read ");
		Serial.print(MemToUse / 1024);
		Serial.print("Kb … Read result = ");
		Serial.println(Error_Code);
		// QSPI_WaitForReady();
		delay(1000);
		QSPI_PrintData(&pBuf[0], 10);
	}
	
	// QSPI_WaitForReady();
	delay(1000);
	QSPI_Status("Setup");
	// QSPI Speed Test Complete
}

void loop() {
/*
Serial.println(“Wait for QSPI to shutdown”);
uint32_t nConfig = NRF_QSPI->IFCONFIG1;
nConfig |= 1U << QSPI_IFCONFIG1_DPMEN_Pos;
NRF_QSPI->IFCONFIG1 = nConfig;

Serial.println("QSPI is shutting down");
//This never executes
NRF_LOG_INFO("done");

nrfx_qspi_uninit();

NRF_LOG_INFO("end");

Serial.println("All done!");
for (;;)
{
}
*/
}

Hi,

I have put it on github : GitHub - PMCheetham/SEEED_nRF52840_QSPI: SEEED nRF52480 QSPI Example

I changed the lines : while(NRF_QSPI->EVENTS_READY == 0)

As this sometimes caused my board to just wait forever. So I removed the whole WHILE loop and replaced it with : QSPI_WaitForReady();

If you can get the github copy, I also posted the output. On the github version the changes I made are around line 156.

Thanks

Paul

1 Like

Let me know if it works …

1 Like

Hi Paul,
I downloaded it from github and checked with 4 XIAO_BLEs. It worked as expected.
So far I have been using SD card to record the measurement data, but I will try to use flash memory.
Thank you very much.

Hi Msfujino,

That is good to hear. I am just working on buffers to store data in RAM and then move it to the QSPI in a non blocking manner, then onto SD in “dead” time. I would be interested if yu have a quick way of moving data onto an SD card, I have an SD card with 4 data pins, but have only managed to get it working with the normal 2 (Miso/Mosi).

I will post my buffer code and future QSPI integration on the github page.

Paul

1 Like

WOW, I’m standing on the shoulders of giants here, I must say YOU two guys @MSfujino, @Paul and all the folks here are shredding it when it comes to helping each other and doing yo-mans work on the support of this stuff. If Only the whole world work this well.

Merry Christmas…
GL
:wink:

Thanks PJ_Glasso, always thought this is how the internet should work :slight_smile: Good people helping each other out.

1 Like