国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

瑞薩RA系列FSP庫開發實戰指南之QSPI讀寫外部Flash芯片實驗

瑞薩嵌入式小百科 ? 來源:野火電子 ? 2026-03-03 13:57 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

23.3

實驗:讀寫外部Flash芯片

23.3.1

硬件設計

野火啟明6M5開發板的QSPI FLASH電路圖如圖所示:

9b72158a-16b7-11f1-90a1-92fbcf53809c.png

野火啟明4M2開發板的QSPI FLASH電路圖如圖所示:

9bc6d2f0-16b7-11f1-90a1-92fbcf53809c.png

野火啟明2L1開發板的SPI FLASH電路圖如圖所示:

9c1a84b8-16b7-11f1-90a1-92fbcf53809c.png

FLASH芯片連接到MCU的引腳如下表所示。

9c6e6830-16b7-11f1-90a1-92fbcf53809c.png

23.3.2

軟件設計

23.3.2.1

新建工程

因為本章節的QSPI Flash相關實驗例程需要用到板子上的串口功能,因此我們可以直接以前面的“19_UART_Receive_Send”工程為基礎進行修改。

對于e2studio開發環境:拷貝一份我們之前的e2s工程模板“19_UART_Receive_Send”,然后將工程文件夾重命名為“QSPI_Flash”,最后再將它導入到我們的e2studio工作空間中。

對于Keil開發環境:拷貝一份我們之前的Keil工程模板“19_UART_Receive_Send”,然后將工程文件夾重命名為“QSPI_Flash”,并進入該文件夾里面雙擊Keil工程文件,打開該工程。

對于野火啟明2L1開發板,連接外部Flash使用的是普通SPI接口,工程文件夾可重命名為“SPI_Flash”

工程新建好之后,在工程根目錄的“src”文件夾下面新建“qspi_flash”或“spi_flash”文件夾,再進入改文件夾里面新建源文件和頭文件:“bsp_qspi_flash.c/.h”(啟明6M5/啟明4M2開發板)或“bsp_spi_flash.c/.h”(啟明2L1開發板)。

工程文件結構如下。

列表2:文件結構

左右滑動查看完整內容

QSPI_Flash(啟明6M5/啟明4M2開發板)或SPI_Flash(啟明2L1開發板)
├─ ......
└─src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ qspi_flash 或spi_flash(啟明2L1開發板)
│ ├─ bsp_qspi_flash.c或bsp_spi_flash.c(啟明2L1開發板)
│ └─ bsp_qspi_flash.h或bsp_spi_flash.h(啟明2L1開發板)
└─ hal_entry.c

23.3.2.2.FSP配置

打開工程項目的 FSP 配置界面進行配置。 啟明6M5/啟明4M2開發板對比啟明2L1開發板,由于前者使用QSPI外設連接Flash芯片,后者使用SPI外設連接Flash芯片, QSPI與SPI是兩個不同的外設,因此它們的FSP配置方法有比較大的不同。

對于啟明6M5/啟明4M2開發板的 “QSPI_Flash” 工程:

打開工程項目的 FSP 配置界面之后,首先切到“Pins”頁面,配置QSPI引腳。

啟明6M5按照如下圖所示配置:

圖

啟明4M2按照如下圖所示配置:

接著在 FSP 配置界面里面依次點擊“Stacks”->“New Stack”->“Storage”->“QSPI”來添加QSPI模塊。 如下圖所示。

圖

按照如下圖所示對 QSPI 模塊屬性進行配置:

圖

QSPI 模塊的屬性介紹如下:

QSPI 屬性介紹
QSPI屬性 描述
SPI Protocol SPI協議。
Address Byte 地址的長度(字節)。
Read Mode 讀取的模式。
Page Size Bytes 頁寫入長度(字節)。
Command Definitions 指令的定義。
QSPKCLK Divisor CLK時鐘設置
Minimum QSSL Deselect Cycles QSSL保持周期
Pins QSPI引腳配置

注解

當我們需要QSPI四根線進行四線快數讀取數據的時候,我們只需要在Read Mode里選擇 Fast Read Quad I/O 即可。

對于啟明2L1開發板的 “SPI_Flash” 工程:

打開工程項目的 FSP 配置界面之后,首先切到“Pins”頁面,配置SPI引腳。

啟明2L1按照如下圖所示配置:

圖

接著在 FSP 配置界面里面依次點擊Stacks->New Stack->Connectivity->SPI (r_spi)來添加SPI模塊。 如下圖所示。

圖

按照如下圖所示對 SPI 模塊屬性進行配置:

圖

SPI 模塊的屬性介紹如下:

SPI 屬性介紹
SPI屬性 描述
Name 模塊實例名。設置為g_spi0_flash
Channel 通道。這里選擇spi0
Receive Interrupt Priority 接收中斷優先級
Transmit Buffer Empty Interrupt Priority 發送緩存區空中斷優先級
Transfer Complete Interrupt Priority 發送完成中斷優先級
Error Interrupt Priority 錯誤中斷優先級
Operating Mode 操作模式。可選SPI主機或從機
Clock Phase SPI時鐘相位
Clock Polarity SPI時鐘極性
Mode Fault Error 模式錯誤檢測。檢測主從模式沖突
Bit Order 位時序。MSB或LSB
Callback 中斷回調函數。設置為spi_flash_callback
SPI Mode SPI 模式。設置為SPI Operation
Full or Transmit Only Mode 全雙工或僅發送模式選擇
Slave Select Polarity 從機選擇引腳極性。一般是低電平有效
Select SSL(Slave Select) 從機選擇信號
MOSI Idle State 總線空閑時 MOSI 電平
Parity Mode 極性模式
Byte Swapping 字節交換模式
Bitrate 比特率
Clock Delay 時鐘延遲
SSL Negation Delay SSL失效延遲
Next Access Delay 下一次訪問延遲

配置完成之后可以按下快捷鍵“Ctrl + S”保存, 最后點右上角的“Generate Project Content”按鈕,讓軟件自動生成配置代碼即可。

接下來就可以為外部串行Flash編寫操作代碼了。 使用 QSPI 和使用 SPI 操作串行Flash其實是類似的,只是兩者的通信接口不同而已。 下面以啟明6M5開發板為例,對串行Flash芯片進行操作。啟明4M2和啟明2L1的代碼讀者可直接參考相應配套例程。

23.3.2.3.QSPI直接讀寫FLASH函數

當使用QSPI接口時,通過 R_QSPI_DirectWrite 和 R_QSPI_DirectRead 這兩個函數,可以直接讀寫QSPI FLASH, 通過這種方式,用戶需要寫入FLASH芯片的控制指令進行相應操作。

R_QSPI_DirectWrite 的函數原型如下:

fsp_err_t R_QSPI_DirectWrite (spi_flash_ctrl_t  * p_ctrl,uint8_t const * const p_src,uint32_t const  bytes,bool const  read_after_write)

發送一個數組的數據,p_src需要發送的數組,bytes字節的長度,read_after_write是否發送數據的截止信號(意思是將QSSL拉高代表數據的截止),一般我們需要和R_QSPI_DirectRead進行組合發送數據。 在這個函數之后我們需要增加一定時間的延時,或者是通過中斷來進行判斷寫入數據是否成功。

R_QSPI_DirectRead 的函數原型如下:

fsp_err_t R_QSPI_DirectRead (spi_flash_ctrl_t * p_ctrl, uint8_t * const p_dest, uint32_t const bytes)

接收一個數組的數據,p_dest需要接收到的數組,bytes需要接收數組的長度(字節)。在執行讀取的函數命令之后我們需要增加一定時間的延時,或者是通過中斷來進行判斷讀取數據是否成功。

在此之后,我們將使用R_QSPI_DirectWrite和R_QSPI_DirectRead的組合,來實現我們想要的一些功能。

23.3.2.4.讀取FLASH芯片ID

根據“JEDEC”指令的時序,我們把讀取FLASH ID的過程編寫成一個函數,見下

代碼清單 讀取FLASH芯片ID

/**
* @brief  讀取FLASH ID
* @param  無
* @retval FLASH ID
*/
uint32_t QSPI_Flash_ReadID(void)
{
   unsigned char data[6] = {};
   uint32_t back;
   data[0] = JedecDeviceID;

   R_QSPI_DirectWrite(&g_qspi0_flash_ctrl, &data[0], 1, true);     //false: close the spi  true: go go go
   R_QSPI_DirectRead(&g_qspi0_flash_ctrl, &data[0], 3);

   /*把數據組合起來,作為函數的返回值*/
   back = (data[0] << 16) | (data[1] << 8) | (data[2]);

   return back;
}

這段代碼利用FSP里的R_QSPI_DirectWrite函數發送JedecDeviceID指令,然后通過R_QSPI_DirectRead函數讀取三個字節的函數,最后把讀取到的這3個數據合并到一個變量(back)中,然后作為函數返回值,把該返回值與我們預先定義的ID進行對比,即可知道FLASH芯片是否正常。

23.3.2.5.FLASH寫使能

在向FLASH芯片存儲矩陣寫入數據前,首先要使能寫操作,通過“Write Enable”命令即可寫使能,見下。

代碼清單 寫使能命令

/**
* @brief  向FLASH發送 寫使能 命令
* @param  none
* @retval none
*/
void QSPI_Flash_WriteEnable(void)
{
   unsigned char data[6] = {};
   data[0] = WriteEnable;
   R_QSPI_DirectWrite(&g_qspi0_flash_ctrl, &data[0], 1, false);
}

23.3.2.6.讀取當前FLASH狀態

與EEPROM一樣,由于FLASH芯片向內部存儲矩陣寫入數據需要消耗一定的時間,并不是在總線通訊結束的一瞬間完成的, 所以在寫操作后需要確認FLASH芯片“空閑”時才能進行再次寫入。為了表示自己的工作狀態, FLASH芯片定義了一個狀態寄存器,如下圖所示。

圖 FLASH芯片的狀態寄存器

我們只關注這個狀態寄存器的第0位“BUSY”,當這個位為“1”時,表明FLASH芯片處于忙碌狀態,它可能正在對內部的存儲矩陣進行“擦除”或“數據寫入”的操作。

利用指令表中的“Read Status Register”指令可以獲取FLASH芯片狀態寄存器的內容,其時序見下圖。

圖 讀取狀態寄存器的時序

只要向FLASH芯片發送了讀狀態寄存器的指令,FLASH芯片就會持續向主機返回最新的狀態寄存器內容, 直到收到SPI通訊的停止信號。據此我們編寫了具有等待FLASH芯片寫入結束功能的函數,見下。

代碼清單 通過讀狀態寄存器等待FLASH芯片空閑

/**
* @brief  等待WIP(BUSY)標志被置0,即等待FLASH內部數據寫入完畢
* @param  無
*/
fsp_err_t QSPI_Flash_WaitForWriteEnd(void)
{
   spi_flash_status_t status = {.write_in_progress = true};
   int32_t time_out          = (INT32_MAX);
   fsp_err_t err             = FSP_SUCCESS;

   do
   {
      /* Get status from QSPI flash device */
      err = R_QSPI_StatusGet(&g_qspi0_flash_ctrl, &status);
      if (FSP_SUCCESS != err)
      {
            printf("R_QSPI_StatusGet Failed\r\n");
            return err;
      }

      /* Decrement time out to avoid infinite loop in case of consistent failure */
      --time_out;

      if (RESET_VALUE >= time_out)
      {
            printf("\r\n ** Timeout : No result from QSPI flash status register ** \r\n");
            return FSP_ERR_TIMEOUT;
      }

   }
   while (false != status.write_in_progress);

   return err;
}

這段代碼發送R_QSPI_StatusGet函數獲取當前的芯片是否在寫入狀態,并在while循環里持續獲取寄存器的內容并檢驗它的標志位,一直等待到該標志表示寫入結束時才退出本函數,以便繼續后面與FLASH芯片的數據通訊。

23.3.2.7.FLASH扇區擦除

由于FLASH存儲器的特性決定了它只能把原來為“1”的數據位改寫成“0”,而原來為“0”的數據位不能直接改寫為“1”。所以這里涉及到數據“擦除”的概念,在寫入前,必須要對目標存儲矩陣進行擦除操作,把矩陣中的數據位擦除為“1”,在數據寫入的時候,如果要存儲數據“1”,那就不修改存儲矩陣 ,在要存儲數據“0”時,才更改該位。

通常,對存儲矩陣擦除的基本操作單位都是多個字節進行,如本例子中的FLASH芯片支持“扇區擦除”、“塊擦除”以及“整片擦除”,見下表。

本實驗FLASH芯片的擦除單位

擦除單位 大小 指令
扇區擦除Sector Erase 4KB 20h
塊擦除Block Erase 64KB D8h
整片擦除Chip Erase 整個芯片完全擦除 60h

FLASH芯片的最小擦除單位為扇區(Sector),而一個塊(Block)包含16個扇區,其內部存儲矩陣分布見下圖。

圖 FLASH芯片的存儲矩陣

使用扇區擦除指令“Sector Erase”可控制FLASH芯片開始擦寫,其指令時序見下圖。

圖 扇區擦除時序

扇區擦除指令的第一個字節為指令編碼,緊接著發送的3個字節用于表示要擦除的存儲矩陣地址。 要注意的是在扇區擦除指令前,還需要先發送“寫使能”指令,發送扇區擦除指令后, 通過讀取寄存器狀態等待扇區擦除操作完畢,代碼實現見下。

代碼清單 擦除扇區

/**
* @brief  擦除FLASH扇區
* @param  SectorAddr:要擦除的扇區地址
* @retval 無
*/
void QSPI_Flash_SectorErase(uint32_t adress)
{
   unsigned char data[6] = {};

   data[0] = 0x06;     //write_enable_command
   data[1] = 0x20;     //erase_command
   data[2] = (uint8_t)(adress >> 16);
   data[3] = (uint8_t)(adress >> 8);
   data[4] = (uint8_t)(adress);
   R_QSPI->SFMCMD = 1U;
   R_QSPI->SFMCOM = data[0];
   R_QSPI_DirectWrite(&g_qspi0_flash_ctrl, &data[1], 4, false);

   QSPI_Flash_WaitForWriteEnd();
}

這段代碼使用瑞薩FSP調用R_QSPI_DirectWrite()發送四個字節的數據, 先發送寫使能0x06,之后通過R_QSPI_DirectWrite()發送扇區的刪除命令0x02,以及三個字節的地址。調用扇區擦除指令時注意輸入的地址要對齊到4KB。

23.3.2.8.FLASH的頁寫入

目標扇區被擦除完畢后,就可以向它寫入數據了。與EEPROM類似,FLASH芯片也有頁寫入命令, 使用頁寫入命令最多可以一次向FLASH傳輸256個字節的數據,我們把這個單位為頁大小。 FLASH頁寫入的時序見下圖。

圖 FLASH芯片頁寫入

從時序圖可知,第1個字節為“頁寫入指令”編碼,2-4字節為要寫入的“地址Address”,接著的是要寫入的內容,最多可以發送256字節數據,這些數據將會從“地址Address”開始,按順序寫入到FLASH的存儲矩陣。若發送的數據超出256個,則會覆蓋前面發送的數據。

與擦除指令不一樣,頁寫入指令的地址并不要求按256字節對齊,只要確認目標存儲單元是擦除狀態即可(即被擦除后沒有被寫入過)。所以,若對“地址x”執行頁寫入指令后,發送了200個字節數據后終止通訊,下一次再執行頁寫入指令,從“地址(x+200)”開始寫入200個字節也是沒有問題的(小于256均可)。 只是在實際應用中由于基本擦除單元是4KB,一般都以扇區為單位進行讀寫,想深入了解,可學習我們的“FLASH文件系統”相關的例子。

把頁寫入時序封裝成函數,其實現見下。

代碼清單 FLASH的頁寫入

/**
* @brief  對FLASH按頁寫入數據,調用本函數寫入數據前需要先擦除扇區
* @param  pBuffer,要寫入數據的指針
* @param  WriteAddr,寫入地址
* @param  NumByteToWrite,寫入數據長度,必須小于等于頁大小
* @retval 無
*/
void QSPI_Flash_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
   R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite);
   QSPI_Flash_WaitForWriteEnd();
}


static void qspi_d0_byte_write_standard(uint8_t byte)
{
   R_QSPI->SFMCOM = byte;
}

/**
* @brief  讀取flash數據
* @param  p_ctrl
* @param  p_src     需要傳回的數據
* @param  p_dest    數據地址
* @param  byte_count    數據長度
*/
fsp_err_t R_QSPI_Read(spi_flash_ctrl_t     *p_ctrl,
                     uint8_t              *p_src,
                     uint8_t *const       p_dest,
                     uint32_t              byte_count)
{
   qspi_instance_ctrl_t *p_instance_ctrl = (qspi_instance_ctrl_t *) p_ctrl;


   uint32_t chip_address = (uint32_t) p_dest - (uint32_t) QSPI_DEVICE_START_ADDRESS + R_QSPI->SFMCNT1;

   bool restore_spi_mode = false;
   void (* write_command)(uint8_t byte) = qspi_d0_byte_write_standard;
   void (* write_address)(uint8_t byte) = qspi_d0_byte_write_standard;

#if QSPI_CFG_SUPPORT_EXTENDED_SPI_MULTI_LINE_PROGRAM

   /* If the peripheral is in extended SPI mode, and the configuration provided in the BSP allows for programming on
   * multiple data lines, and a unique command is provided for the required mode, update the SPI protocol to send
   * data on multiple lines. */
   if ((SPI_FLASH_DATA_LINES_1 != p_instance_ctrl->data_lines) &&
            (SPI_FLASH_PROTOCOL_EXTENDED_SPI == R_QSPI->SFMSPC_b.SFMSPI))
   {
      R_QSPI->SFMSPC_b.SFMSPI = p_instance_ctrl->data_lines;

      restore_spi_mode = true;

      /* Write command in extended SPI mode on one line. */
      write_command = gp_qspi_prv_byte_write[p_instance_ctrl->data_lines];

      if (SPI_FLASH_DATA_LINES_1 == p_instance_ctrl->p_cfg->page_program_address_lines)
      {
            /* Write address in extended SPI mode on one line. */
            write_address = gp_qspi_prv_byte_write[p_instance_ctrl->data_lines];
      }
   }
#endif

   /* Enter Direct Communication mode */
   R_QSPI->SFMCMD = 1;

   /* Send command to enable writing */
   write_command(0x03);

   /* Write the address. */
   if ((p_instance_ctrl->p_cfg->address_bytes & R_QSPI_SFMSAC_SFMAS_Msk) == SPI_FLASH_ADDRESS_BYTES_4)
   {
      /* Send the most significant byte of the address */
      write_address((uint8_t)(chip_address >> 24));
   }

   /* Send the remaining bytes of the address */
   write_address((uint8_t)(chip_address >> 16));
   write_address((uint8_t)(chip_address >> 8));
   write_address((uint8_t)(chip_address));

   /* Write the data. */
   uint32_t index = 0;
   while (index < byte_count)
   {
      /* Read the device memory into the passed in buffer */
      *(p_src + index) = (uint8_t) R_QSPI->SFMCOM;
      index++;
   }

   /* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
   * in the RA6M3 manual R01UH0886EJ0100. */
   R_QSPI->SFMCMD = 1;


   /* Return to ROM access mode */
   R_QSPI->SFMCMD = 0;

   return FSP_SUCCESS;
}

這段代碼使用瑞薩FSP調用R_QSPI_Write()函數進行進行頁寫入, 先發送“寫使能”命令,接著才開始頁寫入時序,然后發送指令編碼、地址, 再把要寫入的數據一個接一個地發送出去,發送完后結束通訊,通過get_flash_status()函數來 檢查FLASH狀態寄存器, 等待FLASH內部寫入結束。

23.3.2.9.不定量數據寫入

應用的時候我們常常要寫入不定量的數據,直接調用“頁寫入”函數并不是特別方便,所以我們在它的基礎上編寫了“不定量數據寫入”的函數, 基實現見下。

代碼清單 不定量數據寫入

/**
* @brief  對FLASH寫入數據,調用本函數寫入數據前需要先擦除扇區
* @param  pBuffer,要寫入數據的指針
* @param  WriteAddr,寫入地址
* @param  NumByteToWrite,寫入數據長度
* @retval 無
*/
void QSPI_Flash_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
   uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

   /*mod運算求余,若writeAddr是SPI_FLASH_PageSize整數倍,運算結果Addr值為0*/
   Addr = WriteAddr % SPI_FLASH_PageSize;

   /*差count個數據值,剛好可以對齊到頁地址*/
   count = SPI_FLASH_PageSize - Addr;
   /*計算出要寫多少整數頁*/
   NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
   /*mod運算求余,計算出剩余不滿一頁的字節數*/
   NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

   /* Addr=0,則WriteAddr 剛好按頁對齊 aligned  */
   if (Addr == 0)
   {
      /* NumByteToWrite < SPI_FLASH_PageSize */
      if (NumOfPage == 0)
      {
            R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite);
            QSPI_Flash_WaitForWriteEnd();

      }
      else /* NumByteToWrite > SPI_FLASH_PageSize */
      {
            /*先把整數頁都寫了*/
            while (NumOfPage--)
            {
               R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, SPI_FLASH_PageSize);
               QSPI_Flash_WaitForWriteEnd();

               WriteAddr +=  SPI_FLASH_PageSize;
               pBuffer += SPI_FLASH_PageSize;
            }
            /*若有多余的不滿一頁的數據,把它寫完*/
            R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumOfSingle);
            QSPI_Flash_WaitForWriteEnd();

      }
   }
   /* 若地址與 SPI_FLASH_PageSize 不對齊  */
   else
   {
      /* NumByteToWrite < SPI_FLASH_PageSize */
      if (NumOfPage == 0)
      {
            /*當前頁剩余的count個位置比NumOfSingle小,一頁寫不完*/
            if (NumOfSingle > count)
            {
               temp = NumOfSingle - count;
               /*先寫滿當前頁*/
               R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, count);
               QSPI_Flash_WaitForWriteEnd();


               WriteAddr +=  count;
               pBuffer += count;
               /*再寫剩余的數據*/
               R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, temp);
               QSPI_Flash_WaitForWriteEnd();

            }
            else /*當前頁剩余的count個位置能寫完NumOfSingle個數據*/
            {
               R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite);
               QSPI_Flash_WaitForWriteEnd();

            }
      }
      else /* NumByteToWrite > SPI_FLASH_PageSize */
      {
            /*地址不對齊多出的count分開處理,不加入這個運算*/
            NumByteToWrite -= count;
            NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

            /* 先寫完count個數據,為的是讓下一次要寫的地址對齊 */
            R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, count);
            QSPI_Flash_WaitForWriteEnd();

            /* 接下來就重復地址對齊的情況 */
            WriteAddr +=  count;
            pBuffer += count;
            /*把整數頁都寫了*/
            while (NumOfPage--)
            {
               R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, SPI_FLASH_PageSize);
               QSPI_Flash_WaitForWriteEnd();

               WriteAddr +=  SPI_FLASH_PageSize;
               pBuffer += SPI_FLASH_PageSize;
            }
            /*若有多余的不滿一頁的數據,把它寫完*/
            if (NumOfSingle != 0)
            {
               R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumOfSingle);
               QSPI_Flash_WaitForWriteEnd();

            }
      }
   }
}

這段代碼與EEPROM章節中的“快速寫入多字節”函數原理是一樣的,運算過程在此不再贅述。區別是頁的大小以及實際數據寫入的時候,使用的是針對FLASH芯片的頁寫入函數,且在實際調用這個“不定量數據寫入”函數時,還要注意確保目標扇區處于擦除狀態。

23.3.2.10.從FLASH讀取數據

相對于寫入,FLASH芯片的數據讀取要簡單得多,使用讀取指令“Read Data”即可,其指令時序見下圖。

圖 SPI FLASH讀取數據時序

發送了指令編碼及要讀的起始地址后,FLASH芯片就會按地址遞增的方式返回存儲矩陣的內容,讀取的數據量沒有限制, 只要沒有停止通訊,FLASH芯片就會一直返回數據。代碼實現見下。

代碼清單 從FLASH讀取數據

/**
* @brief  讀取FLASH數據,減少ctrl這個標志
* @param  pBuffer,存儲讀出數據的指針
* @param  ReadAddr,讀取地址
* @param  NumByteToRead,讀取數據長度
* @retval 無
*/
void QSPI_Flash_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
   R_QSPI_Read(&g_qspi0_flash_ctrl, pBuffer, ReadAddr, NumByteToRead);
}

/**
* @brief  讀取flash數據
* @param  p_ctrl
* @param  p_src     需要傳回的數據
* @param  p_dest    數據地址
* @param  byte_count    數據長度
*/
fsp_err_t R_QSPI_Read(spi_flash_ctrl_t     *p_ctrl,
                     uint8_t              *p_src,
                     uint8_t *const       p_dest,
                     uint32_t              byte_count)
{
   qspi_instance_ctrl_t *p_instance_ctrl = (qspi_instance_ctrl_t *) p_ctrl;


   uint32_t chip_address = (uint32_t) p_dest - (uint32_t) QSPI_DEVICE_START_ADDRESS + R_QSPI->SFMCNT1;

   bool restore_spi_mode = false;
   void (* write_command)(uint8_t byte) = qspi_d0_byte_write_standard;
   void (* write_address)(uint8_t byte) = qspi_d0_byte_write_standard;

#if QSPI_CFG_SUPPORT_EXTENDED_SPI_MULTI_LINE_PROGRAM

   /* If the peripheral is in extended SPI mode, and the configuration provided in the BSP allows for programming on
   * multiple data lines, and a unique command is provided for the required mode, update the SPI protocol to send
   * data on multiple lines. */
   if ((SPI_FLASH_DATA_LINES_1 != p_instance_ctrl->data_lines) &&
            (SPI_FLASH_PROTOCOL_EXTENDED_SPI == R_QSPI->SFMSPC_b.SFMSPI))
   {
      R_QSPI->SFMSPC_b.SFMSPI = p_instance_ctrl->data_lines;

      restore_spi_mode = true;

      /* Write command in extended SPI mode on one line. */
      write_command = gp_qspi_prv_byte_write[p_instance_ctrl->data_lines];

      if (SPI_FLASH_DATA_LINES_1 == p_instance_ctrl->p_cfg->page_program_address_lines)
      {
            /* Write address in extended SPI mode on one line. */
            write_address = gp_qspi_prv_byte_write[p_instance_ctrl->data_lines];
      }
   }
#endif

   /* Enter Direct Communication mode */
   R_QSPI->SFMCMD = 1;

   /* Send command to enable writing */
   write_command(0x03);

   /* Write the address. */
   if ((p_instance_ctrl->p_cfg->address_bytes & R_QSPI_SFMSAC_SFMAS_Msk) == SPI_FLASH_ADDRESS_BYTES_4)
   {
      /* Send the most significant byte of the address */
      write_address((uint8_t)(chip_address >> 24));
   }

   /* Send the remaining bytes of the address */
   write_address((uint8_t)(chip_address >> 16));
   write_address((uint8_t)(chip_address >> 8));
   write_address((uint8_t)(chip_address));

   /* Write the data. */
   uint32_t index = 0;
   while (index < byte_count)
   {
      /* Read the device memory into the passed in buffer */
      *(p_src + index) = (uint8_t) R_QSPI->SFMCOM;
      index++;
   }

   /* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
   * in the RA6M3 manual R01UH0886EJ0100. */
   R_QSPI->SFMCMD = 1;


   /* Return to ROM access mode */
   R_QSPI->SFMCMD = 0;

   return FSP_SUCCESS;
}

由于讀取的數據量沒有限制,所以發送讀命令后一直接收NumByteToRead個數據到結束即可。

23.3.2.11.hal_entry入口函數

最后我們來編寫 hal_entry 入口函數,進行FLASH芯片讀寫校驗,代碼見下。

代碼清單 hal_entry 入口函數

/* 用戶頭文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "qspi_flash/bsp_qspi_flash.h"


#define  FLASH_WriteAddress     0x00000
#define  FLASH_ReadAddress      FLASH_WriteAddress
#define  FLASH_SectorToErase    FLASH_WriteAddress

/* 發送緩沖區初始化 */
uint8_t Tx_Buffer[] = "感謝您選用野火啟明瑞薩RA開發板";
uint8_t Rx_Buffer[sizeof(Tx_Buffer)];


/*
* 函數名:Buffercmp
* 描述  :比較兩個緩沖區中的數據是否相等
* 輸入  :pBuffer1     src緩沖區指針
*         pBuffer2     dst緩沖區指針
*         BufferLength 緩沖區長度
* 輸出  :無
* 返回  :0 pBuffer1 等于   pBuffer2
*         1 pBuffer1 不等于 pBuffer2
*/
int Buffercmp(uint8_t *pBuffer1, uint8_t *pBuffer2, uint16_t BufferLength)
{
   while (BufferLength--)
   {
      if (*pBuffer1 != *pBuffer2)
      {
            return 1;
      }

      pBuffer1++;
      pBuffer2++;
   }
   return 0;
}


void hal_entry(void)
{
   /* TODO: add your own code here */
   uint32_t FlashID = 0;
   uint32_t FlashDeviceID = 0;

   LED_Init();         // LED 初始化
   Debug_UART4_Init(); // SCI4 UART 調試串口初始化
   QSPI_Flash_Init();  // 串行FLASH初始化

   printf("這是一個串行FLASH的讀寫例程\r\n");
   printf("打開串口助手查看打印的信息\r\n\r\n");


   /* 獲取 SPI g_qspi0_flash ID */
   FlashID = QSPI_Flash_ReadID();
   FlashDeviceID = QSPI_Flash_ReadDeviceID();

   if ((FlashID == FLASH_ID_W25Q32JV) || (FlashID == FLASH_ID_AT25SF321B))
   {
      if(FlashID == FLASH_ID_W25Q32JV)
      {
            printf("檢測到串行FLASH:W25Q32 !\r\n");
      }
      else
      {
            printf("檢測到串行FLASH:AT25SF32 !\r\n");
      }
      printf("FlashID is 0x%X, Manufacturer Device ID is 0x%X.\r\n", FlashID, FlashDeviceID);


      /* 擦除將要寫入的 SPI FLASH 扇區,FLASH寫入前要先擦除 */
      // 這里擦除4K,即一個扇區,擦除的最小單位是扇區
      QSPI_Flash_SectorErase(FLASH_SectorToErase);

      /* 將發送緩沖區的數據寫到flash中 */
      // 這里寫一頁,一頁的大小為256個字節
      QSPI_Flash_BufferWrite(Tx_Buffer, FLASH_WriteAddress, sizeof(Tx_Buffer));
      printf("寫入的數據為:%s \r\n", Tx_Buffer);

      /* 將剛剛寫入的數據讀出來放到接收緩沖區中 */
      QSPI_Flash_BufferRead(Rx_Buffer, FLASH_ReadAddress, sizeof(Tx_Buffer));
      printf("讀出的數據為:%s \r\n", Rx_Buffer);

      if (Buffercmp(Tx_Buffer, Rx_Buffer, sizeof(Tx_Buffer)) == 0)
      {
            printf("\r\n32Mbit串行Flash測試成功!\r\n");
            LED3_ON;
      }
      else
      {
            printf("\r\n32Mbit串行Flash測試失敗!\r\n");
            LED1_ON;
      }


      printf("\r\n測試存儲浮點數和整數示例\r\n");
      /* 存儲小數和整數的數組,各7個 */
      long double double_buffer[7] = {0};
      int int_buffer[7] = {0};

      /*生成要寫入的數據*/
      for (uint8_t k = 0; k < 7; k++)
      {
            double_buffer[k] = k + 0.1;
            int_buffer[k] = k * 500 + 1 ;
      }
      printf("向芯片寫入數據:");
      /*打印到串口*/
      printf("\r\n小數 tx = ");
      for (uint8_t k = 0; k < 7; k++)
      {
            printf("%LF, ", double_buffer[k]);
      }
      printf("\r\n整數 tx = ");
      for (uint8_t k = 0; k < 7; k++)
      {
            printf("%d, ", int_buffer[k]);
      }

      /* 前面已擦除整個扇區和寫入第0頁,現繼續寫入第1頁和第2頁 */
      /*寫入小數數據到第一頁*/
      QSPI_Flash_BufferWrite((void *)double_buffer, SPI_FLASH_PageSize * 1, sizeof(double_buffer));
      /*寫入整數數據到第二頁*/
      QSPI_Flash_BufferWrite((void *)int_buffer, SPI_FLASH_PageSize * 2, sizeof(int_buffer));

      /*讀取小數數據*/
      QSPI_Flash_BufferRead((void *)double_buffer, SPI_FLASH_PageSize * 1, sizeof(double_buffer));
      /*讀取整數數據*/
      QSPI_Flash_BufferRead((void *)int_buffer, SPI_FLASH_PageSize * 2, sizeof(int_buffer));


      printf("\r\n\r\n從芯片讀到數據:");
      printf("\r\n小數 rx = ");
      for (uint8_t k = 0; k < 7; k++)
      {
            printf("%LF, ", double_buffer[k]);
      }
      printf("\r\n整數 rx = ");
      for (uint8_t k = 0; k < 7; k++)
      {
            printf("%d, ", int_buffer[k]);
      }
   }
   else
   {
      printf("\tFLASH_ID 錯誤:0x%X", FlashID);
      LED1_ON;
   }


   while(1);


#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif
}

23.3.3.下載驗證

USB線連接開發板“USB TO UART”接口跟電腦,在電腦端打開串口調試助手,把編譯好的程序下載到開發板。在串口調試助手可看到FLASH測試的調試信息。

圖

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • FlaSh
    +關注

    關注

    10

    文章

    1747

    瀏覽量

    155503
  • 開發板
    +關注

    關注

    26

    文章

    6289

    瀏覽量

    118023
  • 開發環境
    +關注

    關注

    1

    文章

    270

    瀏覽量

    17637
  • QSPI
    +關注

    關注

    0

    文章

    55

    瀏覽量

    13355

原文標題:控制FLASH的指令——瑞薩RA系列FSP庫開發實戰指南(79)

文章出處:【微信號:瑞薩嵌入式小百科,微信公眾號:瑞薩嵌入式小百科】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    RA系列MCU FSP開發實戰指南(09)存儲器映射

    3.3 存儲器映射 前文所述,寄存器與RAM、FLASH一樣都是芯片內部的一種存儲設備。那么,當我們需要訪問它們的時候,我們需要知道它們的存儲地址。 3.3.1 存儲器映射表 如下圖所示為RA
    的頭像 發表于 04-16 15:52 ?1594次閱讀
    <b class='flag-5'>瑞</b><b class='flag-5'>薩</b><b class='flag-5'>RA</b><b class='flag-5'>系列</b>MCU <b class='flag-5'>FSP</b><b class='flag-5'>庫</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(09)存儲器映射

    RA系列FSP開發實戰指南DTC外部中斷觸發傳輸實驗

    實驗的目標是使用外部中斷來觸發DTC傳輸,因此需要用到一個按鍵。
    的頭像 發表于 12-26 09:32 ?3962次閱讀
    <b class='flag-5'>瑞</b><b class='flag-5'>薩</b><b class='flag-5'>RA</b><b class='flag-5'>系列</b><b class='flag-5'>FSP</b><b class='flag-5'>庫</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b><b class='flag-5'>之</b>DTC<b class='flag-5'>外部</b>中斷觸發傳輸<b class='flag-5'>實驗</b>

    RA系列FSP開發實戰指南I2C讀寫EEPROM實驗

    使用官方提供的FPS進行編程,官方提供的FPS具有方便、快捷、簡潔的特性。
    的頭像 發表于 01-27 10:02 ?3420次閱讀
    <b class='flag-5'>瑞</b><b class='flag-5'>薩</b><b class='flag-5'>RA</b><b class='flag-5'>系列</b><b class='flag-5'>FSP</b><b class='flag-5'>庫</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b><b class='flag-5'>之</b>I2C<b class='flag-5'>讀寫</b>EEPROM<b class='flag-5'>實驗</b>

    RA系列FSP開發實戰指南QSPI通訊協議簡介

    QSPI是Queued SPI的簡寫,是Motorola公司推出的SPI接口的擴展,比SPI應用更加廣泛。在SPI協議的基礎上,Motorola公司對其功能進行了增強,增加了隊列傳輸機制,推出了隊列
    的頭像 發表于 03-03 10:56 ?2431次閱讀
    <b class='flag-5'>瑞</b><b class='flag-5'>薩</b><b class='flag-5'>RA</b><b class='flag-5'>系列</b><b class='flag-5'>FSP</b><b class='flag-5'>庫</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b><b class='flag-5'>之</b><b class='flag-5'>QSPI</b>通訊協議簡介

    RA系列FSP開發實戰指南QSPI控制FLASH的指令

    對主機端(RA6M5)來說,只是它遵守最基本的QSPI通訊協議發送出的數據,但在設備端(FLASH 芯片)把這些數據解釋成不同的意義,所以才成為指令。
    的頭像 發表于 03-03 10:56 ?2650次閱讀
    <b class='flag-5'>瑞</b><b class='flag-5'>薩</b><b class='flag-5'>RA</b><b class='flag-5'>系列</b><b class='flag-5'>FSP</b><b class='flag-5'>庫</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b><b class='flag-5'>之</b><b class='flag-5'>QSPI</b>控制<b class='flag-5'>FLASH</b>的指令

    e2studio(1)----芯片搭建FSP環境

    視頻教學 樣品申請 請勿添加外鏈 e2studio軟件 e2studio是的集成開發環境,FSP 提供了眾多可提高效率的工具,用于開發
    發表于 09-30 15:28

    RA6E2地奇星開發板試用】開發板介紹及環境搭建

    物聯網、工業控制、智能硬件等場景。 核心參數一覽 項目 規格 主控芯片 R7FA6E2BB3CNE( RA6E2 系列) 內核 Arm
    發表于 12-22 00:40

    RA4系列開發板體驗】開發環境搭建和新手點燈指南

    RA4系列開發板體驗】開發環境搭建和新手點燈指南
    發表于 11-24 22:54

    RA4系列開發板體驗】體驗過程

    拿到板子之后,做了幾個實驗過程,過程如下:1、在好奇心驅動下,逐步接觸;參照網上的實例,學習開發環境一、開箱驗貨二、搭建環境參考“ 【
    發表于 12-18 16:20

    【野火啟明6M5開發板體驗】開箱+認識開發板+資料

    按鍵檢測29. WiFi——模塊通訊板尺寸:3、資料:*附件:[野火EmbedFire]《RA系列FSP
    發表于 12-20 23:28

    【有獎直播預報名】電子RA系列產品開發工具FSP4.0.0新特性介紹

    為使用電子RA系列ARM微控制器的嵌入式系統設計提供簡單易用且可擴展的高質量軟件。 直播主題
    的頭像 發表于 11-22 12:20 ?1520次閱讀

    【視頻教程】RA單片機FSP開發(3)FSP架構-解釋Blinky架構[上]

    干貨分享 前篇回顧 【視頻教程】RA單片機FSP開發(1)環境搭建(帶RASC) 【視頻教程】
    的頭像 發表于 12-06 12:15 ?1912次閱讀

    RA6系列芯片外擴SRAM方法

    簡介 RA6系列芯片支持外部總線擴展,可實現外擴SRAM或8080總線應用。使用
    的頭像 發表于 02-11 06:00 ?3015次閱讀

    電子RA系列微控制器的可擴展性強的配置軟件包 (FSP)安裝下載與使用指南

    電子RA系列微控制器的可擴展性強的配置軟件包 (FSP)安裝下載與使用指南
    的頭像 發表于 06-11 17:21 ?1842次閱讀

    RA MCU眾測寶典 | 在CPKCOR-RA8D1B核心板上實現QSPI讀取外部Flash

    RA生態工作室關注我們“RAMCU眾測寶典”中I2C/SPI通信與顯示驅動專題更新了。這次我們聚焦【CPKCOR-RA8D1B核心板】開發
    的頭像 發表于 02-06 18:02 ?5229次閱讀
    <b class='flag-5'>RA</b> MCU眾測寶典 | 在<b class='flag-5'>瑞</b><b class='flag-5'>薩</b>CPKCOR-<b class='flag-5'>RA</b>8D1B核心板上實現<b class='flag-5'>QSPI</b>讀取<b class='flag-5'>外部</b><b class='flag-5'>Flash</b>