这次工作中接触到了AD9528这个亚诺半导体的时钟发生芯片,可以产生多路同步的时钟。这个时钟使用spi接口进行相应的配置。后面记录自己如何针对这个过程进行相应FPGA代码编写。
AD9528 基本介绍:
具体的配置参数及方法:
具体我们在项目中使用了,五路时钟,我们的配置要求如下所示:
官网中有相应的可以下载的上位机软件,可以进行相应的配置,配置之后就可以知道相应的需要配置的寄存器参数是什么。
这个是咨询厂家之后的基本配置方法,具体的配置原理,要详细的阅读datasheet,具体的就是OUT1 OUT2两个时钟需要使用从PLL2 divider中产生的时钟
具体需要配置的寄存器如下:
代码编写及AD9528的spi时序图:
AD9528 原理图:
AD9528时序图:
针对这个时序图,我们就开始展开代码编写了:
Verilog代码实现SPI配置寄存器:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : xibo wu (Gatsby)
// File : spi.v
// Create : 2023-10-25 16:16:08
// Revise : 2023-10-27 13:46:23
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module spi_module
(
input i_clk , // 全局时钟100MHz
input i_rst_n , // 复位信号,低电平有效
// 四线标准SPI信号定义
input i_spi_miso , // SPI串行输入,用来接收从机的数据
output o_spi_sck , // SPI时钟
output o_spi_cs , // SPI片选信号
output o_spi_mosi // SPI输出,用来给从机发送数据
);
//-------parameter--------//
parameter TX_IDLE = 4'b0001;
parameter TX_HEADER = 4'b0010;
parameter TX_PAYLOAD = 4'b0100;
parameter TX_END = 4'b1000;
//-----parameter_reg-------//
//---form “addr_data”------//
parameter REG_1 = 24'h0104_01;
parameter REG_2 = 24'h0100_01;
parameter REG_3 = 24'h0102_01;
parameter REG_4 = 24'h0204_01;
parameter REG_5 = 24'h0201_14;
parameter REG_6 = 24'h010A_02;
parameter REG_7 = 24'h0208_13;
parameter REG_8 = 24'h0501_E0;
parameter REG_9 = 24'h0502_3F;
parameter REG_10 = 24'h0302_11;
parameter REG_11 = 24'h030E_23;
parameter REG_12 = 24'h0303_00;
parameter REG_13 = 24'h0305_05;
parameter REG_14 = 24'h0308_05;
//---------regs-----------//
reg [4:0] tx_bit_sel;
reg wr_en;
reg [3:0] clk_cnt;
reg cs_n_t;
reg sck_t = 1'b0;
reg [4:0] reg_cnt = 5'd14; //此处还不明确后续需要更改
reg mosi_t;
reg [23:0] reg_cfg; //可变的寄存器写入配置数值
//--------debug-----------//
reg [23:0] reg_debug;
reg [3:0] cur_state;
reg [3:0] nxt_state;
reg skip_en;
//-------assign----------//
assign o_spi_sck = sck_t;
assign o_spi_cs = cs_n_t;
assign o_spi_mosi = mosi_t;
//---------debug-----------//
always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
reg_debug <= 24'b0;
end else if(cs_n_t == 1'b0 && clk_cnt == 'd1) begin
reg_debug <= {reg_debug[22:0],mosi_t};
end else begin
reg_debug <= reg_debug;
end
end
//---------main------------//
always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
clk_cnt <= 4'b0;
end else if(clk_cnt == 'd3) begin
clk_cnt <= 'd0;
end else begin
clk_cnt <= clk_cnt + 4'd1;
end
end
//要在sck上升沿之前把数据准备好
always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
wr_en <= 1'b0;
end else if(clk_cnt == 'd2) begin
wr_en <= 1'b1;
end else begin
wr_en <= 1'b0;
end
end
always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
sck_t <= 1'b0;
end else if(clk_cnt == 'd1 && cs_n_t == 1'b0) begin
sck_t <= 1'b1;
end else if(clk_cnt == 'd3 && cs_n_t == 1'b0) begin
sck_t <= 1'b0;
end else begin
sck_t <= sck_t;
end
end
// 寄存器数据配置单元 j
always @(posedge i_clk or posedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
reg_cfg <= REG_1;
end else begin
case(reg_cnt)
5'd14 : reg_cfg <= REG_1 ;
//中间后续填充相应的寄存器数值
5'd13 : reg_cfg <= REG_2 ;
5'd12 : reg_cfg <= REG_3 ;
5'd11 : reg_cfg <= REG_4 ;
5'd10 : reg_cfg <= REG_5 ;
5'd9 : reg_cfg <= REG_6 ;
5'd8 : reg_cfg <= REG_7 ;
5'd7 : reg_cfg <= REG_8 ;
5'd6 : reg_cfg <= REG_9 ;
5'd5 : reg_cfg <= REG_10;
5'd4 : reg_cfg <= REG_11;
5'd3 : reg_cfg <= REG_12;
5'd2 : reg_cfg <= REG_13;
5'd1 : reg_cfg <= REG_14;
default:;
endcase
end
end
always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
cur_state <= TX_IDLE;
end else begin
cur_state <= nxt_state;
end
end
always @(*) begin
nxt_state = TX_IDLE;
case (cur_state)
TX_IDLE : begin
if (skip_en) begin
nxt_state = TX_HEADER;
end else begin
nxt_state = TX_IDLE;
end
end
TX_HEADER : begin
if (skip_en) begin
nxt_state = TX_PAYLOAD;
end else begin
nxt_state = TX_HEADER;
end
end
TX_PAYLOAD : begin
if (skip_en) begin
nxt_state = TX_END;
end else begin
nxt_state = TX_PAYLOAD;
end
end
TX_END : begin
if (skip_en) begin
nxt_state = TX_IDLE;
end else begin
nxt_state = TX_END;
end
end
default : nxt_state = TX_IDLE;
endcase
end
always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
skip_en <= 1'b0;
cs_n_t <= 1'b1;
tx_bit_sel <= 5'd0;
end else begin
skip_en <= 1'b0;
case (nxt_state)
TX_IDLE : begin
if(reg_cnt != 'd0) begin
skip_en <= 1'b1;
end else begin
cs_n_t <= 1'b1;
end
end
TX_HEADER : begin
cs_n_t <= 1'b0;
if (wr_en == 1'b1) begin
tx_bit_sel <= tx_bit_sel + 'd1;
cs_n_t <= 1'b0;
case(tx_bit_sel)
5'd0 : mosi_t <= reg_cfg[23]; //读写位
5'd1 : mosi_t <= reg_cfg[22]; //空
5'd2 : mosi_t <= reg_cfg[21]; //空
5'd3 : mosi_t <= reg_cfg[20]; //addr[12]
5'd4 : mosi_t <= reg_cfg[19]; //addr[11]
5'd5 : mosi_t <= reg_cfg[18]; //addr[10]
5'd6 : mosi_t <= reg_cfg[17]; //addr[9]
5'd7 : mosi_t <= reg_cfg[16]; //addr[8]
5'd8 : mosi_t <= reg_cfg[15]; //addr[7]
5'd9 : mosi_t <= reg_cfg[14]; //addr[6]
5'd10 : mosi_t <= reg_cfg[13]; //addr[5]
5'd11 : mosi_t <= reg_cfg[12]; //addr[4]
5'd12 : mosi_t <= reg_cfg[11]; //addr[3]
5'd13 : mosi_t <= reg_cfg[10]; //addr[2]
5'd14 : mosi_t <= reg_cfg[9]; //addr[1]
5'd15 : begin
mosi_t <= reg_cfg[8]; //addr[0]
tx_bit_sel <= 'd0;
skip_en <= 1'b1;
end
endcase
end
end
TX_PAYLOAD : begin
cs_n_t <= 1'b0;
if (wr_en == 1'b1) begin
tx_bit_sel <= tx_bit_sel + 'd1;
case(tx_bit_sel)
5'd0 : mosi_t <= reg_cfg[7]; //data[7]
5'd1 : mosi_t <= reg_cfg[6]; //data[6]
5'd2 : mosi_t <= reg_cfg[5]; //data[5]
5'd3 : mosi_t <= reg_cfg[4]; //data[4]
5'd4 : mosi_t <= reg_cfg[3]; //data[3]
5'd5 : mosi_t <= reg_cfg[2]; //data[2]
5'd6 : mosi_t <= reg_cfg[1]; //data[1]
5'd7 : begin
mosi_t <= reg_cfg[0]; //data[0]
tx_bit_sel <= 'd0;
skip_en <= 1'b1;
end
endcase
end
end
TX_END : begin
if(reg_cnt != 'd0) begin
reg_cnt <= reg_cnt - 1'd1;
skip_en <= 1'b1;
end
end
default :;
endcase
end
end
endmodule
仿真结果如下:
该文章仅用来交流,有错误欢迎各位指正。