Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(二)

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(前导)

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(三)


 五、实验目的

        本次实验使用电脑上的网络调试助手,将命令帧通过以太网芯片RTL8211(RGMII接口)发送至ACZ7015开发板,提取UDP报文内容转换成控制命令,从而实现对ACM9238模块采样频率、数据采样个数以及采样通道的配置。

         配置完成之后,ACM9238模块开始采集数据,将采集的数据存储至DDR3中,然后通过网口以UDP协议传输到电脑。用户可以在电脑上通过网口调试工具进行指令的下发,并以文件的形式保存接收到的数据,然后使用MATLAB软件进行进一步的数据处理分析。

PL 部分的模块说明如下:

  • pll 模块: 锁相环模块, 输入时钟 50M, 由 PS 输出给 PL; 输出 100M 的时钟给到 DDR3 控制器使用; 输出 50M 的时钟给其它模块使用。
  • eth_receive_cmd 模块: 以太网接收命令模块, 对以太网接收到的数据进行分析, 将接收的数据转换成相应的控制数据并输出到对应的模块。
  • ad9238_ctrl 模块: ACM9238 控制器模块, 该模块内部包含速度控制模块,以及数据位宽转换模块。
  • state_ctrl 模块: ADC 采集数据 DDR3 缓存以太网发送状态控制模块, 协调各个模块的信号控制, 程序状态的总控制模块。
  • fifo_axi4_adapter 模块: fifo 接口到 AXI4 接口的转换模块(含 2 个 FIFO)。

六、ACM9238模块

两路,

采样上限50Msps,如果期望以1Msps 的采样速率采样, 则只需要每间隔 50 个采样数据取一个结果存储或使用, 其他 49 个数据直接舍弃。不建议降低ADC芯片的时钟信号。

七、设计实例

7.1模块设计

7.1.1 eth_receive_cmd模块

将以太网接收到的数据进行解析, 得到控制命令。

 

(1)eth_udp_rx_gmii模块和rgmii_to_gmii模块
(2)mmcm模块

锁相环模块,将 rgmii 接口时钟信号 rgmii_rx_clk_i 偏移90 °得到 rgmii_rx_clk 时钟信号。(为了在时钟信号的上升沿/下降沿取数据时,取得数据刚好是数据信号 rgmii_rxd 的正中间, 使得采样的数据处于最稳定的状态。)

 锁相环IP配置

(3)fifo_rx模块

使用该 IP 核解决采集过程中会出现的跨时钟域数据交互问题(以太网125MHz,ACM9238-50MHz)。

(4)eth_cmd模块

接收转命令模块。

//非空时产生FIFO读请求信号
always@(posedge clk or negedge reset_n)
if(!reset_n)fifo_rd_req <= 1'b0;
else if(!rx_empty)fifo_rd_req <= 1'b1;
elsefifo_rd_req <= 1'b0;//获得帧命令数据
always@(posedge clk)
if(fifo_rd_req)begindata_0[7] <= #1 fifodout;data_0[6] <= #1 data_0[7];data_0[5] <= #1 data_0[6];data_0[4] <= #1 data_0[5];data_0[3] <= #1 data_0[4];data_0[2] <= #1 data_0[3];data_0[1] <= #1 data_0[2];data_0[0] <= #1 data_0[1];
end//判断帧命令数据
always@(posedge clk or negedge reset_n)
if(!reset_n)beginaddress <= 0;cmd_data <= 32'd0;cmdvalid <= 1'b0;
end
else if(fifo_rx_done)beginif((data_0[0]==8'h55)&&(data_0[1]==8'hA5)&&(data_0[7]==8'hF0))begincmd_data[7:0] <= #1 data_0[6];cmd_data[15:8] <= #1 data_0[5];cmd_data[23:16] <= #1 data_0[4];cmd_data[31:24] <= #1 data_0[3];address <= #1 data_0[2];cmdvalid <= #1 1;endelsecmdvalid <= #1 0;
end
else
cmdvalid <= #1 0;
(5)cmd_rx模块

接收数据转换为控制数据。

寄存器说明:

always@(posedge clk or negedge reset_n)
if(!reset_n)beginChannelSel <= 2'b00;DataNum <= 32'd0;ADC_Speed_Set <= 32'd0;RestartReq <= 1'b0;
end
else if(cmdvalid)begincase(cmd_addr)0: RestartReq <= 1'b1;1: ChannelSel <= cmd_data[1:0];2: DataNum <= cmd_data[31:0];3: ADC_Speed_Set <= cmd_data[31:0];default:;endcase
end
elseRestartReq <= 1'b0;

7.1.2 ad9238_ctrl 模块

控制ADC的采样速率,将12位数据转换为16位数据。

(1)speed_ctrl模块

always@(posedge clk or negedge reset_n)
if(!reset_n)div_cnt <= 0;
else if(ad_sample_en)beginif(div_cnt >= div_set)div_cnt <= 0;elsediv_cnt <= div_cnt + 1'd1;
end
elsediv_cnt <= 0;always@(posedge clk or negedge reset_n)
if(!reset_n)adc_data_en <= 0;
else if(div_cnt == div_set)adc_data_en <= 1;
elseadc_data_en <= 0;
(2)ad_12bit_to_16bit模块

always @(posedge clk)ad_out_valid <= ad_sample_en;assign s_ad_in1 = ad_in1 + 12'd2048;
assign s_ad_in2 = ad_in2 + 12'd2048;always @(posedge clk)
if(ad_sample_en && ch_sel == 2'b01)ad_out<={4'd0,s_ad_in1};//这样补 0 为了适应上位机
else if(ad_sample_en && ch_sel == 2'b10)ad_out<={4'd0,s_ad_in2};//
else if(ad_sample_en && ch_sel == 2'b00)ad_out<={4'd0,adc_test_data};
elsead_out <= 16'd0;

7.1.3 state_ctrl 模块

控制信号的产生以及 ADC 何时启动数据传输。

localparam IDLE = 4'd0; //空闲状态
localparam DDR_WR_FIFO_CLEAR = 4'd1; //DDR 写 FIFO 清除状态
localparam ADC_SAMPLE = 4'd2; //ADC 采样数据状态
localparam DDR_RD_FIFO_CLEAR = 4'd3; //DDR 读 FIFO 清除状态
localparam DATA_SEND_START = 4'd4; //数据发送状态
localparam DATA_SEND_WORKING = 4'd5; //数据发送完成状态

(1)IDLE

//ADC 模块开始采样标志信号寄存
always@(posedge clk or posedge reset)begin
if(reset)start_sample_rm <= 1'b0;
else if(state==IDLE)start_sample_rm <= start_sample;
elsestart_sample_rm <= 1'b0;
end/*状态切换IDLE->DDR_WR_FIFO_CLEAR
beginif(start_sample_rm) begin //DDR 初始化完成并且产生启动采样信号state <= DDR_WR_FIFO_CLEAR; //进入写 FIFO 清除状态endelse beginstate <= state;end
end
*/

(2)DDR_WR_FIFO_CLEAR

//延时10个节拍
always@(posedge clk or posedge reset)begin
if(reset)wrfifo_clr_cnt<=0;
else if(state==DDR_WR_FIFO_CLEAR)//如果进入了清 fifo 状态
beginif(wrfifo_clr_cnt==9)wrfifo_clr_cnt<=5'd9;elsewrfifo_clr_cnt<=wrfifo_clr_cnt+1'b1;
end
elsewrfifo_clr_cnt<=5'd0;
end//给清FIFO信号足够的拉高时间
always@(posedge clk or posedge reset)begin
if (reset)wrfifo_clr<=0;
else if(state==DDR_WR_FIFO_CLEAR)beginif(wrfifo_clr_cnt==0||wrfifo_clr_cnt==1||wrfifo_clr_cnt==2)wrfifo_clr<=1'b1;elsewrfifo_clr<=1'b0;end
elsewrfifo_clr<=1'b0;
end/*状态切换DDR_WR_FIFO_CLEAR->ADC_SAMPLE
beginif(!wrfifo_full && (wrfifo_clr_cnt==9))state<=ADC_SAMPLE;elsestate<=DDR_WR_FIFO_CLEAR;
end
*/

(3)ADC_SAMPLE

//根据ADC输出使能信号计数
always@(posedge clk or posedge reset)begin
if(reset)adc_sample_cnt<=1'b0;
else if(state==ADC_SAMPLE)beginif(adc_data_en)adc_sample_cnt<=adc_sample_cnt+1'b1;elseadc_sample_cnt<=adc_sample_cnt;
end
elseadc_sample_cnt<=1'b0;
end//产生采样使能信号给其他模块
always@(posedge clk or posedge reset)begin
if(reset)ad_sample_en<=0;
else if(state==ADC_SAMPLE)ad_sample_en<=1;
elsead_sample_en<=0;
end/*状态切换ADC_SAMPLE->DDR_RD_FIFO_CLEAR
beginif((adc_sample_cnt>=set_sample_num-1'b1)&& adc_data_en)state<=DDR_RD_FIFO_CLEAR;elsestate<=state;
end
*/

(4)DDR_RD_FIFO_CLEAR

//延时10个节拍
always@(posedge clk or posedge reset)begin
if(reset)rdfifo_clr_cnt<=0;
else if(state==DDR_RD_FIFO_CLEAR)//如果进入了清 fifo 状态
beginif(rdfifo_clr_cnt==9)rdfifo_clr_cnt<=5'd9;elserdfifo_clr_cnt<=rdfifo_clr_cnt+1'b1;
end
elserdfifo_clr_cnt<=5'd0;
end//给清FIFO信号足够的拉高时间
always@(posedge clk or posedge reset)begin
if (reset)rdfifo_clr<=0;
else if(state==DDR_RD_FIFO_CLEAR)
beginif(rdfifo_clr_cnt==0||rdfifo_clr_cnt==1||rdfifo_clr_cnt==2)rdfifo_clr<=1'b1;
elserdfifo_clr<=1'b0;
end
elserdfifo_clr<=1'b0;
end/*状态切换DDR_RD_FIFO_CLEAR->DATA_SEND_START
beginif(!rdfifo_empty && (rdfifo_clr_cnt==9))beginstate<=DATA_SEND_START;endelsestate<=state;
end
*/

(5)DATA_SEND_START

/*状态切换DATA_SEND_START->DATA_SEND_WORKING
beginstate <= DATA_SEND_WORKING;
end
*/

(6)DATA_SEND_WORKING

//发送数据计数
always@(posedge clk or posedge reset)begin
if(reset)send_data_cnt<=32'd0;
else if(state==IDLE)send_data_cnt<=32'd0;
else if(rdfifo_rden)send_data_cnt<=send_data_cnt+1;
elsesend_data_cnt<=send_data_cnt;
end//DDR数据存到以太网缓存
always@(posedge clk or posedge reset)
if(reset) begineth_fifo_wrreq <= 1'b0;eth_fifo_wrdata <= 'd0;
end
else if(rdfifo_rden) begineth_fifo_wrreq <= 1'b1;eth_fifo_wrdata <= rdfifo_dout;
end
else begineth_fifo_wrreq <= 1'b0;eth_fifo_wrdata <= 'd0;
end/*状态切换DATA_SEND_WORKING->IDLE、DATA_SEND_WORKING->DATA_SEND_WORKING
beginif(send_data_cnt >= set_sample_num-1'b1) beginrdfifo_rden <= 1'b0;state <= IDLE;endelse beginrdfifo_rden <= 1'b1;state <= DATA_SEND_WORKING;end
end
*/

7.1.4 fifo_axi_adapter模块

看文章开头:Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)。

S_IDLE:
beginif(start)next_state = S_ARB;elsenext_state = S_IDLE;
end
module fifo_axi4_adapter #(parameter FIFO_DW                = 16     ,parameter WR_AXI_BYTE_ADDR_BEGIN = 0      ,parameter WR_AXI_BYTE_ADDR_END   = 1023   ,parameter RD_AXI_BYTE_ADDR_BEGIN = 0      ,parameter RD_AXI_BYTE_ADDR_END   = 1023   ,parameter AXI_DATA_WIDTH         = 128    ,parameter AXI_ADDR_WIDTH         = 28     ,parameter AXI_ID                 = 4'b0000,parameter AXI_BURST_LEN          = 8'd31    //burst length = 32
)
(input                          start         ,// clock resetinput                          clk           ,input                          reset         ,// wr_fifo wr Interfaceinput                          wrfifo_clr    ,input                          wrfifo_clk    ,input                          wrfifo_wren   ,input   [FIFO_DW-1:0]          wrfifo_din    ,output                         wrfifo_full   ,output  [15:0]                 wrfifo_wr_cnt ,// rd_fifo rd Interfaceinput                          rdfifo_clr    ,input                          rdfifo_clk    ,input                          rdfifo_rden   ,output  [FIFO_DW-1:0]          rdfifo_dout   ,output                         rdfifo_empty  ,output  [15:0]                 rdfifo_rd_cnt ,// Master Interface Write Address Portsoutput  [3:0]                  m_axi_awid    ,output  [AXI_ADDR_WIDTH-1:0]   m_axi_awaddr  ,output  [7:0]                  m_axi_awlen   ,output  [2:0]                  m_axi_awsize  ,output  [1:0]                  m_axi_awburst ,output  [0:0]                  m_axi_awlock  ,output  [3:0]                  m_axi_awcache ,output  [2:0]                  m_axi_awprot  ,output  [3:0]                  m_axi_awqos   ,output  [3:0]                  m_axi_awregion,output                         m_axi_awvalid ,input                          m_axi_awready ,// Master Interface Write Data Portsoutput  [AXI_DATA_WIDTH-1:0]   m_axi_wdata   ,output  [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb   ,output                         m_axi_wlast   ,output                         m_axi_wvalid  ,input                          m_axi_wready  ,// Master Interface Write Response Portsinput   [3:0]                  m_axi_bid     ,input   [1:0]                  m_axi_bresp   ,input                          m_axi_bvalid  ,output                         m_axi_bready  ,// Master Interface Read Address Portsoutput  [3:0]                  m_axi_arid    ,output  [AXI_ADDR_WIDTH-1:0]   m_axi_araddr  ,output  [7:0]                  m_axi_arlen   ,output  [2:0]                  m_axi_arsize  ,output  [1:0]                  m_axi_arburst ,output  [0:0]                  m_axi_arlock  ,output  [3:0]                  m_axi_arcache ,output  [2:0]                  m_axi_arprot  ,output  [3:0]                  m_axi_arqos   ,output  [3:0]                  m_axi_arregion,output                         m_axi_arvalid ,input                          m_axi_arready ,// Master Interface Read Data Portsinput   [3:0]                  m_axi_rid     ,input   [AXI_DATA_WIDTH-1:0]   m_axi_rdata   ,input   [1:0]                  m_axi_rresp   ,input                          m_axi_rlast   ,input                          m_axi_rvalid  ,output                         m_axi_rready  
);

7.1.5 eth_send_data 模块

将DDR读出的ADC数据发送到电脑端。

(1)fifo_tx模块

First Word Fall Through( FWFT)可以不需要读命令, 自动将最新的数据放在 dout 上。

(2)eth_send_ctrl模块

以太网帧最大长度 1518 字节(数据段 1500 字节) , 其中数据段 1500 字节还包括 20 字节 IP 报文头部和 8 字节 UDP 报文头部, 所以数据帧发送的ACM9238 采集的数据最大长度为 1472 字节。

  always@(posedge clk125M or negedge reset_n)if(!reset_n) beginpkt_tx_en <= 1'd0;pkt_length <= 16'd0;data_num <= 32'd0;state <= 0;cnt_dly_time <= 28'd0;endelse begincase(state)0://得到 pkt_length 信号的初始值beginif(restart_req)begindata_num <= total_data_num;if((total_data_num << 1) >= 16'd1472)beginpkt_length <= 16'd1472;	//一个数据2个字节state <= 1;endelse if((total_data_num << 1) > 0)beginpkt_length <= total_data_num << 1; //一个数据2个字节state <= 1;endelse beginstate <= 0;endendend1: beginif(fifo_rd_cnt >= (pkt_length -2)) beginpkt_tx_en <= 1'd1;state <= 2;endelse beginstate <= 1;pkt_tx_en <= 1'd0;endend2:beginpkt_tx_en <= 1'd0;if(eth_tx_done)begindata_num <= data_num - pkt_length/2;state <= 3;endend3:if(cnt_dly_time >= cnt_dly_min)beginstate <= 4;cnt_dly_time <= 28'd0;endelse begincnt_dly_time <= cnt_dly_time + 1'b1;state <= 3;end4:beginif(data_num * 2 >= 16'd1472)beginpkt_length <= 16'd1472;state <= 1;endelse if(data_num * 2 > 0)beginpkt_length <= data_num * 2;state <= 1;endelse beginstate <= 0;endenddefault:state <= 0;endcaseend

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分析开源机器学习框架TensorFlow

TensorFlow是一个开源的机器学习框架&#xff0c;由Google开发和维护。它提供了一个灵活的编程环境&#xff0c;可用于构建和训练各种机器学习模型。TensorFlow的基本概念和使用场景如下&#xff1a; 张量&#xff08;Tensor&#xff09;&#xff1a;在TensorFlow中&#xff0c…

Unity引擎关于APP后台下载支持的实现问题

1&#xff09;Unity引擎关于APP后台下载支持的实现问题 2&#xff09;Prefab对DLL中脚本的引用丢失 3&#xff09;Unity DOTS资源加载问题 4&#xff09;UnitySendMessage和_MultiplyMatrixArrayWithBase4x4_NEON调用导致崩溃 这是第376篇UWA技术知识分享的推送&#xff0c;精选…

webstorm 创建运行纯Typescript项目

创建一个空项目&#xff0c;在项目根目录创建一个tsconfig.json文件自动配置&#xff1a; 打开终端输入tsc --init&#xff0c;即可自动生成tsconfig.json文件手动配置&#xff1a; 在项目根目录下新建一个tsconfig.json文件,并配置如下内容 具体配置可以直接使用下面的配置&am…

三星成功研发出业界首款12层堆叠HBM3E

三星电子有限公司成功研发出业界首款12层堆叠HBM3E DRAM——HBM3E 12H&#xff0c;这是迄今为止容量最大的HBM产品。这款新型HBM3E 12H内存模块提供了高达1,280GB/s的史上最高带宽&#xff0c;并拥有36GB的存储容量&#xff0c;相较于之前的8层堆叠HBM3 8H&#xff0c;在带宽和…

案例介绍:汽车维修系统的信息抽取技术与数据治理应用(开源)

一、引言 在当今汽车产业的快速发展中&#xff0c;软件已经成为提升车辆性能、安全性和用户体验的关键因素。从车载操作系统到智能驾驶辅助系统&#xff0c;软件技术的进步正在重塑我们对汽车的传统认知。我有幸参与了一个创新项目&#xff0c;该项目专注于开发和集成先进的汽…

Redis是单线程还是多线程?

单线程为什么这么快的原因&#xff1a; 后来引入了多线程是因为&#xff1a;

首尔之春在线资源最新电影1080p高清

打开下面这个链接就可以看到 首尔之春在线资源最新电影1080p高清 如果链接打不开&#xff0c;就复制下面的网址到浏览器打开 https://www.zhufaka.cn/liebiao/A09504AE3BF8BD06 用阿里云盘下载&#xff0c;下载完成之后&#xff0c;用迅雷播放 首尔之春在线资源最新电影10…

Linux:ansible-playbook配置文件(剧本)(进阶)

Linux&#xff1a;ansible-playbook配置文件&#xff08;剧本&#xff09;_ansible-playbook -i参数-CSDN博客https://blog.csdn.net/w14768855/article/details/132579492?ops_request_misc%257B%2522request%255Fid%2522%253A%2522170930036016800215061982%2522%252C%2522s…

linux系统Jenkins工具配置webhook自动部署

Jenkins工具webhook自动部署 webhook自动部署webhook的意义操作流程jenkins页面操作gitlab页面操作 webhook自动部署 webhook的意义 自动化部署&#xff1a;Webhook 可以在代码提交、合并请求或其他特定事件发生时自动触发 Jenkins 构建和部署任务&#xff0c;从而实现自动化…

【Java EE 】认识文件与Java文件操作

目录 &#x1f340;认识文件&#x1f338;树型结构组织 和 目录&#x1f338;文件路径&#xff08;Path&#xff09;&#x1f338;其他知识 &#x1f333;Java 中操作文件&#x1f338;File 概述&#x1f33b;属性&#x1f33b;构造方法&#x1f33b;方法 &#x1f338;代码示例…

放弃了字节32k的工作,回老家拿了8K的offer,我不后悔!

字节一年&#xff0c;人间三年。 虽然之前反复纠结和犹豫&#xff0c;在飞书的流程也是点了又关&#xff0c;但真正到了离开的这一刻&#xff0c;我居然没有太多不舍了。 可能是确实太累了&#xff0c;在字节工作的五百多个日夜里&#xff0c;基本没有在8点之前下过班&#xff…

小米澎湃和华为原生鸿蒙,那个更有发展前景?

小米的澎湃系统暂时不了解&#xff0c;但华为的鸿蒙系统值得一说。 就目前鸿蒙而言&#xff1b;24年初鸿蒙星河版面向开发者开放申请。其底座全线自研&#xff0c;去掉了传统的 Linux 内核以及 AOSP 安卓开放源代码项目等代码&#xff0c;仅支持鸿蒙内核和鸿蒙系统的应用。星河…

网络协议栈--应用层--HTTPS协议

目录 一、HTTPS协议原理1.1 HTTPS协议是什么&#xff1f;1.2 概念准备1.2.1 什么是“加密”&#xff1f;1.2.2 为什么要加密&#xff1f;1.2.3 常见的加密方式1.2.3.1 对称加密1.2.3.2 非对称加密 1.2.4 数据摘要&&数据指纹1.2.5 数字签名1.2.6 理解链-承上启下 1.3 HT…

职场卷王:我用可视化大屏模板做工作汇报,惊艳了同事和领导。

2023结束了&#xff0c;我和我的小伙伴们纷纷开始忙碌的年终总结和汇报。 正忙着汇总Excel数据、写word讲稿、找PPT模板时&#xff0c;我发现隔壁组的老王独自在大会议室偷偷调试起了那台汇报用的电视机。 不会吧不会吧&#xff0c;年终汇报还有一周呢&#xff0c;这家伙PPT都…

yum 和 rpm

rpm说明 rpm -qa &#xff1a;列出所有已安装的软件包 [roothub ~] rpm -qa geoipupdate-2.5.0-1.el7.x86_64 ncurses-base-5.9-14.20130511.el7_4.noarch libndp-1.2-9.el7.x86_64 libfastjson-0.99.4-3.el7.x86_64 。。。 rpm -qf FILENAME &#xff1a;查找提供 FILENAME…

HCIA-HarmonyOS设备开发V2.0证书

目录 一、不墨迹&#xff0c;上证书二、考试总结三、习题四、知识点五、坚持就有收获 HCIA-HarmonyOS Device Developer V2.0 开发者能力认证考试已通过。 一、不墨迹&#xff0c;上证书 一个多月的努力&#xff0c;验证了自己的学习成果&#xff0c;也认识到自己有待提升之处…

用边缘计算网关解决离散行业数采问题-天拓四方

一、引言 随着工业4.0时代的来临&#xff0c;离散制造行业正面临数字化转型的关键节点。离散制造的特点是小批量、多品种、高复杂度&#xff0c;如何实现高效、精准的数据采集与分析&#xff0c;提升生产效率和产品质量&#xff0c;成为行业亟待解决的问题。边缘计算网关作为一…

C#高级:Winform桌面开发中DataGridView的详解

一、每条数据增加一个按钮&#xff0c;点击输出对应实体 请先确保正确添加实体的名称和文本&#xff1a; private void button6_Click(object sender, EventArgs e) {//SQL查询到数据&#xff0c;存于list中List<InforMessage> list bll.QueryInforMessage();//含有字段…

xss.haozi:0x00

0x00没有什么过滤所以怎么写都没有关系有很多解 <script>alert(1)</script>

Java基于微信小程序的4S店汽车保养小程序

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…