ZYNQ - 以太网远程更新SD卡应用程序

news2024/11/20 7:05:16

写在前面

对于ZYNQ系列的板卡固化,可以通过JTAG接口,使用SDK固化到FLASH中,或者可将SD卡取出将SD卡中保存的固化工程进行修改,但在很多情况下,离线更新会很不方便,本文借鉴网上常见的远程更新QSPI FLASH的相关示例,对表贴式SD卡的应用程序进行了在线更新的操作适配,便于ZYNQ设备进行远程更新保存在表贴式SD卡中的固化程序。

传统SD卡与表贴SD卡区别

对于传统SD卡,直接将SD卡取出,使用读卡器进行脱机更新很方便,但是由于SD卡插拔时容易损坏,对于一些需要SD卡设备,但需要高可靠性的应用场景,使用传统的SD卡托很容易造成卡托和TF卡的脱落,很难保持SD卡长时间的稳定读取。

相比传统的SD卡,使用表贴式的SD卡,将会增加系统的可靠性和稳定性,这里硬件方案选择雷龙公司的NAND Flash(贴片式TF卡)CSNP4GCR01-AMW,产品说明如下:

产品说明

相比传统的SD卡,表贴式SD卡除了保留了SD卡大容量容易读写操作的特点外,在PCB板上的占用面积也相比传统表贴卡托的面积要小。对传统的SD卡的电路设计可实现快速替代。

程序简述说明

程序大体框架借鉴了正点原子的远程更新的例程架构,只对更新QSPI的部分进行改写替换,替换成对SD卡的固化程序进行更新的相关代码。本文使用的板卡为PYNQ-Z2,这里只是为了验证表贴SD卡的功能,使用转接板对传统的SD卡进行了替代。相关样片和转接板样品可在雷龙公司官网进行申请试用。

大致实现功能为:用 LWIP 协议栈的 tcp 协议实现远程更新 表贴SD卡的功能,当输入“ update”命令时更新 SD卡并反馈信息,当输入“ clear”命令时之前传输的数据无效。

硬件平台搭建

新建工程,创建 block design。添加ZYNQ7 IP,对zynq进行初始化配置,对应板卡配置勾选SD,UART以及ENET资源,

配置ZYNQ核

如使用相同型号的板卡,可设置该部分为相同配置。

配置引脚

勾选DDR,并设置为PYNQZ2板卡的DDR的信息,

配置DDR

取消勾选多余资源,点击OK,完成硬件设计。如下图:

硬件设计

然后我们进行generate output product 然后生成HDL封装。这里没有进行使用PL资源,也不需要进行综合布局,在导出硬件时也不用包含bit流文件。

导出硬件

SDK软件部分

打开SDK后,新建application project,这里为了方便lwip设置,可选用使用lwip的相关模板,这里选择lwip tcp回环测试模板,保存新建工程。

新建工程

选中新建好的工程,选择右击选中设置板载支持包,除了勾选lwip的板级支持包外,还需勾选sd卡需要的文件模式支持包。

修改板级支持包

点击standalone下的xilffs,可以对文件系统进行配置,这里可以使能长文件名有效,改变勾选为true。

修改板级支持包

保留模板例程的中的platform配置文件,删除其余文件。

保留platform配置文件

修改main.c文件

修改main.c文件为如下:

#include <stdio.h>
#include "xparameters.h"
#include "netif/xadapter.h"
#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;

    //设置开发板的MAC地址
    unsigned char mac_ethernet_address[] = {
        0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };

    netif = &server_netif;

    init_platform();

    print_app_header();

    //初始化lwIP
    lwip_init();

    //将网络接口添加到netif,并将其设置为默认值
    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();

    //指定网络是否已启动
    netif_set_up(netif);

#if (LWIP_IPV6==0)
#if (LWIP_DHCP==1)
    //创建新的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 */

    //启动应用程序
    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 <stdio.h>

//服务器端口
#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 <stdio.h>
#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卡,注册文件系统对象
	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);
	}
}
//写数据
void sd_write_data(u8 wr_dat[], u32 wr_len){
	FIL fil;
	UINT bw;
	//创建或者打开文件
	f_open(&fil,FILE_NAME,FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
	//移动读写指针
	f_lseek(&fil, 0);
	//写数据
	f_write(&fil,wr_dat,wr_len,&bw);
	//关闭文件
	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");
}

//接收回调函数
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
    //设置接收回调
    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();
    //创建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;
    }

    //此处不需要回调函数的任何参数
    tcp_arg(pcb, NULL);

    //侦听连接
    pcb = tcp_listen(pcb);
    if (!pcb) {
        xil_printf("Out of memory while tcp_listen\n\r");
        return -3;
    }

    //指定用于传入连接的回调
    tcp_accept(pcb, accept_callback);
    xil_printf("TCP server started @ port %d\n\r", SER_PORT);

    return 0;
}

完成代码编写后,进行烧写验证。

下载验证

打开网络调试助手,选择协议类型为TCP客户端,选择远程主机的IP地址和端口,选择需要加载的应用程序的bin文件,勾选加载文件数据源,点击发送。

发送完成后在发送框选择输入“update”更新SD卡的应用程序。

在串口终端中查看调试信息,表示SD卡程序更新完成。

调试信息

使用读卡器查看贴片SD卡转接卡是否正常存储到SD卡中,读取文件可知已经正常写入。

image-20221229225450216

将板卡启动模式调整至SD卡模式,上电重启板卡程序,观察到板卡程序成功启动。

image-20221229224940998

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/127836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

思维导图 制作

PPT e.g&#xff1a; WPS中ppt 新建 文本框一定要 在外框之内。 左键 全选中&#xff0c;就会出来如下。 可直接点居中&#xff0c;就不用浪费time调位置 全选中&#xff0c;右键&#xff0c;【组合】 形成整体&#xff0c;可复制到word中 选择性粘贴到word中 skill: 左键…

《Linux运维总结:Centos7.6源码安装单实例redis6.2.8》

一、部署redis服务 1.1、环境信息 环境信息如下&#xff1a; 主机IP操作系统Redis版本CPU架构192.168.1.191Centos7.66.2.8x86_641.2、二进制方式 1、安装环境依赖 [rootlocalhost ~]# yum -y install gcc2、安装包下载 [rootlocalhost ~]# wget https://download.redis.io…

Linux-线程(LWP)

文章目录线程线程概念进程今天的进程 vs之前的进程私有和共享资源实验验证线程的优点&#xff1a;线程的缺点&#xff1a;线程异常线程的用途&#xff1a;线程控制创建线程(1)先创建两个线程&#xff1a;链接时要引入第三方库。(2)创建多个线程&#xff1a;(3)线程的健壮性不强…

Pandas 替换 NaN 值

替换Pandas DataFram中的 NaN 值 问题 NaN 代表 Not A Number&#xff0c;是表示数据中缺失值的常用方法之一。它是一个特殊的浮点值&#xff0c;不能转换为 float 以外的任何其他类型。NaN 值是数据分析中的主要问题之一。为了得到理想的结果&#xff0c;对 NaN 进行处理是非…

LeetCode刷题复盘笔记—一文搞懂动态规划之53. 最大子数组和问题(动态规划系列第三十五篇)

今日主要总结一下动态规划的一道题目&#xff0c;53. 最大子数组和 题目&#xff1a;53. 最大子数组和 Leetcode题目地址 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#…

成年人的崩溃一触即发,掌握学习能力才能突破认知结界,干货满满!

成年人的崩溃一触即发&#xff0c;掌握学习能力才能突破认知结界&#xff01; 今年2月份&#xff08;2022-02&#xff09;&#xff0c;从小红书上购买了知识博主【老明读书】&#xff0c;针对学习能力的讲解分析&#xff0c;真是干货满满&#xff01;非常感谢老明&#xff0c;…

FPGA项目案例展示

MIPI视频拼接&#xff1a; 在无人机、智能驾驶中&#xff0c;摄像头多达十几路 为解决图像处理芯片&#xff08;如海思、高通平台&#xff09;的接口瓶颈 需要将多个摄像头合成一路处理。 SLVS-EC转MIPI SLVS-EC采集&#xff0c;LANE速率可达4.6G MIPI输出&#xff0c;速率2.…

基于ASP.net Mvc的超市管理系统

摘 要 网络的广泛应用给生活带来了十分的便利。所以把天美意超市管理与现在网络相结合&#xff0c;利用net语言建设天美意超市管理系统&#xff0c;实现天美意超市管理的信息化。则对于进一步提高天美意超市发展&#xff0c;丰富天美意超市管理系统能起到不少的促进作用。 天美…

SHELL 脚本练习 一

习题一 &#xff1a;在当前主机编写脚本文件history_max.sh显示主机中执行频率最高的前5个命令 习题二 &#xff1a; 判断主机是否存在rhel用户&#xff0c;如果存在则设置密码为redhat,如果不存在则创建用户 并设置密码 习题三 &#xff1a;通过设置变量HISTTIMEFORMAT&#x…

【小白课程】openKylin系统音频大体框架介绍

在桌面操作系统中&#xff0c;音频是至关重要的一环&#xff0c;音频的稳定支持直接决定了用户的听感以及使用体验&#xff0c;今天我们就给大家介绍openKylin桌面操作系统背后关于音频的那些故事。 先看一张框架图&#xff0c;这张图大致介绍了openKylin操作系统音频的框架组…

动态配置开发模式在转转的落地实践

文章目录一、问题背景1.1 场景概述1.2 风险问题1.3 效率问题二、问题剖析2.1 以往的应对方式2.2 主要矛盾点与问题本质的探索2.2.1 主要矛盾点2.2.2 问题本质的探索三、方案设计3.1 视图展示的标准化3.2 视图构建的自动化3.3 开发体验的沉浸化3.4 整体架构设计四、落地现状五、…

谷粒商城之高级篇

谷粒商城之高级篇 目录谷粒商城之高级篇前言2 商城业务2.1 商品上架2.1.1 商品Mapping2.1.2 上架细节2.1.3 数据一致性2.1.4 代码实现2.2 商城系统首页2.2.1 渲染首页2.2.2 渲染一级分类数据2.2.3 渲染二级三级分类数据2.2.4 nginx 搭建域名访问环境2.3 检索业务2.3.1 页面环境…

Vue3富文本编辑器wangEditor 5使用总结

wangEditor 是一个开源 Web 富文本编辑器&#xff0c;开箱即用&#xff0c;配置简单 官网链接&#xff1a;https://www.wangeditor.com 使用流程&#xff1a; 1.在项目中安装wangEditor 输入以下命令安装 npm install wangeditor/editor --save npm install wangeditor/edi…

React通用后台管理系统-笔记1

环境 node: 16.17.1 npm: 8.15.0 Ant Design of React官网&#xff1a;https://ant.design/docs/react/introduce-cn 一、创建项目 npm init vite Project name: lege-management Select a framework: react Select a variant: react-ts 打开package.json&#xff0c;参考以下…

三、串(字符串)

一、定义及常见术语 串相等&#xff1a;当两个串的长度相等且对应位置上的字符都相同时&#xff0c;这两个串才是相等的 所有的空串都是相等的 二、两种存储结构 2.1顺序存储结构&#xff08;更常用&#xff09; #define MAXLEN 255 typedef struct {char ch[MAXLEN1];//存…

Flink Shuffle 3.0: Vision, Roadmap and Progress

摘要&#xff1a;摘要&#xff1a;本文整理自阿里云高级技术专家宋辛童 (五藏)&#xff0c;在 FFA 2022 核心技术专场的分享。本篇内容主要分为五个部分&#xff1a;Flink Shuffle 的演进流批融合云原生自适应Shuffle 3.0Tips&#xff1a;点击「阅读原文」查看原文视频&演讲…

Node.js--》模块化、npm与包的讲解与使用

目录 模块化 Node.js中模块的分类 模块作用域 模块的加载机制 npm与包 npm包管理工具的安装与使用 包管理配置文件 包下载速度 包的分类 发布包 模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层把系统划分成若干模块的过程。对整个系统来说&#xff0…

数据湖---hudi简介

文章目录hudi是什么核心概念TilelineTable TypesQuery Types特性官网 hudi是什么 apache hudi是下一代流式数据湖平台。hudi将数仓和数据库的核心功能带到了数据湖。提供表、事务、高效的upsert、delete&#xff0c;高级索引&#xff0c;流式注入服务&#xff0c;数据集群、压…

圣诞特辑 · Three.js加载圣诞树模型

&#x1f384;2022年圣诞节到来啦&#xff0c;很高兴这次我们能一起度过~&#x1f384; 文章目录&#x1f389;前言&#x1f50e;预览&#x1f381;项目&资源项目源码地址打包运行✒️编码实现&#x1f389;前言 2022圣诞节来了&#xff0c;让我们一起使用Three.js实现导入…

渗透测试-ctfshow 爆破(web入门)

大家可以关注一下我的公众号-小白渗透测试&#xff0c;互相学习。 web1 打开环境 先随便输入一个账号密码&#xff0c;使用bp进行抓包分析。 可以看到&#xff0c;账号密码进行了base64加密&#xff0c;使用工具进行解密&#xff0c;可以看到是账号:密码这样一个格式。 然后我…