ADC芯片TLC549的Verilog HDL

   1.TLC549简介

  TLC549是美国德州仪器公司生产的8位串行A/D转换器芯片,可与通用微处理器、控制器通过CLK、CS、DATA OUT三条口线进行串行接口。具有4MHz片内系统时钟和软、硬件控制电路,转换时间最长17μs, TLC549为40 000次/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时序图

  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 = ((5*data&0xff)*10)>>8




 

 

  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
cyclone4 FPGA读写8位AD转换TLC549实验Verilog逻辑源码Quartus11.0工程文件, FPGA型号为EP4CE6E22C8,可以做为你的学习设计参考。 ( clk, //系统50MHZ时钟 adc_sclk, //AD TLC549的时钟 data, //AD TLC549的数据口 cs, //AD TLC549的片选择 wei, //数码管的为选择 duan //数码管的7段码 ); input clk; input data; output cs; output adc_sclk; output[7:0] duan; output[3:0] wei; reg cs,adc_sclk,clk1k,clk1ms; reg[15:0] count; reg[24:0] count1ms; reg[3:0] cnt; reg[2:0] number; reg[1:0] state; reg[3:0] wei; reg ledcs; reg [7:0] duan; reg[7:0] dataout; reg[16:0] tenvalue; parameter sample=2'b00, display=2'b01; /**********产生100k的采集时钟信号*********/ always@(posedge clk) begin if(count<=250) count<=count+1'b1; else begin count<=0; adc_sclk<=~adc_sclk; end end /*******产生周期为1ms即1kHz的信号*********/ always@(posedge clk) begin if(count1ms>25'd25000) begin clk1ms<=~clk1ms; count1ms<=0; end else count1ms<=count1ms+1; end /*********AD采样程序**************/ always@(negedge adc_sclk) begin case(state) sample: begin cs<=0; dataout[7:0]<={dataout[6:0],data}; if(cnt>4'd7) begin cnt<=0; state<=display; end else begin cnt<=cnt+1; state<=sample; end end display: begin cs<=1;//关AD片选 tenvalue<=(tendata((dataout>>4)&8'b0000_1111)*16+ tendata(dataout&8'b0000_1111))*129;// //得到采集的数据 state<=sample; end default: state<=display; endcase end /***********2进制转十进制函数*************/ function[7:0] tendata;//返回一个4位的数字 input[7:0] datain; begin case(datain) 4'b00000000: tendata=4'd0;//0 4'b00000001: te

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值