寫在前面
對于ZYNQ系列的板卡固化,可以通過JTAG接口,使用SDK固化到FLASH中,或者可將SD卡取出將SD卡中保存的固化工程進(jìn)行修改,但在很多情況下,離線更新會很不方便,本文借鑒網(wǎng)上常見的遠(yuǎn)程更新QSPI FLASH的相關(guān)示例,對表貼式SD卡的應(yīng)用程序進(jìn)行了在線更新的操作適配,便于ZYNQ設(shè)備進(jìn)行遠(yuǎn)程更新保存在表貼式SD卡中的固化程序。
傳統(tǒng)SD卡與表貼SD卡區(qū)別
對于傳統(tǒng)SD卡,直接將SD卡取出,使用讀卡器進(jìn)行脫機(jī)更新很方便,但是由于SD卡插拔時容易損壞,對于一些需要SD卡設(shè)備,但需要高可靠性的應(yīng)用場景,使用傳統(tǒng)的SD卡托很容易造成卡托和TF卡的脫落,很難保持SD卡長時間的穩(wěn)定讀取。
相比傳統(tǒng)的SD卡,使用表貼式的SD卡,將會增加系統(tǒng)的可靠性和穩(wěn)定性,這里硬件方案選擇雷龍公司的NAND Flash(貼片式TF卡)CSNP4GCR01-AMW,產(chǎn)品說明如下:
?
相比傳統(tǒng)的SD卡,表貼式SD卡除了保留了SD卡大容量容易讀寫操作的特點(diǎn)外,在PCB板上的占用面積也相比傳統(tǒng)表貼卡托的面積要小。對傳統(tǒng)的SD卡的電路設(shè)計(jì)可實(shí)現(xiàn)快速替代。
程序簡述說明
程序大體框架借鑒了正點(diǎn)原子的遠(yuǎn)程更新的例程架構(gòu),只對更新QSPI的部分進(jìn)行改寫替換,替換成對SD卡的固化程序進(jìn)行更新的相關(guān)代碼。本文使用的板卡為PYNQ-Z2,這里只是為了驗(yàn)證表貼SD卡的功能,使用轉(zhuǎn)接板對傳統(tǒng)的SD卡進(jìn)行了替代。相關(guān)樣片和轉(zhuǎn)接板樣品可在雷龍公司官網(wǎng)進(jìn)行申請?jiān)囉谩?/p>
大致實(shí)現(xiàn)功能為:用 LWIP 協(xié)議棧的 tcp 協(xié)議實(shí)現(xiàn)遠(yuǎn)程更新 表貼SD卡的功能,當(dāng)輸入“ update”命令時更新 SD卡并反饋信息,當(dāng)輸入“ clear”命令時之前傳輸?shù)臄?shù)據(jù)無效。
硬件平臺搭建
新建工程,創(chuàng)建 block design。添加ZYNQ7 IP,對zynq進(jìn)行初始化配置,對應(yīng)板卡配置勾選SD,UART以及ENET資源,
?
如使用相同型號的板卡,可設(shè)置該部分為相同配置。
?
勾選DDR,并設(shè)置為PYNQZ2板卡的DDR的信息,
?
取消勾選多余資源,點(diǎn)擊OK,完成硬件設(shè)計(jì)。如下圖:
?
然后我們進(jìn)行g(shù)enerate output product 然后生成HDL封裝。這里沒有進(jìn)行使用PL資源,也不需要進(jìn)行綜合布局,在導(dǎo)出硬件時也不用包含bit流文件。
SDK軟件部分
打開SDK后,新建application project,這里為了方便lwip設(shè)置,可選用使用lwip的相關(guān)模板,這里選擇lwip tcp回環(huán)測試模板,保存新建工程。
?
選中新建好的工程,選擇右擊選中設(shè)置板載支持包,除了勾選lwip的板級支持包外,還需勾選sd卡需要的文件模式支持包。
編輯
?
點(diǎn)擊standalone下的xilffs,可以對文件系統(tǒng)進(jìn)行配置,這里可以使能長文件名有效,改變勾選為true。
保留模板例程的中的platform配置文件,刪除其余文件。
?
修改main.c文件
修改main.c文件為如下:
#include
#include "platform.h"
#include "platform_config.h"
#include "lwipopts.h"
#include "xil_printf.h"
#include "sleep.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/init.h"
#include "lwip/inet.h"
#if LWIP_IPV6==1
#include "lwip/ip6_addr.h"
#include "lwip/ip6.h"
#else
#if LWIP_DHCP==1
#include "lwip/dhcp.h"
extern volatile int dhcp_timoutcntr;
#endif
#define DEFAULT_IP_ADDRESS "192.168.1.10"
#define DEFAULT_IP_MASK "255.255.255.0"
#define DEFAULT_GW_ADDRESS "192.168.1.1"
#endif /* LWIP_IPV6 */
extern volatile int TcpFastTmrFlag;
extern volatile int TcpSlowTmrFlag;
void platform_enable_interrupts(void);
void start_application(void);
void print_app_header(void);
int transfer_data();
struct netif server_netif;
#if LWIP_IPV6==1
static void print_ipv6(char *msg, ip_addr_t *ip)
{
print(msg);
xil_printf(" %s\n\r", inet6_ntoa(*ip));
}
#else
static void print_ip(char *msg, ip_addr_t *ip)
{
print(msg);
xil_printf("%d.%d.%d.%d\r\n", ip4_addr1(ip), ip4_addr2(ip),
ip4_addr3(ip), ip4_addr4(ip));
}
static void print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{
print_ip("Board IP: ", ip);
print_ip("Netmask : ", mask);
print_ip("Gateway : ", gw);
}
static void assign_default_ip(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{
int err;
xil_printf("Configuring default IP %s \r\n", DEFAULT_IP_ADDRESS);
err = inet_aton(DEFAULT_IP_ADDRESS, ip);
if (!err)
xil_printf("Invalid default IP address: %d\r\n", err);
err = inet_aton(DEFAULT_IP_MASK, mask);
if (!err)
xil_printf("Invalid default IP MASK: %d\r\n", err);
err = inet_aton(DEFAULT_GW_ADDRESS, gw);
if (!err)
xil_printf("Invalid default gateway address: %d\r\n", err);
}
#endif /* LWIP_IPV6 */
int main(void)
{
struct netif *netif;
//設(shè)置開發(fā)板的MAC地址
unsigned char mac_ethernet_address[] = {
0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
netif = &server_netif;
init_platform();
print_app_header();
//初始化lwIP
lwip_init();
//將網(wǎng)絡(luò)接口添加到netif,并將其設(shè)置為默認(rèn)值
if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address,
PLATFORM_EMAC_BASEADDR)) {
xil_printf("Error adding N/W interface\r\n");
return -1;
}
#if LWIP_IPV6==1
netif->ip6_autoconfig_enabled = 1;
netif_create_ip6_linklocal_address(netif, 1);
netif_ip6_addr_set_state(netif, 0, IP6_ADDR_VALID);
print_ipv6("\n\rlink local IPv6 address is:", &netif->ip6_addr[0]);
#endif /* LWIP_IPV6 */
netif_set_default(netif);
//使能中斷
platform_enable_interrupts();
//指定網(wǎng)絡(luò)是否已啟動
netif_set_up(netif);
#if (LWIP_IPV6==0)
#if (LWIP_DHCP==1)
//創(chuàng)建新的DHCP客戶端
dhcp_start(netif);
dhcp_timoutcntr = 2;
while (((netif->ip_addr.addr) == 0) && (dhcp_timoutcntr > 0))
xemacif_input(netif);
if (dhcp_timoutcntr <= 0) {
if ((netif->ip_addr.addr) == 0) {
xil_printf("ERROR: DHCP request timed out\r\n");
assign_default_ip(&(netif->ip_addr),
&(netif->netmask), &(netif->gw));
}
}
#else
assign_default_ip(&(netif->ip_addr), &(netif->netmask), &(netif->gw));
#endif
print_ip_settings(&(netif->ip_addr), &(netif->netmask), &(netif->gw));
#endif /* LWIP_IPV6 */
//啟動應(yīng)用程序
start_application();
while (1) {
if (TcpFastTmrFlag) {
tcp_fasttmr();
TcpFastTmrFlag = 0;
}
if (TcpSlowTmrFlag) {
tcp_slowtmr();
TcpSlowTmrFlag = 0;
}
xemacif_input(netif);
transfer_data();
}
cleanup_platform();
return 0;
}
添加remote_update.h文件
#ifndef REMOTE_UPDATE_H_
#define REMOTE_UPDATE_H_
#include "xparameters.h"
#include "xtime_l.h"
#include "xstatus.h"
#include
//服務(wù)器端口
#define SER_PORT 5678
//接收的最大文件大小16MB
#define MAX_FLASH_LEN 16*1024*1024
void sent_msg(const char *msg);
#endif
添加remote_update.c文件
#include "remote_update.h"
#include "xparameters.h"
#include "ff.h"
#include "string.h"
#include
#include "lwip/err.h"
#include "lwip/tcp.h"
#include "xil_printf.h"
u8 start_update_flag = 0;
u8 rxbuffer[MAX_FLASH_LEN];
u32 total_bytes = 0;
#define FILE_NAME "BOOT.bin"
struct tcp_pcb *c_pcb;
FATFS fs;
void print_app_header()
{
xil_printf("-----SD remote update demo------\n");
}
//掛載sd卡
void sd_mount(){
FRESULT status;
BYTE work[FF_MAX_SS];
//掛載sd卡,注冊文件系統(tǒng)對象
status=f_mount(&fs,"",1);
if(status != FR_OK){
printf("%d\n",status);
printf("It isn't FAT format\n");
f_mkfs("",FM_FAT32,0,work,sizeof work);
f_mount(&fs,"",1);
}
}
//寫數(shù)據(jù)
void sd_write_data(u8 wr_dat[], u32 wr_len){
FIL fil;
UINT bw;
//創(chuàng)建或者打開文件
f_open(&fil,FILE_NAME,FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
//移動讀寫指針
f_lseek(&fil, 0);
//寫數(shù)據(jù)
f_write(&fil,wr_dat,wr_len,&bw);
//關(guān)閉文件
f_close(&fil);
}
//將接收到的BOOT.bin文件寫入到SD中
int transfer_data()
{
char msg[60];
if (start_update_flag) {
xil_printf("\r\nStart SD Update!\r\n");
xil_printf("file size of BOOT.bin is %lu Bytes\r\n", total_bytes);
sprintf(msg, "file size of BOOT.bin is %lu Bytes\r\n",total_bytes);
sent_msg(msg);
sd_write_data(rxbuffer,total_bytes);
xil_printf("SD Update finish!\n");
total_bytes = 0;
}
start_update_flag = 0;
return 0;
}
//向客戶端回送信息
void sent_msg(const char *msg)
{
err_t err;
tcp_nagle_disable(c_pcb);
if (tcp_sndbuf(c_pcb) > strlen(msg)) {
err = tcp_write(c_pcb, msg, strlen(msg), TCP_WRITE_FLAG_COPY);
if (err != ERR_OK)
xil_printf("tcp_server: Error on tcp_write: %d\r\n", err);
err = tcp_output(c_pcb);
if (err != ERR_OK)
xil_printf("tcp_server: Error on tcp_output: %d\r\n", err);
} else
xil_printf("no space in tcp_sndbuf\r\n");
}
//接收回調(diào)函數(shù)
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
struct pbuf *q;
if (!p) {
tcp_close(tpcb);
tcp_recv(tpcb, NULL);
xil_printf("tcp connection closed\r\n");
return ERR_OK;
}
q = p;
if (q->tot_len == 6 && !(memcmp("update", p->payload, 6))) {
start_update_flag = 1;
sent_msg("\r\nStart SD Update\r\n");
} else if (q->tot_len == 5 && !(memcmp("clear", p->payload, 5))) {
start_update_flag = 0;
total_bytes = 0;
sent_msg("Clear received data\r\n");
xil_printf("Clear received data\r\n");
} else {
while (q->tot_len != q->len) {
memcpy(&rxbuffer[total_bytes], q->payload, q->len);
total_bytes += q->len;
q = q->next;
}
memcpy(&rxbuffer[total_bytes], q->payload, q->len);
total_bytes += q->len;
}
tcp_recved(tpcb, p->tot_len);
pbuf_free(p);
return ERR_OK;
}
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
xil_printf("tcp_server: Connection Accepted\r\n");
c_pcb = newpcb; //保存連接的客戶端PCB
//設(shè)置接收回調(diào)
tcp_recv(c_pcb, recv_callback);
tcp_arg(c_pcb, NULL);
return ERR_OK;
}
int start_application()
{
struct tcp_pcb *pcb;
err_t err;
//掛載SD卡
sd_mount();
xil_printf("Successfully init SD\r\n");
print_app_header();
//創(chuàng)建TCP PCB
pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
if (!pcb) {
xil_printf("Error creating PCB. Out of Memory\n\r");
return -1;
}
//綁定端口號
err = tcp_bind(pcb, IP_ANY_TYPE, SER_PORT);
if (err != ERR_OK) {
xil_printf("Unable to bind to port %d: err = %d\n\r", SER_PORT, err);
return -2;
}
//此處不需要回調(diào)函數(shù)的任何參數(shù)
tcp_arg(pcb, NULL);
//偵聽連接
pcb = tcp_listen(pcb);
if (!pcb) {
xil_printf("Out of memory while tcp_listen\n\r");
return -3;
}
//指定用于傳入連接的回調(diào)
tcp_accept(pcb, accept_callback);
xil_printf("TCP server started @ port %d\n\r", SER_PORT);
return 0;
}
完成代碼編寫后,進(jìn)行燒寫驗(yàn)證。
下載驗(yàn)證
打開網(wǎng)絡(luò)調(diào)試助手,選擇協(xié)議類型為TCP客戶端,選擇遠(yuǎn)程主機(jī)的IP地址和端口,選擇需要加載的應(yīng)用程序的bin文件,勾選加載文件數(shù)據(jù)源,點(diǎn)擊發(fā)送。
?
發(fā)送完成后在發(fā)送框選擇輸入“update”更新SD卡的應(yīng)用程序。
?
串口終端中查看調(diào)試信息,表示SD卡程序更新完成。
?
使用讀卡器查看貼片SD卡轉(zhuǎn)接卡是否正常存儲到SD卡中,讀取文件可知已經(jīng)正常寫入。
?
將板卡啟動模式調(diào)整至SD卡模式,上電重啟板卡程序,觀察到板卡程序成功啟動。
-
SD卡
+關(guān)注
關(guān)注
2文章
565瀏覽量
63921 -
NAND閃存
+關(guān)注
關(guān)注
2文章
220瀏覽量
22777 -
存儲芯片
+關(guān)注
關(guān)注
11文章
897瀏覽量
43151 -
Nand flash
+關(guān)注
關(guān)注
6文章
241瀏覽量
39863
發(fā)布評論請先 登錄
相關(guān)推薦
評論