欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

FPGA控制——基于FSM的TLC5615之SPI建模

程序员文章站 2024-02-23 22:52:10
...

真的是好久不见!

此篇博客介绍TLC5615的控制之SPI建模。

PART1:TLC5615及其时序控制原理介绍

TLC5615是TI公司的一款10位DAC芯片,控制兼容SPI接口。其引脚图如下:

FPGA控制——基于FSM的TLC5615之SPI建模

从引脚图看到,通信接口为:DIN、SCLK、CS_N和DOUT,其中DOUT用于级联方式,本次设计采用的是非级联方式,故不用到DOUT;模拟接口:OUT(模拟电压输出)、REF(基准电压输入)。

(2019.07.12)对tlc5615芯片描述进行相关补充:

1、sclk串行移位时钟的最快频率限制:该值有spi传输协议限制,一般取决于Tsclk的高低电平的最小值之和。由手册可知,tw(CH)=tw(CL)=25ns(最小值),则fsclk_max=1/50ns=20MHz,留有一定空间,故fsclk不能过大,手册给出经验值14MHz,设计时按照此值即可。

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),关于这点,我会做了验证之后再下结论。

验证:实验验证得,最大输出正弦波频率约25KHz。输出波形如下(已失真,这可能与芯片有关,这不一定是原装芯片,但从这个趋势知其输出一定是达到不了80KHz的,实际中建议控制在20KHz以内):

FPGA控制——基于FSM的TLC5615之SPI建模

3、更新速率:手册上是1.21MHz,即tp(CS)=tp(CSH)+tp(CSL) = 16*tsclk+tw(CS)=16*50ns+20ns=820ns,则fupdata=1/820ns=1.21MHz

其通信协议为SPI(串行外设接口),相关spi协议知识,可参看:https://blog.csdn.net/zjy900507/article/details/79660273

因为tlc5615作为从设备,故通信数据线只需要sda即可。(2019.05.31)

 

PART2:基于状态机的spi协议的实现。(2019.6.11)

然而,从6月初开始,就忙于各种考试了(泪奔),所以没有时间更新博客。补充一点:之前写过一次tlc5615的驱动,但最终没能成功出波,故在6月份的时候就暂停了对它的调试。回到正题,先总结一下之前遇到的问题:

-->有关对spi协议中sclk的理解:是否可以sclk线上一直输出时钟波形?

-->data_init模块和tlc5615_logic模块之间进行数据交互,如何控制好通信的握手机制(就是说:data_init模块向外发送数据,包括数据脉冲,以及如何判断tlc5615_logic模块已经完成一次da转换)。

-->对spi协议的几种模式没有认真理解,即CPOL和CPHA的值对应不同的协议状态。

回答上述问题:

1、理论上是可以的(所以最开始用了PLL生成sclk,这样sclk的控制就变得很简单),但最好不要这样,原因有二:第一是,官方文档中有如下描述:就是说在cs_n1时应该保持sclk0,这样可以减小时钟馈通干扰(因为时钟边沿处,容易对外产生干扰);第二,虽然连续的sclk很好生成,但却不利于整个时序的控制,移位cs_n要在sclk为低时才能发生变化。

2spi接口没有指定的流控制,没有应答机制确认是否接收到数据。故最终的处理方式是:每隔固定的间隔data_init模块向外送数据(这个固定间隔对应生成波形的频率),至于da转换完成,可以不设定转换完成标志回发,因为在这个时间间隔内,da转换完成后就会自动进入空闲状态(因为本次实现使用了状态机),故固定时间间隔后,就自动进入下一次数据输出即可。

3、这也是本次设计想要着重的一点,就是使用状态机进行行为级建模,能够将建模的思路大大地优化,这样,整个时序的控制会变得简单的多(下文会详细说明)。

FPGA控制——基于FSM的TLC5615之SPI建模

 

关于之前错误的代码,这里就不贴出了。下面直接对最新的代码进行分析。

 

-->Spi协议的状态机实现分析:

Q:进行建模之前分析整个控制协议过程主要包含哪些个阶段?

FPGA控制——基于FSM的TLC5615之SPI建模

 

如上图,cs_n为1时,表示空闲状态或者上一次da转换完成,此可以作为一个有效状态IDLE;

Da转换开始也作为一个状态,即当data_init发送的da_start信号被接收时,进入START状态;

下一个状态应该是da转换状态WRITE,是整个状态机所处时间最长的那个状态(12次移位输出,在sclk下),但是从START进入到WRITE,需要等待3个时钟(clk时钟50MHz),这个等待是人为控制的,为了提供充足的信号建立和保持时间;

最后应该是da转换完成状态FINISH,从WRITE进入FINISH的条件是bit_cnt为12(即向外移位12个bit的数据);

最后FINISH状态结束后,过一个clk周期会进入IDLE空闲状态,系统等待下一个数据输入和da_start信号到来。由此,这个状态机的状态设定是比较合理的,特别是从START进入到WRITE,需要等待3个时钟的设定。

最终建模出的状态机状态转换图如下:知设计实现与预期相匹配。

FPGA控制——基于FSM的TLC5615之SPI建模

PART3:仿真调试与设计细节展示

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_cnt13时输出对应态,不采用取反方式。

//sclk电平判断
reg 	[3 : 0] 	sclk_cnt;
always @ (posedge clk, negedge rst_n) begin
	if (!rst_n)
		sclk_cnt <= 4'd0;
	else if (sclk_cnt == 4'd3 || cs_n)
		sclk_cnt <= 4'd0;
	else if (!cs_n)
		sclk_cnt <= sclk_cnt + 1'b1;
end

always @ (posedge clk, negedge rst_n) begin
	if (!rst_n)
		sclk <= 1'b0;
	else if (state == WRITE || state == START) begin
		if (sclk_cnt == 4'd1)
			sclk <= 1'b1;
		else if (sclk_cnt == 4'd3)
			sclk <= 1'b0;
	end
	else if (cs_n)
		sclk <= 1'b0;
end

step3:sda_buf移位输出初始位始终为0,究其原因,还是触发器建模会延迟一拍,故需要在state == WRITE || state == START时进行移位,而不仅仅是state == START时才进行移位。 

always @ (posedge clk, negedge rst_n) begin
	if (!rst_n)
		sda_buf1 <= {DAT_WID{1'b0}};
	else if (da_start)
		sda_buf1 <= data_in;
end

always @ (negedge clk, negedge rst_n) begin
	if (!rst_n)
		sda_buf <= {DAT_WID{1'b0}};
	else if ((state == WRITE || state == START) && sclk_cnt == 4'd0)
		sda_buf <= sda_buf1 << bit_cnt;
end

 

step4:确保sclk的上升沿时sda的数据稳定,则需要在clk的下降沿将数据写入。

//tlc615是在sclk上升沿从sda中读入数据,那这个模块对应在sclk的下降沿写入数据到sda中,

//这样可以确保在sclk的上升沿保证sda的数据稳定

always @ (negedge clk, negedge rst_n) begin
	if (!rst_n)
		sda_buf <= {DAT_WID{1'b0}};
	else if ((state == WRITE || state == START) && sclk_cnt == 4'd0)
		sda_buf <= sda_buf1 << bit_cnt;
end

以上step4至关重要,充分体现了相异边沿对数据的写入和读取的思想,如果目标需要上升沿的数据读取,那么就应该在下降沿将数据写入(对应上文中的移位输出),这样就能够在上升沿稳定读取到目标数据了。

 

Step5:仿真波形展示

如下清晰可见,sda上的数据在sclk的上升沿时是稳定的(么有出现sclk的上升沿对应数据的边沿这种情况)。分析知,仿真波形是正确的。

FPGA控制——基于FSM的TLC5615之SPI建模

 

Step6:实物效果展示

-->目标输出:

//输出1kHz正弦波(对应512个点,也可以1024个点),则t0 = 1 / 512 * 1000000

//转换为50MHz的主频,则计数个数F_NUM = t0 / 20 = 97

 

-->实际波形:

实测波形频率为1.02kHz,峰峰值为5Vpp(对应Vref=2.5V,输出最大为2*Vref=5V)

NOTE:实物板是我之前淘宝店的模块,之前保留了一块哈哈,现在翻出来测试效果还是不错的(需要硬件模块的可以找我,开个玩笑,淘宝店已经下架了哈哈)。

FPGA控制——基于FSM的TLC5615之SPI建模

 硬件实物板tlc5615

FPGA控制——基于FSM的TLC5615之SPI建模

实际输出波形f=1.02kHz

PART4:总结

到此,整个TLC5615的spi驱动建模已经完成,总结一下:

(1)在这次建模中,着重强调状态机的重要性,体会到了状态机是FPGA设计的灵魂的这句话的意思;

(2)体会到在相异边沿对数据的写入和读取的思想,这样可以避免数据和时钟沿对其的情况(实际上对应亚稳态),因为在时钟边沿与数据边沿对齐时,此时读取的数据是不确定的。

(3)老生常谈,还是触发器建模,输出会延迟一个时钟输出,因为实际设计时容易忘记,特此提出。

 

附:工程文件顶层RTL视图

FPGA控制——基于FSM的TLC5615之SPI建模

工程代码链接:https://download.csdn.net/download/huigeyu/11238116

以上为个人见解,如觉得不妥,欢迎交流指正!