XIAO store data in QSPI flash memory (Mbed)

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

Hello,
Can you use more than 64k of this RAM with this code?
From 128k, it seems that the function QSPI_WaitForReady(); freezes.

The last debug lines:

(QSPI_Erase) Erasing memory
(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 *QSPI_Status_Ptr)
(QSPI_Erase (Finished Waiting)) QSPI is busy/idle ... Result = 0
(QSPI_Erase (Finished Waiting)) QSPI Status flag = 0xA (from NRF_QSPI) or 0xA (from *QSPI_Status_Ptr)
(QSPI_Initialise_Page) QSPI took 0ms to erase a 64Kb page
(Setup) QSPI read after erase
(Setup) QSPI took 20ms to read 128Kb ... Read result = 0
Data : 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF