尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

瑞萨单片机开发教程[三十三] QSPI 外部flash读写实验

瑞萨单片机开发教程[三十三] QSPI 外部flash读写实验 开发板官网https://www.edevkit.com/本系列教程目录- 瑞萨单片机开发教程[一] e2-studio DAP LINK开发环境搭建- 瑞萨单片机开发教程[二] Keil/RASC开发环境搭建- 瑞萨单片机开发教程[三] 新建RASC工程- 瑞萨单片机开发教程[四] GPIO输出——led实验- 瑞萨单片机开发教程[五] GPIO输入——按键key实验- 瑞萨单片机开发教程[六] ICU外部中断实验- 瑞萨单片机开发教程[七] SCI UART串口通信实验- 瑞萨单片机开发教程[八] DMAC 串口数据传输实验- 瑞萨单片机开发教程[九] ADC 电压采集实验QSPI 外部 Flash 读写实验本章目标了解 RA6M5 处理器的 QSPI 模块学会使用 RASC 配置 QSPI 模块使用 API 读写数据。硬件设计略参见配套硬件原理图软件设计新建工程复制一份 07 SCI_UART_Recv_send 修改文件夹名 33 QSPI_flash双击打开EBF_RA6M5.uvprojx工程通过 Keil 的新建文件新建qspi.c和qspi.h保存到目录下的\\src文件夹下。FSP 配置Keil 界面选择 “Tools”→“RA Smart Configurator” 打开 RASC 界面。在 “Pins” 添加 QSPI 设备。添加 QSPI Stack 模块。配置 QSPI Stack 参数。配置完成之后可以按下快捷键Ctrl S保存最后点击 Generate Project Content 按钮让软件自动生成配置代码即可。程序设计实现 QSPI 的读写函数根据数据长度自动换页写入一组字符串一组float数据一组int数据分别读取出来打印到串口查看数据是否正确。打开 QSPI 设备fsp_err_t R_QSPI_Open(spi_flash_ctrl_t * p_ctrl, spi_flash_cfg_t const * const p_cfg);擦除扇区fsp_err_t R_QSPI_Erase(spi_flash_ctrl_t * p_ctrl, uint8_t * const p_device_address, uint32_t byte_count);直接写指令数据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);直接读指令数据fsp_err_t R_QSPI_DirectRead(spi_flash_ctrl_t * p_ctrl, uint8_t * const p_dest, uint32_t const bytes);写入页数据fsp_err_t R_QSPI_Write(spi_flash_ctrl_t * p_ctrl, uint8_t const * const p_src, uint8_t * const p_device_address, uint32_t byte_count);读取页数据fsp_err_t R_QSPI_Read(spi_flash_ctrl_t * p_ctrl, uint8_t * const p_device_address, uint8_t * const p_dest, uint32_t byte_count);状态读取fsp_err_t R_QSPI_StatusGet(spi_flash_ctrl_t * p_ctrl, spi_flash_status_t * const p_status);程序编写qspi.h头文件定义相关函数和宏定义。#ifndef __BSP_QSPI_FLASH_H #define __BSP_QSPI_FLASH_H #include hal_data.h #define W25X16 0xEF3015 #define W25Q16 0xEF4015 #define W25Q128 0XEF4018 #define W25Q64 0XEF4017 #define W25Q32 0xEF4016 #define AT25SF321B 0x1F8701 #define sFLASH_ID W25Q32 #define FLASH_ID_W25Q32JV 0xEF4016 // W25Q32JV #define FLASH_ID_AT25SF321B 0x1F8701 // AT25SF321B #define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256 #define RESET_VALUE (0x00) /*命令定义-开头*******************************/ #define WriteEnable 0x06 #define WriteDisable 0x04 #define ReadStatusReg 0x05 #define WriteStatusReg 0x01 #define ReadData 0x03 #define FastReadData 0x0B #define FastReadDual 0x3B #define PageProgram 0x02 #define BlockErase 0xD8 #define SectorErase 0x20 #define ChipErase 0xC7 #define PowerDown 0xB9 #define ReleasePowerDown 0xAB #define DeviceID 0xAB #define ManufactDeviceID 0x90 #define JedecDeviceID 0x9F /* WIP(busy)标志FLASH内部正在写入 */ #define WIP_Flag 0x01 #define Dummy_Byte 0xFF /*命令定义-结尾*******************************/ void QSPI_Flash_Init(void); uint32_t QSPI_Flash_ReadID(void); uint32_t QSPI_Flash_ReadDeviceID(void); void QSPI_Flash_PowerDown(void); void QSPI_Flash_WakeUp(void); void QSPI_Flash_WriteEnable(void); fsp_err_t QSPI_Flash_WaitForWriteEnd(void); void QSPI_Flash_SectorErase(uint32_t adress); void QSPI_Flash_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //页写入 void QSPI_Flash_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); void QSPI_Flash_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead); //static void qspi_d0_byte_write_standard (uint8_t byte); 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); #endifqspi.c中实现 ID 读取设备 ID 读取用于判断 Flash 是否存在。#include qspi.h #include stdio.h void QSPI_Flash_Init(void) { R_QSPI_Open(g_qspi_flash_ctrl, g_qspi_flash_cfg); R_BSP_SoftwareDelay(10u, BSP_DELAY_UNITS_MILLISECONDS); } /** * 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_qspi_flash_ctrl, data[0], 1, true); R_QSPI_DirectRead(g_qspi_flash_ctrl, data[0], 3); back ((uint32_t)data[0] 16) | ((uint32_t)data[1] 8) | ((uint32_t)data[2]); return back; } /** * brief 读取FLASH 设备ID * param 无 * retval FLASH ID */ uint32_t QSPI_Flash_ReadDeviceID(void) { unsigned char data[6] {}; uint32_t back; data[0] DeviceID; data[1] 0xff; data[2] 0xff; data[3] 0xff; R_QSPI_DirectWrite(g_qspi_flash_ctrl, data[0], 4, true); R_QSPI_DirectRead(g_qspi_flash_ctrl, data[0], 3); back ((uint32_t)data[0] 16) | ((uint32_t)data[1] 8) | ((uint32_t)data[2]); return back; } /** * brief 进入掉电模式 * param 无 */ void QSPI_Flash_PowerDown(void) { unsigned char data[6] {}; data[0] PowerDown; R_QSPI_DirectWrite(g_qspi_flash_ctrl, data[0], 1, false); } /** * brief 唤醒 * param 无 */ void QSPI_Flash_WakeUp(void) { unsigned char data[6] {}; data[0] ReleasePowerDown; R_QSPI_DirectWrite(g_qspi_flash_ctrl, data[0], 1, false); } /** * brief 向FLASH发送 写使能 命令 * param none * retval none */ void QSPI_Flash_WriteEnable(void) { unsigned char data[6] {}; data[0] WriteEnable; R_QSPI_DirectWrite(g_qspi_flash_ctrl, data[0], 1, false); }等待写入数据完成。/** * 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_qspi_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; }擦除扇区函数根据写入数据长度自动换页函数读取函数。/** * 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_qspi_flash_ctrl, data[1], 4, false); QSPI_Flash_WaitForWriteEnd(); } /** * 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_qspi_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite); QSPI_Flash_WaitForWriteEnd(); } /** * brief 对FLASH写入数据调用本函数写入数据前需要先擦除扇区 * param pBuffer要写入数据的指针 * param WriteAddr写入地址 * param NumByteToWrite写入数据长度 * retval 无 */ void QSPI_Flash_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint32_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; //Addr0,则WriteAddr 刚好按页对齐 aligned if (Addr 0) { //NumByteToWrite SPI_FLASH_PageSize if (NumOfPage 0) { R_QSPI_Write(g_qspi_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite); QSPI_Flash_WaitForWriteEnd(); } else //NumByteToWrite SPI_FLASH_PageSize { //先把整数页都写了 while (NumOfPage--) { R_QSPI_Write(g_qspi_flash_ctrl, pBuffer, WriteAddr, SPI_FLASH_PageSize); QSPI_Flash_WaitForWriteEnd(); WriteAddr SPI_FLASH_PageSize; pBuffer SPI_FLASH_PageSize; } //若有多余的不满一页的数据把它写完 R_QSPI_Write(g_qspi_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_qspi_flash_ctrl, pBuffer, WriteAddr, count); QSPI_Flash_WaitForWriteEnd(); WriteAddr count; pBuffer count; //再写剩余的数据 R_QSPI_Write(g_qspi_flash_ctrl, pBuffer, WriteAddr, temp); QSPI_Flash_WaitForWriteEnd(); } else //当前页剩余的count个位置能写完NumOfSingle个数据 { R_QSPI_Write(g_qspi_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_qspi_flash_ctrl, pBuffer, WriteAddr, count); QSPI_Flash_WaitForWriteEnd(); // 接下来就重复地址对齐的情况 WriteAddr count; pBuffer count; //把整数页都写 while (NumOfPage--) { R_QSPI_Write(g_qspi_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_qspi_flash_ctrl, pBuffer, WriteAddr, NumOfSingle); QSPI_Flash_WaitForWriteEnd(); } } } } /** * 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_qspi_flash_ctrl, pBuffer, ReadAddr, NumByteToRead); }在hal_entry.c文件中添加qspi.h头文件添加测试代码写入一组字符串一组float数据一组int数据分别读出来打印到串口查看读写数据。#define Flash_Opt_Addr 0x00000000 uint8_t txString[] either write something worth reading or do something worth writing.; uint8_t rxString[sizeof(txString)]; double txFloat[10] {1.2, 2.54, 3.47, 4.12, 5.22, 6.31, 7.15, 8.65, 9.71, 10.94}; double rxFloat[10] {0}; int txInt[10] {11, 22, 33, 44, 55, 66, 77, 88, 99, 100}; int rxInt[10] {0}; void hal_entry(void) { /* TODO: add your own code here */ led_Init(); uart0_Init(); QSPI_Flash_Init(); uint32_t Id QSPI_Flash_ReadID(); uint32_t devId QSPI_Flash_ReadDeviceID(); if(Id ! 0 devId ! 0) { printf(FlashID: 0x%X, DeviceID: 0x%X \r\n, Id, devId); //擦除将要写入的 SPI FLASH 扇区FLASH写入前要先擦除 //擦除4K即一个扇区擦除的最小单位是扇区 QSPI_Flash_SectorErase(Flash_Opt_Addr); //写一页一页的大小为256个字节 QSPI_Flash_BufferWrite(txString, Flash_Opt_Addr, sizeof(txString)); printf(write string: %s \r\n, txString); //将写入的数据读出来 QSPI_Flash_BufferRead(rxString, Flash_Opt_Addr, sizeof(txString)); printf(read string: %s \r\n, rxString); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒 //前面已擦除整个扇区和写入第0页现继续写入第1页和第2页 //写入float数据到第一页 QSPI_Flash_BufferWrite((void *)txFloat, SPI_FLASH_PageSize * 1, sizeof(txFloat)); printf(write float: ); for(int i0; i10; i) printf(%f, , txFloat[i]); printf(\r\n); //读取float数据 QSPI_Flash_BufferRead((void *)rxFloat, SPI_FLASH_PageSize * 1, sizeof(txFloat)); printf(read float: ); for(int i0; i10; i) printf(%f, , rxFloat[i]); printf(\r\n); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒 //写入第二页 QSPI_Flash_BufferWrite((void *)txInt, SPI_FLASH_PageSize * 2, sizeof(txInt)); printf(write int: ); for(int i0; i10; i) printf(%d, , txInt[i]); printf(\r\n); //读取整数数据 QSPI_Flash_BufferRead((void *)rxInt, SPI_FLASH_PageSize * 2, sizeof(txInt)); printf(read int: ); for(int i0; i10; i) printf(%d, , rxInt[i]); printf(\r\n); } while(1) { LED1_TOGGLE(); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒 } #if BSP_TZ_SECURE_BUILD /* Enter non-secure code */ R_BSP_NonSecureEnter(); #endif }下载验证连接下载器将程序编译并下载到开发板之后按下复位按键来复位开发板程序启动初始化完。可以看到打印读写的数据。
返回列表