ref
TLC549引脚图
REF+:正基准电压输入 2.5V≤REF+≤Vcc+0.1。
REF-:负基准电压输入端,-0.1V≤REF-≤2.5V。且要求:(REF+)-(REF-)≥1V。
VCC:系统电源3V≤Vcc≤6V。
GND:接地端。
/CS:芯片选择输入端,要求输入高电平 VIN≥2V,输入低电平 VIN≤0.8V。
DATA OUT:转换结果数据串行输出端,与 TTL 电平兼容,输出时高位在前,低位在后。
ANALOGIN:模拟信号输入端,0≤ANALOGIN≤Vcc,当 ANALOGIN≥REF+电压时,转换结果为全"1"(0FFH),ANALOGIN≤REF-电压时,转换结果为全"0"(00H)。
I/O CLOCK:外接输入/输出时钟输入端,同于同步芯片的输入输出操作,无需与芯片内部系统时钟同步。
https://www.cnblogs.com/yuphone/archive/2011/04/24/2026377.html
`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
//
显示在数码管上
//程序实现的功能:将测得模拟电压(0-255)转化数字电压显示在数码管上;
module adc_tlc549 //顶层模块;
(
clk,
rst_n,
analog_data,
adc_cs_n,
adc_clk,
smg_sel,
smg_data
);
input clk; //系统时钟,50MHZ;
input rst_n; //复位信号,低电平有效;
input analog_data; //模拟数据;
output adc_cs_n; //ADC_TLC549片选信号,低电平有效;
output adc_clk; //ADC_TLC549 I/O时钟,最大不超过1.1MHZ;
output[1:0]smg_sel; //数码管片选;
output[7:0]smg_data; //数码管段选;
wire adc_en; //增加了adc_en使能信号来决定是否需要开启AD转换,当一个系统内部有多个模块时
assign adc_en=1'b1; //每一个模块都是可控制的,禁止使能,模块内部停止工作,此时功耗最低;
wire[3:0] vol_int; //从A/D转换芯片输出的电压值的整数部分;
wire[3:0] vol_dec; //从A/D转换芯片输出的电压值的小数部分;
adc_ctl adc_ctl //ADC控制模块
(
.clk(clk),
.rst_n(rst_n),
.analog_data(analog_data),
.adc_en(adc_en),
.adc_cs_n(adc_cs_n),
.adc_clk(adc_clk),
.vol_int(vol_int),
.vol_dec(vol_dec)
);
smg smg //数码管模块
(
.clk(clk),
.rst_n(rst_n),
.data_1(vol_int),
.data_2(vol_dec),
.smg_sel(smg_sel),
.smg_data(smg_data)
);
endmodule
module adc_ctl //ADC控制模块
(
clk,
rst_n,
analog_data,
adc_en,
adc_cs_n,
adc_clk,
vol_int,
vol_dec
);
input clk; //系统时钟,50MHZ;
input rst_n; //复位信号,低电平有效;
input analog_data; //模拟数据;
input adc_en; //ADC使能信号;
output adc_cs_n; //ADC_TLC549片选信号,低电平有效;
output adc_clk; //ADC_TLC549 I/O时钟,最大不超过1.1MHZ;
output[3:0]vol_int; //从A/D转换芯片输出的电压值的整数部分;
output[3:0]vol_dec; //从A/D转换芯片输出的电压值的小数部分;
//1.1MHZ(909ns) 909ns/20ns=46=0x2E, 0x2E/2=0X17;
//17us 17000ns/20ns=850=0x352;
parameter ADC_CLK_CNT=6'h2e;
parameter ADC_CLK_HALF=6'h17;
parameter IDLE= 2'b00; // 状态机的初始状态;
parameter READY= 2'b01; // adc_cs_n有效时到adc_clk到第一个时钟到来时的状态,至少要1.4us;
parameter READ= 2'b10; //是读ADC转化后数据的状态,8个时钟的上升沿逐位移入ADC数据线上的8位串行数据;
parameter WAIT_CONV=2'b11; //是ADC正在转化数据需要消耗的时间,是17us;
reg[1:0] cstate;
reg[1:0] nstate;
reg[5:0] time_cnt;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
time_cnt<=6'd0;
else if(!adc_en)
time_cnt<=6'd0;
else if(time_cnt==ADC_CLK_CNT)
time_cnt<=6'd0;
else
time_cnt<=time_cnt+1'b1;
end
reg [5:0] bit_cnt;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
bit_cnt<=6'd0;
else if(!adc_en)
bit_cnt<=6'd0;
else if(cstate!=nstate)
bit_cnt<=6'd0;
else if(time_cnt==ADC_CLK_HALF)
bit_cnt<=bit_cnt+1'b1;
end
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
cstate<=IDLE;
else
cstate<=nstate;
end
always @ (*)
begin
case(cstate)
IDLE :
if((bit_cnt==6'h2)&&(time_cnt==ADC_CLK_CNT)&&(adc_en))
nstate=READY;
else
nstate=IDLE;
READY :
if((bit_cnt==6'h1)&&(time_cnt==ADC_CLK_CNT)) //延时大约1.4us;
nstate=READ;
else
nstate=READY;
READ :
if((bit_cnt==6'h8)&&(time_cnt==ADC_CLK_CNT))
nstate=WAIT_CONV;
else
nstate=READ;
WAIT_CONV :
if((bit_cnt==6'h12)&&(time_cnt==ADC_CLK_CNT))
nstate=READY;
else
nstate=WAIT_CONV;
default : nstate=IDLE;
endcase
end
reg adc_clk_r;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
adc_clk_r<=1'b0;
else if(cstate!=READ)
adc_clk_r<=1'b0;
else if(time_cnt==ADC_CLK_HALF)
adc_clk_r<=1'b1;
else if(time_cnt==ADC_CLK_CNT)
adc_clk_r<=1'b0;
end
assign adc_clk=adc_clk_r;
reg adc_cs_n_r;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
adc_cs_n_r<=1'b0;
else if(!adc_en)
adc_cs_n_r<=1'b1;
else if((cstate==READY)||(cstate==READ))
adc_cs_n_r<=1'b0;
else
adc_cs_n_r<=1'b1;
end
assign adc_cs_n=adc_cs_n_r;
// 脉冲边沿检测
reg adc_clk_1;
reg adc_clk_2;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
adc_clk_1<=1'b0;
adc_clk_2<=1'b0;
end
else
begin
adc_clk_1<=adc_clk;
adc_clk_2<=adc_clk_1;
end
end
wire pos_clk;
assign pos_clk=adc_clk_1&(~adc_clk_2) ; //脉冲上升沿边沿检测;
reg[7:0] adc_data_r ;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
adc_data_r<=8'd0;
else if((cstate==READ)&&(pos_clk))
adc_data_r={adc_data_r[6:0],analog_data};
end
reg[7:0] adc_data;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
adc_data<=8'd0;
else if(cstate==WAIT_CONV)
adc_data<=adc_data_r;
end
//将读到数字(0-255)转化成电压信号;
//adc_data/ff=voltage/5;
//voltage=5*adc_data/ff;
//整数部分: =((adc_data<<2)+adc_data)>>8;
//小数部分: =((5adc_data&&0xff)*10)>>8;
wire[11:0] num_1;
assign num_1={2'h0,adc_data[7:0],2'h0}+{4'h0,adc_data[7:0]}; //5adc_data;
wire[11:0] num_2;
assign num_2={1'h0,num_1[7:0],3'h0}+{3'h0,num_1[7:0],1'h0}; //*10;
assign vol_int=num_1[11:8]; //整数部分
assign vol_dec=num_2[11:8]; //小数部分
endmodule
module smg //数码管模块
(
clk,
rst_n,
data_1,
data_2,
smg_sel,
smg_data
);
input clk ;
input rst_n;
input[3:0] data_1;
input[3:0] data_2;
output[1:0] smg_sel;
output[7:0] smg_data;
parameter scan_num=16'hc350; //1ms;
reg[15:0] cnt;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt<=16'd0;
else if(cnt==scan_num)
cnt<=16'd0;
else
cnt<=cnt+1'b1;
end
reg smg_sel_num;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
smg_sel_num<=1'b0;
else if(cnt==scan_num)
smg_sel_num<=smg_sel_num+1'b1;
end
reg[1:0] smg_sel_r;
always @(*) //数码管片选;
begin
case(smg_sel_num)
1'b0 : smg_sel_r=2'b01;
1'b1 : smg_sel_r=2'b10;
default: smg_sel_r=2'b11;
endcase
end
assign smg_sel=smg_sel_r;
reg[3:0] smg_data_num; //数码管要显示的数;
always @(*)
begin
case(smg_sel_num)
1'b0 : smg_data_num=data_1;
1'b1 : smg_data_num=data_2;
default : smg_data_num=4'h0;
endcase
end
reg[7:0] smg_data_r;
always @(*) //是否要显示小数点
begin
case(smg_sel_num)
1'b0 : smg_data_r[7]=1'b0;
1'b1 : smg_data_r[7]=1'b1;
default: smg_data_r[7]=1'b1;
endcase
end
always @ (*)
begin
case(smg_data_num)
4'h0 :smg_data_r[6:0]=7'b100_0000;
4'h1: smg_data_r[6:0]=7'b111_1001;
4'h2: smg_data_r[6:0]=7'b010_0100;
4'h3: smg_data_r[6:0]=7'b011_0000;
4'h4: smg_data_r[6:0]=7'b001_1001;
4'h5: smg_data_r[6:0]=7'b001_0010;
4'h6: smg_data_r[6:0]=7'b000_0010;
4'h7: smg_data_r[6:0]=7'b111_1000;
4'h8: smg_data_r[6:0]=7'b000_0000;
4'h9: smg_data_r[6:0]=7'b001_0000;
default:smg_data_r[6:0]=7'b111_1111;
endcase
end
assign smg_data=smg_data_r;
endmodule
https://www.cnblogs.com/yuphone/archive/2011/04/24/2026377.html