注明:虽然此实验来自于诸多入门教程,其中包括gao石的,但我个人觉得他的时序写的不好,没有完全遵循芯片的时序,它直接用AD——CLK的上升沿读取AD_DATA, 但是,事实上,芯片时序,并不是在8位全在上升沿读取。
8位串行模数转换器
8位开关电容逐次逼近的方法实现A/D
内部具有4MHz的系统时钟,转换速度小于17us
允许的最大转换速率是40000次/s(一个周期,采样下来,再读取。)
电源3V到6V
方便采用3线串行接口方式与各种微处理器进行连接
最高有效位(A7)在CS降低后自动置于数据输出总线上。剩下的7位(a6 - a0)在前7个I / O时钟的边缘上被锁定了。B7-B0遵循同样的方式。
根据NOTEs的意思,也就是DA_DATA遵循某种时序串行吐出data,那么,在何时才吐出的是DATA的哪一位呢?
这就要遵循一定的时序关系。
这种关系便是:
在第一个CLK的上升沿读取A7,第一个CLK到第六个CLK的下降沿读取A6——A0。
思路:状态机:3个状态
1:转换状态(也可视作初始状态)
2:1.4us(CS由高变低状态)
3:DA_CLK(采样状态)
//-----------------------------------------------------------------------------2018.11.6 23:00
//------------------------------------------------------------------------------- 明日再来写
系统时钟是4M,并非是采样时钟,注意采样在SAMPLE阶段,也就是后4个时钟周期进行的。外接时钟速率1.1M并不是指的采样速率,那是接口读取速率。串行芯片,我门需要注意的是一个周期,采样时间+转换时间=1/40_000 而在并行芯片中,往往是给的CLOCK即采样时钟。
下图是AD904芯片时序,在DATA_change阶段进行采样,并进行完成相关的量化。
下面的代码,串行TLC549,采样后,存储在8位寄存器当中
module AD_TLC549
#(
parameter T_conv=12'd850,//转换时间50M*17us=850
parameter T_su=8'd70,//准备时间50M*1.4us=70us
parameter AD_CLK_nop=4'd8,//定义8个AD_CLK
parameter AD_CLK_circle_time=6'd46,//0.92us
parameter AD_CLK_circle_time_half=6'd23 //23*50=0.46us
)
(
input sys_clk,
input RST_N,
input AD_DATA,
//-------------------
output AD_CLK,
output CS_N,
output reg [AD_CLK_nop-1:0] AD_out_DATA //串行的AD_DATA转并行的8位寄存器
);
wire AD_CLK_edg;
reg AD_out_reg_finish;//串行转并行完成标志
reg [11:0] T_conv_cnt=0;//转换时间的计数器
reg [7:0] T_su_cnt=0;//准备时间的计数器
reg [3:0] AD_CLK_cnt=0;//AD_CLK个数计数器
reg [5:0] AD_CLK_circle_time_cnt=0;//一个AD_CLK的高/低电平时间计数器
reg AD_CLK_reg_reg;//用于缓存一拍AD_CLK,实现下降沿检测
//设置输出寄存器
reg AD_CLK_reg=0;
reg CS_N_reg;
reg [AD_CLK_nop-1:0] AD_out_reg;
//采用三段式状态机
/*有限状态机,三段式建模风格*/
/*三个过程*/
//状态编码
localparam transition_state = 3'b001,//数据转换状态
ready_1_4_state = 3'b010,//准备采样状态
Acess_sample_state = 3'b100;//采样状态
//这里可以把数据转换状态视作初始状态,1没有时钟,2 CS_N为高,也无效。
reg [2:0] now_state=transition_state;
reg [2:0] next_state=transition_state;
//1.实现状态转换:实现当前状态now_state到next_state的转换
always @ (posedge sys_clk)
begin
if(!RST_N)
now_state<=transition_state;
else
now_state<=next_state;
end
//2.设置状态切换条件,产生下一个状态
always@(*)
begin
case(now_state)
transition_state:
if(T_conv_cnt==T_conv) //如果转换时间36
next_state=ready_1_4_state;
else
next_state=transition_state;
ready_1_4_state:
if(T_su_cnt==T_su)
next_state=Acess_sample_state;
else
next_state=ready_1_4_state;
Acess_sample_state:
if(AD_CLK_cnt<AD_CLK_nop)
next_state=Acess_sample_state;
else
next_state=transition_state;
default:;
endcase
end
//3.产生每个状态机的条件输出值
always @ (posedge sys_clk)
begin
case(next_state)
transition_state:
begin
T_conv_cnt<=T_conv_cnt+1;
T_su_cnt<=0;
AD_CLK_cnt<=0;
end
ready_1_4_state:
begin
T_su_cnt<=T_su_cnt+1;
T_conv_cnt<=0;
end
Acess_sample_state:
begin
if (AD_CLK_edg)
AD_CLK_cnt <= AD_CLK_cnt + 1'b1;
else
AD_CLK_cnt <= AD_CLK_cnt;
end
endcase
end
//4.设置每个输出值
//1.CS_N的输出值
always@(posedge sys_clk)
begin
if(!RST_N)
begin
CS_N_reg<=1; //
end
else if(now_state==transition_state)
begin
CS_N_reg<=1; //
end
else
begin
CS_N_reg<=0; //CS_N低电平有效
end
end
assign CS_N=CS_N_reg;
//2.AD_CLK的输出值
always@(posedge sys_clk)
begin
if(!RST_N)
AD_CLK_circle_time_cnt<=0;
else if(now_state==Acess_sample_state)
AD_CLK_circle_time_cnt<=(AD_CLK_circle_time_cnt<AD_CLK_circle_time-1)?(AD_CLK_circle_time_cnt+1'b1):0;
else
AD_CLK_circle_time_cnt<=0;
end
always@ (posedge sys_clk)
begin
if(!RST_N)
AD_CLK_reg<=0;
else if(now_state==Acess_sample_state)
AD_CLK_reg <= (AD_CLK_circle_time_cnt <=AD_CLK_circle_time_half-1) ? 1'b1 : 1'b0;
else
AD_CLK_reg<=0;
end
//3.AD_CLK_nop_edg控制
always @ (posedge sys_clk)
begin
AD_CLK_reg_reg<=AD_CLK_reg; //寄存一拍
end
assign AD_CLK_edg = AD_CLK_reg_reg&(~AD_CLK_reg);//判断下降沿
assign AD_CLK=AD_CLK_reg;
//4.AD_DATA_reg控制读取........控制在什么时候,把串行AD_DATA的数据写入多位的寄存器AD_DATA_reg之中,时机不对,自然写入的数肯定就不对
always @ (posedge sys_clk)
begin
if((now_state==ready_1_4_state)&(next_state==Acess_sample_state))
AD_out_reg[AD_CLK_nop-1]<=AD_DATA;
else if(AD_CLK_edg&(AD_CLK_cnt<=AD_CLK_nop-1))
AD_out_reg[AD_CLK_nop-1-1:0]<={AD_out_reg[AD_CLK_nop-1-1-1:0],AD_DATA};//移位循环实现串行转并行
else if(AD_CLK_cnt==AD_CLK_nop)//在第8个时钟下降沿的时候,代表了AD_DATA_reg已经移位完成,之所以没有刚好设置第7个下降沿,是担心第7个下降沿还在读取。
AD_out_reg_finish<=1;
else
begin
AD_out_reg<=AD_out_reg;
AD_out_reg_finish<=0;
end
end
always @ (posedge sys_clk)
begin
if(AD_out_reg_finish==1) //只有在读取8位完毕后,再将这8位数更新
AD_out_DATA<=AD_out_reg;
else
AD_out_DATA<=AD_out_DATA;
end
endmodule
// Copyright (C) 1991-2013 Altera Corporation
// Your use of Altera Corporation's design tools, logic functions
// and other software and tools, and its AMPP partner logic
// functions, and any output files from any of the foregoing
// (including device programming or simulation files), and any
// associated documentation or information are expressly subject
// to the terms and conditions of the Altera Program License
// Subscription Agreement, Altera MegaCore Function License
// Agreement, or other applicable license agreement, including,
// without limitation, that your use is for the sole purpose of
// programming logic devices manufactured by Altera and sold by
// Altera or its authorized distributors. Please refer to the
// applicable agreement for further details.
// *****************************************************************************
// This file contains a Verilog test bench template that is freely editable to
// suit user's needs .Comments are provided in each section to help the user
// fill out necessary details.
// *****************************************************************************
// Generated on "11/08/2018 16:19:41"
// Verilog Test Bench template for design : AD_TLC549
//
// Simulation tool : ModelSim (Verilog)
//
`timescale 1 ns/ 1 ps
module AD_TLC549_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg AD_DATA;
reg RST_N;
reg sys_clk;
// wires
wire AD_CLK;
wire [7:0] AD_out_DATA;
wire CS_N;
// assign statements (if any)
AD_TLC549 i1 (
// port map - connection between master ports and signals/registers
.AD_CLK(AD_CLK),
.AD_DATA(AD_DATA),
.AD_out_DATA(AD_out_DATA),
.CS_N(CS_N),
.RST_N(RST_N),
.sys_clk(sys_clk)
);
initial
begin
$display("Running testbench");
#0 sys_clk=0;
#0 RST_N=0;
#40 RST_N=1;
#0 AD_DATA=1;
end
//------------------------------------------
always
begin
#10 sys_clk=~sys_clk;
end
endmodule
在第8个AD_clk过后才更新AD_out_DATA
整体效果:
符合AD_CLK 一个周期0.92us,半个周期长度也符合,检查了
符合1.4us
以下是工程连接:
https://download.csdn.net/download/ciscomonkey/10773104