学习笔记:FPGA学习之AD通信协议

一,AD控制原理

https://baike.baidu.com/item/%E6%A8%A1%E6%95%B0%E8%BD%AC%E6%8D%A2/94393

二,AD通信协议

在AD TLC549中,vcc引脚是 系统电源输入端,I/O CLOCK引脚是外接时钟输入端,DATA OUT是转换结果数据串行输出端,CS引脚是片选信号,GND是接地端,REF+引脚是正基准电压输入端,ANALOGIN引脚模拟信号输入端,REF-引脚是负基准电压输入端。

 在TCL549内部具有4MHZ的系统时钟,并且该时钟与I/O CLOCK是独立工作的,所以无需特殊的速度或相位匹配。

从时序图中,我们知道:

(1)当CS为高时,转换结果数据串行输出端DATA OUT处于高阻状态,此时I/O CLOCK也不起作用。

(2)当CS为低时,AD前一次转换的数据A的最高位A7立即出现在数据线DATA OUT上,其余的7位数据在I/O CLOCK的下降沿一次由时钟同步输出。因此,可在I/O CLOCK的上升沿读取数据;这里需注意:一:当CS变为低电平到I/O CLOCK第一个时钟到来至少要1.4us。二:I/O CLOCK不能超过1.1MHZ。

(3)读取8位数据后,AD开始转换这一次转换的采样数据B,以便下一次读取。转换时片选信号CS必须置高电平,每次转换的时间不超过17us,开始于CS变为低电平后I/O clock的第8个下降沿,没有转换完成标志信号;也没有启动控制端,只要读取前一次数据后马上就可以开始新的AD转换,转换完成后就进入保持状态。

三,AD实际应用

(1)在数码管上显示ANALOGIN引脚输入的电压。

(2)功能模块

(3)verilog代码

`define AD_CLK_TIME			10'd45	//1.1M, 909ns,909 / (1 / 50M) = 45 =0x2D
`define AD_CLK_TIME_HALF	10'd22	//909ns / 2 = 454.5ns 45 / 2 = 22	

module Ad_Module
(	
	//Input
	CLK_50M,RST_N,
	//Output
	AD_CS,AD_CLK,AD_DATA,o_vol_int,o_vol_dec
);
	
//---------------------------------------------------------------------------
//--	外部端口声明
//---------------------------------------------------------------------------
input					CLK_50M;				//时钟的端口,开发板用的50M晶振
input					RST_N;				//复位的端口,低电平复位
input					AD_DATA;				//AD数据端口
output				AD_CS;				//AD片选端口
output				AD_CLK;				//AD时钟端口,最大不超过1.1MHz
output	[ 3:0]	o_vol_int;			//从A/D转换芯片输出的电压值的整数部分;
output	[ 3:0]	o_vol_dec;			//从A/D转换芯片输出的电压值的小数部分;

//---------------------------------------------------------------------------
//--	内部端口声明
//---------------------------------------------------------------------------
reg					AD_CS;				//AD片选信号端口
reg					AD_CS_N;				//AD_CS的下一个状态
reg					AD_CLK;				//AD时钟,最大不超过1.1MHz
reg					AD_CLK_N;			//AD_CLK的下一个状态

reg		[ 2:0]	ad_fsm_cs;			//状态机的当前状态
reg		[ 2:0]	ad_fsm_ns;			//状态机的下一个状态

reg		[ 3:0]	o_vol_int;			//从A/D转换芯片输出的电压值的整数部分;
wire		[ 3:0]	o_vol_int_n;		//o_vol_int的下一个状态
reg		[ 3:0]	o_vol_dec;			//从A/D转换芯片输出的电压值的小数部分;
wire		[ 3:0]	o_vol_dec_n;		//o_vol_dec的下一个状态

reg		[ 5:0]	time_cnt;			//用于记录一个时钟所用时间的定时器
reg		[ 5:0]	time_cnt_n;			//time_cnt的下一个状态
reg		[ 5:0]	bit_cnt;				//用来记录时钟周期个数的计数器
reg		[ 5:0]	bit_cnt_n;			//bit_cnt的下一个状态

reg		[ 7:0]	data_out;			//用来保存稳定的AD数据
reg		[ 7:0]	data_out_n;			//data_out的下一个状态
reg		[ 7:0]	ad_data_reg;		//用于保存数据的移位寄存器
reg		[ 7:0]	ad_data_reg_n;		//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;	//结束的状态

//---------------------------------------------------------------------------
//--	逻辑功能实现	
//---------------------------------------------------------------------------
//时序电路,用来给ad_fsm_cs寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		ad_fsm_cs <= 1'b0;				//初始化ad_fsm_cs值
	else
		ad_fsm_cs <= ad_fsm_ns;			//用来给ad_fsm_ns赋值
end

//组合电路,用来实现状态机
always @ (*)
begin
	case(ad_fsm_cs)						//判断状态机的当前状态
		FSM_IDLE:
												//3 x 0.909us = 2.727us用于初始化延时 
			if((bit_cnt == 6'd3 ) && (time_cnt == `AD_CLK_TIME))
				ad_fsm_ns = FSM_READY;	//如果空闲状态完成就进入延时状态
			else
				ad_fsm_ns = ad_fsm_cs;	//否则保持原状态不变
		FSM_READY:
												//2 x 0.909us = 1.818us用于延迟1.4us
			if((bit_cnt == 6'd2 ) && (time_cnt == `AD_CLK_TIME))
				ad_fsm_ns = FSM_DATA;	//如果延时状态完成就进入读取数据状态
			else
				ad_fsm_ns = ad_fsm_cs;  //否则保持原状态不变 
		FSM_DATA:
												//读取数据8位,1~8个时钟脉冲
			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:
												//19 x 0.909us = 17.271us用于延迟17us
			if((bit_cnt == 6'd19) && (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

//时序电路,用来给time_cnt寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		time_cnt <= 6'h0;					//初始化time_cnt值
	else
		time_cnt <= time_cnt_n;			//用来给time_cnt赋值
end

//组合电路,实现0.909us的定时计数器
always @ (*)
begin
	if(time_cnt == `AD_CLK_TIME)		//判断0.909us时间
		time_cnt_n = 6'h0;				//如果到达0.909us,定时器清零
	else
		time_cnt_n = time_cnt + 6'h1;	//如果未到0.909us,定时器继续加1
end

//时序电路,用来给bit_cnt寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		bit_cnt <= 6'h0;					//初始化bit_cnt值
	else
		bit_cnt <= bit_cnt_n;			//用来给bit_cnt赋值
end

//组合电路,用来记录时钟周期个数的计数器
always @ (*)
begin
	if(ad_fsm_cs != ad_fsm_ns)			//判断状态机的当前状态
		bit_cnt_n = 6'h0;					//如果当前的状态不等于下一个状态,计时器就清零
	else if(time_cnt == `AD_CLK_TIME_HALF)//判断0.4545us时间
		bit_cnt_n = bit_cnt + 6'h1;	//如果到达0.4545us,计数器就加1
	else
		bit_cnt_n = bit_cnt;				//否则计数器保持不变
end

//时序电路,用来给AD_CLK寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		AD_CLK <= 1'h0;					//初始化AD_CLK值
	else
		AD_CLK <= AD_CLK_N;				//用来给AD_CLK赋值
end

//组合电路,用来生成AD的时钟波形
always @ (*)
begin
	if(ad_fsm_cs != FSM_DATA)			//判断状态机的当前状态
		AD_CLK_N = 1'h0;					//如果当前的状态不等于读取数据状态,AD_CLK_N就置0
	else if(time_cnt == `AD_CLK_TIME_HALF)//判断0.4545us时间
		AD_CLK_N = 1'h1;					//如果到达0.4545us,ADC_CLK_N就置1
	else if(time_cnt == `AD_CLK_TIME)//判断0.909us时间
		AD_CLK_N = 1'h0;					//如果到达0.909us,AD_CLK_N就置0
	else
		AD_CLK_N = AD_CLK;				//否则保持不变
end

//时序电路,用来给AD_CS寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		AD_CS <= 1'h0;						//初始化AD_CS值
	else
		AD_CS <= AD_CS_N;					//用来给AD_CS赋值
end

//组合电路,用来生成AD的片选波形
always @ (*)
begin
	if((ad_fsm_cs == FSM_DATA) || (ad_fsm_cs == FSM_READY))//判断状态机的当前状态
		AD_CS_N = 1'h0;//如果当前的状态等于读取数据状态或等于延时1.4us状态,AD_CS_N就置0
	else
		AD_CS_N = 1'h1;//如果当前的状态不等于读取数据状态或不等于延时1.4us状态,AD_CS_N就置1
end

//时序电路,用来给ad_data_reg寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		ad_data_reg <= 8'h0;				//初始化ad_data_reg值
	else
		ad_data_reg <= ad_data_reg_n;	//用来给ad_data_reg赋值
end

//组合电路,将AD线上的数据保存到移位寄存器中
always @(*)
begin
	if((ad_fsm_cs == FSM_DATA) && (!AD_CLK) && (AD_CLK_N))//判断每一个时钟的上升沿
		ad_data_reg_n = {ad_data_reg[6:0],AD_DATA};//将数据存入移位寄存器中,高位优先
	else
		ad_data_reg_n = ad_data_reg;	//否则保持不变
end

//时序电路,用来给data_out寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		data_out <= 8'h0;					//初始化data_out值
	else
		data_out <= data_out_n;			//用来给data_out赋值
end

//组合电路,将移位寄存器中的数据存入data_out中,可用于输出
always @ (*)
begin
	if(ad_fsm_cs == FSM_END)			//判断复位
		data_out_n = ad_data_reg;		//初始化data_out值
	else
		data_out_n = data_out;			//用来给data_out赋值
end

//时序电路,用来给o_vol_int寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		o_vol_int <= 4'h0;				//初始化o_vol_int值
	else
		o_vol_int <= o_vol_int_n;		//用来给o_vol_int赋值
end

//时序电路,用来给o_vol_dec寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)								//判断复位
		o_vol_dec <= 4'h0;				//初始化o_vol_dec值
	else
		o_vol_dec <= o_vol_dec_n;		//用来给o_vol_dec赋值
end

//将AD的8位数据转换成数码管显示电压
assign o_vol_int_n = mid1[11:8];//整数部分
assign o_vol_dec_n = mid2[11:8];//小数部分

// AD的电压计算公式:AD_DATA / FF = voltage / 5 (5V为ADC的参考电压)
// voltage = 5 * AD_DATA / FF
// voltage = 5 * AD_DATA >> 8

// voltage(整数部分) = ((AD_DATA << 2) + AD_DATA) >> 8
assign mid1 = {2'h0,data_out[7:0],2'h0} + {4'h0,data_out[7:0] };
// voltage(小数部分) = ((5 * AD_DATA & 0XFF) * 10 ) >> 8
assign mid2 = {1'h0,  mid1[7:0],3'h0} + {3'h0,  mid1[7:0],1'h0};

endmodule

猜你喜欢

转载自blog.csdn.net/Archar_Saber/article/details/82530485