基于AD9833的信号发生器

基于AD9833的信号发生器本文利用 FPGA 控制 AD9833 实现信号发生器的功能

大家好,欢迎来到IT知识分享网。

本文利用FPGA控制AD9833,实现信号发生器的功能。本文将对AD9833的手册进行详细的解读,并对其配置方法进行解析,最后在Verilog中进行编码,将代码烧录置FPGA中,FPGA通过外部引脚控制AD9833输出所需要的正弦波、方波和三角波。三种波形能够输出的频率范围为0~12.5Mhz。


前言

AD9833是一种低功率可编程波形发生器,能够产生正弦、三角形和方波输出。在各种类型的传感、驱动和时域反射测量应用中,都需要产生波形。输出频率和相位是软件可编程的,允许易于调整。不需要任何外部组件。频率寄存器为28位;以25MHz的时钟速率,可以达到0.1 Hz的分辨率。类似地,使用1MHz的时钟速率,AD9833可以被调谐到0.004 Hz的分辨率。

AD9833是通过三线串行接口写入的。该串行接口以时钟速率高达40MHz,并与DSP和微控制器标准兼容。该设备的电源供电范围为2.3V至5.5V。


一、AD9833数据手册分析

博主将首先分析AD9833的数据手册,并列出博主认为手册中需要着重注意的点,以便于后续代码的提出。

1.1 AD9833特性分析

在AD9833的规格中,我们可以知道,其 V o u t V_{out} Vout 也就是最大输出电压可以达到0.65V,其具体的表达式如下:
V O U T = V R E F × 18 × R LOAD / R S E T × ( 1 + ( S I N ( 2 π ( F R E Q R E G × f M C L K × t / 2 28 + P H A S E R E G / 2 12 ) ) ) ) V_{\mathsf{OUT}}=V_{\mathsf{REF}}\times18\times\mathsf{R}_\text{LOAD}/{ \mathsf{R}_{\mathsf{SET}}}\times(1+(\mathsf{SIN}(2\pi(\mathsf{FREQREG}\times\mathbf{f}_{\mathsf{MCLK}}\times\mathbf{t}_{/2}28+\mathsf{PHASEREG}_{/2}12)))) VOUT=VREF×18×RLOAD/RSET×(1+(SIN(2π(FREQREG×fMCLK×t/228+PHASEREG/212))))

即:输出电压的大小跟参考电压和负载电阻密切相关,同时随着设置波形频率的变化而变化。

另外,AD9833的VDD的范围为2.3V-5.5V,也就是说你需要提供一定数值的电源电压。

1.2 AD9833时钟分析


图1 AD9833时钟

在图1中,我们需要着重关注的是AD9833的MCLK周期最小为40ns,也就是说你需要提供25Mhz的激励时钟信号,用于驱动AD9833进行内部数据操作。

AD9833操作时序


图2 AD9833操作时序

观察图2,我们可知:如何需要写入AD9833的控制字,我们首先需要将FSYNC拉低,在SCLK操作时钟的周期性变化下,将SDATA也就是操作字逐一写入。需要注意的是,SCLK的操作时钟周期最小需要25ns,高低电平的维持时间最小需要10ns,在最后一个下降沿数据写入后需要在10ns-20ns的时间内将FSYNC的电平拉高,否则写入无效。

1.3 AD9833引脚分析


图3 AD9833引脚图

在AD9833的引脚中,MCLK为激励时钟,我们采用25Mhz;FSYNC在拉低后进行写入操作,随着SCLK的电平变化将SDATA的值读入。另外,VDD和AGND之间应连接一个0.1uF和一个10uF的解耦电容。

1.4 AD9833操作原理分析


图4 AD9833操作字

在上述的16个控制位中,博主将讲解需要着重观察的控制位,其他控制位请读者自行查看手册。

  • B28
    当B28 = 1时允许连续两次写入将完整单词加载到频率寄存器中。第一次写包含频率单词的14个LSB,下一次写将包含14个MSB。每个16位字的前两位定义了该字被加载到的频率寄存器,因此,对于这两个连续的写操作,前两位应该是相同的。
    当B28 = 0时,28位频率寄存器作为两个14位寄存器运行,一个包含14个MSB,另一个包含14个LSB。这意味着频率词的14个MSB可以独立于14个LSB进行改变,反之亦然。为了改变14个MSB或14个LSB,需要对适当的频率地址进行一次写入。控制位D12(HLB)通知AD9833,要改变的位是14个MSB还是14个LSB。
    博主在后文中将B28 = 1,也就是连续写入两个16位字对频率寄存器进行更改。


  • RESET
    RESET = 1将内部寄存器重置为0,这对应于中度的模拟输出。
    RESET = 0将禁用重置。
    需要注意的是,RESET将不重置相位、频率和控制寄存器。另外,为了避免AD9833在初始化的时候出现虚假的DAC输出,需要在进行写入操作时也就是B28 = 1时将RESET位置1。


  • MODE
    MODE位与OPBITEN(D5)一起使用。这个位的功能是控制芯片上DAC连接到VOUT时VOUT引脚的输出。
    当MODE = 1时三角波将从DAC中输出。
    当MODE = 0时正弦波将输出。


接下来我们将利用代码来详细论述操作字的写入流程。

二、FPGA控制代码

我们将利用Verilog语言编写FPGA的代码,并用FPGA控制AD9833的SCLK、SDATA、FSYNC引脚,进行写入操作。

2.1 主函数

主函数中主要包含了各个模块,并将模块之间的变量联系起来。

module Signal_Generate( input sys_clk, input sys_rst_n, input key0, // 按键检测切换波形状态 input key1, output MCP_CS, // MCP使能标志 低电平有效 output SCK, // MCP输入信号状态 低电平信号变化 下降沿读信号 output SI, // MCP输入信号 output AD_FSYNC // AD使能标志 ); // parameter define parameter CLK_FREQ = 26'd50_000_000; // MCP模块的驱动时钟频率 parameter MCP_FREQ = 18'd25_000_000; // MCP的SCK时钟频率 // wire define wire MCP_CLK; // MCP时钟信号 wire locked; wire [15:0] TxData; // 写入AD的操作数 wire TxData_en; // 开始写入标志位 wire TxData_done; // 写入完成标志位 wire [ 7:0] counter_rem; // 实时观察写入情况 wire [ 7:0] counter_rem_driver; // 实时观察驱动情况 //*************************************************** //**** main code **** //*************************************************** // 例化按键消抖模块 key_debounce u_key0_debounce( .clk (sys_clk), .rst_n (sys_rst_n), .key (key0), .key_value (key0_value), // 按下后为低电平 输出 .key_flag (key0_flag) // 按下后标志位拉高 输出 ); key_debounce u_key1_debounce( .clk (sys_clk), .rst_n (sys_rst_n), .key (key1), .key_value (key1_value), // 按下后为低电平 输出 .key_flag (key1_flag) // 按下后标志位拉高 输出 ); // AD根据操作数写入16bits模块 AD_Write u_AD_Write( .clk (sys_clk), .rst_n (sys_rst_n), .TxData (TxData), // 写入16位二进制操作数 .TxData_en (TxData_en), // 开始写入标志位 输入 .TxData_done(TxData_done), // 写入完成标志位 输出 .counter_rem(counter_rem), // 实时检查写入位置 .AD_SCK (SCK), // AD操作时钟 .AD_FSYNC (AD_FSYNC), // AD使能标志位 .AD_SI (SI) // AD写入数据 ); // AD驱动模块 AD_Driver u_AD_Driver( .clk (sys_clk), .rst_n (sys_rst_n), .key0_value (key0_value), .key0_flag (key0_flag), .key1_value (key1_value), .key1_flag (key1_flag), .counter_rem_driver (counter_rem_driver), // 实时检查驱动位置 .TxData_done (TxData_done), // 写入完成标志位 输入 .TxData (TxData), // 写入操作数 .TxData_en (TxData_en) // 开始写入标志位 输出 ); // ILA检测模块 ila_0 u_ila_0( .clk (sys_clk), // input wire clk .probe0 (AD_FSYNC), // input wire [0:0] probe0 .probe1 (SCK), // input wire [0:0] probe1 .probe2 (SI), // input wire [0:0] probe2 .probe3 (TxData_en), // input wire [0:0] probe3 .probe4 (TxData_done), // input wire [0:0] probe4 .probe5 (TxData), // input wire [15:0] probe5 .probe6 (counter_rem_driver) // input wire [7:0] probe6 ); endmodule 

接下来,我将对函数的主要功能进行介绍。

  • key_debounce
    此函数的主要功能是对外部按键KEY0和KEY1是否按下进行判断,当KEY0按下后,将进行波形变换操作,当KEY1按下后,将进行频率变换操作。后文会有提及。
  • AD_Write
    此函数主要功能是将一个16位的操作字根据一定的时序写入到AD9833中,在写入完成后将TxData_done置位,AD_Driver接收到置位信息后,将后续需要写入的操作字赋值给TxData中,并将开始写入标志位TxData_en置位。
  • AD_Driver
    此函数的主要功能是根据当前状态变更状态字,以及控制AD9833需要输出的波形和频率。
  • ILA
    检测模块ILA用于实时检测。

2.2 按键检测模块

按键检测模块通过延时消抖来检测按键值变化是否有效。

module key_debounce( input clk, input rst_n, input key, // 外部输入按键值 output reg key_value, // 消抖后的按键值 output reg key_flag // 消抖后的按键值有效标志 ); reg [19:0] cnt; reg key_reg; // 按键值消抖 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 20'd0; key_reg <= 1'b1; end else begin key_reg <= key; if(key_reg != key) begin // 检测到按键值变化 cnt <= 20'd100_0000; // 开启延时 end else begin if(cnt > 20'd0) // 延时过程 cnt <= cnt - 1'b1; else cnt <= 20'd0; end end end // 输出最终值 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin key_value <= 1'b1; // 没有按下时默认为高电平 key_flag <= 1'b0; // 标志位为无效标志 end // 计时器递减到1时输出按键值 else if(cnt == 20'd1) begin key_value <= key; // 将消抖后的数值保存 key_flag <= 1'b1; // 标志位为有效标志 end else begin key_value <= key_value; // 计时器为结束则保持原有数值 key_flag <= 1'b0; end end endmodule 

KEY0和KEY1在复位状态下为高电平,当按键按下后变更为低电平。在此模块中,我们用key_reg寄存器来存储上一时刻KEY的状态,当检测到其与当前时刻的KEY状态不一致时进行100_0000个时钟周期的延时,也就是20ns*100_0000=20ms的延时,如果按键状态在该时间内为发生变化说明按下有效。将key_valuekey_flag位变更。

2.3 操作写入模块

此模块,用于将操作字根据一定的时序写入。

module AD_Write( input clk, input rst_n, input [15:0] TxData, // 写入16位二进制操作数 input TxData_en, // 开始写入标志位 output reg TxData_done, // 写入完成标志位 output counter_rem, // 实时检查写入位置 output reg AD_SCK, // AD操作时钟 output reg AD_FSYNC, // AD使能标志位 output reg AD_SI // AD写入数据 ); // reg define reg [15:0] data; // 写入的16位操作数 reg [ 7:0] counter; // 写入时序 reg tx_en; // 保存写入标志位 // wire define //*************************************************** //**** main code **** //*************************************************** assign counter_rem = counter; // 使能后频数计数器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin counter <= 8'd0; TxData_done <= 1'b0; tx_en <= 1'b0; end else begin // 接收到发送信号 if(TxData_en) begin tx_en <= 1'b1; // 保存开始写入标志位 end else begin if(tx_en) begin // 数据发送时使能计数器 if(counter < 8'd33) begin counter <= counter + 1'b1; TxData_done <= 1'b0; end else begin // 在counter计数到完成操作后重置 counter <= 8'd0; tx_en <= 1'b0; // 结束发送 TxData_done <= 1'b1; // 置位操作完成位 end end else begin counter <= 8'd0; TxData_done <= 1'b0; tx_en <= tx_en; end end end end // Control the AD_SCK, AD_FSYNC, and DAT pins based on the TxData input // AD在SCK为高电平时改变状态 在SCK下降沿时写入数据 always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin AD_SCK <= 1; AD_FSYNC <= 1; AD_SI <= 0; end else begin // 开始发送16bits数据 if(tx_en) begin case(counter) // 准备工作 8'd0: begin data <= TxData; // 寄存数据 AD_SCK <= 1; // FSYNC拉低时SCK为高 AD_FSYNC <= 0; // 使能发送 AD_SI <= 0; // 默认 end // first 16bits 8'd1: begin AD_SCK <= 1; AD_SI <= data[15]; // SI取数据最高位 end 8'd2: begin AD_SCK <= 0; end 8'd3: begin AD_SCK <= 1; AD_SI <= data[14]; end 8'd4: begin AD_SCK <= 0; end 8'd5: begin AD_SCK <= 1; AD_SI <= data[13]; end 8'd6: begin AD_SCK <= 0; end 8'd7: begin AD_SCK <= 1; AD_SI <= data[12]; end 8'd8: begin AD_SCK <= 0; end 8'd9: begin AD_SCK <= 1; AD_SI <= data[11]; end 8'd10: begin AD_SCK <= 0; end 8'd11: begin AD_SCK <= 1; AD_SI <= data[10]; end 8'd12: begin AD_SCK <= 0; end 8'd13: begin AD_SCK <= 1; AD_SI <= data[9]; end 8'd14: begin AD_SCK <= 0; end 8'd15: begin AD_SCK <= 1; AD_SI <= data[8]; end 8'd16: begin AD_SCK <= 0; end 8'd17: begin AD_SCK <= 1; AD_SI <= data[7]; end 8'd18: begin AD_SCK <= 0; end 8'd19: begin AD_SCK <= 1; AD_SI <= data[6]; end 8'd20: begin AD_SCK <= 0; end 8'd21: begin AD_SCK <= 1; AD_SI <= data[5]; end 8'd22: begin AD_SCK <= 0; end 8'd23: begin AD_SCK <= 1; AD_SI <= data[4]; end 8'd24: begin AD_SCK <= 0; end 8'd25: begin AD_SCK <= 1; AD_SI <= data[3]; end 8'd26: begin AD_SCK <= 0; end 8'd27: begin AD_SCK <= 1; AD_SI <= data[2]; end 8'd28: begin AD_SCK <= 0; end 8'd29: begin AD_SCK <= 1; AD_SI <= data[1]; end 8'd30: begin AD_SCK <= 0; end 8'd31: begin AD_SCK <= 1; AD_SI <= data[0]; // SI取数据最低位 end 8'd32: begin AD_SCK <= 0; // 写入最后一位数据 end // the end 8'd33: begin AD_SCK <= 1; AD_FSYNC <= 1; AD_SI <= 0; end default: begin AD_SCK <= 1; AD_FSYNC <= 1; AD_SI <= 0; end endcase end else begin AD_SCK <= 1; AD_FSYNC <= 1; AD_SI <= 0; end end end endmodule 

当检测到操作写入信号TxData_en被置位时,将模块内寄存器tx_en置位,用于保存置位信号,因为TxData_en将在一个时钟周期后被拉低。
当置位信号保存完成后,将开始写入操作,counter作为写入的寄存器用于寄存当前写入的位置。当counter为0时,将需要写入的数据保存到data中,并将FSYNC拉低,表示我们将要开始写入操作。注意:在FSYNC拉低时,我们需要确保SCK为高电平,否则FSYNC的拉低无效。
FSYNC拉低后,我们在SCK为高电平时,变更SI为需要写入数据的位次,保持一个时钟周期20ns后,将SCK拉低,AD9833在SCK为下降沿时将SI的数据读入。以此往复直至写入完成的操作字。
在写入完成后,我们需要在10ns-20ns的时间内将FSYNC拉高,否则写入无效。并将TxData_done置位,表示写入完成了,可以进行后续操作。


2.4 驱动操作模块

驱动模块,主要对写入的操作字进行变更,通过选择合适的波形和频率控制AD9833输出。

module AD_Driver( input clk, input rst_n, input key0_value, input key0_flag, input key1_value, input key1_flag, output [ 7:0] counter_rem_driver, input TxData_done, // 写入完成标志位 输入 output reg [15:0] TxData, // 写入操作数 输出 output reg TxData_en // 开始写入标志位 输出 ); // local parameter define localparam [15:0] reset_add = 16'h0100; localparam [15:0] write_freq = 16'h2100; // 为了避免在AD9833被初始化时出现虚假的DAC输出 // 复位位应该被设置为1 直到部件准备好开始生成输出为止 // fMck=25Mhz // (fMclk/2^28/fwanted)^(-1)=FREQREG; fwanted(min)=0.0931, fwanted(max)=25M localparam [15:0] write_freq_lsb_3M = 16'h70b7; // 输出3M信号 localparam [15:0] write_freq_msb_3M = 16'h47ae; localparam [15:0] write_freq_lsb_1M = 16'h6592; // 输出1M信号 localparam [15:0] write_freq_msb_1M = 16'h428f; localparam [15:0] write_freq_lsb_1k = 16'h69f1; // 输出1k信号 localparam [15:0] write_freq_msb_1k = 16'h4000; localparam [15:0] write_phase_c0 = 16'hc000; // 相位配置 localparam [15:0] write_sin = 16'h2000; // 正弦波 localparam [15:0] write_traingle = 16'h2002; // 三角波 localparam [15:0] write_square = 16'h2028; // 方波 // reg define reg [ 7:0] counter; // 写入操作开始后的位置 reg [31:0] delay_counter; // 开始写入操作前的延迟 reg delay_start; // 延迟开始标志位 reg delay_end; // 延迟结束标志位 reg [ 2:0] waveform_select; // 选择波形 reg [ 2:0] freq_select; // 选择频率 reg [15:0] write_waveform; // 写入的波形类型 reg [15:0] write_freq_lsb_c0; // 写入频率数据的低位 reg [15:0] write_freq_msb_c0; // 写入频率数据的高位 // wire define //*************************************************** //**** main code **** //*************************************************** assign counter_rem_driver = counter; // 开始延迟计数器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin delay_counter <= 32'b0; delay_start <= 1'b1; delay_end <= 1'b0; waveform_select <= 2'd0; freq_select <= 2'd0; end else begin // 初始化和波形频率选择后根据delay_start开启延迟 if(delay_start) begin if(delay_counter < 10'd1000) begin delay_counter = delay_counter + 1'b1; end else begin delay_counter <= 32'b0; delay_start <= 1'b0; // 将delay_end作为延迟结束的标志 置1只存在一个周期 delay_end <= 1'b1; end end // 根据按键调整波形和频率 else begin // key0按下调整输出波形 if((key0_flag == 1) && (key0_value == 0)) begin waveform_select <= waveform_select + 1'd1; delay_start <= 1'b1; delay_counter <= 32'b0; delay_end <= 1'b0; end // key1按下调整输出频率 else if((key1_flag == 1) && (key1_value == 0)) begin freq_select <= freq_select + 1'd1; delay_start <= 1'b1; delay_counter <= 32'b0; delay_end <= 1'b0; end else begin // default波形和频率不变化 waveform_select <= waveform_select; freq_select <= freq_select; delay_counter <= 32'b0; delay_end <= 1'b0; end end end end // 切换波形种类 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin write_waveform <= write_sin; end else begin case(waveform_select) 2'd0: begin write_waveform <= write_sin; end 2'd1: begin write_waveform <= write_traingle; end 2'd2: begin write_waveform <= write_square; end default: begin write_waveform <= write_sin; end endcase end end // 切换频率 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin write_freq_lsb_c0 <= write_freq_lsb_1M; write_freq_msb_c0 <= write_freq_msb_1M; end else begin case(freq_select) 2'd0: begin write_freq_lsb_c0 <= write_freq_lsb_1M; write_freq_msb_c0 <= write_freq_msb_1M; end 2'd1: begin write_freq_lsb_c0 <= write_freq_lsb_3M; write_freq_msb_c0 <= write_freq_msb_3M; end 2'd2: begin write_freq_lsb_c0 <= write_freq_lsb_1k; write_freq_msb_c0 <= write_freq_msb_1k; end default: begin write_freq_lsb_c0 <= write_freq_lsb_1M; write_freq_msb_c0 <= write_freq_msb_1M; end endcase end end always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin TxData <= 16'd0; TxData_en <= 1'b0; counter <= 8'd12; end else begin // delay_end被置位后 将counter的值归零 开始写入操作数 // 否则写入操作会在结束处不断循环 if(delay_end) begin counter <= 8'd0; end else begin case(counter) // 重置寄存器 8'd0: begin TxData <= reset_add; TxData_en <= 1'b1; counter <= counter + 1'b1; end 8'd1: begin TxData <= TxData; TxData_en <= 1'b0; counter <= counter; if(TxData_done) begin counter <= counter + 1'b1; end end // 选择数据一次写入 8'd2: begin TxData <= write_freq; TxData_en <= 1'b1; counter <= counter + 1'b1; end 8'd3: begin TxData <= TxData; TxData_en <= 1'b0; counter <= counter; if(TxData_done) begin counter <= counter + 1'b1; end end // 写入频率寄存器低148'd4: begin TxData <= write_freq_lsb_c0; TxData_en <= 1'b1; counter <= counter + 1'b1; end 8'd5: begin TxData <= TxData; TxData_en <= 1'b0; counter <= counter; if(TxData_done) begin counter <= counter + 1'b1; end end // 写入频率寄存器高148'd6: begin TxData <= write_freq_msb_c0; TxData_en <= 1'b1; counter <= counter + 1'b1; end 8'd7: begin TxData <= TxData; TxData_en <= 1'b0; counter <= counter; if(TxData_done) begin counter <= counter + 1'b1; end end // 写入相位寄存器12// 不确定相位寄存器是否同时写入 在图例中相位寄存器无任何写入的使能标志位 8'd8: begin TxData <= write_phase_c0; TxData_en <= 1'b1; counter <= counter + 1'b1; end 8'd9: begin TxData <= TxData; TxData_en <= 1'b0; counter <= counter; if(TxData_done) begin counter <= counter + 1'b1; end end // 选择波形输出器 8'd10: begin TxData <= write_waveform; TxData_en <= 1'b1; counter <= counter + 1'b1; end 8'd11: begin TxData <= TxData; TxData_en <= 1'b0; counter <= counter; if(TxData_done) begin counter <= counter + 1'b1; end end // 结束 将使能位TxData_en拉低 8'd12: begin TxData <= 16'd0; TxData_en <= 1'b0; counter <= counter; end default: begin counter <= counter; end endcase end end end endmodule 

FPGA重置后,delay_start置位,经过一定的延时后,开始初始化写入1Mhz的正弦波信号。写入的过程如下:

  1. 重置寄存器
  2. 选择数据一次写入
  3. 写入频率寄存器低14位
  4. 写入频率寄存器高14位
  5. 写入相位寄存器12位
  6. 选择波形输出
  7. 循环等待按键按下重置上述流程

当检测到按键按下时,即key_flagkey_value变化时,波形选择寄存器和频率选择寄存器会发生变化。以此来选择正弦波、三角波、方波和1M、3M和1K的频率信号。

三、结果展示


图5 FPGA引脚输出

图5是FPGA的引脚输出状态。可以看到FSYNC为低电平时开始写入操作,TxData为写入操作字,SI逐一读取写入位,跟随着SCK的变化写入。


图6 AD9833输出图 3M正弦波

可以观察到周期是333ns,也就是3M,输出频率很精准。另外是436mV的峰峰值(经过外部运放放大后的,放大倍数为6.1倍),不超过手册中的650mV。


图7 AD9833输出图 1K正弦波

其余波形和频率,博主忘记拍了=.=,但确实完整输出了三角波和方波(只要按一下按键就行)。就不重新连了,有点懒。
最后的最后,展示一下AD9833在原理图中的连接图。
在这里插入图片描述


图8 AD9833原理图


总结

本文,首先分析了AD9833的芯片手册,根据其芯片手册的内容,我们用Verilog语言编写了控制程序,并烧录至FPGA中用于控制AD9833。最后得到不同波形和频率的输出结果。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118001.html

(0)
上一篇 2025-11-16 15:45
下一篇 2025-11-16 16:10

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信