ADC芯片TLC549的Verilog HDL

1.TLC549简介

TLC549是美国德州仪器公司生产的8位串行A/D转换器芯片,可与通用微处理器、控制器通过CLK、CS、DATAOUT三条口线进行串行接口。具有4MHz片内系统时钟和软、硬件控制电路,转换时间最长17μs, TLC549为40000次/s。总失调误差最大为±0.5LSB,典型功耗值为6mW。采用差分参考电压高阻输入,抗干扰,可按比例量程校准转换范围,VREF-接地,VREF+-VREF-≥1V,可用于较小信号的采样。

芯片顶视图.png

REF+:正基准信号输入端

ANALOG IN:模拟信号输入端

REF-:负基准电压输入端

GND:接地端

#CS:片选信号,低电平有效

DATA OUT:转换结果串行输出端

I/O CLOCK:外接时钟输入端

VCC:电源输入端

其他信息可参照TLC549的datasheet。

2.AD通信协议

TLC549时序图.png

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容