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

一,总体介绍

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类型。

clip_image002

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

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

1. 初始化

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

clip_image004

我们用一个简单的状态机来实现对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;

parameter RST_IDLE = 3'b001, //IDLE 状态

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

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

reg [2:0] RSTSM, RSTSMNXT;

wire PHASE_RST_IDLE = RSTSM[0];

wire PHASE_RST_MINIT = RSTSM[1];

wire PHASE_RST_SINIT = RSTSM[2];

wire PHASENXT_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;

else if(~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;

else if(~PHASE_RST_SINIT)

SLAVE_CNT <= 10'b0;

else

SLAVE_CNT <= SLAVE_CNT + 10'b1;

end

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

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

SLAVE_IS_INIT <= 1'b1;

else if(SLAVE_CNT == 10'd70)

SLAVE_IS_INIT <= DQ_IN;

else if(PHASE_RST_MINIT)

SLAVE_IS_INIT <= 1'b1;

else

SLAVE_IS_INIT <= SLAVE_IS_INIT;

end

always @(RSTSM or 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;

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

RSTSMNXT = RST_MINIT;

else

RSTSMNXT = RST_SINIT;

default:

RSTSMNXT = RST_IDLE;

endcase

end

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

下图是用示波器抓出的初始化过程DQ信号的波形(红色)。

clip_image006

2. 单bit读操作

在1-wire总线上,读数据的操作实际上是按bit来完成的。每次master可以从slave读回一个bit的数据。读回的数据可能是1或者0。下图是DS18B20的datasheet上给出的单bit读操作的时序要求图示。

clip_image008

需要注意的是,对于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 ;

parameter RD_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 sample data, 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;

wire PHASE_RD_IDLE = RDSM[0];

wire PHASE_RD_MPL = RDSM[1];

wire PHASE_RD_MSAP = RDSM[2];

wire PHASE_RD_OVER = RDSM[4];

reg [5:0] RD_CNT;

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

RD_CNT <= 6'b0;

else if(~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 @(RDSM or 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

reg RD_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;

else if(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。下图是DS18B20的datasheet上给出的单bit写操作的时序要求图示:

clip_image010

需要注意的是,对于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写操作开始信号

wire WD_DATA_OUT; //要写入到slave的值

parameter WD_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;

wire PHASE_WD_IDLE = WDSM[0];

wire PHASE_WD_MPL = WDSM[1];

wire PHASE_WD_MOUT = WDSM[2];

wire PHASE_WD_OVER = WDSM[3];

wire PHASENXT_WD_MOUT = WDSMNXT[2];

reg [5:0] WD_CNT;

always @(posedge CLK1MHZ or negedge RESET)

begin

if(~RESET)

WD_CNT <= 6'b0;

else if(~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 @(WDSM or 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;

else if(PHASENXT_WD_MOUT )

begin

DQ_OUT <= WD_DATA_OUT;

else if(PHASE_WD_IDLE)

DQ_OUT <= 1'b1;

else

DQ_OUT <= DQ_OUT;

end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值