文章目录
-
- 一.传感器介绍
-
- 1.特点
- 2.内部结构
- 3.ds18b20管脚
- 4.ds18b20内部高速暂存存储器
- 5.ds18b20工作时序
- 6.ds18b20单线通信
- 二.FPGA代码实现
-
- 1.状态图
- 2.代码实现
-
- 2.1DS18B20驱动模块
- 2.2数据处理模块
- 2.3数码管驱动模块
- 2.4顶层模块
- 3.仿真
一.传感器介绍 ds18b20是常用的数字温度传感器,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。
1.特点
ds18b20单线数字温度传感器具有独特的优点:
( 1 )采用单总线的接口方式 与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。2.内部结构
( 2 )测量温度范围宽,测量精度高 DS18B20的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C范围内,精度为 ± 0.5°C 。
( 3 )在使用中不需要任何外围元件。
( 4 )持多点组网功能 多个 DS18B20 可以并联在惟一的单线上,实现多点测温。
( 5 )供电方式灵活 DS18B20 可以通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,可以不接外部电源,从而使系统结构更趋简单,可靠性 更高。
( 6 )测量参数可配置 DS18B20的测量分辨率可通过程序设定 9~12 位。
( 7 ) 负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。
( 8 )掉电保护功能 DS18B20 内部含有 EEPROM ,在系统掉电以后,它仍可保存分 辨率及报警温度的设定值。
ds18b20内部主要有四部分组成:64位ROM,温度传感器,非挥发的温度报警触发器TH和TL,配置寄存器。
64位ROM
ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列码,每
个DS18B20的64位序列号均不相同。
64位ROM的排的循环冗余校验码
文章图片
。
ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
文章图片
3.ds18b20管脚
- GND为电源地;
- DQ为数字信号输入/输出端;
- VDD为外接供电电源输入端(在寄生电源接线方式时接地)
文章图片
高速暂存存储器由9个字节组成,当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制; 当S=1时,先将补码变为原码,再计算十进制值。
文章图片
说明:
字节0表示温度的低八位数据。DS18B20中的温度传感器完成对温度的测量,用16位二进制形式提供,形式表达,其
字节1表示温度的高八位数据。
字节2表示高温阀值。
字节3表示低温阀值。
字节4表示配置寄存器。
字节5、字节6、字节7是保留字节。
中S为符号位。
文章图片
例如:
+125℃的数字输出07D0H。(正温度直接把16进制数转成10进制即得到温度值 )
-55℃的数字输出为 FC90H。(负温度把得到的16进制数取反后加1 再转成10进制数)
文章图片
5.ds18b20工作时序
初始化时序:
①主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后
的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
②做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出
现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
文章图片
写操作:
写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
文章图片
文章图片
读操作:
对于读数据操作时序也分为读0时序和读1时序两个过程。读时隙是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60us才能完成
文章图片
6.ds18b20单线通信
DS18B20 单线通信功能是分时完成的,他有严格的时隙概念,如果出现序列混乱,
1-WIRE 器件将不响应主机,因此读写时序很重要。系统对 DS18B20 的各种操作必须按协议进行。根据 DS18B20 的协议规定,微控制器控制 DS18B20 完成温度的转换必须经过以下 3个步骤 :
(1)每次读写前对 DS18B20 进行复位初始化。复位要求主 CPU 将数据线下拉500us ,然后释放, DS18B20 收到信号后等待 16us~60us 左右,然后发出60us~240us 的存在低脉冲,主 CPU 收到此信号后表示复位成功。
(2)发送一条 ROM 指令
指令名称 | 指令代码 | 指令功能 |
---|---|---|
读ROM | 33H | 读DS18B20ROM中的编码(即读64位地址) |
ROM匹配(符合ROM) | 55H | 发出此命令之后,接着发出64位 ROM编码,访问单总线上与编码相对应DS18820使之作出响应,为下一步对该 DS18B20的读写作准备 |
搜索ROM | 0F0H | 用于确定挂接在同一总线上 DS18B20的个数和识别64位ROM地址,为操作各器件作好准备 |
跳过ROM | 0CCH | 忽略64位ROM地址,直接向DS18B20发温度变换命令,适用于单片机工作 |
警报搜索 | 0ECH | 该指令执行后,只有温度超过设定值上限或下限的片子才做出响应 |
指令名称 | 指令代码 | 指令功能 |
---|---|---|
温度变换 | 44H | 启动 DS18B20进行温度转换,转换时间最长为500ms(典型为200ms ),结果存入内部9字节 RAM中 |
读暂存器 | 0BEH | 读内部 RAM中9宇节的内容 |
写暂存器 | 4EH | 发出向内部RAM的第3, 4字节写上,下限温度数据命令,紧跟该命令之后,是传送两字节的数据 |
复制暂存器 | 48H | 将RAM中第3,4字节的内容复制到EEPROM中 |
重调EEPROM | 0B8H | EEPROM中的内容恢复到 RAM中的第3, 4字节 |
读供电方式 | 0B4H | 读DS18B20的供电模式,寄生供电时 DS18B20发送“0”,外接电源供电DS18B20发送“1” |
FPGA作为主机,DS18B20作为从机。
①主状态机实现发复位脉冲-接收存在脉冲-发ROM指令-发温度转换指令-延时-温度读取指令-读取温度;
②从状态机负责发送数据或读取数据时序:拉低总线-发送数据/采样数据-释放总线;
主从机状态图:
文章图片
状态说明:
M_IDLE:空闲状态,等待开始通信;2.代码实现
M_RST:发送复位脉冲;
M_RELE:释放总线;
M_RACK:接收存在脉冲;
M_RSKP:发送跳过ROM指令;
M_SCON:发送温度转换命令;
M_WAIT:等待750ms;
M_SRTM:发送温度读取指令;
M_RTMP:读取温度值;
S_IDLE:空闲状态,等待传输请求;
S_LOW:发数据前先拉低1us;
S_SEND:发送1bit数据;
S_SAMP:接收1bit数据;
S_RELE:释放总线;
S_DONE:发送/接收一次数据完成;
2.1DS18B20驱动模块
module ds18b20_driver(
inputclk,//时钟信号
inputrst_n,//复位信号
inputdq_in,outputregdq_out,//dq总线FPGA输出
outputregdq_out_en,//输出数据有效信号
outputregtemp_sign,//温度值符号位 0:正 1:负
outputreg[23:0]temp_out,//温度输出
outputregtemp_out_vld //温度输出有效信号
);
//状态机参数
localparam
//主机状态参数
M_IDLE = 9'b0_0000_0001 ,//空闲状态
M_REST = 9'b0_0000_0010 ,//复位
M_RELE = 9'b0_0000_0100 ,//释放总线 -- ds18b20等待
M_RACK = 9'b0_0000_1000 ,//接收应答 -- 主机接收存在脉冲
M_ROMS = 9'b0_0001_0000 ,//ROM命令-- 跳过ROM命令
M_CONT = 9'b0_0010_0000 ,//转化
M_WAIT = 9'b0_0100_0000 ,//等待-- 12bit分辨率下的温度转化时间
M_RCMD = 9'b0_1000_0000 ,//读命令-- 读暂存器命令
M_RTMP = 9'b1_0000_0000 ,//读温度-- 产生读时隙 -- 接收2字节带符号位的补码温度//从机状态参数
S_IDLE = 6'b00_0001,//空闲状态
S_LOW= 6'b00_0010,//拉低总线 -- 时隙的开始
S_SEND = 6'b00_0100,//发送-- 15us内
S_SAMP = 6'b00_1000,//采样-- 在15us内
S_RELE = 6'b01_0000,//释放-- 时隙的恢复时间
S_DONE = 6'b10_0000;
//parameter
TIME_1US = 50,//1us
TIME_RST = 500,//复位脉冲 500us
TIME_REL = 20,//主机释放总线 20us
TIME_PRE = 200,//主机接收存在脉冲 200us
TIME_WAIT= 750000,//主机发完温度转换命令 等待750ms
TIME_LOW = 2,//主机拉低总线 2us
TIME_RW= 60,//主机读、写1bit 60us
TIME_REC = 3;
//主机读、写完1bit释放总线 3uslocalparam
CMD_ROMS = 8'hCC,//跳过ROM指令
CMD_CONT = 8'h44,//温度转化指令
CDM_RTMP = 8'hBE;
//读暂存器指令//信号定义
reg[8:0]m_state_c;
//主机现态
reg[8:0]m_state_n;
//主机次态reg[5:0]s_state_c;
//从机现态
reg[5:0]s_state_n;
//从机次态reg[5:0]cnt_1us;
//1us计数器
wireadd_cnt_1us ;
wireend_cnt_1us ;
reg[19:0]cnt0;
//复位脉冲、释放、存在脉冲、等待750ms
wireadd_cnt0;
wireend_cnt0;
reg[19:0]X;
reg[5:0]cnt1;
//计数从机状态机每个状态持续时间
wireadd_cnt1;
wireend_cnt1;
reg[5:0]Y;
reg[4:0]cnt_bit;
wireadd_cnt_bit ;
wireend_cnt_bit ;
regslave_ack;
//接收存在脉冲
regflag;
//0:发送温度转换命令 1:发送温度读取命令
reg[7:0]wr_data;
reg[15:0]orign_data;
//采样温度值寄存器reg[10:0]temp_data;
wire[23:0]temp_data_w ;
//组合逻辑计算实际温度值 十进制wirem_idle2m_rest;
wirem_rest2m_rele;
wirem_rele2m_rack;
wirem_rack2m_roms;
wirem_roms2m_cont;
wirem_roms2m_rcmd;
wirem_cont2m_wait;
wirem_wait2m_rest;
wirem_rcmd2m_rtmp;
wirem_rtmp2m_idle;
wires_idle2s_low;
wires_low2s_send;
wires_low2s_samp;
wires_send2s_rele;
wires_samp2s_rele;
wires_rele2s_low;
wires_rele2s_done;
//主机状态机设计 描述状态转移
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_rest)
m_state_n = M_REST;
else
m_state_n = m_state_c;
end
M_REST:begin
if(m_rest2m_rele)
m_state_n = M_RELE;
else
m_state_n = m_state_c;
end
M_RELE:begin
if(m_rele2m_rack)
m_state_n = M_RACK;
else
m_state_n = m_state_c;
end
M_RACK:begin
if(m_rack2m_roms)
m_state_n = M_ROMS;
else
m_state_n = m_state_c;
end
M_ROMS:begin
if(m_roms2m_cont)
m_state_n = M_CONT;
else if(m_roms2m_rcmd)
m_state_n = M_RCMD;
else
m_state_n = m_state_c;
end
M_CONT:begin
if(m_cont2m_wait)
m_state_n = M_WAIT;
else
m_state_n = m_state_c;
end
M_WAIT:begin
if(m_wait2m_rest)
m_state_n = M_REST;
else
m_state_n = m_state_c;
end
M_RCMD:begin
if(m_rcmd2m_rtmp)
m_state_n = M_RTMP;
else
m_state_n = m_state_c;
end
M_RTMP:begin
if(m_rtmp2m_idle)
m_state_n = M_IDLE;
else
m_state_n = m_state_c;
end
default:m_state_n = M_IDLE;
endcase
endassign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);
//主机IDLE状态直接转换成复位状态
assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
//500us复位脉冲
assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
//20us释放总线
assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0 && slave_ack == 1'b0);
//200us,主机接收接收脉冲
assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
//主机发送完8bit跳过ROM命令 0:温度转化命令
assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
//主机发送完8bit跳过ROM命令 1:读温度命令
assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
//主机发送8bit温度转化命令
assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
//等待750ms转换完成
assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
//主机发送8bit读命令 --等待从机接收数据完成
assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);
//主机读温度 --等待从机发送数据完成//从机状态机设计 描述状态转移
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)
s_state_n = S_LOW;
else
s_state_n = s_state_c;
end
S_LOW :begin
if(s_low2s_send)
s_state_n = S_SEND;
else if(s_low2s_samp)
s_state_n = S_SAMP;
else
s_state_n = s_state_c;
end
S_SEND:begin
if(s_send2s_rele)
s_state_n = S_RELE;
else
s_state_n = s_state_c;
end
S_SAMP:begin
if(s_samp2s_rele)
s_state_n = S_RELE;
else
s_state_n = s_state_c;
end
S_RELE:begin
if(s_rele2s_done)
s_state_n = S_DONE;
else if(s_rele2s_low)
s_state_n = S_LOW;
else
s_state_n = s_state_c;
end
S_DONE:begin
s_state_n = S_IDLE;
end
default:s_state_n = S_IDLE;
endcase
endassign s_idle2s_low= s_state_c == S_IDLE && (m_state_c == M_ROMS ||
m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);
assign s_low2s_send= s_state_c == S_LOW&& (m_state_c == M_ROMS ||
m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;
//主机发送数据 2us assign s_low2s_samp= s_state_c == S_LOW&& (m_state_c == M_RTMP && end_cnt1);
//从机发送温度数据 2us
assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);
//主机读1bit (60us内完成)
assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);
//主机写1bit (60us内完成)
assign s_rele2s_low= s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);
//主机读写完1bit (3us) 继续下一bit
assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);
//主机读写完1bit (3us) bit写完//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
endend
assign add_cnt_1us = m_state_c != M_IDLE;
//非IDLE状态持续计数
assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US - 1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0) begin
if(end_cnt0)begin
cnt0 <= 0;
end
else begin
cnt0 <= cnt0 + 1;
end
endend
assign add_cnt0 = (m_state_c == M_REST || m_state_c == M_RELE || m_state_c == M_RACK || m_state_c == M_WAIT) && end_cnt_1us;
assign end_cnt0 = add_cnt0 && cnt0 == X - 1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
X <= 0;
end
else if(m_state_c == M_REST)begin//复位:500us (480us)
X <= TIME_RST;
end
else if(m_state_c == M_RELE)begin//释放总线:20us (15-60us内)
X <= TIME_REL;
end
else if(m_state_c == M_RACK)begin//接收应答:200us (60-240us)
X <= TIME_PRE;
end
else if(m_state_c == M_WAIT) begin//等待:750ms (等待转换完成)
X <= TIME_WAIT;
end
endalways@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1) begin
if(end_cnt1)begin
cnt1 <= 0;
end
else begin
cnt1 <= cnt1 + 1;
end
endend
assign add_cnt1 = (s_state_c == S_LOW || s_state_c == S_SEND ||
s_state_c == S_SAMP || s_state_c == S_RELE) && end_cnt_1us;
assign end_cnt1 = add_cnt1 && cnt1 == Y - 1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
Y <= 0;
end
else if(s_state_c == S_LOW)begin
Y <= TIME_LOW;
//主机拉低总线 2us (大于1us)
end
else if(s_state_c == S_SEND || s_state_c == S_SAMP) begin
Y <= TIME_RW;
//主机读写1bit 60us (至少60us)
end
else begin
Y <= TIME_REC;
//主机读写完1bit释放总线 3us (至少1us)
end
endalways@(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
endend
assign add_cnt_bit = s_state_c == S_RELE && end_cnt1;
assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);
//读温度状态有16bit数据,其余状态8bit数据//slave_ack 采样传感器的存在脉冲
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
slave_ack <= 1'b1;
end//接收应答状态计数器计到60us 进行采样
else if(m_state_c == M_RACK && cnt0 == 60 && end_cnt_1us)begin
slave_ack <= dq_in;
end
endalways@(posedge clk or negedge rst_n)begin//命令发送标志 (区分温度转换和温度读取命令)
if(!rst_n)begin
flag <= 0;
end
else if(m_wait2m_rest)begin
flag <= 1'b1;
end
else if(m_rtmp2m_idle) begin
flag <= 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_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin
dq_out <= 1'b0;
end
else if(s_low2s_send) begin
dq_out <= wr_data[cnt_bit];
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_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin
dq_out_en <= 1'b1;
//输出 dq_out
end
else if(m_rest2m_rele | s_send2s_rele | s_low2s_samp) begin
dq_out_en <= 1'b0;
//不输出dq_out
end
end//wr_data 命令
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_data <= 0;
end
else if(m_rack2m_roms)begin///ROM跳过命令
wr_data <= CMD_ROMS;
end
else if(m_roms2m_cont) begin//温度转换命令
wr_data <= CMD_CONT;
end
else if(m_roms2m_rcmd)begin//读暂存器温度命令
wr_data <= CDM_RTMP;
end
end
//orign_data 温度采集
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
orign_data <= 0;
end
else if(s_state_c == S_SAMP && cnt1 == 12 && end_cnt_1us)begin
orign_data[cnt_bit] <= dq_in;
end
end//temp_data 温度判断
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_data <=0;
end
else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin
if(orign_data[15])
temp_data <= ~orign_data[10:0] + 1'b1;
//负数,取反加一
else
temp_data <= orign_data[10:0];
//正数
end
end/*实际的温度值为 temp_data * 0.0625;
为了保留4位小数精度,将实际温度值放大了10000倍,
即temp_data * 625;
*/
assign temp_data_w = temp_data * 625;
//temp_out
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_out <= 0;
end
else if(m_state_c == M_RTMP && s_rele2s_done)begin
temp_out <= temp_data_w;
end
end//temp_out_vld
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_out_vld <= 0;
end
else begin
temp_out_vld <= m_state_c == M_RTMP && s_rele2s_done;
end
end//temp_sign
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_sign <= 0;
end
else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin
temp_sign <= orign_data[15];
end
endendmodule
2.2数据处理模块
module ctrl(
inputclk,//时钟信号
inputrst_n,//复位信号
inputdin_sign,
input[23:0]din,
inputdin_vld ,outputdout_sign,
output[23:0]dout,
outputregdout_vld
);
//中间信号定义
reg[23:0]din_r;
regdin_vld_r0;
regdin_vld_r1;
wire[7:0]tmp_int_w;
//整数部分
reg[7:0]tmp_int_r;
wire[3:0]tmp_int_w2;
wire[3:0]tmp_int_w1;
wire[3:0]tmp_int_w0;
reg[3:0]tmp_int_r2;
reg[3:0]tmp_int_r1;
reg[3:0]tmp_int_r0;
wire[15:0]tmp_dot_w;
//小数部分
reg[15:0]tmp_dot_r;
wire[3:0]tmp_dot_w3;
wire[3:0]tmp_dot_w2;
wire[3:0]tmp_dot_w1;
wire[3:0]tmp_dot_w0;
reg[3:0]tmp_dot_r3;
reg[3:0]tmp_dot_r2;
reg[3:0]tmp_dot_r1;
reg[3:0]tmp_dot_r0;
//din_r
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_r <= 0;
end
else if(din_vld)begin
din_r <= din;
end
end//din_vld_r0 din_vld_r1 打拍
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_vld_r0 <= 0;
din_vld_r1 <= 0;
end
else begin
din_vld_r0 <= din_vld;
din_vld_r1 <= din_vld_r0;
end
endassign tmp_int_w = din_r/10000;
assign tmp_dot_w = din_r%10000;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tmp_int_r <= 0;
end
else if(din_vld_r0)begin
tmp_int_r <= tmp_int_w;
tmp_dot_r <= tmp_dot_w;
end
endassign tmp_int_w2 = tmp_int_r/100;
//百位
assign tmp_int_w1 = tmp_int_r/10%10;
//十位
assign tmp_int_w0 = tmp_int_r%10;
//个位always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tmp_int_r2 <= 0;
tmp_int_r1 <= 0;
tmp_int_r0 <= 0;
end
else if(din_vld_r1)begin
tmp_int_r2 <= tmp_int_w2;
tmp_int_r1 <= tmp_int_w1;
tmp_int_r0 <= tmp_int_w0;
end
endassign tmp_dot_w0 = tmp_dot_r/1000;
assign tmp_dot_w1 = tmp_dot_r/100%10;
assign tmp_dot_w2 = tmp_dot_r/10%10;
assign tmp_dot_w3 = tmp_dot_r%10;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tmp_dot_r0 <= 0;
tmp_dot_r1 <= 0;
tmp_dot_r2 <= 0;
tmp_dot_r3 <= 0;
end
else if(din_vld_r1)begin
tmp_dot_r0 <= tmp_dot_w0;
tmp_dot_r1 <= tmp_dot_w1;
tmp_dot_r2 <= tmp_dot_w2;
tmp_dot_r3 <= tmp_dot_w3;
end
end
assign dout = {tmp_int_r1,tmp_int_r0,tmp_dot_r0,tmp_dot_r1,tmp_dot_r2,tmp_dot_r3};
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_vld <= 1'b0;
end
else begin
dout_vld <= din_vld_r1;
end
endassign dout_sign = din_sign;
endmodule
2.3数码管驱动模块
module seg_driver(
inputclk,//时钟信号
inputrst_n,//复位信号
inputdin_sign,
input[23:0]din,
inputdin_vld ,outputreg[5:0]sel,
outputreg[7:0]dig
);
//参数定义
parameterTIME_1MS = 25_000,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,
P= 7'b000_1111,
N= 7'b011_1111;
//信号定义
reg[19:0]cnt_1ms;
//扫描频率计数器
wireadd_cnt_1ms;
wireend_cnt_1ms;
reg[3 :0]disp_num;
regdot;
//数码管扫描频率计数
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1ms <= 0;
end
else if(add_cnt_1ms) begin
if(end_cnt_1ms)begin
cnt_1ms <= 0;
end
else begin
cnt_1ms <= cnt_1ms + 1;
end
endend
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS - 1;
//seg_sel 数码管片选信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel <= 6'b111110;
end
else if(end_cnt_1ms) begin
sel <= {sel[4:0],sel[5]};
end
end//译码
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
disp_num<= 0;
end
else begin
case(sel)
6'b011111:begin disp_num <= din_sign?4'ha:4'hb;
dot <= 1;
end
6'b101111:begin disp_num <= din[23:20];
dot <= 1;
end
6'b110111:begin disp_num <= din[19:16];
dot <= 0;
end
6'b111011:begin disp_num <= din[15:12];
dot <= 1;
end
6'b111101:begin disp_num <= din[11:8] ;
dot <= 1;
end
6'b111110:begin disp_num <= din[7 :4] ;
dot <= 1;
end
default:begin disp_num <= 4'hF ;
end
endcase
end
end //segment 段选译码
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig <= 8'hff;
end
else begin//显示小数点
case(disp_num)
4'h0:dig <= {dot,ZERO };
4'h1:dig <= {dot,ONE};
4'h2:dig <= {dot,TWO};
4'h3:dig <= {dot,THREE};
4'h4:dig <= {dot,FOUR };
4'h5:dig <= {dot,FIVE };
4'h6:dig <= {dot,SIX};
4'h7:dig <= {dot,SEVEN};
4'h8:dig <= {dot,EIGHT};
4'h9:dig <= {dot,NINE };
4'ha:dig <= {dot,N};
4'hb:dig <= {dot,P};
default:dig <= 8'hff ;
endcase
end
end
endmodule
2.4顶层模块
module ds18b20_top(
inputclk,//时钟信号
inputrst_n,//复位信号inoutdq,//传感器总线output[5:0]sel,
output[7:0]dig
);
//信号定义
wiredq_in;
wiredq_out;
wiredq_out_en;
wiretemp_sign;
wire[23:0]temp_out;
wiretemp_out_vld;
wiredout_sign;
wire[23:0]dout;
wiredout_vld;
assign dq = dq_out_en?dq_out:1'bz;
assign dq_in = dq;
//模块例化
ds18b20_driver u_ds18b20_driver(
.clk(clk),
.rst_n(rst_n),
.dq_in(dq_in),//dq总线DS18B20输出
.dq_out(dq_out),//dq总线FPGA输出,DS18B20输入
.dq_out_en(dq_out_en),//dq总线输出使能控制信号
.temp_sign(temp_sign),//温度的正负
.temp_out(temp_out),//输出十进制温度
.temp_out_vld(temp_out_vld) //温度采集数据有效
);
ctrl u_ctrl(
.clk(clk),
.rst_n(rst_n),
.din_sign(temp_sign),
.din(temp_out),
.din_vld(temp_out_vld),
.dout_sign(dout_sign),
.dout(dout),//输出温度值数码管对应位置的bcd码
.dout_vld(dout_vld)
);
seg_driver seg_driver(
.clk(clk),
.rst_n(rst_n),
.din_sign(temp_sign),
.din(dout),
.din_vld(dout_vld),
.sel(sel),
.dig(dig)
);
endmodule
3.仿真
ds18b20模块测试文件
`timescale 1ns/1psmodule ds18b20_driver_tb();
//参数定义
parameter CYCLE = 20;
defparamu_ds18b20_driver.TIME_RST = 200,
u_ds18b20_driver.TIME_PRE = 100,
u_ds18b20_driver.TIME_WAIT = 750;
//信号定义
regclk;
regrst_n;
regdq_in;
wiredq_out;
wiredq_out_en;
wiretemp_sign;
wire[23:0]temp_out;
wiretemp_out_vld;
//模块例化
ds18b20_driver u_ds18b20_driver(
.clk(clk),//时钟信号
.rst_n(rst_n),//复位信号
.dq_in(dq_in),.dq_out(dq_out),//dq总线FPGA输出
.dq_out_en(dq_out_en),//输出数据有效信号
.temp_sign(temp_sign),//温度值符号位 0:正 1:负
.temp_out(temp_out),//温度输出
.temp_out_vld(temp_out_vld) //温度输出有效信号
);
always #(CYCLE/2) clk = ~clk;
integer i=0;
initial begin
clk = 1'b1;
rst_n = 1'b0;
dq_in = 0;
#(CYCLE*20);
rst_n = 1'b1;
repeat(5)begin
for(i=0;
i<500000;
i=i+1)begin
dq_in = {$random};
#(CYCLE*20);
end
#(CYCLE*20);
end
$stop;
end
endmodule
主机状态机从IDLE–>REST:
在IDLE状态直接转换为REST状态。
文章图片
文章图片
主机状态机从REST–>RELE–>RACK:
复位脉冲要持续500us(在仿真文件里修改为200us)才会转移为释放状态,且释放状态持续20us后转移为接收存在脉冲状态。
文章图片
文章图片
主机状态机RACK–>ROM:
接收存在脉冲状态持续200us(仿真文件改为100us)且采样值为0后转移为ROM命令状态(由于只有一个温度传感器,这里使用的是跳过命令)。
文章图片
文章图片
【FPGA|基于FPGA的ds18b20温度传感器使用】从机状态机IDLE–>LOW–>SEND:
文章图片
ROM命令结束,从机状态机从发送状态转移为释放状态恢复时隙,然后拉低总线,一个新的时隙的开始。
文章图片
略
推荐阅读
- FPGA|基于FPGA的VGA协议实现
- 硬件经验|Lightning 转 USB Type-A/Type-C 思路
- 基础知识|【FPGA基础篇】底层结构组成
- fpga开发|TDC进位链
- HDLBITS学习笔记|HDLBITS笔记37(testbench错误检测集合1)
- VerilogHDL|FPGA 内部详细架构
- XILINX FPGA底层硬件资源
- DDR3|FPGA-DDR总线电源硬件设计技巧-Fly-by走线阻抗
- FPGA|FPGA基础学习(7) -- 内部结构之CLB