锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

基于FPGA的ds18b20温度传感器使用

时间:2022-08-11 07:30:00 jst连接器b10pr3传感器参数传感器重复性的正负f0温度传感器然后外接传感器等一些器件传感器fc15

文章目录

    • 一.传感器介绍
      • 1.特点
      • 2.内部结构
      • 3.ds18b20管脚
      • 4.ds18b20内部高速临时存储器
      • 5.ds18b20工作时序
      • 6.ds18b20单线通信
    • 二.FPGA代码实现
      • 1.状态图
      • 2.代码实现
        • 2.1DS18B20驱动模块
        • 2.2数据处理模块
        • 2.3数字管驱动模块
        • 2.4顶层模块
      • 3.仿真

一.传感器介绍

ds18b20是一种常用的数字温度传感器,具有体积小、硬件成本低、抗干扰能力强、精度高等特点。

1.特点

ds18b单线数字温度传感器具有独特的优点:

( 1 )采用单总线接口法,微处理器微处理器连接只需一条线即可实现DS18B20双向通信。单总线具有经济性好、抗干扰能力强、现场温度测量适合恶劣环境、使用方便等优点,使用户能够轻松构建传感器网络,为测量系统的构建引入新的概念。
( 2 )测量温度范围宽。测量精度高 DS18B测量范围为20 -55 ℃ ~ 125 ℃ ; 在 -10~ 85°C范围内,精度为 ± 0.5°C 。
( 3 )使用中不需要任何外围元件。
( 4 )支持多点网络功能。多点网络功能。 DS18B20 可并联在唯一的单线上,实现多点测温。
( 5 )灵活的供电方式。DS18B20 数据线可以通过内部寄生电路获取电源。因此,当数据线上的时间顺序满足一定的要求时,不能连接外部电源,使系统结构更简单、更可靠。
( 6 )可配置测量参数。DS18B通过程序设置20的测量分辨率 9~12 位。
( 7 ) 当负压特性电源极性反转时,温度计不会因发热而燃烧,但不能正常工作。
( 8 )掉电保护功能。DS18B20 内部含有 EEPROM ,系统断电后,仍能保存分辨率和报警温度的设定值。

2.内部结构

ds18b20主要由四部分组成:64位ROM,温度传感器、非挥发性温度报警触发器TH和TL,配置寄存器。
64位ROM
ROM64位序列号出厂前用光刻制,可视为DS18B20地址序列码,每个DS18B2064位序列号不同。
64位ROM循环冗余校验码:
CRC=X X X 1
ROM它的作用是让每一个DS18B20都不一样,这样一条总线就可以挂多个DS18B20的目的。

3.ds18b20管脚

  1. GND为电源地;
  2. DQ输入/输出数字信号;
  3. VDD外部供电电源输入端(寄生电源接接地)

4.ds18b20内部高速临时存储器

高速临时存储器由9个字节组成。当温度转换命令发布时,转换获得的温度值以二字节补码的形式存储在高速临时存储器的第0和第1个字节中。单片机可以通过单线接口读取数据。读取时,相应的温度计算:当符号位置时S=0时,二进制位直接转换为十进制;当S=一、先将补码变成原码,再计算十进制值。

说明:

字节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微秒后释放为高电平,直到写周期结束。作为从机DS18B检测到总线后,等待15微秒,然后从15微秒开始us到45us总线开始采样,采样期内总线高电平为1,采样期内总线低电平为0。

读操作:
读数据操作时序也分为读0时序和读1时序两个过程。读时间隙是从主机拉下单总线后,在1微秒后释放单总线为高电平DS18B将数据传输到单总线。DS18B在检测到总线被拉低1微秒后,20开始发送数据,如果要发送0,将总线拉到低电平,直到读取周期结束。如果要发出1,释放总线为高电平。主机在开始拉低总线1微秒后释放总线,然后在15微秒内完成总线采样检测,包括前拉低总线电平1微秒。如果总线在采样期为低电平,则确认为0。采样期内总线为高电平,确认为1。至少需要60个读时过程才能完成读时过程us才能完成。

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为下一步做出回应 DS18B读写准备
搜索ROM 0F0H 用于确定同一总线上的挂接 DS18B20的个数和识别64位ROM地址,准备操作各种设备
跳过ROM 0CCH 忽略64位ROM地址,直接方向DS18B20发温度变换命令,适用于单片机工作
警报搜索 0ECH 该指令执行后,只有温度超过设定值上限或下限的电影才能响应

(3)发送存储指令

指令名称 指令代码 指令功能
温度变换 44H 启动 DS1820进行温度转换,转换时间最长为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驱动模块

在该模块,主从状态机都是属于FPGA主机部分,主状态机控制整个流程,而从状态机在主状态机的控制下实现相关主机状态下的操作。

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   ;

//主机状态机设计 描述状态转移
    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
    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);//主机读温度 --等待从机发送数据完成

//从机状态机设计 描述状态转移
    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
  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);//主状态机要发送ROM命令 温度转化命令 读温度命令 读温度过程 每1bit数据 按照协议要求主机拉低

    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
        end
    
    end
    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
        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;

    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
    end

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

    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
    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
    
    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 采样传感器的存在脉冲
    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
    end

    always@(posedge clk or negedge rst_n)begin//命令发送标志 (区分温度转换和温度读取命令)
        if(!rst_n)begin
            flag <= 0;
        end
        else if(m_wait2m_rest)begin//从等待状态转移到复位状态 flag置一读温度
            flag <= 1'b1;
        end
        else if(m_rtmp2m_idle) begin//从读温度状态转移到复位状态 flag置零读温度命令
            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 元器件数据手册IC替代型号,打造电子元器件IC百科大全!
          

相关文章