第一章 zynq裸机程序开发之qspi在线烧写flash
文章目录
- 第一章 zynq裸机程序开发之qspi在线烧写flash
- 前言
- 一、基本原理
- 二、操作步骤
-
- 1.qspi初始化
- 2.擦除flash
- 3.写flash
- 总结
前言
zynq可以从flash中以qspi方式启动,一般采用JTAG方式将zynq bin文件烧写到flash中,bin文件来源于sdk生成,烧写到flash之后上电启动。然而这种方式较为繁琐。笔者在项目中将bin文件传输到zynq挂载的emmc中,emmc采用简单的fat32文件系统,因此可以直接读取bin文件并通过qspi在线烧写到flash中。笔者采用的型号是zynq0720,开发环境为sdk2018.3。
一、基本原理
通过初始化 eMMC 和 QSPI 控制器,将 eMMC 中的 bin 文件按块读入内存缓冲区,再按 Flash 的擦除 / 写入规则,将缓冲区数据逐页写入 QSPI Flash。核心依赖对 Zynq PS 外设寄存器的配置(如时钟、引脚、命令)和对 eMMC/Flash 协议的实现(如命令交互、状态处理)
二、操作步骤
1.qspi初始化
初始化qspi直接按照sdk中的例程操作,具体代码如下(示例):
int QspiFlashInit(XQspiPs *QspiInstancePtr, u16 QspiDeviceId)
{
int Status;
u8 UniqueValue;
int Count;
XQspiPs_Config *QspiConfig;
/* Initialize the QSPI driver so that it's ready to use*/
QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);
if (QspiConfig == NULL) {
return XST_FAILURE;
}
Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig,
QspiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Perform a self-test to check hardware build*/
Status = XQspiPs_SelfTest(QspiInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE_FLASH;
Count++, UniqueValue++) {
WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test);
}
memset(ReadBuffer, 0x00, sizeof(ReadBuffer));
XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |
XQSPIPS_FORCE_SSELECT_OPTION |
XQSPIPS_HOLD_B_DRIVE_OPTION);
/* Set the prescaler for QSPI clock*/
XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);
/* Assert the FLASH chip select.*/
XQspiPs_SetSlaveSelect(QspiInstancePtr);
FlashReadID();
FlashQuadEnable(QspiInstancePtr);
return XST_SUCCESS;
}
2.擦除flash
笔者采用的是32M flash,可以直接选择整片擦除;或者是根据bin文件大小擦除,例如bin文件大小为6M,可以只擦除起始地址到8M的空间,由此可以节约时间,提高效率。代码如下(示例):
void FlashEraseSector()
{
if(0 == g_Sector%5)//用于计算升级进度,按照6:3:1的时间比例设置擦除、写、校验的时间
{
global_update_pro=(float)(g_Sector*1.0/(REAL_DATA / SECTOR_SIZE)*ERASE_PRO*0.1)*100;
xil_printf("global_update_pro=%d \\n\\r", global_update_pro);//Sector=512
}
XQspiPs_PolledTransfer(&QspiInstance, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, NULL,
SEC_ERASE_SIZE);
g_zynq_erase_finish_flag=true;
while (1) {
XQspiPs_PolledTransfer(&QspiInstance, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
if ((FlashStatus[1] & 0x01) == 0) {
break;
}
}
Address += SECTOR_SIZE;
g_Sector++;
if(g_Sector >= ((g_byte_count / SECTOR_SIZE) + 1))
{
xil_printf("FlashErase finished!\\r\\n");
XTime_GetTime(&tEnd);
tUsed = (tEnd – tCur)/(COUNTS_PER_SECOND);
xil_printf("FlashErase time elapsed is %d s\\r\\n",tUsed);
sleep(1);
g_zynq_erase_flag=false;
g_zynq_write_flag=true;
xil_printf("FlashWrite starting……\\r\\n");
}
}
笔者在擦除过程中记录了执行的进度。
3.写flash
按页写,这里将写操作置于主循环中,避免阻塞主程序,逐页执行写操作,直到达到最后一页。
void FlashWritePage()
{
memset(g_bufferFile,0xFF,PAGE_SIZE_FLASH);
memset(g_bufferRead,0xFF,PAGE_SIZE_FLASH);
f_read(&g_zynq_fil, (void* )&g_bufferFile, PAGE_SIZE_FLASH, &br);
FlashWrite(&QspiInstance, (g_page * PAGE_SIZE_FLASH) + TEST_ADDRESS,g_bufferFile, PAGE_SIZE_FLASH, WRITE_CMD);
if(0 == g_page%100)//用于计算升级进度,按照6:3:1的时间比例设置擦除、写、校验的时间
{
global_update_pro=(float)(g_page*1.0/(g_zynq_filesize/PAGE_SIZE_FLASH)*WRITE_PRO*0.1+ERASE_PRO*0.1)*100;
//xil_printf("write global_update_pro=%d \\n\\r", global_update_pro);
}
g_page++;
if(g_page >= (g_zynq_filesize/PAGE_SIZE_FLASH+1))
{
xil_printf("FlashWrite finished!\\r\\n");
XTime_GetTime(&tEnd);
tUsed = (tEnd – tCur)/(COUNTS_PER_SECOND);
xil_printf("FlashWrite time elapsed is %d s\\r\\n",tUsed);
sleep(1);
xil_printf("FlashCheck starting…….\\r\\n");
g_zynq_write_flag=false;
g_zynq_check_flag=true;
g_page=0;
f_lseek(&g_zynq_fil,0);
}
## 4.校验flash```
```c
void FlashCheckPage()
{
f_read(&g_zynq_fil, (void* )&g_bufferFile, PAGE_SIZE_FLASH, &br);
FlashRead(&QspiInstance, (g_page * PAGE_SIZE_FLASH) + TEST_ADDRESS, g_bufferRead,PAGE_SIZE_FLASH, QUAD_READ_CMD);
if(g_page < g_zynq_filesize/PAGE_SIZE_FLASH)
{
for(int i=0;i<PAGE_SIZE_FLASH;i++)
{
if(g_bufferFile[i]!=g_bufferRead[i])
{
global_update_res = FALSE;
xil_printf("FlashCheck error,page and byte is %d %d\\r\\n",g_page,i);
f_close(&g_zynq_fil);
g_zynq_check_flag=false;
}
}
}
else//最后一页的大小可能低于256
{
for(int i=0;i<g_zynq_filesize%PAGE_SIZE_FLASH;i++)
{
if(g_bufferFile[i]!=g_bufferRead[i])
{
global_update_res = FALSE;
xil_printf("FlashCheck error,page and byte is %d %d\\r\\n",g_page,i);
f_close(&g_zynq_fil);
g_zynq_check_flag=false;
}
}
}
if(0 == g_page%100||g_zynq_filesize/PAGE_SIZE_FLASH == g_page)//用于计算升级进度,按照6:3:1的时间比例设置擦除、写、校验的时间
{
global_update_pro=(float)(g_page*1.0/(g_zynq_filesize/PAGE_SIZE_FLASH)*CHECK_PRO*0.1+(ERASE_PRO+WRITE_PRO)*0.1)*100;
//xil_printf("write global_update_pro=%d \\n\\r", global_update_pro);
}
g_page++;
if(g_page >= (g_zynq_filesize/PAGE_SIZE_FLASH+1))
{
sleep(1);
xil_printf("FlashCheck finished!\\r\\n");
g_zynq_check_flag=false;
g_page=0;
global_update_res = TRUE;
f_close(&g_zynq_fil);
xil_printf("flash write and check finish,update zynq success!\\r\\n");
}
}
总结
采用这种烧写方式可以实现在线升级flash,无须连接jtag,只需要将bin文件传输到zynq挂载的emmc中,在接收到外部发送的升级指令之后执行升级操作,且可以实时查询升级进度,校验升级结果,确保能够顺利完成升级。如果有什么问题或者需要完整的程序可以私信。
评论前必须登录!
注册