2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘録】Seeed Studio XIAO nRF52840 SenseでFlash Memoryを使う

Posted at

概要

Seeed studio XIAO nRF52840SenseのFlash Memoryを使いたい。
ただ、参考になる日本語のサイトが無いので、Seeedのページを参考にしながら実装してみた。

開発環境

VS Code + Platformio

はじめに

趣味で電子工作をしており、充電モジュール&ローバット検知モジュールがついているSeeed studio XIAO nRF52840Senseを触っている。
データをFlash Memory上にためたいが、どうやればいいのかわからなかった。

VS Codeの設定

Platformioから、以下のようにBoard、Frameworkを選ぶ。
Screenshot 2024-02-04 22.30.19.png

コーディング

のコードを貼り付ける。

このままだとエラーがでるので、
Arduino.hとnrf52840.hをインクルードする。
また、このままだとQSIP_Configure_Memory()がnot defineというエラーがでるので、QSPI_Initialise()の前にQSIP_Configure_Memory()を持ってくる。

これでコンパイルエラーがなくなって書き込めるはず。

これで書き込めはするのですが、VS Code上ではよくわからないエラーが出ます。
Screenshot 2024-02-04 22.40.08.png

これを解消するには、.vscodeフォルダの中のc_cpp_properties.jsonconfigurationsdefine"__STATIC_INLINE= static inline",を追加してください。そうすればエラーが出なくなると思います。
Screenshot 2024-02-04 22.43.18.png

最終的なコードは以下の通り。

#include <Arduino.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <nrf52840.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"
#include "avr/interrupt.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 uint32_t *BufMem = (uint32_t *)&pBuf;
static bool QSPIWait = false;
// 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.println(" (from *QSPI_Status_Ptr)");
}

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_WaitForReady()
{
  while (QSPI_IsReady() == NRFX_ERROR_BUSY)
  {
    if (Debug_On)
    {
      Serial.print("*QSPI_Status_Ptr & 8 = ");
      Serial.print(*QSPI_Status_Ptr & 8);
      Serial.print(", *QSPI_Status_Ptr & 0x01000000 = 0x");
      Serial.println(*QSPI_Status_Ptr & 0x01000000, HEX);
      QSPI_Status("QSPI_WaitForReady");
    }
  }
  return NRFX_SUCCESS;
}

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_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");
      }
    }
  }
}

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->TASKS_ACTIVATE = 1;
  QSPI_WaitForReady();
  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");
    }
  }
}

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();
  }
  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();
    QSPI_PrintData(&pBuf[0], 10);
  }
  if (Debug_On)
  {
    Serial.println("QSPI Erasing 64Kb of memory");
  }
  QSPI_Erase(0);
  if (Debug_On)
  {
    Serial.println("(Setup) QSPI read after erase");
    TimeTaken = millis();
  }
  QSPI_WaitForReady();
  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();
    QSPI_PrintData(&pBuf[0], 10);
  }
  for (i = 0; i < MemToUse / 2; i++)
  {
    pBuf[i] = i * 2;
  }
  QSPI_WaitForReady();
  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();
  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();
    QSPI_PrintData(&pBuf[0], 10);
  }
  QSPI_WaitForReady();
  QSPI_Status("Setup");
  // QSPI Speed Test Complete
}

void loop()
{
  delay(10000);
}

ちょっとしたメモ

nrfx_qspi_writeでデータの書き込みができるのだが、QSPI_Eraseをしないと書き込みできないっぽい。

これからやりたいこと

  • 充電制御、ローバット検知
  • ArduinoBLEを用いたBLE通信
  • マルチタスク制御

最後に

nRF52840 Senseは低消費かつ充電モジュール&ローバット検知モジュールがついているマイコンボードなので、非常に使い勝手は良いと思います。これからも情報発信していたいと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?