☰
AD9914 DDS驱动与频率控制字换算:从寄存器配置到FPGA移植
2026/10/26 9:24:39 网站建设 项目流程

简介:面向FPGA及嵌入式开发者的AD9914高性能DDS驱动资源包,主要解决直接数字频率合成器在工程中的快速配置与控制问题,适用于通信、测试测量、雷达等需要高精度频率生成的场景。包内共24个文件,以C源文件、头文件、Keil工程文件(uvproj/uvopt)及编译输出(hex/axf/map)为主,附带汇编启动文件与链接脚本等,可完整支持在ADuC7026平台上的编译、烧录与调试。压缩包仅59KB,轻量易用。已有447人学习浏览。资源结合AD9914内置12位DAC、3.5GSPS采样率等特点,给出驱动初始化、频率/幅度/相位参数配置以及SPI或并行接口控制的示例代码,便于开发者理解寄存器操作与同步时序,并快速移植到自有FPGA或MCU项目中,缩短高频信号源系统的开发周期。

1. 一块 3.5 GSPS 的 DDS 芯片,一份能直接跑的 AD9914 驱动工程

射频调试台前最绕不开的一类器件,就是 DDS 信号发生器。AD9914 是 Analog Devices 的高性能直接数字频率合成器,3.5 GSPS 采样率配 12 位 DAC,频率、相位、幅度都能用数字字精确控制,跳频通信、雷达激励、测试测量里经常见到它。这份 ExampleCode_DDS_AD9914 压缩包是一套完整的 ADuC7026(ADI 的 ARM7 内核 MCU)Keil 工程,SPI 初始化、寄存器配置、FTW 频率字换算都写在 ad9914test.c 里,工程里连 hex 和 axf 都编译好了,属于拿到就能开干的那类代码。做 FPGA 驱动的人同样值得下——AD9914 的 SPI 时序和主控平台无关,把 C 驱动里的寄存器序列搬到 Verilog 状态机里就能用。适合正在调 AD9914 寄存器、或者打算用 FPGA 控制 DDS 输出频率的开发者。

2. AD9914 寄存器体系与频率控制字换算:把 MHz 变成十六进制之前先看懂这两张表

2.1 DDS 核心:相位累加器与 32 位频率控制字

DDS 内部绕不开的是一个相位累加器。每个 SYSCLK 周期,累加器加上一个步进值,这个步进就是频率控制字 FTW。当累加器溢出回绕,输出就完成一个周期。所以输出频率和 FTW 是严格的线性关系:

f_out = (FTW × f_SYSCLK) / 2^32

AD9914 的 SYSCLK 最高 3.5 GHz,FTW 是 32 位寄存器。这个公式的工程含义是:频率分辨率固定等于 f_SYSCLK / 2^32,约 0.815 Hz,与输出频率无关。你不需要查表,不需要线性化校准,直接把目标频率代入公式就能算出 FTW。反过来,如果发现输出频率对不上,第一步就是检验 FTW 是不是按当前实际 SYSCLK 算的——很多人栽在参考时钟不是标称 3.5 GHz 上。

FTW 决定频率,POW0 是 16 位相位偏移字,相位分辨率 360/65536 ≈ 0.0055 度;ACW0 是 12 位幅度控制字,配合 12 位 DAC 使用。相参体制、多片同步时需要设置 POW。频率切换能有多快,取决于更新 FTW 的方式:直接写 FTW0 后由 IO_UPDATE 锁存,或者用 Profile 模式,预先在 8 个 profile 里存好不同的 FTW/POW/ACW,切换时只拉 Profile0-2 引脚,不碰 SPI,切换时间只受 SYSCLK 周期限制,这部分在第 6 章展开。

2.2 关键寄存器地址与 SPI 指令字节

AD9914 的寄存器空间用 6 位地址编址,调试最常用的几个地址列在这张表里。

地址寄存器长度用途
0x00CFR132 位SPI 模式、LSB first、SDO 使能等全局配置
0x01CFR232 位SYNC_CLK 输出、IO_UPDATE 行为
0x02CFR332 位参考时钟倍频、Profile 相关配置
0x03CFR432 位辅助控制位
0x04FTW032 位频率控制字,主输出通道
0x05POW016 位相位偏移字
0x06ACW012 位幅度控制字

SPI 每次传输的格式是:1 个指令字节 + 若干数据字节。指令字节 bit7 是读写标志(0 写 1 读),bit6:1 是 6 位寄存器地址,bit0 恒为 0。数据字节数由目标寄存器长度决定,比如 CFR1 和 FTW0 都是 4 字节,POW0 是 2 字节,全部 MSB first。这个位域定义在不同版本数据手册里的表达方式可能略有差异,但实际线上时序就是:先送指令字节,再送数据,CSB 在整个传输期间拉低。

提示:拿到芯片后第一件事不是写代码,而是确认手头数据手册版本对应的寄存器表和读时序图,压缩包里 ad9914test.c 的地址宏可以作为交叉验证的参考。

2.3 三个频率计算实例与一段可复现的换算代码

假设 SYSCLK = 3.5 GHz,FTW 公式代入后得到下面的结果。

目标频率FTW 整数实际输出误差
1 GHz1227133513约 0.1 Hz
100 MHz122713351小于 0.5 Hz
25 MHz30683338小于 0.5 Hz

后端如果用 C 写,直接乘 2^32 会溢出 32 位整型,我一般先用 double 算,再截断成 uint32_t,反算一遍确认误差。

#include <stdio.h> #include <stdint.h> int main(void) { double sysclk = 3.5e9; /* AD9914 最高 SYSCLK */ double f_target = 1e9; /* 目标频率,单位 Hz */ uint32_t ftw; /* 用 double 算出理论 FTW,再转 32 位整型 */ ftw = (uint32_t)((f_target / sysclk) * 4294967296.0); printf("FTW = 0x%08X (%u)\n", ftw, ftw); /* 反算实际输出频率,确认舍入误差 */ double f_real = (double)ftw * sysclk / 4294967296.0; printf("f_out = %.3f Hz\n", f_real); return 0; }

这段代码的逻辑:先算比例 f_target / sysclk,再乘 2^32,得到的就是理论上应该写进 FTW0 的整数。反算那一步很关键,它能直接告诉你舍入带来的误差量级,避免把错误的控制字烧进芯片。参数上注意 sysclk 一定要用真实值,比如外部晶振 100 MHz 配合片内 35 倍频,那 sysclk 才是 3.5 GHz;如果 PLL 没使能,实际只有 100 MHz,同样的 FTW 输出会差 35 倍,这是后面避坑章节的高频事故。

3. 解包 ExampleCode_DDS_AD9914:Keil 工程结构、SPI 初始化与寄存器写入主流程

3.1 压缩包里的工程结构:哪些文件有用,哪些是中间产物

解压后第一眼会看到一堆后缀各异、看起来乱糟糟的文件。别被吓到,我把它们按用途分一下类。

文件作用是否需要关注
AD9914Test.uvprojKeil µVision 4 工程文件是,双击打开它
AD9914Test.c主程序,SPI 初始化与 AD9914 配置是,核心代码
ADuC7026.hADuC7026 外设寄存器定义头文件是,查 SPI/GPIO 寄存器时用
ADuC702x.sARM 启动文件,时钟与异常向量一般不用改
irq_arm.cARM 中断处理,工程里中断服务入口看需求,关中断可忽略
AD9914Test.hex / .axf编译产物是,可直接烧录
AD9914Test.map / .lst链接映射与反汇编排错时用
ad9914test.o / .crf / .d / .plg编译器中间文件忽略

真正值得反复看的是 AD9914Test.c 和 ADuC7026.h。.uvproj 用 Keil 打开后直接 F7 编译就能过,工程里带的 hex 是已经编译好的,用 ULINK 或者 J-Link 烧进 ADuC7026 的 Flash 就能跑。ADuC7026 是 3.3V IO,AD9914 的数字 IO 也是 3.3V 域,两者可以直连,不需要电平转换,这给调试省了很多事。

3.2 SPI 底层接口:一个字节收发函数打天下

ADuC7026 的 SPI 外设寄存器操作属于平台相关代码。为了让你在别的 MCU 或 FPGA 上也能复用,我把底层抽象成一个字节收发函数,上层的寄存器读写逻辑全部平台无关。

/* spi_platform.h * 平台相关层:只暴露一个字节收发函数。 * ADuC7026 上的实现是操作 SPITX/SPIRX/SPISTA 寄存器, * 换成 STM32、GD32 或 FPGA 时,替换成各自的 SPI 收发即可。 */ static uint8_t spi_write_read_byte(uint8_t tx) { /* ADuC7026 参考实现: * 1) SPIEN = 1 使能 SPI; * 2) SPITX = tx,启动传输; * 3) 等待 SPISTA 的 SPIF 置位; * 4) return SPIRX; * 具体寄存器名以 ADuC7026.h 为准 */ return 0xFF; /* 占位 */ }

这段代码的意义在于把"一个字节怎么过 SPI 总线"和"AD9914 的寄存器协议"解耦。你只需要保证 spi_write_read_byte 能发一个字节并读回一个字节,其他所有逻辑都不依赖具体平台。参数上注意:ADuC7026 的 SPI 主模式建议先跑 1 MHz 验证时序,确认通信稳定后再往上提,AD9914 的 SCLK 上限很高,但 MCU 这边往往受限于外设分频。

3.3 写寄存器函数:指令字节拼装与 IO_UPDATE 锁存

核心写函数如下,它拼装指令字节、按大端序发送数据,最后拉一次 IO_UPDATE 把数据锁存进 AD9914 内部寄存器。

/* ad9914_drv.h 平台无关的 AD9914 驱动核心 */ #define AD9914_CFR1 0x00 #define AD9914_CFR2 0x01 #define AD9914_CFR3 0x02 #define AD9914_FTW0 0x04 #define AD9914_POW0 0x05 #define AD9914_ACW0 0x06 /* 写寄存器:instr 的 bit7=0 表示写,bit6:1 放 6 位地址,bit0 恒 0 */ void ad9914_write(uint8_t addr, uint8_t len, uint32_t data) { uint8_t instr = (addr << 1) & 0x7E; /* 地址放入 bit6:1 */ uint8_t buf[4]; int i; buf[0] = instr; /* 大端序:最高字节在前 */ for (i = 0; i < len; i++) buf[1 + i] = (data >> (8 * (len - 1 - i))) & 0xFF; cs_low(); for (i = 0; i < 1 + len; i++) spi_write_read_byte(buf[i]); cs_high(); /* IO_UPDATE 上升沿把数据锁存进内部寄存器 */ io_update_pulse(); }

指令字节的拼装逻辑是 (addr << 1) & 0x7E:addr 是 6 位寄存器地址,左移一位放进 bit6:1,bit7 保持 0 就是写操作,bit0 恒为 0。数据按大端发送,先送最高字节,因为 AD9914 的 SPI 是 MSB first。io_update_pulse 一般用 GPIO 拉高再拉低,脉冲宽度我给 100 ns 以上,工程里用两个 GPIO 翻转就行。

需要注意的是:CFR 寄存器其实在 SPI 传输完成后立即生效,而 FTW0、POW0、ACW0 必须等 IO_UPDATE 上升沿才锁存。上面的函数统一在每次写完后发脉冲,省事且不会漏。

3.4 初始化序列与一个 1 GHz 输出示例

下面这段初始化代码可以直接套用,FTW0 的值就是第 2 章算出来的 1227133513。

void ad9914_init(void) { /* CFR1: SPI 模式、SDO 使能等, * 具体位含义对照数据手册 CFR1 描述逐位核对 */ ad9914_write(AD9914_CFR1, 4, 0x00001000); ad9914_write(AD9914_CFR2, 4, 0x00000042); ad9914_write(AD9914_CFR3, 4, 0x00000000); /* 设置 1 GHz 输出:FTW0 = 1227133513 = 0x49249249 */ ad9914_write(AD9914_FTW0, 4, 1227133513UL); } int main(void) { spi_master_init(); /* ADuC7026 SPI 主模式,速率先跑 1 MHz */ ad9914_init(); while (1) { /* 这里放测试序列: * 改 FTW、触发 IO_UPDATE、读回校验 */ } }

spi_master_init 在 ADuC7026 工程里已经写好,无非是配置 SPI 引脚复用、主从模式、时钟极性和相位。CFR1 的值 0x00001000 是我在测试板上常用的配置,不同板子的 PLL 使能、SDO 使能位不一样,拿到工程后对照数据手册 CFR1 的位定义核对一遍再上电。整套流程走通后,频谱仪上应该能看到 1 GHz 的谱线。

3.5 读回校验:SPI 半双工必须处理的引脚方向

配置完寄存器,最好读回来确认。AD9914 支持 SPI 读操作,指令字节 bit7 置 1 即可。

uint32_t ad9914_read(uint8_t addr, uint8_t len) { uint8_t instr = ((addr << 1) & 0x7E) | 0x80; /* bit7=1 表示读 */ uint32_t data = 0; int i; cs_low(); spi_write_read_byte(instr); /* AD9914 在读指令后直接输出数据,主机继续供时钟 */ for (i = 0; i < len; i++) data = (data << 8) | spi_write_read_byte(0x00); cs_high(); return data; }

读操作要特别注意:ADuC7026 的 SPI 如果配置成半双工单线模式,SDIO 在发完指令字节后必须从输出切到输入,否则读回来的全是发送数据本身。我在第 5 章会把"读回全 0 或全 1"这个坑单独拿出来讲。函数里 data 不断左移再或上新字节,实现大端拼接,和写操作的字节顺序一致。

4. 把驱动移植到 FPGA:Verilog 状态机 SPI 控制器与多片同步设计

4.1 FPGA 侧引脚规划:先定接口,再写逻辑

FPGA 驱动 AD9914 和 MCU 驱动最大的区别是:SPI 时序要自己用状态机造出来,好处是 SCLK 速度、脉冲宽度完全可控。先规划好引脚,再动手写代码。

AD9914 引脚方向FPGA 侧说明
SCLK输出SPI 时钟,建议先跑 10 MHz
SDIO双向写时输出,读时切为输入
CSB输出片选,低有效,整个传输期间保持低
IO_UPDATE输出寄存器锁存脉冲,写 FTW/POW 后给一个上升沿
SYNC_CLK输入AD9914 输出,多片同步时作为参考
PROFILE0-2输出Profile 快速切换,不用时接地

SYNC_CLK 的作用容易被忽略。它是 AD9914 输出的同步时钟信号,多片级联时必须把主片的 SYNC_CLK 接到从片,让所有芯片的 IO_UPDATE 对齐。单片的场合可以不管它,但如果系统里有好几片 AD9914 做相参输出,SYNC_CLK 就是那个让相位对齐的关键信号。

4.2 Verilog SPI 状态机:一个可直接例化的模块

下面这个状态机模块按 bit 发送,指令字节加数据一次性传完。SCLK 空闲为低,数据在 SCLK 低电平期间切换,上升沿稳定送入 AD9914。

/* ad9914_spi_master.v * 参数 CLK_DIV 决定 SCLK 频率:sclk = clk / (2*CLK_DIV) * 时序:SCLK 空闲低,数据在低电平期间切换,上升沿被 AD9914 采样 */ module ad9914_spi_master #( parameter CLK_DIV = 4 )( input wire clk, input wire rst_n, input wire start, /* 上升沿启动一次传输 */ input wire [7:0] instr, /* 指令字节 */ input wire [31:0] data, /* 数据,大端在前 */ input wire [1:0] data_len, /* 数据字节数:0=1字节,3=4字节 */ output reg sclk, output reg cs_n, output reg sdo, output reg busy, input wire sdi /* 读回,写模式可不接 */ ); localparam IDLE = 2'd0, PRE_BIT = 2'd1, BIT_HIGH = 2'd2, POST_BIT = 2'd3; reg [1:0] state; reg [7:0] bit_cnt; reg [4:0] total_bits; reg [31:0] shift_reg; reg [15:0] div_cnt; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; cs_n <= 1'b1; sclk <= 1'b0; busy <= 1'b0; end else begin case (state) IDLE: begin if (start) begin cs_n <= 1'b0; /* 总位数 = 8 位指令 + N 字节数据 * 8 */ total_bits <= 8 + ({2'b00, data_len} + 1) * 8; shift_reg <= {instr, data}; bit_cnt <= 0; div_cnt <= 0; busy <= 1'b1; state <= PRE_BIT; end end PRE_BIT: begin /* SCLK 低电平期间把 sdo 切到当前最高位 */ sdo <= shift_reg[31]; if (div_cnt == CLK_DIV) begin sclk <= 1'b1; div_cnt <= 0; state <= BIT_HIGH; end else begin div_cnt <= div_cnt + 1; end end BIT_HIGH: begin /* 上升沿期间数据保持稳定,供 AD9914 采样 */ if (div_cnt == CLK_DIV) begin sclk <= 1'b0; shift_reg <= {shift_reg[30:0], 1'b0}; bit_cnt <= bit_cnt + 1; div_cnt <= 0; state <= POST_BIT; end else begin div_cnt <= div_cnt + 1; end end POST_BIT: begin if (bit_cnt == total_bits - 1) begin cs_n <= 1'b1; busy <= 1'b0; state <= IDLE; end else begin state <= PRE_BIT; end end default: state <= IDLE; endcase end end endmodule

这个状态机的核心思想是每个 bit 分成三段:PRE_BIT 里 SCLK 为低、sdo 切到当前最高位,BIT_HIGH 里 SCLK 拉高,POST_BIT 里切到下一 bit 并继续。total_bits 由指令字节位数和数据字节数算出,数据长度通过 data_len 参数控制,2 位可以编码 1 到 4 字节。需要读回时,在 BIT_HIGH 末尾用 sdi 采样进一个移位寄存器即可,写模式下 sdi 悬空或接 0。

例化这个模块写一次 FTW0 的代码如下。

/* 例化并触发一次 FTW0 写操作:地址 0x04,数据 0x49249249(1 GHz) */ ad9914_spi_master #(.CLK_DIV(4)) u_spi ( .clk (clk_50m), .rst_n (rst_n), .start (spi_start), .instr (8'h08), /* 地址 0x04 左移一位,bit7=0 写 */ .data (32'h49249249), .data_len (2'd3), /* 4 字节 */ .sclk (ad9914_sclk), .cs_n (ad9914_cs_n), .sdo (ad9914_sdio), .busy (spi_busy), .sdi (1'b0) );

注意 instr 传的是 8'h08,也就是 0x04 左移一位的结果,和 C 驱动里 (addr << 1) & 0x7E 完全对应。写 FTW 这类需要锁存的寄存器,等 busy 拉低后再给 IO_UPDATE 脉冲,幅值 100 ns 以上。SPI 传输完成后,用一句 reg 翻转生成 IO_UPDATE:检测 busy 下降沿,延时几个时钟,拉高再拉低。

4.3 MCU 驱动和 FPGA 驱动的差异:三个必须调整的地方

第一,SCLK 可以跑得更高。ADuC7026 的 SPI 外设分频有限,通常跑到几 MHz 就到头了,FPGA 这边 CLK_DIV 参数一调就能上 20 MHz,配置 8 个 profile 的时间明显缩短。第二,读回时序。半双工模式下 SDIO 需要三态控制,发完指令字节后把 sdo 置为高阻,然后从 sdi 采样,MCU 的 SPI 外设一般自动处理方向切换,FPGA 要自己写一个 oe 信号。第三,多片同步。FPGA 天然适合处理 SYNC_CLK 和 IO_UPDATE 对齐这件事,把所有 AD9914 的 IO_UPDATE 约束到同一个时序路径,比 MCU 用 GPIO 逐个拉要可靠得多。

5. 避坑清单:AD9914 配置翻车最常见的五个原因

5.1 寄存器写了没反应,输出还是上一个频率

现象:SPI 波形看起来是对的,CSB 拉低、指令字节和数据字节都发了,但频谱仪上输出频率纹丝不动。

原因:FTW0、POW0、ACW0 这类寄存器必须等 IO_UPDATE 上升沿才锁存,只写完 SPI 不会生效。我见过不少第一次调 AD9914 的人在这里卡了两三天,波形上什么都对,就是忘了 IO_UPDATE 这根线。

解决:每次写完这三个寄存器后,拉一个 IO_UPDATE 脉冲,宽度给足 100 ns 以上。或者配置 CFR2 里的自动锁存模式,让寄存器在 SPI 传输完成后自动生效,省掉外部引脚,但我不建议新手这么干,调试时少一个可控变量就多一分困惑。

5.2 输出频率和设定值差一个固定倍数

现象:设 1 GHz,实测 350 MHz;设 100 MHz,实测 35 MHz,比例和标称 SYSCLK 与真实 SYSCLK 的比值一致。

原因:这是频率控制字公式里 SYSCLK 用错了。外部晶振 100 MHz,片内 PLL 没使能或者倍频系数不对,真实 SYSCLK 只有 100 MHz,计算 FTW 时却按 3.5 GHz 代进去,输出自然差了 35 倍。

解决:上电后用示波器或频谱仪先测 REFCLK 频率,再确认 CFR3 里倍频配置的最终 SYSCLK。我一般会在初始化代码里加一个调试宏,把当前 SYSCLK 值打印出来,和 FTW 一起核对,避免这种低级错误烧掉半天时间。

5.3 SPI 读回全是 0xFF 或者全 0

现象:写进去的值读不回来,读 CFR1 得到 0xFFFFFFFF,或者全是 0。

原因:半双工模式下 SDIO 引脚方向没切换。指令字节发送阶段 SDIO 是输出,进入数据阶段必须切成输入,否则读到的还是驱动电平。另外部分 ADI 的 DDS 读操作需要在指令字节后补一个 dummy 时钟周期,时序图没仔细看就会踩到。

解决:发完指令字节后把 SDIO 置为高阻,再继续供时钟;对照数据手册读时序图确认是否要 dummy 周期。FPGA 里就是那个 oe 信号翻转的时机问题,MCU 里则是 SPI 外设的"接收使能"配置,逐条排查。

5.4 输出杂散大,SFDR 远低于手册值

现象:频谱仪上除了主频还有一堆杂散谱线,SFDR 掉到 40 dBc 以下,手册上明明写的是 50 dBc 起步。

原因:先分两类看。单根对称杂散,多半是 DDS 本身的杂散,跟频率控制字的值有关,换个 FTW 位置杂散跟着变;宽带抬高的底噪,则指向电源去耦不足或 SYSCLK 抖动过大。

解决:电源引脚全部加 0.1 uF 和 1 uF 去耦电容,评估板上的滤波网络别省;REFCLK 用低抖动晶振,别用电平毛刺大的 FPGA 时钟直接怼上去;输出端加一个合适的低通滤波器,把 DAC 镜像频率压下去。

5.5 多片同步时相位对不齐

现象:两片 AD9914 配同样的 FTW 和 POW,输出的相位差却不固定,每次上电都不一样。

原因:多片之间 SYNC_CLK 没有对齐,IO_UPDATE 到达各片的时间不同步。AD9914 的 SYNC_CLK 输出必须作为所有片的公共参考,IO_UPDATE 要在同一时刻锁存。

解决:把主片 SYNC_CLK 接到所有从片的 SYNC_CLK 输入,IO_UPDATE 由 FPGA 统一产生,并约束到同一时钟域。FPGA 里对 SYNC_CLK 做相位对齐,确保 IO_UPDATE 的上升沿落在所有片的安全窗口内。

6. 上电验证与 Profile 快速切换:把 3.5 GSPS 的跳频速度真正用起来

把工程烧进去之后别急着改需求,先做一套固定的上电验证序列。第一步写 FTW0 = 100 MHz 对应的 122713351,读回校验;第二步切换成 1 GHz 的 0x49249249,看频谱仪谱线是否跳过去;第三步检查 SFDR,确认电源和参考时钟的底子没问题。这套序列我每次接新的 DDS 板子都会强制走一遍,能过滤掉八成以上的低级问题。

Profile 模式才是 AD9914 在跳频场景里的杀手锏。把两三个常用频点预先算好 FTW,初始化时写进对应的 profile 寄存器,运行过程中只改 PROFILE0-2 引脚电平,不碰 SPI,跳频时间就从微秒级压缩到纳秒级。传统方式写 FTW 再拉 IO_UPDATE,受限于 SPI 时钟和软件开销,跳频间隔很难压下去;Profile 切换绕开了这条链路,直接把频率字的读取交给硬件引脚。

我习惯把起始频率放 PROFILE 0,跳变目标频率放 PROFILE 1,初始化时一次写完,调试时用逻辑分析仪抓 PROFILE 引脚和输出包络的对应关系。曾经有一回我图省事,直接改 FTW0 做跳频,结果跳频时间不达标,折腾了两天才意识到该用 Profile,从那以后每次设计 DDS 跳频链路都会把 Profile 切换放在第一位。硬件上的坑已经帮你趟过一遍,寄存器地址、指令字节位域、IO_UPDATE 时序这三处最容易翻车,按上面这套流程走,基本能一次点亮。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询