前言:
前一篇文章【GPIO模拟SPI时序】记录了如何利用模拟IO来驱动Flash,这一篇内容将在此基础上利用TI驱动库来实现Flash的读写操作
硬件介绍:
开发平台:AWR1843EVM
嵌入式平台:CCS
Flash芯片:MX25V1635F
TI-QSPIFlash驱动库的应用
驱动库的介绍
TI的SDK中包含了部分Flash的驱动程序,具体如下图所示,如果Flash的型号不在内则需要按要求进行驱动库的更新修改。
基于TI驱动库的Flash读写操作
Step 1. 硬件引脚配置
Pinmux_Set_OverrideCtrl(SOC_XWR18XX_PINR12_PADAP, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR18XX_PINR12_PADAP, SOC_XWR18XX_PINR12_PADAP_QSPI_CLK);
Pinmux_Set_OverrideCtrl(SOC_XWR18XX_PINP11_PADAQ, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR18XX_PINP11_PADAQ, SOC_XWR18XX_PINP11_PADAQ_QSPI_CSN);
Pinmux_Set_OverrideCtrl(SOC_XWR18XX_PINR13_PADAL, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR18XX_PINR13_PADAL, SOC_XWR18XX_PINR13_PADAL_QSPI_D0);
Pinmux_Set_OverrideCtrl(SOC_XWR18XX_PINN12_PADAM, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR18XX_PINN12_PADAM, SOC_XWR18XX_PINN12_PADAM_QSPI_D1);
Pinmux_Set_OverrideCtrl(SOC_XWR18XX_PINR14_PADAN, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR18XX_PINR14_PADAN, SOC_XWR18XX_PINR14_PADAN_QSPI_D2);
Pinmux_Set_OverrideCtrl(SOC_XWR18XX_PINP12_PADAO, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR18XX_PINP12_PADAO, SOC_XWR18XX_PINP12_PADAO_QSPI_D3);
注:对照AWR1843芯片手册查看引脚并配置相应功能,例如R12引脚可以用来配置为QSPI-Flash的时钟信号线
Step 2. Flash初始化
typedef struct mmwDemo_Flash_t
{
/*! @brief QSPI driver handle */
QSPI_Handle QSPIHandle;
/*! @brief QSPI flash driver handle */
QSPIFlash_Handle QSPIFlashHandle;
uint32_t flashAddr;
/*! @brief Module initialized flag */
bool initialized;
}mmwDemo_Flash;
/**
* @brief
* Global Variable for tracking information required by the mmw Demo
*/
mmwDemo_Flash gMmwDemoFlash;
/**
* @b Description
* @n
* The function is used to initialize QSPI and Flash interface.
*
*
* @retval
* Success - 0
* @retval
* Error - <0
*/
static int32_t init_qspiflash(void)
{
QSPI_Params QSPIParams;
int32_t retVal = 0;
memset((void *)&gMmwDemoFlash, 0, sizeof(mmwDemo_Flash));
/* Initialize the QSPI Driver */
QSPI_init();
/* Initialize the QSPI Flash */
QSPIFlash_init();
/* Open QSPI driver */
QSPI_Params_init(&QSPIParams);
/* Set the QSPI peripheral clock to 200MHz */
QSPIParams.qspiClk = 200 * 1000000U;
QSPIParams.clkMode = QSPI_CLOCK_MODE_0;
/* Running at 40MHz QSPI bit rate
* QSPI bit clock rate derives from QSPI peripheral clock(qspiClk)
and divide clock internally down to bit clock rate
BitClockRate = qspiClk/divisor(=5, setup by QSPI driver internally)
*/
QSPIParams.bitRate = 40 * 1000000U;
gMmwDemoFlash.QSPIHandle = QSPI_open(&QSPIParams, &retVal);
if(gMmwDemoFlash.QSPIHandle == NULL)
{
System_printf("Error: Unable to open the QSPI Instance\n");
retVal = -1;
goto exit;
}
/* Open the QSPI Instance */
gMmwDemoFlash.QSPIFlashHandle = QSPIFlash_open(gMmwDemoFlash.QSPIHandle, &retVal);
if (gMmwDemoFlash.QSPIFlashHandle == NULL )
{
System_printf("Error: Unable to open the QSPIflash Instance\n");
retVal = -1;
goto exit;
}
gMmwDemoFlash.initialized = true;
gMmwDemoFlash.flashAddr = QSPIFlash_getExtFlashAddr(gMmwDemoFlash.QSPIFlashHandle);
exit:
return retVal;
}
注: 需要添加对应的头文件:#include <ti/drivers/qspiflash/qspiflash.h>和#include <ti/demo/utils/mmwdemo_flash.h>
如果编译报错就根据报错内容添加对应文件即可
上述代码可以成功开启Flash并读取到Flash的首地址,如果无法读取到gMmwDemoFlash相关信息,一般考虑是Flash的QE位没有置1,如果需要修改,我这边采用的思路是用模拟IO写状态寄存器来配置QE位(模拟IO的思路见文章【GPIO模拟SPI时序】)
下面是可以用于参考的MX25V1635F的写状态寄存器相关的核心代码
int32_t SPI_WriteSR_Debug(void) //写状态寄存器
{
int32_t retVal = 0;
SPI_Start();
SPI_WriteByte(Command_WRSR);
SPI_WriteByte(0x64); //0100 0000
SPI_WriteByte(0x00); //0000 0000
SPI_Stop();
retVal++;
return retVal;
}
下面简单展示一下MX25V1635F的寄存器配置相关的部分内容,更多的请参考芯片手册
基于Flash的芯片手册,可以将状态寄存器设置为0100 0000,配置寄存器设置为0000 0000,这样就可以将QE位设置为1。由于Flash是掉电非易失的,因此只需设置一次即可。设置完成后可以通过读状态寄存器和读配置寄存器来进行结果的验证。
如果在QE位已经置1的情况下,还是无法成功读取到无法读取到gMmwDemoFlash相关信息,就进行其余问题的排除。
Step 3. 基于驱动库的Flash读写操作
int32_t QSPI_debug(void)
{
init_qspiflash();
int32_t retVal;
SPIFLASH_devID devId;
/**************************************************************************
* Test: Read ID API test
**************************************************************************/
/* Get SPI Flash id */
QSPIFlash_getDeviceID(gMmwDemoFlash.QSPIFlashHandle, &devId); //发送指令0x9F,具体定义在qspiflash_internal.h和qspiflash.c
if(devId.Manufacture == SPANSION_DEV)
{
System_printf("SPANSION \n");
}
else if (devId.Manufacture == MACRONIX_DEV) //即MX25R1635F,
{
System_printf("MACRONIX \n");
}
else
{
System_printf("UNKNOWN \n");
}
/**************************************************************************
* Test: single write, single read
**************************************************************************/
/* Generate data */
FlashCfg_fixed.flash_dfeDataOutputMode = 1;
FlashCfg_fixed.flash_channelCfg.rxChannelEn = 15;
FlashCfg_fixed.flash_channelCfg.txChannelEn = 7;
FlashCfg_fixed.flash_channelCfg.cascading = 0;
FlashCfg_fixed.flash_adcCfg.adcOutputFmt = 2;
FlashCfg_fixed.flash_adcCfg.numADCBits = 1;
/* Set flash address for the test */
gMmwDemoFlash.flashAddr = gMmwDemoFlash.flashAddr + 0x10000U;
QSPIFlash_singleRead(gMmwDemoFlash.QSPIFlashHandle, gMmwDemoFlash.flashAddr, sizeof(MmDemo_fixCfg_Struct), (uint8_t *)&readFixDataArray);
System_printf("Debug \n");
QSPIFlash_sectorErase(gMmwDemoFlash.QSPIFlashHandle, gMmwDemoFlash.flashAddr); // Erase a sector for test
QSPIFlash_singleWrite(gMmwDemoFlash.QSPIFlashHandle, gMmwDemoFlash.flashAddr, sizeof(MmDemo_fixCfg_Struct), (uint8_t *)&FlashCfg_fixed);
QSPIFlash_singleRead(gMmwDemoFlash.QSPIFlashHandle, gMmwDemoFlash.flashAddr, sizeof(MmDemo_fixCfg_Struct), (uint8_t *)&readFixDataArray);
System_printf("Debug \n");
/* Check data */
if(readFixDataArray.flash_dfeDataOutputMode == 1 & readFixDataArray.flash_channelCfg.cascading == 0 & readFixDataArray.flash_adcCfg.adcOutputFmt == 2)
{
System_printf ("QSPIFlash single write /single read API / Success \n");
}
else
{
System_printf ("QSPIFlash single write /single read API / Fail \n");
}
retVal = 0;
return retVal;
}
上述代码可以用于验证Flash读写是否成功,最好再Debug模式下运行,可以方便查找各个变量的值。
欢迎大家批评指正!