RA MCU众测宝典 | 在瑞萨CPKCOR-RA8D1B核心板上实现QSPI读取外部Flash

描述

 

RA MCU众测宝典”中I2C/SPI通信与显示驱动专题更新了。这次我们聚焦瑞萨【CPKCOR-RA8D1B核心板】开发板,一步步实现QSPI读取外部Flash。


 

开启宝典

 

QSPI是Queued SPI的简写,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即QSPI协议)。


 

QSPI是一种专用的通信接口,连接单、双或四(条数据线)SPI Flash存储介质。QSPI是一个内存控制器,用于连接具有SPI兼容接口的串行ROM(非易失性存储器)。


 

我们看一下核心板上的外扩Flash:


 

mcu

外扩的Flash的型号是AT25SF128B。


 

QSPI使用6个信号连接Flash,分别是四个数据线QIO0~QIO3,一个时钟输出CLK,一个片选输出(低电平有效)QSSL,它们的作用介绍如下:

QSSL:片选输出(低电平有效),适用于FLASH 1。如果QSPI始终在双闪存模式下工作,则其也可用于FLASH 2从设备选择信号线。QSPI通讯以QSSL线置低电平为开始信号,以QSSL线被拉高作为结束信号。

CLK:时钟输出,适用于两个存储器,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,两个设备之间通讯时,通讯速率受限于低速设备。

QIO0QIO0~QIO3:四线模式中为双向IO。


 

接下来进行软件配置:


 

 添加OSPI功能模块:

mcu


 

接下来我们配置一下引脚:

mcu


 

一共是6个引脚,接下来配置模块信息:

mcu


 

下面是部分Flash的命令,我们可以初始化这些内容:

mcu


 

接下来我们代码测试一下QSPI的功能。


 

我们定义了一些基础功能测试:

左右滑动查看完整内容

  •  

uint8_t g_read_data [OSPI_B_APP_DATA_SIZE]  = {RESET_VALUE};
uint8_t g_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_t g_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 * const p_id){fsp_err_t                   err             = FSP_SUCCESS;spi_flash_direct_transfer_t transfer        = {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 \r\n");        }/* Get flash device ID */    *p_id = transfer.data;return err;}staticfsp_err_tospi_b_write_enable(void){fsp_err_t                   err             = FSP_SUCCESS;spi_flash_direct_transfer_t transfer        = {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\r\n");    }return err;}staticfsp_err_tospi_b_wait_operation(uint32_t timeout){fsp_err_t          err    = FSP_SUCCESS;spi_flash_status_t status = {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\n");        }        R_BSP_SoftwareDelay(1, OSPI_B_TIME_UNIT);        timeout --;    }return err;}staticfsp_err_tospi_b_erase_operation(uint8_t * const p_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_t err = 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,我们写入一页数据,但是之读取其中的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\r\n", (int)(OSPI_B_APP_DATA_SIZE));    }    else    {        printf("Write operation failure\r\n");    }    printf("Write Data:\r\n");    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("\r\nRead %d bytes completed successfully\r\n", (int)(OSPI_B_APP_DATA_SIZE));    }    else    {        printf("\r\nRead operation failure\r\n");    }    printf("Read Data:\r\n");    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("\r\nData read matched data written\r\n");        printf("flash读写数据成功\r\n");    }    else    {        printf("\r\nData read does not match data written\r\n");        printf("flash读写数据失败\r\n");    }    /* Performs OSPI erase operation */    err = ospi_b_erase_operation(p_address);    if(err==FSP_SUCCESS)    {        /* Print execution time */        printf("Erase sector completed successfully\r\n");    }    else    {        printf("erase operation failure\r\n");    }    return err;
}


 

串口打印结果如下:

mcu


 

从QSPI的六引脚配置、OSPI功能模块添加,到命令集定义、Flash读写擦除的代码实现,再到串口打印验证数据匹配。我们不仅掌握了不同通信协议的配置逻辑,还摸清了它们在存储外设交互中的核心应用——这次通过QSPI实现Flash的读写擦除与数据验证,正是高速通信在存储场景的典型落地。

 

I2C/SPI通信与显示驱动专题的技能专题还在持续拓展!如果你在实操中对QSPI的协议时序、Flash命令配置有新的感悟,或是想分享更多通信驱动的实战场景,欢迎在评论区交流。


 

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分