原创 两段式DS1820 verilog的实现

2011-7-24 22:28 788 0 分类: FPGA/CPLD

 

写DS1820的时候发现网上的都是一段式状态机,就想写一个两段式的,可是花了一个星期才写好,编译的时候总是有组合环合锁存器的警告,怎么都去不掉,一直在想是不是两段式根本写不了,都快放弃了,可是书上说没有什么是两段式写不了的。

    搞了好久,后来看了特权的《两段式不可能完成的状态机》,才知道问题出在哪,就是敏感变量不能出现作为输出出现在第二段的always段中,也不能改变其值,如果要改变,必须设一个中间变量,最后在板子上调通了,下来是代码,我的注释应该还算是详细吧,我是菜鸟,有问题帮我指出来吧.

 

module DS1820_ctrl (
CLK,RSTn,
DQ,
readtemp,
clk1us
);


input CLK;
//50MHz
input RSTn; //复位信号,低有效

inout DQ;
//一线数据信号
output[7:0] readtemp; //读出的温度并送给7段数码管
output clk1us;



reg[
7:0] counter_1us;
reg clk_1us;

parameter DELAY_1US
= 49;
parameter DELAY_800MS
= 800000;
parameter DELAY_1MS
= 1000;
parameter DELAY_2MS
= 2000;
parameter DELAY_10MS
= 10000;
parameter DELAY_20MS
= 20000;
parameter DELAY_30MS
= 30000;
parameter DELAY_40MS
= 40000;
parameter DELAY_50MS
= 50000;
parameter DELAY_100MS
= 100000;
parameter DELAY_200MS
= 200000;
parameter DELAY_300MS
= 300000;
parameter DELAY_400MS
= 400000;
parameter DELAY_500MS
= 500000;
parameter DELAY_600MS
= 600000;
parameter DELAY_1S
= 1000000;
parameter DELAY_2S
= 2000000;
parameter DELAY_10S
= 10000000;
parameter DELAY_100US
= 100;
parameter DELAY_200US
= 200;
parameter DELAY_300US
= 300;
parameter DELAY_400US
= 400;
parameter DELAY_500US
= 500;
parameter DELAY_600US
= 600;
parameter DELAY_550US
= 550;
parameter DELAY_2US
= 2;
parameter DELAY_4US
= 4;
parameter DELAY_6US
= 6;
parameter DELAY_8US
= 8;
parameter DELAY_12US
= 12;
parameter DELAY_15US
= 15;
parameter DELAY_20US
= 20;
parameter DELAY_25US
= 25;
parameter DELAY_30US
= 30;
parameter DELAY_40US
= 40;
parameter DELAY_70US
= 70;
parameter DELAY_850US
= 850;

always@(posedge CLK or negedge RSTn) begin
if(!RSTn) counter_1us<=8'd0;
else if(counter_1us==DELAY_1US) begin
counter_1us
<=8'd0;
clk_1us<=1'b1;
end
else begin
counter_1us
<=counter_1us+1'b1;
clk_1us<=1'b0;
end
end


reg[
23:0] counter; //
reg[23:0] counter1;
reg clk1us_start;

always@(posedge CLK or negedge RSTn) begin
if(!RSTn) counter<=24'd0;
else if(!clk1us_start) begin
counter
<=24'd0; //只要允许位置0则counter归0
end
else if(clk_1us) begin
counter
<=counter1+1'b1;
end
end


parameter CMD_SKIP
= 8'b1100_1100; //
parameter CMD_READ = 8'b1011_1110;
parameter CMD_CONVERT = 8'b0100_0100;

parameter RESET1
= 3'b000;
parameter WRITE_BYTE1 = 3'b001;
parameter WAIT_CONVERT = 3'b010;
parameter RESET2 = 3'b011;
parameter WRITE_BYTE2 = 3'b100;
parameter READ_BYTE = 3'b101;
parameter WAIT1S = 3'b110;

//第一段:状态转移

reg[
2:0] cstate; //current state
reg[2:0] nstate; //next state

reg[
7:0] temp_data; //
reg[3:0] data_bit;

//reg[7:0] read_temp; //
//reg[7:0] read_temp1; //

reg[
7:0] temp_data1; //
reg[3:0] data_bit1;

reg dq_ctrl;
//dq_ctrl=1'b1;output dq_ctrl=1'b0;high Z
reg dq_r;

reg dq_ctrl1;
//dq_ctrl=1'b1;output dq_ctrl=1'b0;high Z
reg dq_r1;

always @ (posedge CLK or negedge RSTn)
//异步复位
if(!RSTn) begin
cstate
<= RESET1;
end
else begin
cstate
<= nstate;//注意,使用的是非阻塞赋值
temp_data<=temp_data1;
data_bit
<=data_bit1;
dq_ctrl
<=dq_ctrl1;
dq_r
<=dq_r1;
end


//第二段;状态逻辑判断
always@(cstate or DQ or counter or data_bit or temp_data or dq_r or dq_ctrl) begin //任何一个敏感变量变化都引起变化才不会产生锁存器
nstate=cstate;
temp_data1
=temp_data;
data_bit1
=data_bit;
dq_ctrl1
=dq_ctrl;
dq_r1
=dq_r;
counter1
=counter;
case(cstate)
//=========================================================
/*主机拉低DQ到0至少480US,然后释放DQ,这时DQ会被上拉电阻从0上拉到1,一旦DS1820检测到
DQ引脚从0->1这个上升沿,DS1820就会等待15-60US然后发送应答脉冲(一个60-240US的低电平)
*/
RESET1: begin
clk1us_start
=1'b1; //启动1us时钟计数
case(counter)
DELAY_2US: begin
dq_ctrl1
=1'b1; //set to output
dq_r1=1'b0; //pull down to 0 for at least 480us
nstate=RESET1;
end
DELAY_500US: begin
//拉低数据线至少480US,这里设为500US
dq_ctrl1=1'b0; //enable input
dq_r1=1'b1;
nstate=RESET1;
end
DELAY_550US: begin
//DS1820在0->1上升沿等待15-60US再应答,这里设为50US
if(DQ==1'b1) begin
nstate=WAIT1S;//如果接收到的数据线电平是1,则复位错误
//nstate=RESET1;
end
else if(DQ==1'b0) begin
nstate=RESET1;
end
end
DELAY_850US: begin
//收到应答信号后等待270US才进入发数据状态
temp_data1=CMD_SKIP;
nstate
=WRITE_BYTE1;
clk1us_start
=1'b0; //停止1us时钟计数
dq_ctrl1=1'b1; //set to output
dq_r1=1'b1;
data_bit1=0;
end
default: nstate=RESET1;
endcase
end
//=========================================================
//写1要先拉低1-15us,写0要拉低一直到写0结束,每一位的1到0作为读周期的开始
//写0写1都要保持60us
//2个写时序之间至少1us恢复期
//主机把数据线从1拉到0告诉DS1820写数据开始
WRITE_BYTE1: begin
clk1us_start
=1'b1; //启动1us时钟计数
case(counter)
DELAY_20US: begin
dq_r1
=1'b0; //不论写0写1都要拉低数据
nstate=WRITE_BYTE1;
end
DELAY_30US: begin
case(data_bit)
4'd0: dq_r1=temp_data[0]; //from LSB
4'd1: dq_r1=temp_data[1];
4'd2: dq_r1=temp_data[2];
4'd3: dq_r1=temp_data[3];
4'd4: dq_r1=temp_data[4];
4'd5: dq_r1=temp_data[5];
4'd6: dq_r1=temp_data[6];
4'd7: dq_r1=temp_data[7];
default: ;
endcase
nstate
=WRITE_BYTE1;
end
DELAY_100US: begin
//写0写1都要保持60us
clk1us_start=1'b0;//counter清0
if(data_bit<7) begin
data_bit1
=data_bit+1'b1;
dq_r1=1'b1; //写完1位拉高数据
nstate=WRITE_BYTE1;
end
else if(temp_data==CMD_SKIP) begin
data_bit1
=0;
dq_r1
=1'b1; //写完1位拉高数据,第7位虽然写完,但在循环语句中没有拉高
temp_data1=CMD_CONVERT;
nstate
=WRITE_BYTE1;
end
else if(temp_data==CMD_CONVERT) begin
data_bit1
=0;
dq_r1
=1'b1; //写完1位拉高数据,第7位虽然写完,但在循环语句中没有拉高
nstate=WAIT_CONVERT;
end
end
default: nstate=WRITE_BYTE1;
endcase
end
//=========================================================
WAIT_CONVERT: begin
clk1us_start
=1'b1; //启动1us时钟计数
if(counter==DELAY_800MS) begin
clk1us_start
=1'b0;
nstate=RESET2;
end
else begin
nstate
=WAIT_CONVERT;
end
end
//=========================================================
RESET2: begin
clk1us_start
=1'b1; //启动1us时钟计数
case(counter)
DELAY_2US: begin
dq_ctrl1
=1'b1; //set to output
dq_r1=1'b0; //pull down to 0 for at least 480us
nstate=RESET2;
end
DELAY_500US: begin
//拉低数据线至少480US,这里设为500US
dq_ctrl1=1'b0; //set to input
nstate=RESET2;
end
DELAY_550US: begin
//DS1820在0->1上升沿等待15-60US再应答,这里设为50US
if(DQ==1'b1) begin
nstate=WAIT1S;//如果接收到的数据线电平是1,则复位错误
//nstate=RESET2;
end
else if(DQ==1'b0) begin
nstate=RESET2;
end
end
DELAY_850US: begin
//收到应答信号后等待270US才进入发数据状态
temp_data1=CMD_SKIP;
nstate
=WRITE_BYTE2;
clk1us_start
=1'b0; //停止1us时钟计数
dq_ctrl1=1'b1; //set to output
dq_r1=1'b1;
data_bit1=0;
end
default: nstate=RESET2;
endcase
end
//=========================================================
WRITE_BYTE2: begin
clk1us_start
=1'b1; //启动1us时钟计数
case(counter)
DELAY_20US: begin
dq_r1
=1'b0; //不论写0写1都要拉低数据
nstate=WRITE_BYTE2;
end
DELAY_30US: begin
case(data_bit)
4'd0: dq_r1=temp_data[0]; //from LSB
4'd1: dq_r1=temp_data[1];
4'd2: dq_r1=temp_data[2];
4'd3: dq_r1=temp_data[3];
4'd4: dq_r1=temp_data[4];
4'd5: dq_r1=temp_data[5];
4'd6: dq_r1=temp_data[6];
4'd7: dq_r1=temp_data[7];
default: ;
endcase
nstate
=WRITE_BYTE2;
end
DELAY_100US: begin
//写0写1都要保持60us
//DELAY_6US: begin //写0写1都要保持60us
clk1us_start=1'b0;//counter清0
if(data_bit<7) begin
data_bit1
=data_bit+1'b1;
dq_r1=1'b1; //写完1位拉高数据
nstate=WRITE_BYTE2;
end
else if(temp_data==CMD_SKIP) begin
data_bit1
=0;
dq_r1
=1'b1; //写完1位拉高数据
temp_data1=CMD_READ;
nstate
=WRITE_BYTE2;
end
else if(temp_data==CMD_READ) begin
data_bit1
=0;
dq_r1
=1'b1; //写完1位拉高数据
nstate=READ_BYTE;
end
end
default: nstate=WRITE_BYTE2;
endcase
end

//=========================================================
//每一位的1到0作为读周期的开始
//读完1位数至少要延迟60us
//要右移9位
READ_BYTE: begin
clk1us_start
=1'b1; //启动1us时钟计数
case(counter)
DELAY_12US: begin
dq_r1
=1'b0; //不论写0写1都要拉低数据
nstate=READ_BYTE;
end
DELAY_15US: begin
//主机拉低数据后至少要保持1US的低电平
dq_ctrl1=1'b0;//set to high Z
nstate=READ_BYTE;
end
DELAY_25US: begin
//DS1820在主机拉低后开始的15US内都保持数据有效
case(data_bit)
//4'd0: temp_data1[0]=DQ; //from LSB
4'd1: temp_data1[0]=DQ;
4'd2: temp_data1[1]=DQ;
4'd3: temp_data1[2]=DQ;
4'd4: temp_data1[3]=DQ;
4'd5: temp_data1[4]=DQ;
4'd6: temp_data1[5]=DQ;
4'd7: temp_data1[6]=DQ;
4'd8: temp_data1[7]=DQ;
default: ;
endcase
nstate
=READ_BYTE;
end
DELAY_100US: begin
if(data_bit<8) begin
data_bit1
=data_bit+1'b1;
dq_ctrl1=1'b1; //set to output
clk1us_start=1'b0;//counter清0,开始下一位
nstate=READ_BYTE;
end
else begin
data_bit1
=0;
dq_ctrl1
=1'b1; //set to output
dq_r1=1'b1;
clk1us_start=1'b0;
nstate=WAIT1S;
end
end
default: nstate=READ_BYTE;
endcase
end
//=========================================================
WAIT1S: begin
clk1us_start
=1'b1; //启动1us时钟计数
if(counter==DELAY_800MS) begin
clk1us_start
=1'b0;//此处少了一句清0,致使在RESET时候要等counter计数溢出才会执行
nstate=RESET1;
end
else begin
nstate
=WAIT1S;
end
end
//=========================================================
default: nstate=RESET1;
endcase
end

assign DQ
= dq_ctrl ? dq_r:1'bz;
assign readtemp = temp_data;
assign clk1us
= clk_1us;

endmodule
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
0
关闭 站长推荐上一条 /3 下一条