云计算百科
云计算领域专业知识百科平台

zynq裸机程序开发

第一章 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中,在接收到外部发送的升级指令之后执行升级操作,且可以实时查询升级进度,校验升级结果,确保能够顺利完成升级。如果有什么问题或者需要完整的程序可以私信。

赞(0)
未经允许不得转载:网硕互联帮助中心 » zynq裸机程序开发
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!