ZYNQ 使用网口实现程序软件升级

1,这个模块能使用的前提是在vivado工程中勾选FLASH配置和eth0或eth1支持(不然硬件不支持后面文件会报错)。无论是裸机还是带操作系统升级程序都需要勾选。

2,SDK裸机实现程序升级,进入需要使能 lwip 141库,并进行设置

3,use_axieth_on_zynq 和 use_emaclite_on_zynq 设为 0;修改 lwip_memory_options 设置,将 mem_size,memp_n_pbuf 这 2 个参数值设大,这样会提高 UDP 传输效率。修改 pbuf_options 设置,将 pbuf_pool_size 设大,增加可用的 pbuf 数量,这样同样会提高 UDP 传输效率。修改 tcp_options 设置,将 lwip_tcp 设置为 false,tcp_queue_ooseq 设为 0,关闭 tcp功能;配置如图:

4,此项不是必须设置的,如果为了提高升级效率可以进行配置。修改 temac_adapter_options 设置,将 n_rx_descriptors 和 n_tx_descriptors 参数设大。这样可以提高 zynq 内部 emac dma 的数据搬移效率:

5,使能 DHCP 功能,将 memory 空间尽可能设置大一些,增大缓存空间,提高效率,,前面已经设置过了

6,将qspi_update模块添加到SDK工程里面,里面包含了qspi.h和qspi.c头文件

qspi.h

#ifndef SRC_QSPI_H_
#define SRC_QSPI_H_
#include "xparameters.h"	/* SDK generated parameters */
#include "xqspips.h"		/* QSPI device driver */
#include "xil_printf.h"
#define QSPI_DEVICE_ID		XPAR_XQSPIPS_0_DEVICE_ID
#define WRITE_STATUS_CMD	0x01
#define WRITE_CMD		0x02
#define READ_CMD		0x03
#define WRITE_DISABLE_CMD	0x04
#define READ_STATUS_CMD		0x05
#define WRITE_ENABLE_CMD	0x06
#define FAST_READ_CMD		0x0B
#define DUAL_READ_CMD		0x3B
#define QUAD_READ_CMD		0x6B
#define BULK_ERASE_CMD		0xC7
#define	SEC_ERASE_CMD		0xD8
#define READ_ID			0x9F
#define COMMAND_OFFSET		0 /* FLASH instruction */
#define ADDRESS_1_OFFSET	1 /* MSB byte of address to read or write */
#define ADDRESS_2_OFFSET	2 /* Middle byte of address to read or write */
#define ADDRESS_3_OFFSET	3 /* LSB byte of address to read or write */
#define DATA_OFFSET		4 /* Start of Data for Read/Write */
#define DUMMY_OFFSET		4 /* Dummy byte offset for fast, dual and quad
				     reads */
#define DUMMY_SIZE		1 /* Number of dummy bytes for fast, dual and
				     quad reads */
#define RD_ID_SIZE		4 /* Read ID command + 3 bytes ID response */
#define BULK_ERASE_SIZE		1 /* Bulk Erase command size */
#define SEC_ERASE_SIZE		4 /* Sector Erase command + Sector address */
#define OVERHEAD_SIZE		4
#define SECTOR_SIZE		0x10000
#define NUM_SECTORS		0x100
#define NUM_PAGES		0x10000
#define PAGE_SIZE		256
#define PAGE_COUNT		16
#define TEST_ADDRESS		0x00055000
#define UNIQUE_VALUE		0x05
#define MAX_DATA		PAGE_COUNT * PAGE_SIZE
int update_qspi(XQspiPs *QspiInstancePtr, u16 QspiDeviceId, unsigned int TotoalLen, char *FlashDataToSend) ;
#endif /* SRC_QSPI_H_ */
/***************************** Include Files *********************************/
#include "qspi.h"
#include "xtime_l.h"
#include "stdio.h"
/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/

/************************** Function Prototypes ******************************/
void FlashWriteEnable(XQspiPs *QspiPtr);

void FlashWriteDisable(XQspiPs *QspiPtr) ;

void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount);

void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);

void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);

int FlashReadID(void);

void print_percent(int percent) ;

/************************** Variable Definitions *****************************/
XQspiPs QspiInstance;

/*
 * The following variables are used to read and write to the flash and they
 * are global to avoid having large buffers on the stack
 */
u8 ReadBuffer[PAGE_SIZE + DATA_OFFSET + DUMMY_SIZE];
u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET];
int update_qspi(XQspiPs *QspiInstancePtr, u16 QspiDeviceId, unsigned int TotoalLen, char *FlashDataToSend)
{
	int Status;
	int i ;
	unsigned int HasSendNum = 0 ;
	unsigned int WriteAddr = 0 ;
	unsigned int HasRecvNum = 0 ;
	unsigned int ReadAddr = 0 ;
	XTime TimerStart, TimerEnd;
	float elapsed_time ;

	int PercentCurr = -1 ;
	int PercentLast = -1 ;

	XQspiPs_Config *QspiConfig;

	/*
	 * Initialize the QSPI driver so that it's ready to use
	 */
	QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);
	if (NULL == QspiConfig) {
		return XST_FAILURE;
	}

	Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig,
			QspiConfig->BaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |
			XQSPIPS_FORCE_SSELECT_OPTION |
			XQSPIPS_HOLD_B_DRIVE_OPTION);
	XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);
	XQspiPs_SetSlaveSelect(QspiInstancePtr);


	FlashReadID();
	printf("Performing Erase Operation...\r\n") ;
	XTime_SetTime(0) ;
	XTime_GetTime(&TimerStart) ;
	FlashErase(QspiInstancePtr, 0, TotoalLen);
	XTime_GetTime(&TimerEnd) ;
	printf("100%%\r\n") ;
	elapsed_time = ((float)(TimerEnd-TimerStart))/((float)COUNTS_PER_SECOND) ;
	printf("INFO:Elapsed time = %.2f sec\r\n", elapsed_time) ;
	printf("Erase Operation Successful.\r\n") ;
	printf("Performing Program Operation...\r\n") ;
	XTime_SetTime(0) ;
	XTime_GetTime(&TimerStart) ;
	do
        {
		PercentCurr = (int)(((float)HasSendNum/(float)TotoalLen)*10) ;
		if (PercentCurr != PercentLast)
			print_percent(PercentCurr) ;
		PercentLast = PercentCurr ;
		if ((HasSendNum+PAGE_SIZE) > TotoalLen)
		{
			for (i = 0 ; i < PAGE_SIZE ; i++)
			{
				if (i >= TotoalLen-HasSendNum)
				{
					WriteBuffer[DATA_OFFSET + i] = 0 ;
				}
				else
				{
		WriteBuffer[DATA_OFFSET + i] = (u8)(FlashDataToSend[HasSendNum+i]);
				}
			}
			FlashWrite(QspiInstancePtr, WriteAddr, PAGE_SIZE, WRITE_CMD);
			printf("100%%\r\n") ;
			XTime_GetTime(&TimerEnd) ;
			elapsed_time = (float)(TimerEnd-TimerStart)/(COUNTS_PER_SECOND) ;
			printf("INFO:Elapsed time = %.2f sec\r\n", elapsed_time) ;
			printf("Program Operation Successful.\r\n") ;
			HasSendNum+= PAGE_SIZE ;
		}
		else
		{
			for (i = 0 ; i < PAGE_SIZE ; i++)
			{
				WriteBuffer[DATA_OFFSET + i] = (u8)(FlashDataToSend[HasSendNum+i]);
			}
			FlashWrite(QspiInstancePtr, WriteAddr, PAGE_SIZE, WRITE_CMD);
			HasSendNum+= PAGE_SIZE ;
			WriteAddr+= PAGE_SIZE ;
		}
	}while(HasSendNum < TotoalLen) ;
	HasSendNum = 0 ;
	WriteAddr = 0 ;
	printf("Performing Verify Operation...\r\n") ;
	memset(ReadBuffer, 0x00, sizeof(ReadBuffer));
	XTime_SetTime(0) ;
	XTime_GetTime(&TimerStart) ;
	do{
		PercentCurr = (int)(((float)HasRecvNum/(float)TotoalLen)*10) ;

		if (PercentCurr != PercentLast)
			print_percent(PercentCurr) ;

		PercentLast = PercentCurr ;
		if ((HasRecvNum+PAGE_SIZE) > TotoalLen)
		{
			FlashRead(QspiInstancePtr, ReadAddr, PAGE_SIZE, FAST_READ_CMD);
			for (i = 0 ; i < TotoalLen-HasRecvNum; i++)
			{
				if (ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i] != (u8)(FlashDataToSend[HasRecvNum+i]))
				{
					printf("Verify data error, address is 0x%x\tSend Data is 0x%x\tRead Data is 0x%x\r\n",			ReadAddr+i+1,FlashDataToSend[HasRecvNum+i], ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i]) ;
					break ;
				}
			}
			HasRecvNum+= PAGE_SIZE ;
			printf("100%%\r\n") ;
			XTime_GetTime(&TimerEnd) ;
			elapsed_time = (float)(TimerEnd-TimerStart)/(COUNTS_PER_SECOND) ;
			printf("INFO:Elapsed time = %.2f sec\r\n", elapsed_time) ;
			printf("Verify Operation Successful.\r\n") ;
		}
		else
		{
			FlashRead(QspiInstancePtr, ReadAddr, PAGE_SIZE, FAST_READ_CMD);
			for (i = 0 ; i < PAGE_SIZE ; i++)
			{
				if (ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i] != (u8)(FlashDataToSend[HasRecvNum+i]))
				{
					printf("Verify data error, address is 0x%x\tSend Data is 0x%x\tRead Data is 0x%x\r\n",
							ReadAddr+i+1,FlashDataToSend[HasRecvNum+i], ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i]) ;
					break ;
				}
			}
			HasRecvNum+= PAGE_SIZE ;
			ReadAddr+= PAGE_SIZE ;
		}
	}while(HasRecvNum < TotoalLen) ;
	HasRecvNum = 0 ;
	ReadAddr = 0 ;
	return XST_SUCCESS;
}

void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command)
{
	u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	u8 FlashStatus[2];
	XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));
	WriteBuffer[COMMAND_OFFSET]   = Command;
	WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
	WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
	WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
	XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
			ByteCount + OVERHEAD_SIZE);
	while (1) 
        {
		XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus,
				sizeof(ReadStatusCmd));
		if ((FlashStatus[1] & 0x01) == 0) 
                {
			break;
		}
	}
}
void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command)
{
	WriteBuffer[COMMAND_OFFSET]   = Command;
	WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
	WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
	WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);

	if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) ||
			(Command == QUAD_READ_CMD)) 
        {
		ByteCount += DUMMY_SIZE;
	}
	XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer,ByteCount + OVERHEAD_SIZE);
}
void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount)
{
	u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	u8 FlashStatus[2];
	int Sector;
	unsigned int EraseSecNum ;
	int PercentCurr = -1 ;
	int PercentLast = -1 ;
	if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) 
        {
		XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
				sizeof(WriteEnableCmd));
		WriteBuffer[COMMAND_OFFSET]   = BULK_ERASE_CMD;
		XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
				BULK_ERASE_SIZE);
		while (1) 
                {
			XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
					FlashStatus,
					sizeof(ReadStatusCmd));
			if ((FlashStatus[1] & 0x01) == 0) 
                        {
				xil_printf("Bulk Erase Done!\r\n") ;
				break;
			}
		}

		return;
	}
	EraseSecNum = ((ByteCount / SECTOR_SIZE) + 1) ;
	xil_printf("Erase Size is %u Bytes\r\n", EraseSecNum*SECTOR_SIZE) ;
	for (Sector = 0; Sector < EraseSecNum ; Sector++) 
        {
		PercentCurr = (int)(((float)Sector/(float)EraseSecNum)*10) ;
		if (PercentCurr != PercentLast)
			print_percent(PercentCurr) ;
		PercentLast = PercentCurr ;
		XQspiPs_PolledTransfer(QspiPtr, &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(QspiPtr, WriteBuffer, NULL,SEC_ERASE_SIZE);
		while (1) 
                {
			XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
					FlashStatus,
					sizeof(ReadStatusCmd));
			if ((FlashStatus[1] & 0x01) == 0) 
                        {
				break;
			}
		}

		Address += SECTOR_SIZE;
	}
}
int FlashReadID(void)
{
	int Status;

	WriteBuffer[COMMAND_OFFSET]   = READ_ID;
	WriteBuffer[ADDRESS_1_OFFSET] = 0x23;		/* 3 dummy bytes */
	WriteBuffer[ADDRESS_2_OFFSET] = 0x08;
	WriteBuffer[ADDRESS_3_OFFSET] = 0x09;
	Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer,RD_ID_SIZE);
	if (Status != XST_SUCCESS) 
        {
		return XST_FAILURE;
	}
	xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2],ReadBuffer[3]);
	return XST_SUCCESS;
}


void print_percent(int percent)
{
	switch(percent)
	{
	case 0 : xil_printf("0%%..") ; break ;
	case 1 : xil_printf("10%%..") ; break ;
	case 2 : xil_printf("20%%..") ; break ;
	case 3 : xil_printf("30%%..") ; break ;
	case 4 : xil_printf("40%%..") ; break ;
	case 5 : xil_printf("50%%..") ; break ;
	case 6 : xil_printf("60%%..") ; break ;
	case 7 : xil_printf("70%%..") ; break ;
	case 8 : xil_printf("80%%..") ; break ;
	case 9 : xil_printf("90%%..") ; break ;
	case 10 : xil_printf("100%..") ; break ;
	default : break ;
	}
}

 7,主函数中加入文件

(1)在主函数main.ct中添加头文件#include "src/qspi_update/qspi.h"

然后再主函数外面添加下列定义:

static struct udp_pcb *udp8080_pcb = NULL;//PS端软件端口号

unsigned int ReceivedCount = 0 ;

unsigned int StartUpdate = 0;//程序升级标志

struct ip_addr target_addr;//接收来自电脑的目标IP地址

#define MAX_FLASH_LEN   32*1024*1024

char FlashRxBuffer[MAX_FLASH_LEN] ;

XQspiPs QspiInstance;

#define IP_ADDR_ANY         ((ip_addr_t *)&ip_addr_any)//绑定IP地址

(2)在主函数添加udp判断函数:udp_recive(void *arg, struct udp_pcb *pcb, struct pbuf *p_rx,struct ip_addr *addr, u16_t port)

用来判断上位机或网口助手发过来得是否是升级程序BOOT.BIN文件,如果是,就将升级程序标志位置1,准备升级

void udp_recive(void *arg, struct udp_pcb *pcb, struct pbuf *p_rx,struct ip_addr *addr, u16_t port)

{

char *pData;

if (p_rx != NULL)

{

pData = (char *) p_rx->payload;

int udp_len = p_rx->len ;

if (udp_len == 6 && !(memcmp("update", p_rx->payload, 6)))

{

xil_printf("Received Size is %u Bytes\r\n", ReceivedCount) ;

xil_printf("Initialization done, programming the memory\r\n") ;

StartUpdate = 1 ;

}

else

{

memcpy(&FlashRxBuffer[ReceivedCount], pData, udp_len);

ReceivedCount += udp_len ;

}

pbuf_free(p_rx);

}

}

(3)主函数main.c中加入:

udp8080_pcb = udp_new();

 udp_bind(udp8080_pcb, IP_ADDR_ANY, 8080);

udp_recv(udp8080_pcb, udp_recive, 0);

IP4_ADDR(&target_addr, 192,168,0,223);//上位机IP

while(1)

{

xemacif_input(echo_netif);

if (StartUpdate)

{

int Status = update_qspi(&QspiInstance, QSPI_DEVICE_ID, ReceivedCount, FlashRxBuffer) ;

if (Status != XST_SUCCESS)

xil_printf("Write Flash Error!\r\n") ;

else

{

StartUpdate = 0 ;

ReceivedCount = 0;

}

}

…………………………………………….
}

8,run as或debug 加载该程序程序(zynq ps ip设为192.168.0.16):

IP4_ADDR(&(echo_netif->ip_addr), 192, 168,   0, 16);

IP4_ADDR(&(echo_netif->netmask), 255, 255, 255,  0);

IP4_ADDR(&(echo_netif->gw),      192, 168,   0,  1);

此时从cmd中能ping通192.168.0.16就说明加载的程序已经运行起来了可以开始升级

9,SDK程序升级,使用网口助手发送:

 (1)选择UDP协议类型,填入电脑本地的IP地址和端口号(自己定一个),填入ZYNQ PS端的IP地址192.168.0.16和端口号8080;点击启用文件数据源,从文件夹中选择要升级的文件BOOT.bin并打开,最后点击发送直到发送完成;

(2)去掉启用文件数据源,然后从网口助手输入“update”字符串,最后点击发送ZYNQ主控收到字符串就会拉高标志位,然后执行程序升级,然后等一段时间等待升级完成。

(3)重启板子电源,然后从cmd ping 192.168.0.10(新程序BOOT.bin的IP),能ping通,说明新程序裸机升级到ZYNQ成功;

附录:ZYNQ带系统实现程序升级:

使用TFTP从PC机将需要升级的文件传输到系统替换原来的新程序,在系统中使用脚本自动运行程序,就算升级完成。

猜你喜欢

转载自blog.csdn.net/wangjie36/article/details/117408789