基于FPGA的ds18b20温度传感器设计程序

基于FPGA的DS18B20控制程序设计及其Verilog实现 ()

 

(2012-05-20 22:20:41)

转载

标签: 

杂谈

一,总体介绍

DS18B20是一个1-wire总线,12bit的数字温度传感器,其详细的参数这里不做具体的介绍,只讨论其基于Verilog的控制程序的设计。

实际上,对DS18B20的控制,主要是实现1-wire总线的初始化,读,写等操作,然后再根据DS18B20的控制要求,实现对其控制的verilog逻辑。

在1-Wire总线上,有一个master,可以有1个或者多个slave。而对于FPGA+DS18B20的温度测试设计来讲,需要在FPGA上实现一个1-Wire总线的master。DS18B20作为1-wire总线的slave设备存在,可以有一个或者多个,不过为了简化程序,例程里假定只存在一个DS18B2020。

1-Wire总线的操作形式上相对简单,但操作本身相对却又比较复杂。用Verilog做控制程序设计时,可以采用多层次嵌套的状态机来实现。

二,FPGA +DS18B20的硬件设计

硬件的设计非常简单,只需要将DS18B20的DQ与FPGA的一个IO连接,并加4.7K左右的上拉电阻就可以了。VDD和VPU可以为3.0~5.0V。这里我们参照FPGA本身的IO电压,选择3.3V。

另外要注意的一点是,由于DQ的数据是双向的,所以FPGA的该IO要设定为inout类型。


三,1-Wire总线的基本操作及Verilog实现。

根据1-Wire总线的特点,可以把1-Wire总线的操作归结为初始化,单bit读操作,单bit写操作等最基础的几种。下面分别是几种基本操作的介绍和verilog实现。由于DS18B20的时序操作的最小单位基本上是1us,所以在该设计中,全部采用1MHz的时钟。

1. 初始化

初始化实际上就是1-wire总线上的Reset操作。由master发出一定长度的初始化信号。Slave看到该初始化信号后,在一定时间内发出规定长度的响应信号,然后初始化操作就结束了。

我们用一个简单的状态机来实现对DS18B20初始化的操作。根据初始化的时序要求,设计一个有3个状态的简单的状态机,这三个状态分别是RST_IDLE,RST_MINIT和RST_SINIT。系统初始化时,处于RST_IDLE状态,当RST_EN信号有效时,进入RST_MINIT状态,由master发出初始化信号。当master的初始化信号发出一定时间以后,直接进入RST_SINIT状态。在RST_SINIT状态时,master去观察slave是否输出了正确的状态:如果slave没有输出正确的状态,则状态机重新回到RST_MINIT状态,由master重新发出初始化信号;如果slave输出了正确的状态,则意味着初始化正确完成,状态机回到RST_IDLE状态,整个初始化过程完成(这个文章里涉及到比较多的状态机,但状态机的转换都很简单,所以不会给出状态机的状态转换图,仅仅会用文字做简单叙述,有疑问的地方,可以仔细阅读相关代码)。

wire RST_EN;

wire RST_OVER;

parameterRST_IDLE = 3'b001, //IDLE 状态

RST_MINIT =3'b010, //master 初始化操作

RST_SINIT =3'b100; //slave 初始化应答

reg [2:0]RSTSM, RSTSMNXT;

wirePHASE_RST_IDLE = RSTSM[0];

wirePHASE_RST_MINIT = RSTSM[1];

wirePHASE_RST_SINIT = RSTSM[2];

wirePHASENXT_RST_IDLE = RSTSMNXT[0];

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RSTSM <=RST_IDLE;

else

RSTSM <=RSTSMNXT;

end

reg [9:0]MASTER_CNT; //用来控制master发出初始化信号的长度

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

MASTER_CNT<= 10'b0;

elseif(~PHASE_RST_MINIT)

MASTER_CNT<= 10'b0;

else

MASTER_CNT<= MASTER_CNT + 10'b1;

end

reg [9:0]SLAVE_CNT; //用来判断slave是否在恰当时间返回初始化结束的信号

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

SLAVE_CNT<= 10'b0;

elseif(~PHASE_RST_SINIT)

SLAVE_CNT<= 10'b0;

else

SLAVE_CNT<= SLAVE_CNT + 10'b1;

end

regSLAVE_IS_INIT; //采集并保存slave发出的初始化结束信号

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

SLAVE_IS_INIT<= 1'b1;

elseif(SLAVE_CNT == 10'd70)

SLAVE_IS_INIT<= DQ_IN;

elseif(PHASE_RST_MINIT)

SLAVE_IS_INIT<= 1'b1;

else

SLAVE_IS_INIT<= SLAVE_IS_INIT;

end

always @(RSTSMor RST_EN or MASTER_CNT or SLAVE_CNT or SLAVE_IS_INIT ) begin

case(RSTSM)

RST_IDLE:

if(RST_EN)

RSTSMNXT =RST_MINIT;

else

RSTSMNXT =RST_IDLE;

RST_MINIT:

if(MASTER_CNT== 10'd500)

RSTSMNXT =RST_SINIT;

else

RSTSMNXT =RST_MINIT;

RST_SINIT:

if( (SLAVE_CNT== 10'd500) & ~SLAVE_IS_INIT)

RSTSMNXT =RST_IDLE;

elseif((SLAVE_CNT == 10'd500) & SLAVE_IS_INIT)

RSTSMNXT =RST_MINIT;

else

RSTSMNXT =RST_SINIT;

default:

RSTSMNXT =RST_IDLE;

endcase

end

assignRST_OVER = PHASE_RST_SINIT & PHASENXT_RST_IDLE; //初始化完成标志信号



2. 单bit读操作

在1-wire总线上,读数据的操作实际上是按bit来完成的。每次master可以从slave读回一个bit的数据。读回的数据可能是1或者0。


需要注意的是,对于master来讲,无论读回来的数据是1还是0,其本身的操作及时序都是一样的,没有差异。

仍然用一个简单的状态机来实现对DS18B20的单bit读操作。设计一个有5个状态的简单的状态机,这五个状态分别是RD_IDLE,RD_MPL,RD_MSAP,RD_WAIT和RD_OVER。系统初始化时,处于RD_IDLE状态,当RDBEGIN信号有效时,进入RD_MPL状态,由master发出读信号。3us以后,进入RD_MSAP状态(master在该状态结束的前一个us读取DQ上的值作为读bit的结果),在11us以后,进入RD_WAIT状态,而在读bit开始后的59us,系统进入RD_OVER状态,意味着读bit操作结束。RD_OVER状态是为了符合1-Wire总线的操作规范(在每个操作之间至少有1us的总线空闲时间)而存在的。

wire RDBEGIN ;

parameterRD_IDLE = 5'b00001, //resister pullup, larger than 1us

RD_MPL =5'b00010, //master pull low, larger than 1us

RD_MSAP =5'b00100, //ds18b20 pull low(read 0) or resister pullup(read 1), master sampledata, near 15us

RD_WAIT =5'b01000, //ds18b20 pull low(read 0) or resister pullup(read 1)

RD_OVER =5'b10000; //resister pullup, larger than 1us

reg [4:0]RDSM, RDSMNXT;

wirePHASE_RD_IDLE = RDSM[0];

wirePHASE_RD_MPL = RDSM[1];

wirePHASE_RD_MSAP = RDSM[2];

wirePHASE_RD_OVER = RDSM[4];

reg [5:0]RD_CNT;

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RD_CNT <=6'b0;

elseif(~PHASE_RD_IDLE)

RD_CNT <=RD_CNT + 6'b1;

else

RD_CNT <=6'b0;

end

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RDSM <=RD_IDLE;

else

RDSM <=RDSMNXT;

end

always @(RDSMor RDBEGIN or RD_CNT) begin

case(RDSM)

RD_IDLE:

if(RDBEGIN)

RDSMNXT =RD_MPL;

else

RDSMNXT =RD_IDLE;

RD_MPL:

if(RD_CNT ==6'd3)

RDSMNXT =RD_MSAP;

else

RDSMNXT =RD_MPL;

RD_MSAP:

if(RD_CNT ==6'd14)

RDSMNXT =RD_WAIT;

else

RDSMNXT =RD_MSAP;

RD_WAIT:

if(RD_CNT ==6'd59)

RDSMNXT =RD_OVER;

else

RDSMNXT =RD_WAIT;

RD_OVER:

if(RD_CNT ==6'd61)

RDSMNXT =RD_IDLE;

else

RDSMNXT =RD_OVER;

default:

RDSMNXT =RD_IDLE;

endcase

end

regRD_BIT_DATA; //读bit操作获得的数据

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RD_BIT_DATA<= 1'b0;

else if(PHASE_RD_MSAP & (RD_CNT == 6'd13) )

RD_BIT_DATA<= DQ;

elseif(PHASE_RD_IDLE)

RD_BIT_DATA<= 1'b0;

else

RD_BIT_DATA<= RD_BIT_DATA;

end

3. 单bit写操作

在1-Wire总线上,写数据的操作也是按bit来完成的。每次master可以向slave写入一个bit的数据。写数据可能是1或者0。

需要注意的是,对于master来讲,写数据不同的时候(1或者0),其本身的操作及时序是有差别的。

对DS18B20的单bit写操作可以用一个有4个状态的简单的状态机来实现。这三个状态分别是WD_IDLE,WD_MPL,WD_OUT,和RD_OVER。系统初始化时,处于WD_IDLE状态,当WDBEGIN信号有效时,进入WD_MPL状态,由master发出写信号。9us以后,进入WD_MOUT状态(master将要写到slave的数据放到DQ上),而在写bit开始后的59us,系统进入RD_OVER状态,意味着写bit操作结束。WD_OVER状态是为了符合1-Wire总线的操作规范(在每个操作之间至少有1us的总线空闲时间)而存在的。

wire WDBEGIN;//单bit写操作开始信号

wireWD_DATA_OUT; //要写入到slave的值

parameterWD_IDLE = 4'b0001, //resister pullup, no time request

WD_MPL =4'b0010, //master pull low, larger than 1us, use 10us.

WD_MOUT =4'b0100, //master pull low(write 0) or resister pull up(write 1), use 50us.

WD_OVER =4'b1000; //resister pullup, larger than 1us

reg [3:0]WDSM, WDSMNXT;

wirePHASE_WD_IDLE = WDSM[0];

wirePHASE_WD_MPL = WDSM[1];

wirePHASE_WD_MOUT = WDSM[2];

wirePHASE_WD_OVER = WDSM[3];

wirePHASENXT_WD_MOUT = WDSMNXT[2];

reg [5:0]WD_CNT;

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

WD_CNT <=6'b0;

elseif(~PHASE_WD_IDLE)

WD_CNT <=WD_CNT + 6'b1;

else

WD_CNT <=6'b0;

end

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

WDSM <=WD_IDLE;

else

WDSM <=WDSMNXT;

end

always @(WDSMor WDBEGIN or WD_CNT) begin

case(WDSM)

WD_IDLE:

if(WDBEGIN)

WDSMNXT =WD_MPL;

else

WDSMNXT =WD_IDLE;

WD_MPL:

if(WD_CNT ==6'd9)

WDSMNXT =WD_MOUT;

else

WDSMNXT =WD_MPL;

WD_MOUT:

if(WD_CNT ==6'd59)

WDSMNXT =WD_OVER;

else

WDSMNXT =WD_MOUT;

WD_OVER:

if(WD_CNT ==6'd61)

WDSMNXT =WD_IDLE;

else

WDSMNXT =WD_OVER;

default:

WDSMNXT =WD_IDLE;

endcase

end

always@(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

DQ_OUT <=1'b0;

elseif(PHASENXT_WD_MOUT )

begin

DQ_OUT <=WD_DATA_OUT;

elseif(PHASE_WD_IDLE)

DQ_OUT <=1'b1;

else

DQ_OUT <=DQ_OUT;

end

三,1-Wire总线上按Byte读写的Verilog实现及DS18B20的Byte操作

上面用简单状态机实现了1-Wire总线上单bit数据的读写操作。在此基础上,可以通过状态机嵌套的方法实现按Byte的读写操作。实现Byte读写控制的状态机,控制8bit数据的读写操作,而每一bit的读写操作则通过嵌套单bit数据读写的状态机来实现。

1. 按Byte读操作

从DS18B20的datasheet上我们可以看到,1-Wire总线在发送或者接收数据的时候,LSB的数据在前,MSB的数据在后。所以对于Byte数据的读操作,只要依次在总线上读取8bit数据,并按照bit0~bit7的顺序将其存储到一个8bits的寄存器里即可。

通常情况下,设计状态机的时候,都会设计一个默认的初始状态(也可以称之为IDLE状态)。在这里每个bit数据的读操作可以设定为一个状态。这样,整个状态机就需要九个状态。在我们的设计中,九个状态分别是RBD_IDLE,RBD_BIT0 ~RBD_BIT7,其中 RBD_IDLE是IDLE状态,RBD_BIT0~RBD_BIT7分别是读取bit0~bit7数据的状态。

状态机在系统复位或者操作完成以后,进入RBD_IDLE状态。当读Byte数据的使能信号(RBDBEGIN)有效时,状态机进入RBD_BIT0,并调用1-Wire总线单bit读操作的状态机实现单bit的读操作,读取bit0的数据。为了在状态机进入RBD_BIT0的时候,正确调用单bit读操作的状态机,就要在RBD_BIT0状态下,使能RDBEGIN信号(单bit读操作的使能信号,详见上文)。在读完bit0的数据后(单bit读操作状态机回到IDLE状态),状态机需要进入RBD_BIT1状态,读取bit1的数据。也就是说,状态机从RBD_BIT0进入到RBD_BIT1的条件是bit0读操作结束(即其状态机回到IDLE状态),这个条件可以用单bit读操作状态机的PHASE_RD_OVER生成。上述的操作就实现了两个状态机的嵌套,顶层状态机(Byte读操作状态机)的信号触发底层状态机(单bit读操作状态机)进入工作状态,当底层状态机完成工作进入IDLE状态时,触发顶层状态机进入下一个状态。

从RBD_BIT0到RBD_BIT7的操作是相同的,依次读取8bit的数据。不过读完bit7的数据后,状态机要转回到RBD_IDLE状态。另外就是在单bit读的过程中,要根据Byte读状态机的状态,将读到的单bit数据写入对应的寄存器中。

下面是verilog的实现:

reg [7:0] RD_BYTE_DATA; //Byte读数据结果

wire RBDBEGIN ; // Byte读使能信号

reg PHASE_RD_OVER_Q;

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

PHASE_RD_OVER_Q <= 1'b0;

else

PHASE_RD_OVER_Q <= PHASE_RD_OVER;

end

wire RD_BIT_OVER = PHASE_RD_OVER &PHASE_RD_OVER_Q; //单bit读操作结束信号

parameter RBD_IDLE = 9'b0_0000_0001,

RBD_BIT0 = 9'b0_0000_0010,

RBD_BIT1 = 9'b0_0000_0100,

RBD_BIT2 = 9'b0_0000_1000,

RBD_BIT3 = 9'b0_0001_0000,

RBD_BIT4 = 9'b0_0010_0000,

RBD_BIT5 = 9'b0_0100_0000,

RBD_BIT6 = 9'b0_1000_0000,

RBD_BIT7 = 9'b1_0000_0000;

reg [8:0] RBDSM, RBDSMNXT;

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RBDSM <= RBD_IDLE;

else

RBDSM <= RBDSMNXT;

end

always @(RBDSM or RBDBEGIN or RD_BIT_OVER) begin

case(RBDSM)

RBD_IDLE:

if(RBDBEGIN)

RBDSMNXT = RBD_BIT0;

else

RBDSMNXT = RBD_IDLE;

RBD_BIT0:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT1;

else

RBDSMNXT = RBD_BIT0;

RBD_BIT1:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT2;

else

RBDSMNXT = RBD_BIT1;

RBD_BIT2:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT3;

else

RBDSMNXT = RBD_BIT2;

RBD_BIT3:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT4;

else

RBDSMNXT = RBD_BIT3;

RBD_BIT4:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT5;

else

RBDSMNXT = RBD_BIT4;

RBD_BIT5:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT6;

else

RBDSMNXT = RBD_BIT5;

RBD_BIT6:

if(RD_BIT_OVER)

RBDSMNXT = RBD_BIT7;

else

RBDSMNXT = RBD_BIT6;

RBD_BIT7:

if(RD_BIT_OVER)

RBDSMNXT = RBD_IDLE;

else

RBDSMNXT = RBD_BIT7;

default:

RBDSMNXT = RBD_IDLE;

endcase

end

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RD_BYTE_DATA <= 8'b0;

else if(PHASE_RD_MSAP & (RD_CNT == 6'd13) )

begin

case(RBDSM)

9'b0_0000_0010:

RD_BYTE_DATA[0] <= DQ;

9'b0_0000_0100:

RD_BYTE_DATA[1] <= DQ;

9'b0_0000_1000:

RD_BYTE_DATA[2] <= DQ;

9'b0_0001_0000:

RD_BYTE_DATA[3] <= DQ;

9'b0_0010_0000:

RD_BYTE_DATA[4] <= DQ;

9'b0_0100_0000:

RD_BYTE_DATA[5] <= DQ;

9'b0_1000_0000:

RD_BYTE_DATA[6] <= DQ;

9'b1_0000_0000:

RD_BYTE_DATA[7] <= DQ;

default:

RD_BYTE_DATA <= RD_BYTE_DATA;

endcase

end

else

RD_BYTE_DATA <= RD_BYTE_DATA;

end

2. 按Byte写操作

按Byte写操作的状态机与按Byte读操作的状态机的控制原理基本上是相同的。只不过在这里要嵌套的是单bit写操作的状态机。另外就是在单bit写操作的时候,要把对应bit的数据放到DQ总线上。

限于篇幅,状态机的实现不再给出,可自行参照Byte读操作的状态机做修改。

3. DS18B20的Byte操作

DS18B20的控制主要包括初始化,ROM命令和功能命令等。初始化的控制方式前面已经讨论过了。这里主要讨论一下ROM命令和功能命令。实际上不管是ROM命令还是功能命令,都可以归结为一个字节的命令(Byte写操作)加上0或者多个的Byte读/写操作。所以我们完全可以用上面所述的Byte读写状态机来实现ROM和功能命令。

对于只有一个单Byte写操作的命令,我们直接引用单Byte写操作的状态机就可以实现了。


而对于如读ROM数据的操作,则是一个Byte的写命令操作加上连续多个Byte的读数据的操作。在实现的时候,我们完全可以依照Byte读写操作状态机嵌套单bit读写操作状态机的方法,设计一个多BYTE读写操作的状态机,嵌套单Byte读写状态机。

下面是从DS18B20读出9Byte数据的状态机的verilog实现:

reg [7:0] TMP_LSB;

reg [7:0] TMP_MSB;

reg [7:0] USERBYTE1;

reg [7:0] USERBYTE2;

reg [7:0] CFGREG;

reg [7:0] RESERVED5;

reg [7:0] RESERVED6;

reg [7:0] RESERVED7;

reg [7:0] REGCRC;

wire RDSPAD_EN; //读DS18B20 rom数据使能信号

wire BYTE_READ_OVER = RD_BIT_OVER &PHASE_RBD_BIT7; //Byte读操作结束信号。

wire BYTE_WRITE_OVER = WD_BIT_OVER &PHASE_WBD_BIT7; //Byte 写操作结束信号。

parameter RDSPAD_IDLE = 11'b000_0000_0001,

RDSPAD_CMD = 11'b000_0000_0010, //读ROM命令

RDSPAD_BYTE1 = 11'b000_0000_0100, // 读ROM Byte 1

RDSPAD_BYTE2 = 11'b000_0000_1000,

RDSPAD_BYTE3 = 11'b000_0001_0000,

RDSPAD_BYTE4 = 11'b000_0010_0000,

RDSPAD_BYTE5 = 11'b000_0100_0000,

RDSPAD_BYTE6 = 11'b000_1000_0000,

RDSPAD_BYTE7 = 11'b001_0000_0000,

RDSPAD_BYTE8 = 11'b010_0000_0000,

RDSPAD_BYTE9 = 11'b100_0000_0000;

reg [10:0] RDSPADSM, RDSPADSMNXT;

wire PHASE_RDSPAD_IDLE = RDSPADSM[0];

wire PHASE_RDSPAD_CMD = RDSPADSM[1];

wire PHASE_RDSPAD_BYTE1 = RDSPADSM[2];

wire PHASE_RDSPAD_BYTE2 = RDSPADSM[3];

wire PHASE_RDSPAD_BYTE3 = RDSPADSM[4];

wire PHASE_RDSPAD_BYTE4 = RDSPADSM[5];

wire PHASE_RDSPAD_BYTE5 = RDSPADSM[6];

wire PHASE_RDSPAD_BYTE6 = RDSPADSM[7];

wire PHASE_RDSPAD_BYTE7 = RDSPADSM[8];

wire PHASE_RDSPAD_BYTE8 = RDSPADSM[9];

wire PHASE_RDSPAD_BYTE9 = RDSPADSM[10];

wire PHASENXT_RDSPAD_IDLE = RDSPADSMNXT[0];

wire PHASENXT_RDSPAD_CMD = RDSPADSMNXT[1];

wire PHASENXT_RDSPAD_BYTE1 = RDSPADSMNXT[2];

wire PHASENXT_RDSPAD_BYTE2 = RDSPADSMNXT[3];

wire PHASENXT_RDSPAD_BYTE3 = RDSPADSMNXT[4];

wire PHASENXT_RDSPAD_BYTE4 = RDSPADSMNXT[5];

wire PHASENXT_RDSPAD_BYTE5 = RDSPADSMNXT[6];

wire PHASENXT_RDSPAD_BYTE6 = RDSPADSMNXT[7];

wire PHASENXT_RDSPAD_BYTE7 = RDSPADSMNXT[8];

wire PHASENXT_RDSPAD_BYTE8 = RDSPADSMNXT[9];

wire PHASENXT_RDSPAD_BYTE9 = RDSPADSMNXT[10];

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RDSPADSM <= RDSPAD_IDLE;

else

RDSPADSM <= RDSPADSMNXT;

end

always @( RDSPADSM or RDSPAD_EN or BYTE_READ_OVER orBYTE_WRITE_OVER) begin

case(RDSPADSM)

RDSPAD_IDLE:

if(RDSPAD_EN)

RDSPADSMNXT = RDSPAD_CMD;

else

RDSPADSMNXT = RDSPAD_IDLE;

RDSPAD_CMD:

if(BYTE_WRITE_OVER)

RDSPADSMNXT = RDSPAD_BYTE1;

else

RDSPADSMNXT = RDSPAD_CMD;

RDSPAD_BYTE1:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE2;

else

RDSPADSMNXT = RDSPAD_BYTE1;

RDSPAD_BYTE2:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE3;

else

RDSPADSMNXT = RDSPAD_BYTE2;

RDSPAD_BYTE3:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE4;

else

RDSPADSMNXT = RDSPAD_BYTE3;

RDSPAD_BYTE4:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE5;

else

RDSPADSMNXT = RDSPAD_BYTE4;

RDSPAD_BYTE5:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE6;

else

RDSPADSMNXT = RDSPAD_BYTE5;

RDSPAD_BYTE6:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE7;

else

RDSPADSMNXT = RDSPAD_BYTE6;

RDSPAD_BYTE7:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE8;

else

RDSPADSMNXT = RDSPAD_BYTE7;

RDSPAD_BYTE8:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_BYTE9;

else

RDSPADSMNXT = RDSPAD_BYTE8;

RDSPAD_BYTE9:

if(BYTE_READ_OVER)

RDSPADSMNXT = RDSPAD_IDLE;

else

RDSPADSMNXT = RDSPAD_BYTE9;

default:

RDSPADSMNXT = RDSPAD_IDLE;

endcase

end

//采集并保存TMP_LSB

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

TMP_LSB <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE2 & BYTE_READ_OVER)

TMP_LSB <= RD_BYTE_DATA;

else

TMP_LSB <= TMP_LSB;

end

//采集并保存 TMP_MSB

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

TMP_MSB <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE3 & BYTE_READ_OVER)

TMP_MSB <= RD_BYTE_DATA;

else

TMP_MSB <= TMP_MSB;

end

//采集并保存 USERBYTE1

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

USERBYTE1 <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE4 & BYTE_READ_OVER)

USERBYTE1 <= RD_BYTE_DATA;

else

USERBYTE1 <= USERBYTE1;

End

//采集并保存USERBYTE2

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

USERBYTE2 <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE5 & BYTE_READ_OVER)

USERBYTE2 <= RD_BYTE_DATA;

else

USERBYTE2 <= USERBYTE2;

end

//采集并保存 CFGREG

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

CFGREG <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE6 & BYTE_READ_OVER)

CFGREG <= RD_BYTE_DATA;

else

CFGREG <= CFGREG;

end

//采集并保存 RESERVED5

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RESERVED5 <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE7 & BYTE_READ_OVER)

RESERVED5 <= RD_BYTE_DATA;

else

RESERVED5 <= RESERVED5;

end

//采集并保存 RESERVED6

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RESERVED6 <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE8 & BYTE_READ_OVER)

RESERVED6 <= RD_BYTE_DATA;

else

RESERVED6 <= RESERVED6;

end

//采集并保存 RESERVED7

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RESERVED7 <= 8'b0;

else if(PHASENXT_RDSPAD_BYTE9 & BYTE_READ_OVER)

RESERVED7 <= RD_BYTE_DATA;

else

RESERVED7 <= RESERVED7;

end

//采集并保存 REGCRC

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

REGCRC <= 8'b0;

else if(PHASENXT_RDSPAD_IDLE & BYTE_READ_OVER)

REGCRC <= RD_BYTE_DATA;

else

REGCRC <= REGCRC;

end

下图是从示波器上抓出的单Byte读操作的DQ上的波形。


 

三,DS18B20的温度测量控制

在该系统中,1-Wire总线上只存在1个DS18B20,在控制DS18B20进行温度测量的时候,根据1-Wire总线的特性和DS18B20的控制要求,可以采用相对简单的控制流程。

我们用状态机来实现这个控制流程。从控制流程图可以看到,系统从空闲状态到读出温度数据,总共有8个大的步骤。而其中2和6,3和7是相同的,用状态机实现的时候,可以用同一个状态表示2和6,用同一个状态表示3和7。这样整个状态机就只需要6个状态。下面表格是状态机所有状态的定义:

状态名称

含义

TTST_IDLE

系统空闲状态

TTST_RST

总线初始化

TTST_SKPROM

SKIP ROM 命令

TTST_CONVERT

温度转换命令

TTST_WOVER

等待温度转换结束

TTST_GETDATA

读取温度数据

为了区分2和6以及3和7。用IS_CONVERT信号表示总线初始化和SKIPROM命令是用来执行CONVERT命令的。用~IS_CONVERT信号表示总线初始化和SKIPROM命令是用来读取温度数据的。在状态机进入TTST_IDLE状态时,令IS_CONVERT为1并保持,当状态机进入TTST_WOVER状态时,令IS_CONVERT为0并保持。

用TEST_EN信号作为温度测量的开始信号,这个信号至少保持1个时钟周期(CLK1MHZ),如果TEST_EN的长度超过了一次测试所需要的总的时间,则在一次测试完成以后,就直接开始进行第二次测量。

用RST_OVER信号作为总线初始化的结束和下一个状态的开始的标志信号。

用WBD_OVER信号作为命令(SKIPROM,CONVERT)结束和下一个状态开始的标志信号。

用RD_BIT_DATA & PHASE_RD_OVER表示等待温度转换结束和下一个状态开始的信号(详细的原因可参照DS18B20 datasheet的相关部分)。

用BYTE_READ_OVER &PHASENXT_RDSPAD_IDLE表示读取温度数据结束的信号(该两个信号的与表示读DS18B20 9ByteROM数据状态机完成工作并进入IDLE状态)。


下面是verilog的实现:

reg IS_CONVERT;

parameter TTST_IDLE = 6'b00_0001,

TTST_RST = 6'b00_0010,

TTST_SKPROM = 6'b00_0100,

TTST_CONVERT = 6'b00_1000,

TTST_WOVER = 6'b01_0000,

TTST_GETDATA = 6'b10_0000;

reg [5:0] TTSTSM, TTSTSMNXT;

wire PHASE_TTST_IDLE = TTSTSM[0];

wire PHASE_TTST_RST = TTSTSM[1];

wire PHASE_TTST_SKPROM = TTSTSM[2];

wire PHASE_TTST_CONVERT = TTSTSM[3];

wire PHASE_TTST_WOVER = TTSTSM[4];

wire PHASE_TTST_GETDATA = TTSTSM[5];

wire PHASENXT_TTST_IDLE = TTSTSMNXT[0];

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

TTSTSM <= TTST_IDLE;

else

TTSTSM <= TTSTSMNXT;

end

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

IS_CONVERT <= 1'b1;

else if(PHASE_TTST_IDLE)

IS_CONVERT <= 1'b1;

else if(PHASE_TTST_WOVER)

IS_CONVERT <= 1'b0;

else

IS_CONVERT <= IS_CONVERT;

end

always @(TTSTSM or TEST_EN or RST_OVER or IS_CONVERTor WBD_OVER or RD_BIT_DATA or PHASE_RD_OVER or PHASE_RD_OVER or BYTE_READ_OVERor PHASENXT_RDSPAD_IDLE ) begin

case(TTSTSM)

TTST_IDLE:

if(TEST_EN)

TTSTSMNXT = TTST_RST;

else

TTSTSMNXT = TTST_IDLE;

TTST_RST:

if(RST_OVER)

TTSTSMNXT = TTST_SKPROM;

else

TTSTSMNXT = TTST_RST;

TTST_SKPROM:

if(WBD_OVER)

if(IS_CONVERT)

TTSTSMNXT = TTST_CONVERT;

else

TTSTSMNXT = TTST_GETDATA;

else

TTSTSMNXT = TTST_SKPROM;

TTST_CONVERT:

if(WBD_OVER)

TTSTSMNXT = TTST_WOVER;

else

TTSTSMNXT = TTST_CONVERT;

TTST_WOVER:

if(RD_BIT_DATA & PHASE_RD_OVER)

TTSTSMNXT = TTST_RST;

else

TTSTSMNXT = TTST_WOVER;

TTST_GETDATA:

if(BYTE_READ_OVER & PHASENXT_RDSPAD_IDLE )

TTSTSMNXT = TTST_IDLE;

else

TTSTSMNXT = TTST_GETDATA;

default:

TTSTSMNXT = TTST_IDLE;

endcase

end

assign WD_DATA_OUT = PHASE_TTST_SKPROM ? 8'hcc :(PHASE_TTST_CONVERT ? 8'h44 : ( PHASE_RDSPAD_CMD ? 8'hbe : 8'h00) );

四,DQ总线的控制

1-Wire总线只有一根信号线,是双向的,带上拉电阻的。在FPGA中做DQ的逻辑如下,至于这么做的原因,不再做叙述。当弄懂了1-Wire总线的规范和DS18B20的基本操作的话,很容易就理解了。

wire DQ, DQ_OUT, DQ_IN, DQ_OUT_EN;

assign DQ = DQ_OUT_EN ? DQ_OUT : 1'bz;

assign DQ_IN = DQ;

wire RD_DQ_OUT = ~PHASE_RD_MPL;

wire RD_DQ_OUT_EN = PHASE_RD_MPL ;

wire WD_DQ_OUT = 1’b0;

wire WD_DQ_OUT_EN = PHASE_WD_MPL | (~WD_BIT_DATA& PHASE_WD_MOUT);

wire DQ_RST_OUT = ~ PHASE_RST_MINIT;

wire DQ_RST_EN = PHASE_RST_MINIT;

assign DQ_OUT = RD_DQ_OUT & RD_DQ_OUT_EN |WD_DQ_OUT & WD_DQ_OUT_EN | DQ_RST_OUT & DQ_RST_EN;

assign DQ_OUT_EN = RD_DQ_OUT_EN | WD_DQ_OUT_EN |DQ_RST_EN;

你可能感兴趣的:(fpga)