中电网移动|移动中电网|高清图滚动区

在瑞萨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:

a51e96ec-f035-11f0-92de-92fbcf53809c.png

外扩的Flash的型号是AT25SF128B。

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

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

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

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

接下来进行软件配置:

添加OSPI功能模块:

a5768a3c-f035-11f0-92de-92fbcf53809c.png

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

a5e5580e-f035-11f0-92de-92fbcf53809c.png

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

a647284a-f035-11f0-92de-92fbcf53809c.png

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

a6b59e9c-f035-11f0-92de-92fbcf53809c.png

接下来我们代码测试一下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,我们写入一页数据,但是之读取其中的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读写数据成功
");
    }
    else
    {
        printf("
Data read does not match data written
");
        printf("flash读写数据失败
");
    }
    /* 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;


}

 

串口打印结果如下:

a7345250-f035-11f0-92de-92fbcf53809c.png

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

猜你喜欢
中电网移动|移动中电网|频道导航区