- 公開日:2026年03月04日
RA0E1マイコンでSPI Flash Memoryを動かす

メモリ足りてますか?
突然ですが、メモリは足りてますか?
「あと少し容量があれば…」という場面は、組み込み開発では珍しくありません。そんなとき、外付けのSPI Flash Memoryは手軽で強力な選択肢になります。
今回はRA0E1マイコンとSPI Flash Memory Moduleを使い、配線から読み書きまでを実際に動かして確認していきます。
この記事でできるようになること
- RA0E1の簡易SPIでSPI Flash Memoryを制御する
- SPI Flash MemoryのJEDEC IDを読む
- SPI Flash Memoryのデータ読み書き
必要なもの
- RA0E1マイコン ※ 本記事ではFPB-RA0E1を使用しています
- SPI Flash Memory ※ 本記事ではWinbond社のW25Q64FVを搭載したSPI Flash Memory Moduleを使用しています
- ルネサス エレクトロニクス社 統合開発環境 e² studio ※ マクニカの技術記事「e² studioをインストールしてみよう」がとても参考になります
- ルネサス エレクトロニクス社 ソフトウェアパッケージ RA Flexible Software Package (FSP)
- ブレッドボードや配線を行うケーブルなど

配線と簡易SPIの設定
RA0E1マイコンとSPI Flash Memory Moduleの接続ですが、今回使用したモジュールはシンプルなSPI接続(Single SPI)のみとなっているため、電源線(VCC、GND)とSPI制御に必要な4本の信号線の合計6本の線をつなぎます。
RA0E1マイコンのSPI設定
RA0E1マイコンは、SPI専用の通信ペリフェラルを搭載していません。そのため、SAU(Serial Array Unit)の簡易SPIを用いて、SPI Flash Memoryモジュールと接続する必要があります。また、SAUの簡易SPIは3線式のためCS信号(チップセレクト)はユーザプログラムで制御する必要があります。今回はGPIOのP103端子をCS信号に割り当てて制御を行います。

RA0E1マイコンのSAU構成
RA0E1マイコンのSAUは以下の表の構成となっています。今回はユニット0のチャネル0(SPI00)を使用します。
RA0E1マイコンとSPI Flash Memory Moduleの接続
RA0E1マイコンとSPI Flash Memory Moduleとの接続は下図のようになります。

また、FPB RA0E1評価ボードは以下のような端子割り当てになっています。

そのため、実際に接続される際は以下の様にして下さい。

RA0E1マイコンのFSP(RA Flexible Software Package)設定
使用するSPI Flash Memory ModuleにはWinbond社のSerial NOR Flash MemoryであるW25Q64FVが搭載されています。このSerial NOR Flash MemoryのSPI仕様はモード0、および、モード3をサポートしており、最大ビットレートは50MHz(3.0 ~ 3.6V時)となっています。
そのため、RA0E1の簡易SPI設定、IO設定は下図(簡易SPI通信設定、簡易SPI IO設定、GPIO設定)のように設定して下さい。
【FSP設定画面 : 簡易SPI通信設定】

【FSP設定画面 : 簡易SPI IO設定(CLK信号、MOSI信号、MISO信号)】

【FSP設定画面 : GPIO設定(CS信号)】

なお、「Clock Phase」と「Clock Polarity」の設定はFSP SAU SPI Web版ドキュメントに以下のような記載がありますのでご参考ください。
プログラム実装
SPI Flash Memory Moduleに搭載のWinbond W25Q64FVはデファクトスタンダードのコマンド(命令)を用いて制御を行います。様々なコマンドインターフェースを持っていますが、今回の記事では必要最低限のコマンド用いて制御を行います。
実装するコマンドインターフェースの一覧
初期化(パワーダウン解除)
プログラムコード
W25Q64_Init関数
/* -------------------------------------------------------
* 初期化
* ------------------------------------------------------- */
fsp_err_t W25Q64_Init(void)
{
CS_HIGH(); /* 非選択状態を確定 */
fsp_err_t err = R_SAU_SPI_Open(&g_spi0_ctrl, &g_spi0_cfg);
if (FSP_SUCCESS != err) return err;
/* パワーダウン解除 */
uint8_t cmd = W25Q64_CMD_RELEASE_POWER_DOWN;
CS_LOW();
err = spi_write(&cmd, 1U);
CS_HIGH();
/* tRES1 待機(最低3us → 余裕を持って1ms) */
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
return err;
}
入出力波形

JEDEC IDの読み出し
プログラムコード
W25Q64_ReadJEDECID関数
/* -------------------------------------------------------
* JEDEC ID 読み出し
* 正常時:id[0]=0xEF id[1]=0x40 id[2]=0x17
* ------------------------------------------------------- */
fsp_err_t W25Q64_ReadJEDECID(uint8_t *id)
{
uint8_t tx[4] = { W25Q64_CMD_JEDEC_ID, 0xFFU, 0xFFU, 0xFFU };
uint8_t rx[4] = { 0U };
CS_LOW();
fsp_err_t err = spi_write_read(tx, rx, 4U);
CS_HIGH();
if (FSP_SUCCESS == err)
{
id[0] = rx[1]; /* Manufacturer ID : 0xEF */
id[1] = rx[2]; /* Memory Type : 0x40 */
id[2] = rx[3]; /* Capacity : 0x17 */
}
return err;
}
入出力波形

書き込み許可
プログラムコード
write_enable関数
/* -------------------------------------------------------
* 書き込み許可
* ------------------------------------------------------- */
static fsp_err_t write_enable(void)
{
uint8_t cmd = W25Q64_CMD_WRITE_ENABLE;
CS_LOW();
fsp_err_t err = spi_write(&cmd, 1U);
CS_HIGH();
return err;
}
入出力波形

ステータスレジスタ読み出し
プログラムコード
read_status1関数
/* -------------------------------------------------------
* ステータスレジスタ1 読み出し
* ------------------------------------------------------- */
static fsp_err_t read_status1(uint8_t *status)
{
uint8_t tx[2] = { W25Q64_CMD_READ_STATUS1, 0xFFU };
uint8_t rx[2] = { 0U };
CS_LOW();
fsp_err_t err = spi_write_read(tx, rx, 2U);
CS_HIGH();
if (FSP_SUCCESS == err) *status = rx[1];
return err;
}
入出力波形

セクタ消去(4KB)
プログラムコード
W25Q64_SectorErase関数
/* -------------------------------------------------------
* セクタ消去(4KB単位)
* addr は 4KB 境界アドレス(下位12bitは無視される)
* ------------------------------------------------------- */
fsp_err_t W25Q64_SectorErase(uint32_t addr)
{
fsp_err_t err = write_enable();
if (FSP_SUCCESS != err) return err;
uint8_t tx[4] = {
W25Q64_CMD_SECTOR_ERASE,
(uint8_t)(addr >> 16U),
(uint8_t)(addr >> 8U),
(uint8_t)(addr >> 0U)
};
CS_LOW();
err = spi_write(tx, 4U);
CS_HIGH();
if (FSP_SUCCESS == err)
{
W25Q64_WaitDone(); /* tSE 完了待ち(最大400ms) */
}
return err;
}
入出力波形

データ書き込み
プログラムコード
W25Q64_PageProgram関数
/* -------------------------------------------------------
* ページプログラム(最大256バイト)
* addr はページ境界(256バイト境界)を推奨
* ------------------------------------------------------- */
fsp_err_t W25Q64_PageProgram(uint32_t addr, const uint8_t *buf, uint32_t len)
{
if (len == 0U || len > W25Q64_PAGE_SIZE)
{
return FSP_ERR_INVALID_ARGUMENT;
}
fsp_err_t err = write_enable();
if (FSP_SUCCESS != err) return err;
uint8_t hdr[4] = {
W25Q64_CMD_PAGE_PROGRAM,
(uint8_t)(addr >> 16U),
(uint8_t)(addr >> 8U),
(uint8_t)(addr >> 0U)
};
CS_LOW(); /* CS アサート */
err = spi_write(hdr, 4U); /* コマンド+アドレス送信 */
if (FSP_SUCCESS == err)
{
err = spi_write(buf, len); /* データ送信(CS維持) */
}
CS_HIGH(); /* CS ネゲート */
if (FSP_SUCCESS == err)
{
W25Q64_WaitDone(); /* tPP 完了待ち(最大3ms) */
}
return err;
}
入出力波形

データ読み出し
プログラムコード
W25Q64_ReadData関数
/* -------------------------------------------------------
* データ読み出し
* コマンド+アドレス送信後、CSを維持したままデータ受信
* ------------------------------------------------------- */
fsp_err_t W25Q64_ReadData(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint8_t hdr[4] = {
W25Q64_CMD_READ_DATA,
(uint8_t)(addr >> 16U),
(uint8_t)(addr >> 8U),
(uint8_t)(addr >> 0U)
};
CS_LOW(); /* CS アサート */
fsp_err_t err = spi_write(hdr, 4U); /* コマンド+アドレス送信 */
if (FSP_SUCCESS == err)
{
err = spi_read(buf, len); /* データ受信(CS維持) */
}
CS_HIGH(); /* CS ネゲート */
return err;
}
入出力波形

チップ消去
プログラムコード
W25Q64_ChipErase関数
/* -------------------------------------------------------
* チップ全消去
* 完了まで最大200秒かかる場合あり(W25Q64仕様)
* ------------------------------------------------------- */
fsp_err_t W25Q64_ChipErase(void)
{
fsp_err_t err = write_enable();
if (FSP_SUCCESS != err) return err;
uint8_t cmd = W25Q64_CMD_CHIP_ERASE;
CS_LOW();
err = spi_write(&cmd, 1U);
CS_HIGH();
if (FSP_SUCCESS == err)
{
W25Q64_WaitDone();
}
return err;
}
入出力波形
※ チップ消去の入出力波形はありません
SPI Flash Memoryの制御サンプルプログラム(hal_entry関数)
プログラムコード
📄 hal_entry.c – W25Q64 Read/Write Test
void hal_entry関数(void)
{
/* TODO: add your own code here */
/* W25Q64の初期化(SPIの初期化を含む) ---- */
gui_err = W25Q64_Init();
if (FSP_SUCCESS != gui_err) { while (1); }
/* JEDEC IDの確認 / W25Q64のJEDEC IDは : 0xEF, 0x40, 0x17 */
gui_err = W25Q64_ReadJEDECID((uint8_t *)gui_id);
if (FSP_SUCCESS != gui_err || gui_id[0] != 0xEFU) { while (1); }
/* セクタ消去(先頭4KB) */
gui_err = W25Q64_SectorErase(0x000000UL);
if (FSP_SUCCESS != gui_err) { while (1); }
/* 書き込みバッファの初期化(256バイト : 0 - 255) */
for (int i = 0; i < 256; i++) { gui_wbuf[i] = (uint8_t)i; }
/* ページ書き込み(256バイト) */
gui_err = W25Q64_PageProgram(0x000000UL, (uint8_t *)gui_wbuf, sizeof(gui_wbuf));
if (FSP_SUCCESS != gui_err) { while (1); }
/* データ読み出し */
gui_err = W25Q64_ReadData(0x000000UL, (uint8_t *)gui_rbuf, sizeof(gui_rbuf));
if (FSP_SUCCESS != gui_err) { while (1); }
/* 検証 */
if (memcmp((uint8_t *)gui_wbuf, (uint8_t *)gui_rbuf, sizeof(gui_wbuf)) == 0)
{
/* 成功 */
while (1);
}
else
{
/* 不一致エラー */
while (1);
}
}
まとめ
今回は必要最低限のコマンドのみを実装しましたが、基本的な機能のJEDEC IDの読み出し・フラッシュメモリへの書き込みと読み出しが簡単にできることがわかったと思います。
実用的に使う場合、書き込みデータサイズや各セクタ、ブロック単位のページング処理などが必要となりますので、機会があればご紹介させていただきます。
最後までお読みいただき、ありがとうございました。
サンプルコード
w25q64.c
📄 w25q64.c — W25Q64 SPIフラッシュ ドライバ(RA0E1 / SAU-SPI)
▼
/*
* w25q64.c
*
* Created on: 2026/02/21
* Author: 13963
*/
/* ヘッダファイル : 一部デバイス依存 */
#include "hal_data.h"
#include "r_sau_spi.h"
#include "bsp_api.h"
#include "w25q64.h"
/* -------------------------------------------------------
* CS マクロ(BSP I/O access API・アクティブロー)
* R_BSP_PinWrite の前後に Enable/Disable が必要
* 端子設定はデバイス依存
* ------------------------------------------------------- */
#define CS_LOW() R_IOPORT_PinWrite(&g_ioport_ctrl, W25Q64_CS_PORT, BSP_IO_LEVEL_LOW)
#define CS_HIGH() R_IOPORT_PinWrite(&g_ioport_ctrl, W25Q64_CS_PORT, BSP_IO_LEVEL_HIGH)
/* -------------------------------------------------------
* 転送完了フラグ(コールバックでセット)
* ------------------------------------------------------- */
static volatile bool s_spi_done = false;
static volatile bool s_spi_error = false;
/* -------------------------------------------------------
* SAU SPI コールバック
* FSP Configurator の Callback 欄に "sau_spi0_callback" を登録
* ------------------------------------------------------- */
void sau_spi0_callback(spi_callback_args_t *p_args)
{
if (SPI_EVENT_TRANSFER_COMPLETE == p_args->event)
{
s_spi_done = true;
}
else
{
s_spi_error = true;
}
}
/* -------------------------------------------------------
* 内部:転送完了待ち(タイムアウト付き)
* ------------------------------------------------------- */
static fsp_err_t spi_wait(void)
{
uint32_t timeout = 500000UL;
while (!s_spi_done && !s_spi_error)
{
if (--timeout == 0U)
{
return FSP_ERR_TIMEOUT;
}
}
if (s_spi_error)
{
s_spi_error = false;
s_spi_done = false;
return FSP_ERR_TRANSFER_ABORTED;
}
s_spi_done = false;
return FSP_SUCCESS;
}
/* -------------------------------------------------------
* 内部:送信のみ(CSはこの関数の外で制御)
* ------------------------------------------------------- */
static fsp_err_t spi_write(const uint8_t *tx, uint32_t len)
{
fsp_err_t err = R_SAU_SPI_Write(&g_spi0_ctrl, tx, len, SPI_BIT_WIDTH_8_BITS);
if (FSP_SUCCESS != err) return err;
return spi_wait();
}
/* -------------------------------------------------------
* 内部:受信のみ(CSはこの関数の外で制御)
* ------------------------------------------------------- */
static fsp_err_t spi_read(uint8_t *rx, uint32_t len)
{
fsp_err_t err = R_SAU_SPI_Read(&g_spi0_ctrl, rx, len, SPI_BIT_WIDTH_8_BITS);
if (FSP_SUCCESS != err) return err;
return spi_wait();
}
/* -------------------------------------------------------
* 内部:送受信(CSはこの関数の外で制御)
* ------------------------------------------------------- */
static fsp_err_t spi_write_read(const uint8_t *tx, uint8_t *rx, uint32_t len)
{
fsp_err_t err = R_SAU_SPI_WriteRead(&g_spi0_ctrl, tx, rx, len, SPI_BIT_WIDTH_8_BITS);
if (FSP_SUCCESS != err) return err;
return spi_wait();
}
/* -------------------------------------------------------
* 初期化
* ------------------------------------------------------- */
fsp_err_t W25Q64_Init(void)
{
CS_HIGH(); /* 非選択状態を確定 */
fsp_err_t err = R_SAU_SPI_Open(&g_spi0_ctrl, &g_spi0_cfg);
if (FSP_SUCCESS != err) return err;
/* パワーダウン解除 */
uint8_t cmd = W25Q64_CMD_RELEASE_POWER_DOWN;
CS_LOW();
err = spi_write(&cmd, 1U);
CS_HIGH();
/* tRES1 待機(最低3us → 余裕を持って1ms) */
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
return err;
}
/* -------------------------------------------------------
* JEDEC ID 読み出し
* 正常時:id[0]=0xEF id[1]=0x40 id[2]=0x17
* ------------------------------------------------------- */
fsp_err_t W25Q64_ReadJEDECID(uint8_t *id)
{
uint8_t tx[4] = { W25Q64_CMD_JEDEC_ID, 0xFFU, 0xFFU, 0xFFU };
uint8_t rx[4] = { 0U };
CS_LOW();
fsp_err_t err = spi_write_read(tx, rx, 4U);
CS_HIGH();
if (FSP_SUCCESS == err)
{
id[0] = rx[1]; /* Manufacturer ID : 0xEF */
id[1] = rx[2]; /* Memory Type : 0x40 */
id[2] = rx[3]; /* Capacity : 0x17 */
}
return err;
}
/* -------------------------------------------------------
* 内部:ステータスレジスタ1 読み出し
* ------------------------------------------------------- */
static fsp_err_t read_status1(uint8_t *status)
{
uint8_t tx[2] = { W25Q64_CMD_READ_STATUS1, 0xFFU };
uint8_t rx[2] = { 0U };
CS_LOW();
fsp_err_t err = spi_write_read(tx, rx, 2U);
CS_HIGH();
if (FSP_SUCCESS == err) *status = rx[1];
return err;
}
/* -------------------------------------------------------
* ビジー確認
* ------------------------------------------------------- */
bool W25Q64_IsBusy(void)
{
uint8_t status = 0U;
read_status1(&status);
return (bool)(status & W25Q64_STATUS_BUSY);
}
/* -------------------------------------------------------
* 書き込み/消去完了待ち
* ------------------------------------------------------- */
void W25Q64_WaitDone(void)
{
while (W25Q64_IsBusy())
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
}
}
/* -------------------------------------------------------
* 内部:ライトイネーブル
* ------------------------------------------------------- */
static fsp_err_t write_enable(void)
{
uint8_t cmd = W25Q64_CMD_WRITE_ENABLE;
CS_LOW();
fsp_err_t err = spi_write(&cmd, 1U);
CS_HIGH();
return err;
}
/* -------------------------------------------------------
* データ読み出し
* コマンド+アドレス送信後、CSを維持したままデータ受信
* ------------------------------------------------------- */
fsp_err_t W25Q64_ReadData(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint8_t hdr[4] = {
W25Q64_CMD_READ_DATA,
(uint8_t)(addr >> 16U),
(uint8_t)(addr >> 8U),
(uint8_t)(addr >> 0U)
};
CS_LOW(); /* CS アサート */
fsp_err_t err = spi_write(hdr, 4U); /* コマンド+アドレス送信 */
if (FSP_SUCCESS == err)
{
err = spi_read(buf, len); /* データ受信(CS維持) */
}
CS_HIGH(); /* CS ネゲート */
return err;
}
/* -------------------------------------------------------
* ページプログラム(最大256バイト)
* addr はページ境界(256バイト境界)を推奨
* ------------------------------------------------------- */
fsp_err_t W25Q64_PageProgram(uint32_t addr, const uint8_t *buf, uint32_t len)
{
if (len == 0U || len > W25Q64_PAGE_SIZE)
{
return FSP_ERR_INVALID_ARGUMENT;
}
fsp_err_t err = write_enable();
if (FSP_SUCCESS != err) return err;
uint8_t hdr[4] = {
W25Q64_CMD_PAGE_PROGRAM,
(uint8_t)(addr >> 16U),
(uint8_t)(addr >> 8U),
(uint8_t)(addr >> 0U)
};
CS_LOW(); /* CS アサート */
err = spi_write(hdr, 4U); /* コマンド+アドレス送信 */
if (FSP_SUCCESS == err)
{
err = spi_write(buf, len); /* データ送信(CS維持) */
}
CS_HIGH(); /* CS ネゲート */
if (FSP_SUCCESS == err)
{
W25Q64_WaitDone(); /* tPP 完了待ち(最大3ms) */
}
return err;
}
/* -------------------------------------------------------
* セクタ消去(4KB単位)
* addr は 4KB 境界アドレス(下位12bitは無視される)
* ------------------------------------------------------- */
fsp_err_t W25Q64_SectorErase(uint32_t addr)
{
fsp_err_t err = write_enable();
if (FSP_SUCCESS != err) return err;
uint8_t tx[4] = {
W25Q64_CMD_SECTOR_ERASE,
(uint8_t)(addr >> 16U),
(uint8_t)(addr >> 8U),
(uint8_t)(addr >> 0U)
};
CS_LOW();
err = spi_write(tx, 4U);
CS_HIGH();
if (FSP_SUCCESS == err)
{
W25Q64_WaitDone(); /* tSE 完了待ち(最大400ms) */
}
return err;
}
/* -------------------------------------------------------
* チップ全消去
* 完了まで最大200秒かかる場合あり(W25Q64仕様)
* ------------------------------------------------------- */
fsp_err_t W25Q64_ChipErase(void)
{
fsp_err_t err = write_enable();
if (FSP_SUCCESS != err) return err;
uint8_t cmd = W25Q64_CMD_CHIP_ERASE;
CS_LOW();
err = spi_write(&cmd, 1U);
CS_HIGH();
if (FSP_SUCCESS == err)
{
W25Q64_WaitDone();
}
return err;
}
w25q64.h
📄 w25q64.h — W25Q64 SPIフラッシュ ドライバ ヘッダ(RA0E1 / SAU-SPI)
▼
/*
* w25q64.h
*
* Created on: 2026/02/21
* Author: 13963
*/
#ifndef W25Q64_H_
#define W25Q64_H_
/* ヘッダファイル : 一部デバイス依存 */
#include "hal_data.h"
#include <stdint.h>
#include <stdbool.h>
/* -------------------------------------------------------
* W25Q64 コマンド定義
* ------------------------------------------------------- */
#define W25Q64_CMD_JEDEC_ID (0x9FU)
#define W25Q64_CMD_READ_STATUS1 (0x05U)
#define W25Q64_CMD_WRITE_ENABLE (0x06U)
#define W25Q64_CMD_PAGE_PROGRAM (0x02U)
#define W25Q64_CMD_READ_DATA (0x03U)
#define W25Q64_CMD_SECTOR_ERASE (0x20U) /* 4KB */
#define W25Q64_CMD_CHIP_ERASE (0xC7U)
#define W25Q64_CMD_RELEASE_POWER_DOWN (0xABU)
/* ステータスレジスタ1 ビット定義 */
#define W25Q64_STATUS_BUSY (0x01U)
#define W25Q64_STATUS_WEL (0x02U)
/* フラッシュパラメータ */
#define W25Q64_PAGE_SIZE (256U)
#define W25Q64_SECTOR_SIZE (4096U)
/*
* CSピン定義(Pin20)
* ハードウェアマニュアルのピン配置表で実際のポートを確認して変更すること
* 端子設定はデバイス依存
*/
#define W25Q64_CS_PORT BSP_IO_PORT_01_PIN_03
/* -------------------------------------------------------
* 公開API(戻り値 : デバイス依存)
* ------------------------------------------------------- */
fsp_err_t W25Q64_Init(void);
fsp_err_t W25Q64_ReadJEDECID(uint8_t *id);
fsp_err_t W25Q64_ReadData(uint32_t addr, uint8_t *buf, uint32_t len);
fsp_err_t W25Q64_PageProgram(uint32_t addr, const uint8_t *buf, uint32_t len);
fsp_err_t W25Q64_SectorErase(uint32_t addr);
fsp_err_t W25Q64_ChipErase(void);
bool W25Q64_IsBusy(void);
void W25Q64_WaitDone(void);
#endif /* W25Q64_H_ */


