DS18B20 单线数字温度传感器,即“一线器件”,其具有独特的优点:
只有三个引脚,说明ds18b20是单总线
使用三态门的方式去实现单总线
dq_in
dq_out
dq_out_en
// 三态门
assign dq_in = dq;
assign dq = dq_out_en?dq_out:1'bz;
dq就是相对于主机来说的
dq_in 就是ds18b20传感器发来的数据
dq_out就是主机发给ds18b20的数据
dq_out_en就是主机发送使能,1能发,0不能发
数据[10:0]是温度数据
数据[15:11]是温度的正负,1代表负,0代表正
DS18B20的每个暂存器都有8bit存储空间,用来存储相应数据,
byte0和byte1分别为温度数据的低位和高位,用来储存测量到的温度值,且这两个字节都是只读的;
byte2和byte3为TH、TL告警触发值的拷贝,可以在从片内的电可擦可编程只读存储器EEPROM中读出,也可以通过总线控制器发出的[48H]指令将暂存器中TH、TL的值写入到EEPROM,掉电后EEPROM中的数据不会丢失;
byte4的配置寄存器用来配置温度转换的精确度(最大为12位精度);
byte5、6、7为保留位,禁止写入;
byte8亦为只读存储器,用来存储以上8字节的CRC校验码。
命令Command | 命令数据CMD | 说明 |
---|---|---|
SKIP ROM | 8’hCC | 跳过ROM命令,因为我们只有一个外设,不需要去搜索其他的外设,所以不需要其他的ROM命令 |
Convert T | 8’h44 | 温度转换命令,将采集到的数据进 行温度转换 |
READ | 8’hBE | 温度读取命令,将温度转换后的数据进行读取 |
主机的大致流程图
ds18b20温度传感器外设的大致流程图
发送完第一个温度转换的命令后,需要先回到初始化状态,然后再发送第二个读取温度数据的命令
初始化时序图
主机控制总线给从机发送复位脉冲480us,主机释放总线从机等待15-60us,从机控制总线给主机发送存在脉冲60-240us
写时隙(主机向从机传数据)
无论是写0还是写1,一开始都是主机控制总线拉低总线15us,如果是写0,需要在15us-60us内主机控制总线保持低电平,如果是写1,需要在15us-60us内主机释放总线,上拉电阻将总线拉高,
读时隙(从机向主机传数据)
无论是读0还是读1,一开始都是主机控制总线拉低>1us,如果是读0,在2us以后,主机释放总线,从机控制总线拉低,而且需要在2us< and <15us内,主机采集数据,如果是读1,
这里有几个时间注意一下
用的是12位精准度,所以温度转换的时间750ms,还有几个时间根据时序图去对照着看
时间名称 | ||
---|---|---|
Temperature Conversion Time | 750ms | 温度转换时间 |
Reset Time Low | 480us | 主机控制总线发送复位脉冲时间 |
Presence Detect High | 15-60us | 主机释放总线 |
Presence Detect Low | 60-240us | 从机控制总线方发存在脉冲 |
低字节(LSB FIRST)先发
根据图5,图6,图7,图8和图9的时序写出相应的状态图,可以分为主状态机和从状态机
这里可以使用一个状态机就可以实现了,只不过状态转移条件稍微多一点
如果使用主从状态去实现,对读写时隙可以细分,稍微方便一点
红色是IDLE
黄色是RESET PULSE 主机控制总线发送复位脉冲
绿色是RELEASE 主机释放总线 上拉电阻
蓝色是PRESENCE PULSE 从机控制总线发送存在脉冲
SKIP ROM COMMAND 主机向从机发送跳过ROM命令
CONVERT TEMPERATURE COMMAND主机向从机发送温度转换命令
WAIT TEMPERATURE COVERT 等待温度转换的时间
READ SCRATCHPAD COMMAND主机向从机发送读取存储器数据命令
READ TEMPERATURE 主机读从机发来的数据
DONE 主机读取完全部的数据
从状态机就是细分了读写时隙
我们先找一下读写时隙的相同点,
红色是LOW 主机控制总线拉低
绿色是RELEASE 主机释放总线,上拉电阻拉高
然后是读写时隙的不同点
写时隙
紫色是MASTER WRITE 主机向从机发送1bit的0数据
深绿色也是MASTER WRITE 主机向从机发送1bit的1数据
读时隙
蓝色是MASTER SAMPLE 主机采样从机发送的1bit的0数据
橘色也是MASTER SAMPLE 主机采样从机发送的1bit的1数据
主状态机图
从状态图
完整状态图
// ds18b20驱动模块
module ds18b20_driver(
input clk,
input rst_n,
// dq单总线
input dq_in,
output reg dq_out,
output reg dq_out_en,
// 传出的数据
output [23:0] dout,
output dout_vld
);
// 各个状态不同的时间
parameter TIME_1US = 50,// 1us
RESET_TIME = 480,// 480us
M_RELEA_TIME = 20,// 15-60us
PRESE_TIME = 200,// 60-240us
WAITC_TIME = 750000,// 750ms
LOW_TIME = 2,// >1us
WRRD_TIME = 60,//
S_RELEA_TIME = 3;// > 1us
parameter SKROM_CMD = 8'hCC,// 跳过ROM命令
CONTEM_CMD = 8'h44,// 转换温度命令
RDTEM_CMD = 8'hBE;// 读取温度命令
// 主状态机
localparam M_IDLE = 10'b00000_00001,// 默认状态
M_RESET = 10'b00000_00010,// 主机发送复位脉冲
M_RELEA = 10'b00000_00100,// 主机释放脉冲
M_PRESE = 10'b00000_01000,// 从机发送存在脉冲
M_SKROM = 10'b00000_10000,// 主机发送跳过ROM命令
M_CTCMD = 10'b00001_00000,// 主机发送温度转换命令
M_WAITC = 10'b00010_00000,// 温度转换等待
M_RDCMD = 10'b00100_00000,// 主机发送温度读取命令
M_RDTEM = 10'b01000_00000,// 主机读取温度数据
M_DONE = 10'b10000_00000;
// 从状态机
localparam S_IDLE = 6'b000_001,// 默认状态
S_LOW = 6'b000_010,// 无论读写总线拉低
S_MASWR = 6'b000_100,// 写时隙发送1bit
S_MASRD = 6'b001_000,// 读时隙采样1bit
S_RELEA = 6'b010_000,// 无乱读写释放总线
S_DONE = 6'b100_000;// 1bit数据读写完
// 主状态机状态
reg [9:0] m_state_c;
reg [9:0] m_state_n;
// 状态转移条件
wire m_idle2m_reset ;
wire m_reset2m_relea;
wire m_relea2m_prese;
wire m_prese2m_skrom;
wire m_skrom2m_ctcmd;
wire m_skrom2m_rdcmd;
wire m_ctcmd2m_waitc;
wire m_waitc2m_reset;
wire m_rdcmd2m_rdtem;
wire m_rdtem2m_done ;
// 从状态机状态
reg [5:0] s_state_c;
reg [5:0] s_state_n;
// 状态转移条件
wire s_idle2s_low ;
wire s_low2s_maswr ;
wire s_low2s_masrd ;
wire s_maswr2s_relea;
wire s_masrd2s_relea;
wire s_relea2s_done ;
wire s_done2s_low ;
wire s_done2s_idle ;
// 1us计数器作为时间基准
reg [5:0] cnt_1us;
wire add_cnt_1us;
wire end_cnt_1us;
// 各个状态不同时间
reg [19:0] cnt;
wire add_cnt;
wire end_cnt;
reg [19:0] X;
// 字节计数器
reg [4:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
// 跳过ROM命令到温度转换命令还是温度读取命令的标志
reg flag;
// 从机发送的存在脉冲
reg slave_ack;
// 不同状态的command
reg [7:0] command;
// 16bit的原始温度数据
reg [15:0] origin_data;
// 原始温度数据处理
reg [10:0] temper_data;
// 最后接收的温度数据
wire [23:0] rx_data;
// 主状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
m_state_c <= M_IDLE;
end
else begin
m_state_c <= m_state_n;
end
end
always @(*)begin
case (m_state_c)
M_IDLE :begin
if(m_idle2m_reset)begin
m_state_n = M_RESET;
end
else begin
m_state_n = m_state_c;
end
end
M_RESET :begin
if(m_reset2m_relea)begin
m_state_n = M_RELEA;
end
else begin
m_state_n = m_state_c;
end
end
M_RELEA :begin
if(m_relea2m_prese)begin
m_state_n = M_PRESE;
end
else begin
m_state_n = m_state_c;
end
end
M_PRESE :begin
if(m_prese2m_skrom)begin
m_state_n = M_SKROM;
end
else begin
m_state_n = m_state_c;
end
end
M_SKROM :begin
if(m_skrom2m_ctcmd)begin
m_state_n = M_CTCMD;
end
else if(m_skrom2m_rdcmd)begin
m_state_n = M_RDCMD;
end
else begin
m_state_n = m_state_c;
end
end
M_CTCMD :begin
if(m_ctcmd2m_waitc)begin
m_state_n = M_WAITC;
end
else begin
m_state_n = m_state_c;
end
end
M_WAITC :begin
if(m_waitc2m_reset)begin
m_state_n = M_RESET;
end
else begin
m_state_n = m_state_c;
end
end
M_RDCMD :begin
if(m_rdcmd2m_rdtem)begin
m_state_n = M_RDTEM;
end
else begin
m_state_n = m_state_c;
end
end
M_RDTEM :begin
if(m_rdtem2m_done)begin
m_state_n = M_IDLE;
end
else begin
m_state_n = m_state_c;
end
end
M_DONE : m_state_n = M_IDLE;
default: m_state_n = M_IDLE;
endcase
end
assign m_idle2m_reset = m_state_c == M_IDLE && (1'b1);
assign m_reset2m_relea = m_state_c == M_RESET && (end_cnt);// 主机控制总线发送复位脉冲480us
assign m_relea2m_prese = m_state_c == M_RELEA && (end_cnt);// 主机释放总线20us
assign m_prese2m_skrom = m_state_c == M_PRESE && (end_cnt) && (~slave_ack);// 从机控制总线发送存在脉冲200us并且接收到低电平的存在脉冲
assign m_skrom2m_ctcmd = m_state_c == M_SKROM && (end_cnt_bit) && (flag == 0);// 8bit跳过ROM命令发送完,并且温度转换
assign m_skrom2m_rdcmd = m_state_c == M_SKROM && (end_cnt_bit) && (flag == 1);// 8bit跳过ROM命令发送完,并且温度读取
assign m_ctcmd2m_waitc = m_state_c == M_CTCMD && (end_cnt_bit);// 8bit温度转换命令发送完
assign m_waitc2m_reset = m_state_c == M_WAITC && (end_cnt);// 等待温度转换400us
assign m_rdcmd2m_rdtem = m_state_c == M_RDCMD && (end_cnt_bit);// 8bit温度读取命令发送完
assign m_rdtem2m_done = m_state_c == M_RDTEM && (end_cnt_bit);// 16bit温度数据读取完
// 从状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
s_state_c <= S_IDLE;
end
else begin
s_state_c <= s_state_n;
end
end
always @(*)begin
case (s_state_c)
S_IDLE :begin
if(s_idle2s_low)begin
s_state_n = S_LOW;
end
else begin
s_state_n = s_state_c;
end
end
S_LOW :begin
if(s_low2s_maswr)begin
s_state_n = S_MASWR;
end
else if(s_low2s_masrd)begin
s_state_n = S_MASRD;
end
else begin
s_state_n = s_state_c;
end
end
S_MASWR :begin
if(s_maswr2s_relea)begin
s_state_n = S_RELEA;
end
else begin
s_state_n = s_state_c;
end
end
S_MASRD :begin
if(s_masrd2s_relea)begin
s_state_n = S_RELEA;
end
else begin
s_state_n = s_state_c;
end
end
S_RELEA :begin
if(s_relea2s_done)begin
s_state_n = S_DONE;
end
else begin
s_state_n = s_state_c;
end
end
S_DONE :begin
if(s_done2s_low)begin
s_state_n = S_LOW;
end
else if(s_done2s_idle)begin
s_state_n = S_IDLE;
end
else begin
s_state_n = s_state_c;
end
end
default: s_state_n = S_IDLE;
endcase
end
assign s_idle2s_low = s_state_c == S_IDLE && (((m_state_c == M_SKROM) || (m_state_c == M_CTCMD) || (m_state_c == M_RDCMD) || (m_state_c == M_RDTEM)));// 到读写时候
assign s_low2s_maswr = s_state_c == S_LOW && (((m_state_c == M_SKROM) || (m_state_c == M_CTCMD) || (m_state_c == M_RDCMD)) && (end_cnt));// 发送命令先拉低总线2us
assign s_low2s_masrd = s_state_c == S_LOW && ((m_state_c == M_RDTEM) && (end_cnt));// 读取数据先拉低总线2us
assign s_maswr2s_relea = s_state_c == S_MASWR && (end_cnt);// 写1bit数据60us
assign s_masrd2s_relea = s_state_c == S_MASRD && (end_cnt);// 读1bit数据60us
assign s_relea2s_done = s_state_c == S_RELEA && (end_cnt);// 1bit数据读写完释放总线2us
assign s_done2s_low = s_state_c == S_DONE && (~end_cnt_bit);// 8bit或者16bite没有全部读写完
assign s_done2s_idle = s_state_c == S_DONE && (end_cnt_bit);// 8bit或者16bit全部读写完
// 1us计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1us <= 0;
end
else if(add_cnt_1us)begin
if(end_cnt_1us)begin
cnt_1us <= 0;
end
else begin
cnt_1us <= cnt_1us + 1;
end
end
else begin
cnt_1us <= cnt_1us;
end
end
assign add_cnt_1us = 1'b1;
assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US - 1;
// 各个状态不同时间计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
else begin
cnt <= cnt;
end
end
assign add_cnt = end_cnt_1us;
assign end_cnt = add_cnt && cnt == X - 1;
// 各个状态不同时间
always @(*)begin
if(m_state_c == M_RESET)begin
X = RESET_TIME;
end
else if(m_state_c == M_RELEA)begin
X = M_RELEA_TIME;
end
else if(m_state_c == M_PRESE)begin
X = PRESE_TIME;
end
else if(m_state_c == M_WAITC)begin
X = WAITC_TIME;
end
else if(s_state_c == S_LOW)begin
X = LOW_TIME;
end
else if((s_state_c == S_MASWR) || (s_state_c == S_MASRD))begin
X = WRRD_TIME;
end
else if(s_state_c == S_RELEA)begin
X = S_RELEA_TIME;
end
end
// 字节计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = s_relea2s_done;// 1bit数据度写完,并且释放总线时间结束
assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RDTEM)?(16 - 1):(8 - 1));
// 跳过ROM命令到温度转换命令还是温度读取命令的标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 0;
end
else if(m_waitc2m_reset)begin
flag <= 1'b1;
end
else if(m_rdtem2m_done)begin
flag <= 1'b0;
end
end
// 单总线dq
// dq_in从机向主机发送存在脉冲
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
slave_ack <= 1'b1;
end
else if(m_state_c == M_PRESE && cnt == 60 && end_cnt_1us)begin
slave_ack <= dq_in;
end
else if(m_prese2m_skrom)begin
slave_ack <= 1'b1;
end
end
// dq_out_en主机向从机发送使能
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dq_out_en <= 0;
end
else if(m_idle2m_reset || m_waitc2m_reset || s_idle2s_low || s_done2s_low || m_prese2m_skrom || m_skrom2m_ctcmd || m_skrom2m_rdcmd || s_low2s_maswr)begin
dq_out_en <= 1'b1;
end
else if(m_reset2m_relea || s_maswr2s_relea || s_low2s_masrd || m_rdcmd2m_rdtem || s_low2s_masrd)begin
dq_out_en <= 1'b0;
end
end
// dq_out主机向从机发送的数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dq_out <= 0;
end
else if(m_idle2m_reset || m_waitc2m_reset || s_idle2s_low || s_done2s_low)begin
dq_out <= 0;
end
else if(s_low2s_maswr)begin
dq_out <= command[cnt_bit];
end
end
// 不同状态的command
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
command <= 0;
end
else if(m_prese2m_skrom)begin
command <= SKROM_CMD;
end
else if(m_skrom2m_ctcmd)begin
command <= CONTEM_CMD;
end
else if(m_skrom2m_rdcmd)begin
command <= RDTEM_CMD;
end
end
// dq_in从机向主机发送16bit的温度数据
// 1bit数据在2-15us以内采集
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
origin_data <= 0;
end
else if(s_state_c == S_MASRD && cnt == 13 && end_cnt_1us)begin
origin_data[cnt_bit] <= dq_in;
end
end
// 接收到的数据进行处理
// 是一个10进制的数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temper_data <= 0;
end
else if(m_rdtem2m_done)begin
if(origin_data[15])begin
temper_data <= ~origin_data[10:0] + 1'b1;
end
else begin
temper_data <= origin_data[10:0];
end
end
end
// 得到的数据带有3位小数
assign rx_data = temper_data * 625;
assign dout = rx_data;
assign dout_vld = m_rdtem2m_done;
endmodule
module ds18b20_ctrl(
input clk,
input rst_n,
input [23:0] din,
input din_vld,
output [23:0] dout
);
reg [23:0] rx_data;
wire [3:0] rx_data_r0;
wire [3:0] rx_data_r1;
wire [3:0] rx_data_r2;
wire [3:0] rx_data_r3;
wire [3:0] rx_data_r4;
wire [3:0] rx_data_r5;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_data <= 0;
end
else if(din_vld)begin
rx_data <= din;
end
end
assign rx_data_r0 = rx_data%10;
assign rx_data_r1 = (rx_data/10)%10;
assign rx_data_r2 = (rx_data/100)%10;
assign rx_data_r3 = (rx_data/1000)%10;
assign rx_data_r4 = (rx_data/10000)%10;
assign rx_data_r5 = (rx_data/100000)%10;
assign dout ={rx_data_r5,rx_data_r4,rx_data_r3,rx_data_r2,rx_data_r1,rx_data_r0};
endmodule
module top(
input clk,
input rst_n,
inout dq,
output [7:0] seg_dig,
output [5:0] seg_sel,
output uart_tx
);
wire dq_in;
wire dq_out;
wire dq_out_en;
wire [23:0] tem_data;
wire tem_data_vld;
wire [23:0] seg_data;
wire [7:0] uart_tx_data;
// 三态门
assign dq_in = dq;
assign dq = dq_out_en?dq_out:1'bz;
assign uart_tx_data = seg_data[23:16];
// ds18b20驱动模块
ds18b20_driver u_ds18b20_driver(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input */.dq_in (dq_in ),
/* output */.dq_out (dq_out ),
/* output */.dq_out_en (dq_out_en),
/* output [23:0] */.dout (tem_data ),
/* output */.dout_vld (tem_data_vld )
);
// 串口发送模块
uart_tx u_uart_tx(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input */.baud_sel (0),// 波特率的选择
/* input [7:0] */.din (uart_tx_data),// 串并转换的数据
/* input */.din_vld (tem_data_vld ),// 串并转换的数据有效
/* output */.dout (uart_tx ),// 发送模块发送的1bit数据
/* output */.busy (busy ) // 发送模块忙标志
);
// ds18b20控制模块
ds18b20_ctrl u_ds18b20_ctrl(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [23:0] */.din (tem_data),
/* input */.din_vld (tem_data_vld ),
/* output [23:0] */.dout (seg_data )
);
seg_driver u_seg_driver(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [23:0] */.data (seg_data),
/* output reg [7:0] */.seg_dig (seg_dig),
/* output reg [5:0] */.seg_sel (seg_sel)
);
endmodule
// 数码管驱动
module seg_driver(
input clk,
input rst_n,
input [23:0] data,
output reg [7:0] seg_dig,
output reg [5:0] seg_sel
);
localparam ZERO = 7'b100_0000,
ONE = 7'b111_1001,
TWO = 7'b010_0100,
THREE = 7'b011_0000,
FOUR = 7'b001_1001,
FIVE = 7'b001_0010,
SIX = 7'b000_0010,
SEVEN = 7'b111_1000,
EIGHT = 7'b000_0000,
NINE = 7'b001_0000;
parameter SCAN_TIME = 50_000;
// 扫描计数器1ms
reg [17:0] scan_cnt;
wire add_scan_cnt;
wire end_scan_cnt;
reg [3:0] num;
// 小数点
reg point;
// 扫描计数器1ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
scan_cnt <= 0;
end
else if(add_scan_cnt)begin
if(end_scan_cnt)begin
scan_cnt <= 0;
end
else begin
scan_cnt <= scan_cnt + 1;
end
end
else begin
scan_cnt <= scan_cnt;
end
end
assign add_scan_cnt = 1'b1;
assign end_scan_cnt = add_scan_cnt && scan_cnt == SCAN_TIME - 1;
// 计数器扫描位选
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_sel <= 6'b111110;
end
else if(end_scan_cnt)begin
seg_sel <= {seg_sel[4:0],seg_sel[5]};
end
else begin
seg_sel <= seg_sel;
end
end
// 根据位选来给数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
num <= 4'b0;
point <= 1'b0;
end
else begin
case (seg_sel)
6'b111110 : begin
num <= data[3:0];
point <= 1'b1;
end
6'b111101 : begin
num <= data[7:4];
point <= 1'b1;
end
6'b111011 : begin
num <= data[11:8];
point <= 1'b1;
end
6'b110111 : begin
num <= data[15:12];
point <= 1'b1;
end
6'b101111 : begin
num <= data[19:16];
point <= 1'b0;
end
6'b011111 : begin
num <= data[23:20];
point <= 1'b1;
end
default: ;
endcase
end
end
// 根据num来对段选赋值
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_dig <= 8'hff;
end
else begin
case (num)
0 : seg_dig <={point,ZERO};
1 : seg_dig <={point,ONE};
2 : seg_dig <={point,TWO};
3 : seg_dig <={point,THREE};
4 : seg_dig <={point,FOUR};
5 : seg_dig <={point,FIVE};
6 : seg_dig <={point,SIX};
7 : seg_dig <={point,SEVEN};
8 : seg_dig <={point,EIGHT};
9 : seg_dig <={point,NINE};
default: ;
endcase
end
end
endmodule
仿真验证只是看看ds18b20的驱动的状态转移是否正确
`timescale 1ns/1ns
module ds18b20_driver_tb();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
reg tb_dq_in ;
//输出信号定义
wire tb_dq_out ;
wire tb_dq_out_en;
wire [7:0] tb_dout ;
wire tb_dout_vld ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
ds18b20_driver u_ds18b20_driver(
/* input */.clk (tb_clk ),
/* input */.rst_n (tb_rst_n ),
/* input */.dq_in (tb_dq_in ),
/* output reg */.dq_out (tb_dq_out ),
/* output reg */.dq_out_en (tb_dq_out_en),
/* output [7:0] */.dout (tb_dout ),
/* output */.dout_vld (tb_dout_vld )
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
integer i;
//产生激励
initial begin
tb_rst_n = 1'b1;
tb_dq_in = 0;
#(CLOCK_CYCLE*2);
tb_rst_n = 1'b0;
#(CLOCK_CYCLE*20);
tb_rst_n = 1'b1;
// #(CLOCK_CYCLE*1000);
repeat(5)begin
for(i=0;i<10000;i=i+1)begin
tb_dq_in = {$random};
#(CLOCK_CYCLE*10);
end
#(CLOCK_CYCLE*10);
end
#(CLOCK_CYCLE*500);
$stop;
end
endmodule
这个ds18b20不需要什么协议,算是第二个做出来的成就,这个ds18b20让我学会了如何看手册,一步步的去找关键点。
注意一下符号的优先级,我就死在&& 和 || 上 调试了半天。