1.TLC549简介
TLC549是美国德州仪器公司生产的8位串行A/D转换器芯片,可与通用微处理器、控制器通过CLK、CS、DATAOUT三条口线进行串行接口。具有4MHz片内系统时钟和软、硬件控制电路,转换时间最长17μs, TLC549为40000次/s。总失调误差最大为±0.5LSB,典型功耗值为6mW。采用差分参考电压高阻输入,抗干扰,可按比例量程校准转换范围,VREF-接地,VREF+-VREF-≥1V,可用于较小信号的采样。
REF+:正基准信号输入端
ANALOG IN:模拟信号输入端
REF-:负基准电压输入端
GND:接地端
#CS:片选信号,低电平有效
DATA OUT:转换结果串行输出端
I/O CLOCK:外接时钟输入端
VCC:电源输入端
其他信息可参照TLC549的datasheet。
2.AD通信协议
TLC549均有片内系统时钟,该时钟与I/O CLOCK是独立工作的,无须特殊的速度或相位匹配。其工作时序如图2所示。当CS为高时,数据输出(DATA OUT)端处于高阻状态,此时I/O CLOCK不起作用。
当CS为低时,AD前一次转换的数据A的最高位A7立马出现在数据线DATA OUT上,其余七位在I/O CLOCK的下降沿依次由时钟同步输出,,因此可在I/O CLOCK的上升沿读取数据,其中值得注意的是:1.图中他tsu(cs)至少要1.4us;2.I/O CLOCK不能超过1.1MHz。
读完8位数据后,AD开始转换这一次转换的采样数据B,以便下一次读取转换时,片选信号CS置高,每次转换不超过17us,开始于CS拉低后的第八个I/O CLOCK的下降沿,没有转换完成标志,没有启动控制端,只要读取前一次数据后就马上可以开始新的AD转换,转换完成进入保持状态。
3.代码
*File name:ad.v
*Author: ***
*Description:The AD module
***************************************************************/
`define AD_CLK_TIME 10'd45
`define AD_CLK_TIME_HALF 10'd22
module AD
(
input sys_clk_50m,
input rst_n,
output reg poc_ad_cs, //TLC549的片选
output reg poc_ad_clk, //TLC549的I/O CLOCK
input pid_ad_data, //TLC549的数据串行输出端,相对于FPGA为输入
output reg [3:0] o_vol_int, //转换后输出电压的整数部分
output reg [3:0] o_vol_dec //转换后输出电压的小数部分
);
reg n_ad_cs; //AD_CS的下一个状态
reg n_ad_clk; //AD_CLK的下一个状态
reg [ 2:0] ad_fsm_cs; //状态机的当前状态
reg [ 2:0] ad_fsm_ns; //状态机的下一个状态
wire [ 3:0] n_o_vol_int; //o_vol_int的下一个状态
wire [ 3:0] n_o_vol_dec; //o_vol_dec的下一个状态
reg [ 5:0] time_cnt; //用于记录一个时钟所用时间的定时器
reg [ 5:0] n_time_cnt; //time_cnt的下一个状态
reg [ 5:0] bit_cnt; //用来记录时钟周期个数的计数器
reg [ 5:0] n_bit_cnt; //bit_cnt的下一个状态
reg [ 7:0] data_out; //用来保存稳定的AD数据
reg [ 7:0] n_data_out; //data_out的下一个状态
reg [ 7:0] ad_data_reg; //用于保存数据的移位寄存器
reg [ 7:0] n_ad_data_reg; //ad_data_reg_n的下一个状态
wire [11:0] mid1; //数据转换电压的整数部分
wire [11:0] mid2; //数据转换电压的小数部分
parameter FSM_IDLE = 3'h0; //状态机的初始状态;
parameter FSM_READY = 3'h1; //满足CS有效时的第一个1.4us的延时状态
parameter FSM_DATA = 3'h2; //读取8个数据状态
parameter FSM_WAIT_CONV = 3'h3; //等待转换状态,等待17us;
parameter FSM_END = 3'h4; //结束的状态
/********************************************
状态机转换
********************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
ad_fsm_cs <= FSM_IDLE;
else
ad_fsm_cs <= ad_fsm_ns;
always @ (*)
begin
case(ad_fsm_cs)
FSM_IDLE:
if((bit_cnt == 6'd2 ) && (time_cnt == `AD_CLK_TIME))
ad_fsm_ns = FSM_READY;
else
ad_fsm_ns = ad_fsm_cs;
FSM_READY:
if((bit_cnt == 6'd1 ) && (time_cnt == `AD_CLK_TIME))
ad_fsm_ns = FSM_DATA;
else
ad_fsm_ns = ad_fsm_cs;
FSM_DATA:
if((bit_cnt == 6'd8 ) && (time_cnt == `AD_CLK_TIME))
ad_fsm_ns = FSM_WAIT_CONV;
else
ad_fsm_ns = ad_fsm_cs;
FSM_WAIT_CONV:
if((bit_cnt == 6'd18) && (time_cnt == `AD_CLK_TIME))
ad_fsm_ns = FSM_END;
else
ad_fsm_ns = ad_fsm_cs;
FSM_END:
ad_fsm_ns = FSM_READY;
default:ad_fsm_ns = FSM_IDLE;
endcase
end
/*********************************************************
时钟计数器,用以产生1.1M的时钟
*********************************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
time_cnt <= 6'd0;
else
time_cnt <= n_time_cnt;
always @ (*)
if (time_cnt == `AD_CLK_TIME)
n_time_cnt = 6'd0;
else
n_time_cnt = time_cnt + 6'd1;
/*********************************************
位计数器
*********************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
bit_cnt <= 6'd0;
else
bit_cnt <= n_bit_cnt;
always @ (*)
begin
if (ad_fsm_cs != ad_fsm_ns)
n_bit_cnt = 6'h0;
else if ( time_cnt== `AD_CLK_TIME_HALF )
n_bit_cnt = bit_cnt + 6'h1;
else
n_bit_cnt = bit_cnt;
end
/*******************************************************
产生外接时钟输入端的时钟信号,频率为1.1M,占空比约为50%
********************************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
poc_ad_clk <= 1'b0;
else
poc_ad_clk <= n_ad_clk;
always @(*)
begin
if (ad_fsm_cs != FSM_DATA)
n_ad_clk = 1'b0;
else if ( time_cnt == `AD_CLK_TIME_HALF )
n_ad_clk = 1'b1;
else if (time_cnt == `AD_CLK_TIME)
n_ad_clk = 1'b0;
else
n_ad_clk = poc_ad_clk;
end
/****************************************************
对TLC549的片选信号进行控制
*****************************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
poc_ad_cs <= 1'b0;
else
poc_ad_cs <= n_ad_cs;
always @ (*)
if ((ad_fsm_cs == FSM_DATA) || (ad_fsm_cs == FSM_READY))
n_ad_cs = 1'b0;
else
n_ad_cs = 1'b1;
/******************************************************
对数据寄存器赋值
******************************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
ad_data_reg <= 8'd0;
else
ad_data_reg <= n_ad_data_reg;
always @ (*)
begin
if ((ad_fsm_cs == FSM_DATA) && (!poc_ad_clk) && (n_ad_clk))
n_ad_data_reg = {ad_data_reg[6:0],pid_ad_data};
else
n_ad_data_reg = ad_data_reg;
end
/*************************************************************
输出数据处理
*************************************************************/
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
data_out <= 8'd0;
else
data_out <= n_data_out;
always @ (*)
begin
if (ad_fsm_cs == FSM_END)
n_data_out = ad_data_reg;
else
n_data_out = data_out;
end
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
o_vol_int <= 4'd0;
else
o_vol_int <= n_o_vol_int;
always @ (posedge sys_clk_50m or negedge rst_n)
if (!rst_n)
o_vol_dec <= 4'd0;
else
o_vol_dec <= n_o_vol_dec;
assign mid1 = {2'h0,data_out[7:0],2'h0} + {4'h0,data_out[7:0]};
assign mid2 = {1'h0,mid1[7:0],3'h0} + {3'h0,mid1[7:0],1'h0};
assign n_o_vol_int = mid1[11:8];
assign n_o_vol_dec = mid2[11:8];
endmodule
解释一下代码中在的数据转换部分,这里我所用的基准电压为5V,TLC549转换所得数据为8位,即0~255,那么电压计算公式如下:
V = data*5/256
这里不使用取模和求余运算,因为大家知道取模和求余运算会生成一个庞大的电路,运行速度慢,还浪费资源,FPGA所擅长的移位运算,无疑是不错的选择。
整数部分计算如下:
o_vol_int = (data*4+data)/255,转换成移位运算为((data<<2)+data)>>8
小数部分计算如下
o_vol_dec = ((5data&0xff)10)>>8