本篇结合上篇一起食用,本篇为DS18B20读取ID号。需要注意的是,只能在总线上有一个设备的时候使用。
module ds18b20_dri(
//系统时钟_50M
input clk_n,
input clk_p,
input rst_n , //低电平有效的复位信号
// input clk ,
inout dq // 符号使
);
//单濻线(双向信号)
reg [19:0] temp_data ; // 转换后得到的温度倿
reg sign ;
//------------<参数定义>----------------------------------------------
INBUF_DIFF CLK
(
.PADN (clk_n),
.PADP (clk_p),
.Y (clk)
);
localparam INIT1 = 6'b000001 ,
WR_CMD = 6'b000010 ,
RX_ID = 6'b001000 ;
//时间参数定义
localparam T_INIT = 1000 , //初始化最大时间,单位us
T_WAIT = 780_000 ; //转换等待延时,单位us
//命令定义
localparam WR_CMD_DATA = 16'heecc, //跳过 ROM 及温度转换命令,低位在前
RD_CMD_DATA = 16'hbecc, //跳过 ROM 及读取温度命令,低位在前
READ_ROM=8'h33;
//------------<reg定义>----------------------------------------------
reg [5:0] cur_state ; //现濿
reg [5:0] next_state ; //次濿
reg [6:0] cnt ; //50分频计数器,1Mhz(1us)
reg dq_out ; //双向总线输出
reg dq_en ; //双向总线输出使能_1则输出,0则高阻濿
reg flag_ack ; //从机响应标志信号
reg clk_us ; //us时钟
reg [19:0] cnt_us ; //us计数噿,朿大可表示1048ms
reg [5:0] bit_cnt ; //接收数据计数噿
reg [63:0] data_temp ; //读取的ID数据寄存
reg [63:0] data ; //ID
//------------<wire定义>----------------------------------------------
wire dq_in ; //双向总线输入
//==================================================================
//===========================<main code>===========================
//==================================================================
//-----------------------------------------------------------------------
//--双向端口使用方式
//-----------------------------------------------------------------------
assign dq_in = dq; //高阻态的话,则把总线上的数据赋给dq_in
assign dq = dq_en ? dq_out : 1'bz; //使能1则输出,0则高阻濿
//-----------------------------------------------------------------------
//--us时钟生成,因为时序都是以us为单位,承以生成一丿1us的时钟会比较方便
//-----------------------------------------------------------------------
//50分频计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 7'd0;
else if(cnt ==7'd49) //毿25个时钿500ns清零
cnt <= 7'd0;
else
cnt <= cnt + 1'd1;
end
//生成1us时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
clk_us <= 1'b0;
//data_temp<=64'b0;
end
else if(cnt == 7'd49) //毿500ns
clk_us <= ~clk_us; //时钟反转
else
clk_us <= clk_us;
end
//-----------------------------------------------------------------------
//--三段式状态机
//-----------------------------------------------------------------------
//状濁机第一段:同步时序描述状濁转秿
always @(posedge clk_us or negedge rst_n)begin
if(!rst_n)
cur_state <= INIT1;
else
cur_state <= next_state;
end
//状濁机第二段:组合逻辑判断状濁转移条件,描述状濁转移规律以及输凿
always @(*)begin
next_state = INIT1;
case(cur_state)
INIT1 :begin
if(cnt_us == T_INIT && flag_ack) //满足初始化时间且接收到了从机的响应信叿
next_state = WR_CMD; //跳转到写状濿
else
next_state = INIT1; //不满足则保持原有状濿
end
WR_CMD :begin
if(bit_cnt == 6'd7 && cnt_us == 20'd62) //写完亿16个数据,写跳过ROM和写温度转换命令
next_state = RX_ID; //跳转到等待状态,等待温度转换完成
else
next_state = WR_CMD; //不满足则保持原有状濿
end
RX_ID :begin
if(bit_cnt == 6'd63 && cnt_us == 20'd62) //读取完了16个数捿
next_state = INIT1; //跳转到初始化状濁,弿始新丿轮温度采雿
else
next_state = RX_ID;
end
default:next_state = INIT1; //默认初始化状怿
endcase
end
//状濁机第三段:时序逻辑描述输出
always @(posedge clk_us or negedge rst_n)begin
if(!rst_n)begin //默认输出
dq_en <= 1'b0;
dq_out <= 1'b0;
flag_ack <= 1'b0;
cnt_us <= 20'd0;
bit_cnt <= 6'd0;
end
else begin
case(cur_state)
INIT1 :begin
if(cnt_us == T_INIT)begin //时间计数到最大忼(初始化时间)
cnt_us <= 20'd0; //计数器清雿
flag_ack <= 1'b0; //从机响应标志信号拉低
end
else begin //没有计数到最大忿
cnt_us <= cnt_us + 1'd1; //计数器计敿
if(cnt_us <= 20'd499)begin //小于500us旿
dq_en <= 1'b1; //控制总线
dq_out <= 1'b0; //输出0,即拉低总线
end
else begin //圿500us夿
dq_en <= 1'b0; //释放总线,等待从机响庿
if (cnt_us == 20'd570 && !dq_in) //圿570us处采集濻线电平,如果为0则说明从机响应了
flag_ack <= 1'b1; //拉高从机响应标志信号
end
end
end
WR_CMD :begin
if(cnt_us == 20'd62)
begin //丿个写时隙周期63us,满足计时条件则
cnt_us <= 20'd0; //清空计数噿
dq_en <= 1'b0; //释放总线
if(bit_cnt == 6'd7) //如果数据已经写了15丿
bit_cnt <= 6'd0; //则清穿
else //没写15丿
bit_cnt <= bit_cnt + 1'd1; //则数据计数器+1,代表写入了丿个数捿
end
else
begin //丿个写时隙周期63us未完房
cnt_us <= cnt_us + 1'd1; //计数器一直计敿
if(cnt_us <= 20'd1)
begin //0~1us(每两个写数据之间需要间隿2us_
dq_en <= 1'b1; //拉低总线
dq_out <= 1'b0;
end
else
begin
if (READ_ROM[bit_cnt] == 1'b0)
begin //霿要写入的数据丿0
dq_en <= 1'b1; //拉低总线
dq_out <= 1'b0; //
end
else
if(READ_ROM[bit_cnt] == 1'b1)
begin
dq_en <= 1'b0; //霿要写入的数据丿1
dq_out <= 1'b0; //释放总线
end
end
end
end
RX_ID :begin //读ID,FAMILY-1BYTE,SERIAL-6BYTES,CRC-1BYTE
if(cnt_us == 20'd62)begin //丿个读时隙周期63us,满足计时条件则
cnt_us <= 20'd0; //清空计数噿
dq_en <= 1'b0; //释放总线
if(bit_cnt == 6'd63)begin //如果数据已经读取亿15丿
bit_cnt <= 6'd0; //则清穿
data <= data_temp; //临时的数据赋值给data
end
else begin //如果数据没有读取15丿
bit_cnt <= bit_cnt + 1'd1; //则数据计数器+1,意味着读取了一个数捿
data <= data;
end
end
else begin //丿个读时隙周期还没结束
cnt_us <= cnt_us + 1'd1; //计数器累势
if(cnt_us <= 20'd1)begin //0~1us(每两个读数据之间需要间隿2us_
dq_en <= 1'b1; //拉低总线
dq_out <= 1'b0;
end
else begin //2us吿
dq_en <= 1'b0; //释放总掉线
if (cnt_us == 20'd10) //圿10us处读取濻线电平
data_temp <= {dq,data_temp[63:1]}; //读取总线电平
end
end
end
default:;
endcase
end
end
//-----------------------------------------------------------------------
//--12位温度数据处琿
//-----------------------------------------------------------------------
always @(posedge clk_us or negedge rst_n)begin
if(!rst_n)begin //初始状濿
temp_data <= 20'd0;
sign <= 1'b0;
end
else begin
if(!data[15])begin //朿高位丿0则温度为歿
sign <= 1'b0; //标志位为歿
temp_data <= data[10:0] * 11'd625 /7'd100; //12位温度数据处琿
end
else if(data[15])begin //朿高位丿1则温度为贿
sign <= 1'b1; //标志位为贿
temp_data <= (~data[10:0] + 1'b1)* 11'd625 /7'd100; //12位温度数据处琿
end
end
end
endmodule