“RA MCU眾測寶典”中I2C/SPI通信與顯示驅動專題更新了。這次我們聚焦瑞薩【CPKCOR-RA8D1B核心板】開發(fā)板,一步步實現(xiàn)QSPI讀取外部Flash。
開啟寶典
QSPI是Queued SPI的簡寫,是Motorola公司推出的SPI接口的擴展,比SPI應用更加廣泛。在SPI協(xié)議的基礎上,Motorola公司對其功能進行了增強,增加了隊列傳輸機制,推出了隊列串行外圍接口協(xié)議(即QSPI協(xié)議)。
QSPI是一種專用的通信接口,連接單、雙或四(條數(shù)據(jù)線)SPI Flash存儲介質。QSPI是一個內存控制器,用于連接具有SPI兼容接口的串行ROM(非易失性存儲器)。
我們看一下核心板上的外擴Flash:

外擴的Flash的型號是AT25SF128B。
QSPI使用6個信號連接Flash,分別是四個數(shù)據(jù)線QIO0~QIO3,一個時鐘輸出CLK,一個片選輸出(低電平有效)QSSL,它們的作用介紹如下:
QSSL:片選輸出(低電平有效),適用于FLASH 1。如果QSPI始終在雙閃存模式下工作,則其也可用于FLASH 2從設備選擇信號線。QSPI通訊以QSSL線置低電平為開始信號,以QSSL線被拉高作為結束信號。
CLK:時鐘輸出,適用于兩個存儲器,用于通訊數(shù)據(jù)同步。它由通訊主機產生,決定了通訊的速率,不同的設備支持的最高時鐘頻率不一樣,兩個設備之間通訊時,通訊速率受限于低速設備。
QIO0QIO0~QIO3:四線模式中為雙向IO。
接下來進行軟件配置:
添加OSPI功能模塊:

接下來我們配置一下引腳:

一共是6個引腳,接下來配置模塊信息:

下面是部分Flash的命令,我們可以初始化這些內容:

接下來我們代碼測試一下QSPI的功能。
我們定義了一些基礎功能測試:
左右滑動查看完整內容
uint8_tg_read_data [OSPI_B_APP_DATA_SIZE] = {RESET_VALUE}; uint8_tg_write_data [OSPI_B_APP_DATA_SIZE] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, }; spi_flash_direct_transfer_tg_ospi_b_direct_transfer [OSPI_B_TRANSFER_MAX] = { /* Transfer structure for SPI mode */ [OSPI_B_TRANSFER_WRITE_ENABLE_SPI] = { .command = OSPI_B_COMMAND_WRITE_ENABLE_SPI, .address = OSPI_B_ADDRESS_DUMMY, .data = OSPI_B_DATA_DUMMY, .command_length = OSPI_B_COMMAND_LENGTH_SPI, .address_length = OSPI_B_ADDRESS_LENGTH_ZERO, .data_length = OSPI_B_DATA_LENGTH_ZERO, .dummy_cycles = OSPI_B_DUMMY_CYCLE_WRITE_SPI }, [OSPI_B_TRANSFER_READ_STATUS_SPI] = { .command = OSPI_B_COMMAND_READ_STATUS_SPI, .address = OSPI_B_ADDRESS_DUMMY, .data = OSPI_B_DATA_DUMMY, .command_length = OSPI_B_COMMAND_LENGTH_SPI, .address_length = OSPI_B_ADDRESS_LENGTH_ZERO, .data_length = OSPI_B_DATA_LENGTH_ONE, .dummy_cycles = OSPI_B_DUMMY_CYCLE_READ_STATUS_SPI }, [OSPI_B_TRANSFER_READ_DEVICE_ID_SPI] = { .command = OSPI_B_COMMAND_READ_DEVICE_ID_SPI, //0x9f .address = OSPI_B_ADDRESS_DUMMY, //0 .data = OSPI_B_DATA_DUMMY, //0 .command_length = OSPI_B_COMMAND_LENGTH_SPI, //1 .address_length = OSPI_B_ADDRESS_LENGTH_ZERO, //0 .data_length = OSPI_B_DATA_LENGTH_FOUR, //4 .dummy_cycles = OSPI_B_DUMMY_CYCLE_READ_STATUS_SPI //0 } }; fsp_err_tospi_b_read_device_id(uint32_t*constp_id) { fsp_err_t err = FSP_SUCCESS; spi_flash_direct_transfer_ttransfer = {RESET_VALUE}; /* Read and check flash device ID */ transfer = g_ospi_b_direct_transfer[OSPI_B_TRANSFER_READ_DEVICE_ID_SPI]; err =R_OSPI_B_DirectTransfer(&g_qspi0_flash_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); if(err!=FSP_SUCCESS) { printf("R_OSPI_B_DirectTransfer API FAILED "); } /* Get flash device ID */ *p_id = transfer.data; returnerr; } staticfsp_err_tospi_b_write_enable(void) { fsp_err_t err = FSP_SUCCESS; spi_flash_direct_transfer_ttransfer = {RESET_VALUE}; /* Transfer write enable command */ transfer = g_ospi_b_direct_transfer[OSPI_B_TRANSFER_WRITE_ENABLE_SPI]; err =R_OSPI_B_DirectTransfer(&g_qspi0_flash_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); assert(FSP_SUCCESS == err); /* Read Status Register */ transfer = g_ospi_b_direct_transfer[OSPI_B_TRANSFER_READ_STATUS_SPI]; err =R_OSPI_B_DirectTransfer(&g_qspi0_flash_ctrl, &transfer, SPI_FLASH_DIRECT_TRANSFER_DIR_READ); assert(FSP_SUCCESS == err); /* Check Write Enable bit in Status Register */ if(OSPI_B_WEN_BIT_MASK != (transfer.data & OSPI_B_WEN_BIT_MASK)) { printf("Write enable FAILED "); } returnerr; } staticfsp_err_tospi_b_wait_operation(uint32_ttimeout) { fsp_err_t err = FSP_SUCCESS; spi_flash_status_tstatus = {RESET_VALUE}; status.write_in_progress =true; while(status.write_in_progress) { /* Get device status */ R_OSPI_B_StatusGet(&g_qspi0_flash_ctrl, &status); if(RESET_VALUE == timeout) { printf("OSPI time out occurred "); } R_BSP_SoftwareDelay(1, OSPI_B_TIME_UNIT); timeout --; } returnerr; } staticfsp_err_tospi_b_erase_operation(uint8_t*constp_address) { fsp_err_t err = FSP_SUCCESS; uint32_t sector_size = RESET_VALUE; uint32_t erase_timeout = RESET_VALUE; /* Check sector size according to input address pointer, described in S28HS512T data sheet */ if(OSPI_B_SECTOR_4K_END_ADDRESS < (uint32_t)p_address) ? ? { ? ? ? ? sector_size = OSPI_B_SECTOR_SIZE_256K; ? ? ? ? erase_timeout = OSPI_B_TIME_ERASE_256K; ? ? } else ? ? { ? ? ? ? sector_size = OSPI_B_SECTOR_SIZE_4K; ? ? ? ? erase_timeout = OSPI_B_TIME_ERASE_4K; ? ? } /* Performs erase sector */ ? ? err =?R_OSPI_B_Erase(&g_qspi0_flash_ctrl, p_address, sector_size); /* Wait till operation completes */ ? ? err =?ospi_b_wait_operation(erase_timeout); return?err; } staticfsp_err_tospi_b_write_operation(uint8_t?*?const?p_address, uint8_t?*pdata,?uint16_t?len) { fsp_err_t? ?err ? ? ? ? = FSP_SUCCESS; /* Erase sector before write data to flash device */ ? ? err =?ospi_b_erase_operation(p_address); /* Write data to flash device */ ? ? err =?R_OSPI_B_Write(&g_qspi0_flash_ctrl, pdata, p_address, len); /* Wait until write operation completes */ ? ? err =?ospi_b_wait_operation(OSPI_B_TIME_WRITE); return?err; } staticfsp_err_tospi_b_read_operation(uint8_t?*?const?p_address,uint8_t?*pdata,?uint16_t?len) { fsp_err_t?err = FSP_SUCCESS; /* Clean read buffer */ memset(pdata, RESET_VALUE, len); /* Read data from flash device */ memcpy(pdata, p_address, len); return?err; }
在main中我們需要先初始化:
左右滑動查看完整內容
voidqspi_FlashInit(void)
{
/* Open the OSPI instance. */
fsp_err_terr=R_OSPI_B_Open(&g_qspi0_flash_ctrl, &g_qspi0_flash_cfg);
assert(FSP_SUCCESS == err);
/* Switch OSPI module to 1S-1S-1S mode to configure flash device */
err = R_OSPI_B_SpiProtocolSet(&g_qspi0_flash_ctrl, SPI_FLASH_PROTOCOL_EXTENDED_SPI);
assert(FSP_SUCCESS == err);
/* Reset flash device by driving OM_RESET pin */
R_XSPI->LIOCTL_b.RSTCS0 =0;
R_BSP_SoftwareDelay(OSPI_B_TIME_RESET_PULSE, OSPI_B_TIME_UNIT);
R_XSPI->LIOCTL_b.RSTCS1 =1;
R_BSP_SoftwareDelay(OSPI_B_TIME_RESET_SETUP, OSPI_B_TIME_UNIT);
ospi_b_write_enable();
}
然后直接初始化階段測試QSPI,我們寫入一頁數(shù)據(jù),但是之讀取其中的16個,并通過串口打印:
左右滑動查看完整內容
fsp_err_tospi_b_Testoperation(uint8_t*p_address)
{
fsp_err_t err =FSP_SUCCESS;
uint16_t i=0;
err=ospi_b_erase_operation(p_address);
err=ospi_b_write_operation (p_address,g_write_data,OSPI_B_APP_DATA_SIZE);
if(err==FSP_SUCCESS)
{
/* Print execution time */
printf("Write %d bytes completed successfully
", (int)(OSPI_B_APP_DATA_SIZE));
}
else
{
printf("Write operation failure
");
}
printf("Write Data:
");
for(i=0;i<=OSPI_B_APP_DATA_SIZE-1;i++)
? ? {
? ? ? ? printf("%d ",g_write_data[i]);
? ? }
? ? err?=?ospi_b_read_operation (p_address,g_read_data,16);
? ??if(err==FSP_SUCCESS)
? ? {
? ? ? ??/* Print execution time */
? ? ? ? printf("
Read %d bytes completed successfully
", (int)(OSPI_B_APP_DATA_SIZE));
? ? }
? ??else
? ? {
? ? ? ? printf("
Read operation failure
");
? ? }
? ? printf("Read Data:
");
? ??for(i=0;i<=sizeof(g_read_data)-1;i++)
? ? {
? ? ? ? printf("%d ",g_read_data[i]);
? ? }
? ??/* Compare data read and date written */
? ??if(RESET_VALUE?==?memcmp(&g_read_data,?&g_write_data, (size_t)16))
? ? {
? ? ? ? printf("
Data read matched data written
");
? ? ? ? printf("flash讀寫數(shù)據(jù)成功
");
? ? }
? ??else
? ? {
? ? ? ? printf("
Data read does not match data written
");
? ? ? ? printf("flash讀寫數(shù)據(jù)失敗
");
? ? }
? ??/* Performs OSPI erase operation */
? ? err?=?ospi_b_erase_operation(p_address);
? ??if(err==FSP_SUCCESS)
? ? {
? ? ? ??/* Print execution time */
? ? ? ? printf("Erase sector completed successfully
");
? ? }
? ??else
? ? {
? ? ? ? printf("erase operation failure
");
? ? }
? ??return?err;
}
串口打印結果如下:

從QSPI的六引腳配置、OSPI功能模塊添加,到命令集定義、Flash讀寫擦除的代碼實現(xiàn),再到串口打印驗證數(shù)據(jù)匹配。我們不僅掌握了不同通信協(xié)議的配置邏輯,還摸清了它們在存儲外設交互中的核心應用——這次通過QSPI實現(xiàn)Flash的讀寫擦除與數(shù)據(jù)驗證,正是高速通信在存儲場景的典型落地。
I2C/SPI通信與顯示驅動專題的技能專題還在持續(xù)拓展!如果你在實操中對QSPI的協(xié)議時序、Flash命令配置有新的感悟,或是想分享更多通信驅動的實戰(zhàn)場景,歡迎在評論區(qū)交流。
瑞薩樣品/開發(fā)板申請入口如下,可掃描二維碼或復制鏈接到瀏覽器獲得
申請入口
瑞薩樣品/開發(fā)板申請入口:
https://jsj.top/f/AgUyYV
需要技術支持?
如您在使用瑞薩MCU/MPU產品中有任何問題,可識別下方二維碼或復制網(wǎng)址到瀏覽器中打開,進入瑞薩技術論壇尋找答案或獲取在線技術支持。
https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/
-
FlaSh
+關注
關注
10文章
1747瀏覽量
155487 -
瑞薩
+關注
關注
37文章
22481瀏覽量
90828 -
開發(fā)板
+關注
關注
26文章
6288瀏覽量
117992 -
QSPI
+關注
關注
0文章
55瀏覽量
13355 -
核心板
+關注
關注
6文章
1396瀏覽量
31994
原文標題:RA MCU眾測寶典 | 在瑞薩CPKCOR-RA8D1B核心板上實現(xiàn)QSPI讀取外部Flash
文章出處:【微信號:瑞薩嵌入式小百科,微信公眾號:瑞薩嵌入式小百科】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
【瑞薩RA8D1 CPK開發(fā)板試用】開箱與點燈
【瑞薩RA8D1 CPK開發(fā)板】RA8D1移植ThreadX操作系統(tǒng)
【CPKCOR-RA8D1】+ 1.RA8D1開箱點燈
【CPKCOR-RA8D1】介紹、環(huán)境搭建、工程測試
【CPKCOR-RA8D1】1、VSCODE+CMAKE開發(fā)環(huán)境搭建
【CPKCOR-RA8D1】開發(fā)套件全攻略:從硬件解析到攝像頭顯示Demo實戰(zhàn)
【CPKCOR-RA8D1】+ 基礎串口打印測試
【CPKCOR-RA8D1】開發(fā)套件全攻略:從硬件解析到攝像頭顯示Demo實戰(zhàn)
【CPKCOR-RA8D1】基礎串口打印測試
【CPKCOR-RA8D1】基于G.729A算法研究
RA MCU眾測寶典 | 在瑞薩CPKCOR-RA8D1B核心板上實現(xiàn)QSPI讀取外部Flash
在瑞薩CPKCOR-RA8D1B核心板上實現(xiàn)QSPI讀取外部Flash
評論