ZYNQ自定義AXI匯流排IP應用——PWM實現呼吸燈效果

来源:https://www.cnblogs.com/moluoqishi/archive/2020/03/02/12390970.html
-Advertisement-
Play Games

一、前言 在實時性要求較高的場合中,CPU軟體執行的方式顯然不能滿足需求,這時需要硬體邏輯實現部分功能。要想使自定義IP核被CPU訪問,就必須帶有匯流排介面。ZYNQ採用AXI BUS實現PS和PL之間的數據交互。本文以PWM為例設計了自定義AXI匯流排IP,來演示如何靈活運用ARM+FPGA的架構。 ...


一、前言

  在實時性要求較高的場合中,CPU軟體執行的方式顯然不能滿足需求,這時需要硬體邏輯實現部分功能。要想使自定義IP核被CPU訪問,就必須帶有匯流排介面。ZYNQ採用AXI BUS實現PS和PL之間的數據交互。本文以PWM為例設計了自定義AXI匯流排IP,來演示如何靈活運用ARM+FPGA的架構。

功能定義:在上一篇ZYNQ入門實例博文講解的系統中添加自定義IP核,其輸出驅動LED等實現呼吸燈效果。並且軟體通過配置寄存器方式對其進行使能、打開/關閉配置以及選擇占空比變化步長。另外,可以按鍵操作完成占空比變化步長的增減。

平臺:米聯客 MIZ702N (ZYNQ-7020)

軟體:VIVADO+SDK 2017

註:自定義IP邏輯設計採用明德揚至簡設計法

二、PWM IP設計

  PWM無非就是通過控制周期脈衝信號的占空比,也就是改變高電平在一段固定周期內的持續時間來達到控制目的。脈衝周期需要一個計數器來定時,占空比由低變高和由高變低兩種模式同樣需要一個計數器來指示,因此這裡使用兩個嵌套的計數器cnt_cyc和cnt_mode。cnt_mode的加一條件除了要等待cnt_cyc計數完成,還要考慮占空比的變化。

  我們可以使用下降沿位置表示占空比,位置越靠近周期值占空比越高。在模式0中下降沿位置按照步長增大直至大於等於周期值,模式1中下降沿位置則按照步長遞減直到小於步長。使用兩個信號up_stage和down_stage分別指示模式0和模式1。至於步長值,在配置有效時被更新,否則使用預設值。模塊最終的輸出信號在周期計數器小於下降沿位置為1,反之為零。設計完畢,上代碼:

  1 `timescale 1ns / 1ps
  2 //////////////////////////////////////////////////////////////////////////////////
  3 // Company: 
  4 // Engineer: 
  5 // 
  6 // Create Date: 2020/03/01 18:14:44
  7 // Design Name: 
  8 // Module Name: pwm
  9 // Project Name: 
 10 // Target Devices: 
 11 // Tool Versions: 
 12 // Description: 
 13 // 
 14 // Dependencies: 
 15 // 
 16 // Revision:
 17 // Revision 0.01 - File Created
 18 // Additional Comments:
 19 // 
 20 //////////////////////////////////////////////////////////////////////////////////
 21 
 22 
 23 module pwm(
 24 input                       clk,//頻率100MHz 10ns
 25 input                       rst_n,
 26 input                       sw_en,//輸出使能
 27 input                       sw_set_en,//步長設定使能
 28 input       [10-1:0]        sw_freq_step,//步長數值
 29 output reg                  led
 30     );
 31 
 32 parameter FREQ_STEP = 10'd100;
 33 
 34 parameter CNT_CYC_MAX = 50000;
 35 
 36 function integer clogb2 (input integer bit_depth);
 37     begin
 38         for(clogb2=0;bit_depth>0;clogb2=clogb2+1)
 39             bit_depth = bit_depth >> 1;
 40     end
 41 endfunction
 42 
 43 localparam CNT_CYC_WIDTH = clogb2(CNT_CYC_MAX-1);
 44            
 45 
 46 reg [CNT_CYC_WIDTH-1:0] cnt_cyc=0;
 47 wire add_cnt_cyc,end_cnt_cyc;
 48 reg [2-1:0] cnt_mode=0;
 49 wire add_cnt_mode,end_cnt_mode;
 50 wire up_stage,down_stage;
 51 reg [CNT_CYC_WIDTH+1-1:0] neg_loc=0;
 52 reg [10-1:0] freq_step=FREQ_STEP;
 53 
 54 
 55 //周期計數器 計數50ms=50*1000ns = 50000_0ns
 56 always@(posedge clk)begin
 57     if(~rst_n)begin
 58         cnt_cyc <= 0;
 59     end
 60     else if(add_cnt_cyc)begin
 61         if(end_cnt_cyc)
 62             cnt_cyc <= 0;
 63         else
 64             cnt_cyc <= cnt_cyc + 1'b1;
 65     end
 66 end
 67 
 68 assign add_cnt_cyc = sw_en == 1;
 69 assign end_cnt_cyc = add_cnt_cyc && cnt_cyc == CNT_CYC_MAX- 1;
 70 
 71 //模式計數器 0-占空比遞增 1-占空比遞減
 72 always@(posedge clk)begin
 73     if(~rst_n)begin
 74         cnt_mode <= 0;
 75     end
 76     else if(add_cnt_mode)begin
 77         if(end_cnt_mode)
 78             cnt_mode <= 0;
 79         else
 80             cnt_mode <= cnt_mode + 1'b1;
 81     end
 82 end
 83 
 84 assign add_cnt_mode = end_cnt_cyc && ((up_stage && neg_loc >= CNT_CYC_MAX) || (down_stage && neg_loc == 0));
 85 assign end_cnt_mode = add_cnt_mode && cnt_mode == 2 - 1;
 86 
 87 
 88 //變化步長設定
 89 always@(posedge clk)begin
 90     if(~rst_n)begin
 91         freq_step <= FREQ_STEP;
 92     end
 93     else if(sw_set_en)begin
 94         if(sw_freq_step >= 1 && sw_freq_step < 2000)
 95             freq_step <= sw_freq_step;
 96         else
 97             freq_step <= FREQ_STEP;
 98     end
 99 end
100 
101 //脈衝下降沿對應周期計數器數值
102 always@(posedge clk)begin
103     if(~rst_n)begin
104         neg_loc <= 0; 
105     end
106     else if(end_cnt_cyc)begin
107         if(up_stage )begin//占空比遞增階段
108             if(neg_loc < CNT_CYC_MAX)
109                 neg_loc <= neg_loc + freq_step;
110         end
111         else if(down_stage )begin//占空比遞減階段
112             if(neg_loc < freq_step)
113                 neg_loc <= 0;
114             else
115                 neg_loc <= neg_loc - freq_step;
116         end
117     end
118 
119 end
120 
121 assign up_stage = add_cnt_cyc && cnt_mode == 0;
122 assign down_stage = add_cnt_cyc && cnt_mode == 1;
123 
124 
125 //輸出
126 always@(posedge clk)begin
127     if(~rst_n)begin
128         led <= 1'b0;//高電平點亮
129     end
130     else if(add_cnt_cyc && cnt_cyc < neg_loc)begin
131         led <= 1'b1;
132     end
133     else
134         led <= 1'b0;
135 end
136 
137 
138 
139 endmodule
pwm.v

  VIVADO綜合、佈局佈線比較慢,且軟硬體級聯調試費時費力,所以模擬是極其重要的。編寫一個簡單的testbench,定義update_freq_step task更新步長。這裡使用System Verilog語法有一定的好處。首先單驅動信號可以統一定義為logic變數類型,其次等待時長能指定單位。

 1 `timescale 1ns / 1ps
 2 //////////////////////////////////////////////////////////////////////////////////
 3 // Company: 
 4 // Engineer: 
 5 // 
 6 // Create Date: 2020/03/01 20:49:25
 7 // Design Name: 
 8 // Module Name: testbench
 9 // Project Name: 
10 // Target Devices: 
11 // Tool Versions: 
12 // Description: 
13 // 
14 // Dependencies: 
15 // 
16 // Revision:
17 // Revision 0.01 - File Created
18 // Additional Comments:
19 // 
20 //////////////////////////////////////////////////////////////////////////////////
21 
22 
23 module testbench();
24 
25 logic clk,rst_n;
26 logic sw_en,sw_set_en;
27 logic [10-1:0]sw_freq_step;
28 logic led;
29 
30 parameter CYC = 10,
31           RST_TIM = 2;
32 
33 defparam dut.CNT_CYC_MAX = 2000;
34 
35  pwm#(.FREQ_STEP(100))
36  dut(
37 .clk           (clk) ,//頻率100MHz 10ns
38 .rst_n         (rst_n) ,
39 .sw_en         (sw_en) ,//輸出使能
40 .sw_set_en     (sw_set_en) ,//步長設定使能
41 .sw_freq_step  (sw_freq_step) ,//步長數值
42 .led           (led)
43     );
44 
45 initial begin
46     clk = 1;
47     forever begin
48         #(CYC/2.0);
49         clk=~clk;
50     end
51 end
52 
53 initial begin
54     rst_n = 1;
55     #1;
56     rst_n = 0;
57     #(RST_TIM*CYC) rst_n = 1;
58 end
59 
60 initial begin
61     sw_en = 0;
62     sw_set_en = 0;
63     sw_freq_step = 'd10;
64     #1;
65     #(RST_TIM*CYC);
66     #(CYC*10);
67     sw_en = 1;
68 
69     #600us;
70     update_freq_step(50);
71     #600us;
72     $stop;
73 
74 end
75 
76 task update_freq_step([10-1:0] freq_step);
77     sw_set_en = 1;
78     sw_freq_step = freq_step;
79     #(1*CYC);
80     sw_set_en = 0;
81 endtask
82 
83 endmodule
testbench.sv

  設計較簡單,直接使用VIVADO模擬器觀察波形即可:

   可以看到輸出信號led的占空比在不斷起伏變化,當更新freq_step為50後變化更為減慢。

   配置前相鄰兩個neg_loc數值差與更新後分別是100和50。以上證明邏輯功能無誤。

三、硬體系統搭建

   設計完PWM功能模塊還沒有完,需要再包一層匯流排Wrapper才能被CPU訪問創建AXI匯流排IP

  在封裝器中編輯。

  最終IP結構如圖:

   具體操作不過多講述,直接以代碼呈現:

 1 `timescale 1 ns / 1 ps
 2 
 3     module pwm_led_ip_v1_0 #
 4     (
 5         // Users to add parameters here
 6         parameter FREQ_STEP = 10'd100,
 7         // User parameters ends
 8         // Do not modify the parameters beyond this line
 9 
10 
11         // Parameters of Axi Slave Bus Interface S00_AXI
12         parameter integer C_S00_AXI_DATA_WIDTH    = 32,
13         parameter integer C_S00_AXI_ADDR_WIDTH    = 4
14     )
15     (
16         // Users to add ports here
17         output led,
18         // User ports ends
19         // Do not modify the ports beyond this line
20 
21 
22         // Ports of Axi Slave Bus Interface S00_AXI
23         input wire  s00_axi_aclk,
24         input wire  s00_axi_aresetn,
25         input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
26         input wire [2 : 0] s00_axi_awprot,
27         input wire  s00_axi_awvalid,
28         output wire  s00_axi_awready,
29         input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
30         input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
31         input wire  s00_axi_wvalid,
32         output wire  s00_axi_wready,
33         output wire [1 : 0] s00_axi_bresp,
34         output wire  s00_axi_bvalid,
35         input wire  s00_axi_bready,
36         input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
37         input wire [2 : 0] s00_axi_arprot,
38         input wire  s00_axi_arvalid,
39         output wire  s00_axi_arready,
40         output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
41         output wire [1 : 0] s00_axi_rresp,
42         output wire  s00_axi_rvalid,
43         input wire  s00_axi_rready
44     );
45 // Instantiation of Axi Bus Interface S00_AXI
46     pwd_led_ip_v1_0_S00_AXI # ( 
47         .C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
48         .C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH),
49         .FREQ_STEP(FREQ_STEP)
50     ) pwd_led_ip_v1_0_S00_AXI_inst (
51         .S_AXI_ACLK(s00_axi_aclk),
52         .S_AXI_ARESETN(s00_axi_aresetn),
53         .S_AXI_AWADDR(s00_axi_awaddr),
54         .S_AXI_AWPROT(s00_axi_awprot),
55         .S_AXI_AWVALID(s00_axi_awvalid),
56         .S_AXI_AWREADY(s00_axi_awready),
57         .S_AXI_WDATA(s00_axi_wdata),
58         .S_AXI_WSTRB(s00_axi_wstrb),
59         .S_AXI_WVALID(s00_axi_wvalid),
60         .S_AXI_WREADY(s00_axi_wready),
61         .S_AXI_BRESP(s00_axi_bresp),
62         .S_AXI_BVALID(s00_axi_bvalid),
63         .S_AXI_BREADY(s00_axi_bready),
64         .S_AXI_ARADDR(s00_axi_araddr),
65         .S_AXI_ARPROT(s00_axi_arprot),
66         .S_AXI_ARVALID(s00_axi_arvalid),
67         .S_AXI_ARREADY(s00_axi_arready),
68         .S_AXI_RDATA(s00_axi_rdata),
69         .S_AXI_RRESP(s00_axi_rresp),
70         .S_AXI_RVALID(s00_axi_rvalid),
71         .S_AXI_RREADY(s00_axi_rready),
72 
73         .led(led)
74     );
75 
76     // Add user logic here
77 
78     // User logic ends
79 
80     endmodule
pwm_led_ip_v1_0.v
  1 `timescale 1 ns / 1 ps
  2 
  3     module pwm_led_ip_v1_0_S00_AXI #
  4     (
  5         // Users to add parameters here
  6         parameter FREQ_STEP = 10'd100,
  7         // User parameters ends 
  8         // Do not modify the parameters beyond this line
  9 
 10         // Width of S_AXI data bus
 11         parameter integer C_S_AXI_DATA_WIDTH    = 32,
 12         // Width of S_AXI address bus
 13         parameter integer C_S_AXI_ADDR_WIDTH    = 4
 14     )
 15     (
 16         // Users to add ports here
 17         output led,
 18         // User ports ends
 19         // Do not modify the ports beyond this line
 20 
 21         // Global Clock Signal
 22         input wire  S_AXI_ACLK,
 23         // Global Reset Signal. This Signal is Active LOW
 24         input wire  S_AXI_ARESETN,
 25         // Write address (issued by master, acceped by Slave)
 26         input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
 27         // Write channel Protection type. This signal indicates the
 28             // privilege and security level of the transaction, and whether
 29             // the transaction is a data access or an instruction access.
 30         input wire [2 : 0] S_AXI_AWPROT,
 31         // Write address valid. This signal indicates that the master signaling
 32             // valid write address and control information.
 33         input wire  S_AXI_AWVALID,
 34         // Write address ready. This signal indicates that the slave is ready
 35             // to accept an address and associated control signals.
 36         output wire  S_AXI_AWREADY,
 37         // Write data (issued by master, acceped by Slave) 
 38         input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
 39         // Write strobes. This signal indicates which byte lanes hold
 40             // valid data. There is one write strobe bit for each eight
 41             // bits of the write data bus.    
 42         input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
 43         // Write valid. This signal indicates that valid write
 44             // data and strobes are available.
 45         input wire  S_AXI_WVALID,
 46         // Write ready. This signal indicates that the slave
 47             // can accept the write data.
 48         output wire  S_AXI_WREADY,
 49         // Write response. This signal indicates the status
 50             // of the write transaction.
 51         output wire [1 : 0] S_AXI_BRESP,
 52         // Write response valid. This signal indicates that the channel
 53             // is signaling a valid write response.
 54         output wire  S_AXI_BVALID,
 55         // Response ready. This signal indicates that the master
 56             // can accept a write response.
 57         input wire  S_AXI_BREADY,
 58         // Read address (issued by master, acceped by Slave)
 59         input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
 60         // Protection type. This signal indicates the privilege
 61             // and security level of the transaction, and whether the
 62             // transaction is a data access or an instruction access.
 63         input wire [2 : 0] S_AXI_ARPROT,
 64         // Read address valid. This signal indicates that the channel
 65             // is signaling valid read address and control information.
 66         input wire  S_AXI_ARVALID,
 67         // Read address ready. This signal indicates that the slave is
 68             // ready to accept an address and associated control signals.
 69         output wire  S_AXI_ARREADY,
 70         // Read data (issued by slave)
 71         output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
 72         // Read response. This signal indicates the status of the
 73             // read transfer.
 74         output wire [1 : 0] S_AXI_RRESP,
 75         // Read valid. This signal indicates that the channel is
 76             // signaling the required read data.
 77         output wire  S_AXI_RVALID,
 78         // Read ready. This signal indicates that the master can
 79             // accept the read data and response information.
 80         input wire  S_AXI_RREADY
 81     );
 82 
 83     // AXI4LITE signals
 84     reg [C_S_AXI_ADDR_WIDTH-1 : 0]     axi_awaddr;
 85     reg      axi_awready;
 86     reg      axi_wready;
 87     reg [1 : 0]     axi_bresp;
 88     reg      axi_bvalid;
 89     reg [C_S_AXI_ADDR_WIDTH-1 : 0]     axi_araddr;
 90     reg      axi_arready;
 91     reg [C_S_AXI_DATA_WIDTH-1 : 0]     axi_rdata;
 92     reg [1 : 0]     axi_rresp;
 93     reg      axi_rvalid;
 94 
 95     // Example-specific design signals
 96     // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
 97     // ADDR_LSB is used for addressing 32/64 bit registers/memories
 98     // ADDR_LSB = 2 for 32 bits (n downto 2)
 99     // ADDR_LSB = 3 for 64 bits (n downto 3)
100     localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
101     localparam integer OPT_MEM_ADDR_BITS = 1;
102     //----------------------------------------------
103     //-- Signals for user logic register space example
104     //------------------------------------------------
105     //-- Number of Slave Registers 4

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 用 v-model 指令在表單控制項元素上創建雙向數據綁定 v-model 會根據控制項類型自動選取正確的方法來更新元素 輸入框: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>demo</title> </head> <styl ...
  • 事件監聽可以使用 v-on 指令 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>demo</title> </head> <style> .base{color:#fff} .pink{background:pink} </s ...
  • 可以用 v-bind 來設置樣式屬性。 Vue.js v-bind 在處理 class 和 style 時, 專門增強了它。表達式的結果類型除了字元串之外,還可以是對象或數組 為 v-bind:class 設置一個對象,從而動態的切換 class <!DOCTYPE html> <html> <he ...
  • **看一看,瞧一瞧!** 話說要談事件委托和target。那我們首先來看看什麼是事件。話說什麼是事件呢?一般的解釋是比較重大、對一定的人群會產生一定影響的事情。而在JavaScript中就不是這樣了,事件就是指事情發生了,然後得到有效的處理的操作。為了您能更好的明白,我們來看看以下例子,讓你更清楚什 ...
  • 從選伺服器,功能變數名稱,到配置伺服器,安裝環境,打包上傳,pm2進程守護一條龍。 ...
  • [toc] 1.介紹abp 全稱:aspnetboilerplate。 官網:https://aspnetboilerplate.com/ 由美國人發起的開源項目。 web應用框架 2.abp如何工作 微服務框架,可以使用 net core版本,也可使用net framework兩種框架 3.運行D ...
  • 前言 Nginx是一款自由的、開源的、高性能的HTTP伺服器和 反向代理 伺服器;同時也是一個IMAP、POP3、SMTP代理伺服器;Nginx可以作為一個HTTP伺服器進行網站的發佈處理,另外Nginx可以作為反向代理進行負載均衡的實現。 Nginx使用基於事件驅動架構,使得其可以支持數以百萬級別 ...
  • if-else 作為每種編程語言都不可或缺的條件語句,我們在編程時會大量的用到。但if-else 一般不建議嵌套超過三層,如果一段代碼存在過多的 if-else 嵌套,代碼的可讀性就會急速下降,後期維護難度也大大提高。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...