管脚
输入数字数据格式,精度是10bit的数据大小,但是是串行输入的,所以要根据协议去传数据。
fpga向芯片串行发送,TLC5615芯片读完12bits之后,芯片进行内部转换,然后输出模拟电压
具体协议
1、sclk串行移位时钟的最快频率限制:该值有spi传输协议限制,一般取决于Tsclk的高低电平的最小值之和。由手册可知,tw(CH)=tw(CL)=25ns(最小值),则fsclk_max=1/50ns=20MHz,留有一定空间,故fsclk不能过大,手册给出经验值14MHz,设计时按照此值即可。
如果用50MHz的时钟,需要降频,四个50M的时钟做一个ad_clk,降成12.5MHz即可。
2、dac输出极限频率限制:
手册中有个概念,名为Setting time(注意不是建立时间setting up time),其义为:当输入的数字16进制码(12bit)从000变化到3ff(或者从3ff变化到000),dac输出电压保持稳定在目标电压的±0.5LSB范围内的时间,也可以称之为广义的建立时间。按这样定义的话,这个参数决定了dac的输出频率,因为这个时间概念是一个范围概念,其包括了很多个时间之和(隐含从000à001的时间以及001à010的时间等等,他是这之间所有变换的时间之和),则fo_max=1/(2*12.5us)=40KHz,很多资料说是80KHz(只取12.5us,这里的意思是全范围内的输出的更新率,如从0V输出到5V输出,这个时间间隔是12.5us,那么输出正弦波的频率就应该小于40KHz)
状态机控制状态转换
code
Step1:调用rom_ip生成数据源,预先用mif文件初始化rom,这部分网上可以自行百度,其后,需要简单写一个data_init的module,用来从rom调度数据输出,这时调度的间隔对应输出频率。
Step2:sclk的生成,采用20ns*4的周期(tf_sclk最小为50ns,技术文档中有说明),在cs_n拉低后,应该是输出12个时钟(tlc5615的12bit方式),这里,注意细节:若采用sclk=~sclk的方式,最后容易产生不标准的sclk信号(多一次翻转,或者在cs_n拉高后sclk变成高)。解决的办法是:sclk_cnt为1和3时输出对应态,不采用取反方式。
Step3: 移位
step4: 如果目标需要上升沿的数据读取,那么就应该在下降沿将数据写入(对应上文中的移位输出),这样就能够在上升沿稳定读取到目标数据了。充分体现了相异边沿对数据的写入和读取的思想
module da(
clk ,
rst_n ,
//INPUT
DA_data,
Send_start,
//OUT
Da_clk,
Da_din,
Da_cs_n,
Send_finish
);
//参数定义
parameter DATA_W = 10;
//输入信号定义
input clk ;
input rst_n ;
input[DATA_W-1:0] DA_data ;
input Send_start ;
//输出信号定义
output Da_din ;
output Da_clk ;
output Da_cs_n ;
output Send_finish ;
//中间信号定义
reg[2:0] state;
reg[2:0] state_n;
parameter IDLE = 3'b000;
parameter START = 3'b001;
parameter WRITE = 3'b010;
parameter FINISH = 3'b011;
//
reg[4:0] bit_cnt;
reg [3:0] sclk_cnt;
//cs_n
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
Da_cs_n<=1;
end
else if(state==START)begin
Da_cs_n<=0;
end
else if(state==FINISH) begin
Da_cs_n<=1;
end
else begin
Da_cs_n<=Da_cs_n;
end
end
//sclk_cnt 4个50MHz 12.5Mhz的adclk
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
sclk_cnt<=0;
end
else if(Da_cs_n||sclk_cnt==3)begin
sclk_cnt<=0;
end
else if(Da_cs_n==0)begin
sclk_cnt<=sclk_cnt+1;
end
end
//Da_clk
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
Da_clk<=0;
end
else if(Da_cs_n==0)begin//START 和WRITE期间
if(sclk_cnt==1)
Da_clk<=1;
else if(sclk_cnt==3)
Da_clk<=0;
end
else
Da_clk<=0;
end
//wait_time 3个时钟,60ns
reg [1:0] wait_cnt;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
wait_cnt<=0;
end
else if(state_n!=state||wait_cnt==2)begin
wait_cnt<=0;
end
else if(state==START||state==FINISH)
wait_cnt<=wait_cnt+1;
end
//状态机
always @ (posedge clk, negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= state_n;
end
always@(*)begin
case
IDLE:begin
if (Send_start)
state_n = START;
else
state_n = state;
end
START:begin
if(wait_cnt==2)//等待三个时钟
state_n = WRITE;
else
state_n = state;
end
WRITE:begin
if (bit_cnt == 12)
state_n = FINISH;
else
state_n = state;
end
end
FINISH:begin
if(wait_cnt==2)//等待三个时钟
state_n = IDLE;
else
state_n = state;
end
default:state_n=IDLE;
endcase
end
//bit_cnt
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
bit_cnt<=0;
end
else if(bit_cnt == 12) begin
bit_cnt <=0;
end
else if (state == WRITE && sclk_cnt == 4'd3)//下降沿,如果写adclk=0就慢了一个时钟
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt ;
end
//shift_reg
reg[11:0] shift_reg;
reg[11:0] shift_reg_buff;
//shift_reg_buff
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
shift_reg_buff<=0;
end
else if(Send_start)begin
shift_reg_buff<=DA_data;
end
end
//shift_reg
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
shift_reg<=0;
end
else if((state==START||state==WRITE)&&sclk_cnt==0)begin
shift_reg<=shift_reg_buff<<bit_cnt;
end
end
assign Da_din= shift_reg[11]; //将移位寄存器的最高位赋值给DA_DIN
assign Send_finish=(state== IDLE);
endmodule