当前位置:网站首页>基于FPGA的ds18b20温度传感器使用
基于FPGA的ds18b20温度传感器使用
2022-06-13 06:45:00 【机智的橙子】
文章目录
一.传感器介绍
ds18b20是常用的数字温度传感器,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。
1.特点
ds18b20单线数字温度传感器具有独特的优点:
( 1 )采用单总线的接口方式 与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。
( 2 )测量温度范围宽,测量精度高 DS18B20的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C范围内,精度为 ± 0.5°C 。
( 3 )在使用中不需要任何外围元件。
( 4 )持多点组网功能 多个 DS18B20 可以并联在惟一的单线上,实现多点测温。
( 5 )供电方式灵活 DS18B20 可以通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,可以不接外部电源,从而使系统结构更趋简单,可靠性 更高。
( 6 )测量参数可配置 DS18B20的测量分辨率可通过程序设定 9~12 位。
( 7 ) 负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。
( 8 )掉电保护功能 DS18B20 内部含有 EEPROM ,在系统掉电以后,它仍可保存分 辨率及报警温度的设定值。
2.内部结构
ds18b20内部主要有四部分组成:64位ROM,温度传感器,非挥发的温度报警触发器TH和TL,配置寄存器。
64位ROM
ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列码,每
个DS18B20的64位序列号均不相同。
64位ROM的排的循环冗余校验码
。
ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
3.ds18b20管脚
- GND为电源地;
- DQ为数字信号输入/输出端;
- VDD为外接供电电源输入端(在寄生电源接线方式时接地)

4.ds18b20内部高速暂存存储器
高速暂存存储器由9个字节组成,当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变为原码,再计算十进制值。

说明:
字节0表示温度的低八位数据。
字节1表示温度的高八位数据。
字节2表示高温阀值。
字节3表示低温阀值。
字节4表示配置寄存器。
字节5、字节6、字节7是保留字节。
DS18B20中的温度传感器完成对温度的测量,用16位二进制形式提供,形式表达,其
中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 | 该指令执行后,只有温度超过设定值上限或下限的片子才做出响应 |
(3)发送存储器指令
| 指令名称 | 指令代码 | 指令功能 |
|---|---|---|
| 温度变换 | 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代码实现
1.状态图
FPGA作为主机,DS18B20作为从机。
①主状态机实现发复位脉冲-接收存在脉冲-发ROM指令-发温度转换指令-延时-温度读取指令-读取温度;
②从状态机负责发送数据或读取数据时序:拉低总线-发送数据/采样数据-释放总线;
主从机状态图:
状态说明:
M_IDLE:空闲状态,等待开始通信;
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.代码实现
2.1DS18B20驱动模块
module ds18b20_driver(
input clk ,//时钟信号
input rst_n ,//复位信号
input dq_in ,
output reg dq_out ,//dq总线FPGA输出
output reg dq_out_en ,//输出数据有效信号
output reg temp_sign ,//温度值符号位 0:正 1:负
output reg [23:0] temp_out ,//温度输出
output reg temp_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释放总线 3us
localparam
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计数器
wire add_cnt_1us ;
wire end_cnt_1us ;
reg [19:0] cnt0 ;//复位脉冲、释放、存在脉冲、等待750ms
wire add_cnt0 ;
wire end_cnt0 ;
reg [19:0] X ;
reg [5:0] cnt1 ;//计数从机状态机每个状态持续时间
wire add_cnt1 ;
wire end_cnt1 ;
reg [5:0] Y ;
reg [4:0] cnt_bit ;
wire add_cnt_bit ;
wire end_cnt_bit ;
reg slave_ack ;//接收存在脉冲
reg flag ;//0:发送温度转换命令 1:发送温度读取命令
reg [7:0] wr_data ;
reg [15:0] orign_data ;//采样温度值寄存器
reg [10:0] temp_data ;
wire [23:0] temp_data_w ;//组合逻辑计算实际温度值 十进制
wire m_idle2m_rest ;
wire m_rest2m_rele ;
wire m_rele2m_rack ;
wire m_rack2m_roms ;
wire m_roms2m_cont ;
wire m_roms2m_rcmd ;
wire m_cont2m_wait ;
wire m_wait2m_rest ;
wire m_rcmd2m_rtmp ;
wire m_rtmp2m_idle ;
wire s_idle2s_low ;
wire s_low2s_send ;
wire s_low2s_samp ;
wire s_send2s_rele ;
wire s_samp2s_rele ;
wire s_rele2s_low ;
wire s_rele2s_done ;
//主机状态机设计 描述状态转移
[email protected](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
end
assign 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);//主机读温度 --等待从机发送数据完成
//从机状态机设计 描述状态转移
[email protected](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
end
assign 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计数器
[email protected](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
end
assign add_cnt_1us = m_state_c != M_IDLE;//非IDLE状态持续计数
assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US - 1;
[email protected](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
end
end
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;
[email protected](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
end
[email protected](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
end
end
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;
[email protected](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
end
[email protected](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
end
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 采样传感器的存在脉冲
[email protected](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
end
[email protected](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
[email protected](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
[email protected](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 命令
[email protected](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 温度采集
[email protected](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 温度判断
[email protected](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
[email protected](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
[email protected](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
[email protected](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
end
endmodule
2.2数据处理模块
module ctrl(
input clk ,//时钟信号
input rst_n ,//复位信号
input din_sign,
input [23:0] din ,
input din_vld ,
output dout_sign,
output [23:0] dout ,
output reg dout_vld
);
//中间信号定义
reg [23:0] din_r ;
reg din_vld_r0 ;
reg din_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
[email protected](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 打拍
[email protected](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
end
assign tmp_int_w = din_r/10000;
assign tmp_dot_w = din_r%10000;
[email protected](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
end
assign tmp_int_w2 = tmp_int_r/100;//百位
assign tmp_int_w1 = tmp_int_r/10%10;//十位
assign tmp_int_w0 = tmp_int_r%10;//个位
[email protected](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
end
assign 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;
[email protected](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};
[email protected](posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_vld <= 1'b0;
end
else begin
dout_vld <= din_vld_r1;
end
end
assign dout_sign = din_sign;
endmodule
2.3数码管驱动模块
module seg_driver(
input clk ,//时钟信号
input rst_n ,//复位信号
input din_sign,
input [23:0] din ,
input din_vld ,
output reg [5:0] sel ,
output reg [7:0] dig
);
//参数定义
parameter TIME_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;//扫描频率计数器
wire add_cnt_1ms;
wire end_cnt_1ms;
reg [3 :0] disp_num;
reg dot;
//数码管扫描频率计数
[email protected](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
end
end
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS - 1;
//seg_sel 数码管片选信号
[email protected](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
//译码
[email protected](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 段选译码
[email protected](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(
input clk ,//时钟信号
input rst_n ,//复位信号
inout dq ,//传感器总线
output [5:0] sel ,
output [7:0] dig
);
//信号定义
wire dq_in ;
wire dq_out ;
wire dq_out_en ;
wire temp_sign ;
wire [23:0] temp_out ;
wire temp_out_vld;
wire dout_sign ;
wire [23:0] dout ;
wire dout_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/1ps
module ds18b20_driver_tb();
//参数定义
parameter CYCLE = 20;
defparam u_ds18b20_driver.TIME_RST = 200,
u_ds18b20_driver.TIME_PRE = 100,
u_ds18b20_driver.TIME_WAIT = 750;
//信号定义
reg clk ;
reg rst_n ;
reg dq_in ;
wire dq_out ;
wire dq_out_en ;
wire temp_sign ;
wire [23:0] temp_out ;
wire temp_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命令状态(由于只有一个温度传感器,这里使用的是跳过命令)。


从机状态机IDLE–>LOW–>SEND:
ROM命令结束,从机状态机从发送状态转移为释放状态恢复时隙,然后拉低总线,一个新的时隙的开始。
略
边栏推荐
- Learning notes of MySQL series by database and table
- 十五、IO流(一)
- Jinglianwen Technology: current situation and solutions of data annotation industry
- Vue3 route cache component status and set transition animation
- If the key in redis data is in Chinese
- Kotlin foundation extension
- 【sketchup 2021】草图大师的图像输出与渲染之样式说明【边线设置、平面设置、背景设置、水印设置、建模设置、天空背景创建天空、利用水印背景创建天空(重要)】
- Outil de formatage du temps - mode. JS (affichage en temps réel du temps Web)
- Kotlin basic string operation, numeric type conversion and standard library functions
- JS case Xiaomi second kill countdown New Year Countdown
猜你喜欢

Differences between SQL and NoSQL of mongodb series

Machine learning notes - supervised learning memo list

如何从头自己制作开发板?图文并茂,一步步操作给你看。

AIO Introduction (VIII)

Periodontitis investigation (ongoing)

The new business outlet of beautiful Tiantian second mode will be popular in the Internet e-commerce market

16、 IO stream (II)

【马尔科夫链-蒙特卡罗】马尔科夫链-蒙特卡罗方法对先验分布进行抽样

MongoDB系列之SQL和NoSQL的区别

Chain 2+1 reward, what kind of mode is beautiful everyday seconds?
随机推荐
Ijkplayer code walkthrough player network video data reading process details
That is, after the negative impact of gcat advertising e-commerce, is there no stable advertising e-commerce platform?
Select all select none JS code implementation
Wechat game execution wx Navigatetominiprogram jumps to other games and returns to the black screen
机器学习笔记 - 监督学习备忘清单
DataGridView data export to excel (in case of small amount of data)
New Taishan crowdfunding business diversion fission growth model in 2022
Use of kotlin basic common sets list, set and map
What is the essence of social e-commerce disruption? How can businesses get more traffic?
JetPack - - -WorkManger
1154. day of the year
Time formatting tool ----moment JS (real time display of web page time)
Ijkplayer compilation process record
Gold jewelry enterprise operation mode, beautiful tiantians business solution
Kotlin foundation extension
Error in downloading opencv from pip
数据在内存中的存储(C语言)
If the key in redis data is in Chinese
想进行快速钢网设计,还能保证钢网质量? 来看这里
[cloud native | kubernetes] kubernetes configuration