DS18B20 FPGA

最近,写了好几个关于通信的,只要原理理解对了,你按照时序要求,写就好了。可是有些时候,我们对通讯的时序会理解的不对。这个时候多写几个通讯时序,就慢慢熟悉了。

你的DQ数据线要设置为:inout,千万别忘记了

写程序之前,还是先介绍一下ds18b20的时序图:

ds18b20的经典通讯过程如下:

我们就可以按照这个通讯方式去写程序:只要按照这个步骤,一步一步的写就可以了:

首先任何的通讯都需要一个初始化,就是互相告诉彼此,我们要准备通讯了,要准备传输数据了,ds18b20也不例外,

下面是初始化的时序过程:

主机先发送480—960us的低电平信号(你可以给个600us的低电平就可以),然后释放主机总线,交给从机,从机会响应60—240左右的低电平信号,之后,从机会把总线拉高。

然后就是写命令给ds18b20,写数据时,低位先传,然后是高位,写数据也分写0和写1

比如你写0,主机只要拉低总线60us以上就ok了,从机会在15us---60us之间,采集总线上的信号,这时采集的信号正好是0,这样就写完了一个数据,

比如你写1,这个稍微过程麻烦一下,首先主机要先拉低总线1us以上,然后在15us以内释放总线(包括之前的那个拉低1us的时间),从机会在15us---60us,采集总线上的数据,由于主机已经释放了总线,现在总线属于高电平,这时,从机就把1写进去了。

注意:写0和写1时间要大于60us以上,两次写的间隙,主机要释放总线1us以上

,现在,数据可以写入ds18b20中了,那如何从ds18b20中读取温度的值呢,

首先,我们要先发送读暂存器命令(BE),告诉从机要开始发送数据了,这时,我们要开始准备接受从机发过的数据,从机先发BYTE0,然后是BYTE1,....BYTE0先发低位,然后是高位。千万别搞错了。

如何读从机发来的数据呢,看看下面的时序图:

 

主机先拉低1us以上,然后释放总线、这个时候,从机就发送数据过来,从机会在15us,以内拉低总线(发送0),或者拉高总线(发送1)我们就要在15us以内的这段时间(包括之前的拉低1us),读这个值,我们可以在大概10us的时候读这个值,但是我们也要在15us以内释放总线。

下面是别人介绍的关于读写0,1,可以参考一下。

代码:每个人写的代码都不同,只要你原理懂了,用代码实现就可以了

按键按下,启动温度转换,下面输出的数据data就是温度值,按键和led是做测试的用的。要多用sigaltap去调试代码,看看自己错在那个步骤

 

module ds18b20_dri(
    //module clock
    input              clk        ,         // 时钟信号(50MHz)
    input              rst_n      ,         // 复位信号
    input   [1:0]      key,
    output  reg [1:0]  led,
    inout              dq         ,         // DS18B20的DQ引脚数据
    output reg [19:0]  data                 // 转换后得到的温度值
);

reg  [15:0] temp_data;
reg   sign;
reg  [10:0] data1;
wire [19:0] data2;


//状态机
parameter  IDLE                     = 5'b00000; //空闲状态
parameter  RST_PULSE                = 5'b00001; //主机复位脉冲  
parameter  DS18B20_WAIT             = 5'b00010; //从机等待15--60us
parameter  ANSWER_PULSE             = 5'b00011; //从机应答
parameter  SKIP_ROM_DATA            = 5'b00100; //跳过ROM(CC)
parameter  CONVERT_TEMP_DATA        = 5'b00101; //温度转换(44)
parameter  WAITING_500MS            = 5'b00110; //等待温度转换500ms
parameter  AGAIN_RST_PULSE          = 5'b00111; //第二次主机再次复位
parameter  DS18B20_WAIT1            = 5'b01000; //第二次从机等待15--60us
parameter  AGAIN_ANSWER_PULSE       = 5'b01001; //第二次从机应答
parameter  AGAIN_SKIP_ROM_DATA      = 5'b01010; //第二次跳过ROM(CC)
parameter  READ_TEMP_DATA           = 5'b01011; //读高速缓存器的值(BE)
parameter  READ_DATA                = 5'b01100; //读16位数据


reg [4:0] state_c;
reg [4:0] state_n;   


wire idl2rst_pulse_start ;                //空闲到复位脉冲             
wire rst_pulse2ds18b20_wait_start;        //复位脉冲到DS18B20从机等待
wire ds18b20_wait2answer_pluse_start;     //DS18B20从机等待到应答信号     
wire answer_pulse2skip_rom_start;         //应答信号到跳过ROM 
wire skip_rom2conv_temp_start;            //跳过ROM到温度转换  
wire conv_temp2wait_500ms_start;          //温度转换到等待500ms 
wire wait_500ms2again_rst_start;          //500ms到再次复位
wire again_rst2ds18b20_wait_start ;       //复位脉冲到DS18B20从机等待
wire ds18b20_wait2again_answer_start ;    //从机等待到应答
wire again_answer2again_skip_rom_start;   //应答信号到跳过rom
wire again_skip_rom2again_conv_temp_start;//跳过rom到读暂存器
wire again_conv_temp2read_data_start;     //读暂存器到读数据
wire read_data2idle_start;                //读16位数据到空闲 

//parameter define
localparam  ROM_SKIP_CMD = 8'hcc;           // 跳过 ROM 命令  1100 1100
localparam  CONVERT_CMD  = 8'h44;           // 温度转换命令   0100 0100  
localparam  READ_TEMP    = 8'hbe;           // 读 DS1820 温度暂存器命令 1011 1110

parameter  TIME_500MS = 28'd3000_0000;    //500ms
parameter  TIME_600US = 28'd30000;    //600us
parameter  TIME_240US = 28'd12000;    //240us
parameter  TIME_72US  = 28'd3600;    //72us
parameter  TIME_70US  = 28'd3500;    //72us
parameter  TIME_60US  = 28'd3000;    //60us
parameter  TIME_10US  = 28'd500;     //2us
parameter  TIME_3US   = 28'd150;     //2us
parameter  TIME_2US   = 28'd100;     //2us
parameter  TIME_1US   = 28'd50;      //1us

//600ms 50*1000*600=3000_0000
//计数器,计算时间
reg   [27:0] cnt;
reg   flag_cnt;
wire  add_cnt;
wire  end_cnt;
reg   [27:0] Time;

//计算器,计算吧第几个发送出去,一共8个
reg [3:0] cnt1;
wire  add_cnt1;
wire  end_cnt1;
reg write_flag;

//计算器,计算吧第几个读出去,一共16个
reg [4:0] cnt2;
wire  add_cnt2;
wire  end_cnt2;
reg  read_flag;


//按键
wire  neg_key_in1;
wire  neg_key_in2;
reg   key_in1_d0,key_in1_d1;
reg   key_in2_d0,key_in2_d1;
reg   start_flag1,start_flag2;


reg            dq_dir     ;  // DQ数据(SDA)方向控制
reg            dq_out     ;  // DQ输出信号
wire           dq_in      ;  // DQ输入信号 


wire  pos_dq_in,neg_dq_in;
reg   dq_in_d0,dq_in_d1;
//计时
reg  [15:0]  neg_cnt,pos_cnt;



//检测DQ的下降沿和上升沿
assign  pos_dq_in = (~dq_in_d1) & dq_in_d0;    //上升沿检测,如果检测到上升沿,有0到1,之后下个周期为0
assign  neg_dq_in =  dq_in_d1 & (~dq_in_d0);   //下降沿

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        dq_in_d0<=1'b0;
        dq_in_d1<=1'b0;
    end
    else  begin
        dq_in_d0<=dq_in;
        dq_in_d1<=dq_in_d0;
    end
end

//下降沿来了,开始计数,这个方法很好用,专门检测低电平的时间
//当低电平来了,低电平neg_cnt清零并开始自己计数,当上升沿来的那刻,去读neg_cnt这个值,就是低电平的时间
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        neg_cnt<=0;   
    end
    else begin
        if(neg_dq_in) //重新清零
            neg_cnt<=0; 
       else 
            neg_cnt<=neg_cnt+1; 
    end
end

//上升沿来了,开始计数,
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        pos_cnt<=0;   
    end
    else begin
        if(pos_dq_in) //重新清零
            pos_cnt<=0; 
       else 
            pos_cnt<=pos_cnt+1; 
    end
end
assign  dq     = dq_dir ?  dq_out : 1'bz;  // DQ数据输出或高阻
assign  dq_in  = dq ;                      // DQ数据输入

//状态机
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段:组合逻辑always模块,描述状态转移条件判断
always@(*)begin
    case(state_c)
        IDLE:begin
            if(idl2rst_pulse_start)begin
                state_n = RST_PULSE;
            end
            else begin
                state_n = state_c;
            end
        end
        RST_PULSE:begin
            if(rst_pulse2ds18b20_wait_start)begin
                state_n = DS18B20_WAIT;
            end
            else begin
                state_n = state_c;
            end
        end
		  DS18B20_WAIT:begin
            if(ds18b20_wait2answer_pluse_start)begin
                state_n = ANSWER_PULSE;
            end
            else begin
                state_n = state_c;
            end
        end
        ANSWER_PULSE:begin
            if(answer_pulse2skip_rom_start)begin
                state_n = SKIP_ROM_DATA;
            end
            else begin
                state_n = state_c;
            end
        end
        SKIP_ROM_DATA:begin
            if(skip_rom2conv_temp_start)begin
                state_n = CONVERT_TEMP_DATA;
            end
            else begin
                state_n = state_c;
            end
        end
        CONVERT_TEMP_DATA:begin
            if(conv_temp2wait_500ms_start)begin
                state_n = WAITING_500MS;
            end
            else begin
                state_n = state_c;
            end
        end
         WAITING_500MS:begin
            if(wait_500ms2again_rst_start)begin
                state_n = AGAIN_RST_PULSE;
            end
            else begin
                state_n = state_c;
            end
        end
        AGAIN_RST_PULSE:begin
            if(again_rst2ds18b20_wait_start)begin
                state_n = DS18B20_WAIT1;
            end
            else begin
                state_n = state_c;
            end
        end
		  DS18B20_WAIT1:begin
		     if(ds18b20_wait2again_answer_start)begin
                state_n = AGAIN_ANSWER_PULSE;
            end
            else begin
                state_n = state_c;
            end
		  end
         AGAIN_ANSWER_PULSE:begin
            if(again_answer2again_skip_rom_start)begin
                state_n = AGAIN_SKIP_ROM_DATA;
            end
            else begin
                state_n = state_c;
            end
        end
         AGAIN_SKIP_ROM_DATA:begin
            if(again_skip_rom2again_conv_temp_start)begin
                state_n = READ_TEMP_DATA;
            end
            else begin
                state_n = state_c;
            end
        end
        READ_TEMP_DATA:begin
            if(again_conv_temp2read_data_start)begin
                state_n = READ_DATA;
            end
            else begin
                state_n = state_c;
            end
        end
       READ_DATA:begin
            if(read_data2idle_start)begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end

        default:begin
            state_n = IDLE;
        end
    endcase
end
 
//第三段:设计转移条件
assign idl2rst_pulse_start                   = state_c==IDLE                    && end_cnt;
assign rst_pulse2ds18b20_wait_start          = state_c==RST_PULSE               && end_cnt;
assign ds18b20_wait2answer_pluse_start       = state_c==DS18B20_WAIT            && end_cnt;
assign answer_pulse2skip_rom_start           = state_c==ANSWER_PULSE            && (pos_dq_in&&neg_cnt>TIME_60US&&neg_cnt<TIME_240US);
assign skip_rom2conv_temp_start              = state_c==SKIP_ROM_DATA           && end_cnt1;
assign conv_temp2wait_500ms_start            = state_c==CONVERT_TEMP_DATA       && end_cnt1;
assign wait_500ms2again_rst_start            = state_c==WAITING_500MS           && end_cnt;
assign again_rst2ds18b20_wait_start          = state_c==AGAIN_RST_PULSE         && end_cnt;
assign ds18b20_wait2again_answer_start       = state_c==DS18B20_WAIT1           && end_cnt;
assign again_answer2again_skip_rom_start     = state_c==AGAIN_ANSWER_PULSE      && (pos_dq_in&&neg_cnt>TIME_60US&&neg_cnt<TIME_240US);
assign again_skip_rom2again_conv_temp_start  = state_c==AGAIN_SKIP_ROM_DATA     && end_cnt1;
assign again_conv_temp2read_data_start       = state_c==READ_TEMP_DATA          && end_cnt1;
assign read_data2idle_start                  = state_c==READ_DATA               && end_cnt2;

//初始化过程
always @(posedge clk or negedge rst_n) begin
    //复位初始化
    if(rst_n == 1'b0) begin
         dq_dir<=1;  //设置DQ为输出
         dq_out<=1;  //设置DQ输出高电平
         flag_cnt<=0;
         Time<=1;
         temp_data<=0;
    end
    else if(start_flag1)begin  //当按键按下时,温度开始转换
          case(state_c)
            IDLE:begin         //空闲先拉高1us,自己理解的
                  dq_dir<=1;  
                  dq_out<=1;  
                  Time<=TIME_1US;
                  flag_cnt<=1;   
            end
            RST_PULSE:begin      //主机拉低600us发送复位脉冲
                  dq_dir<=1;    
                  dq_out<=0;    
                  Time<=TIME_600US;
                  flag_cnt<=1;  
            end
		   DS18B20_WAIT:begin   //释放总线,等着15-60us之后,从机把总线拉低
				  dq_dir<=0;    
				  Time<=TIME_10US;
                  flag_cnt<=1;  
			end
           ANSWER_PULSE:begin //从机把总线拉低60--240us,之后,从机又把总线拉高,结束应答信号
                 dq_dir<=0;     
                 flag_cnt<=0;   
            end
		  SKIP_ROM_DATA:begin    
                if(ROM_SKIP_CMD[cnt1]==0) begin //判断第一位是0还是1
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;       //拉高
				       dq_out<=1;       
                    end
				    else if(cnt<TIME_70US)begin
					   dq_dir<=1;      
				       dq_out<=0;        
				    end
                    else  begin
                       dq_dir<=0;       //释放总线2us,保证在两次写间隙时间大于1us
                    end
				    Time<=TIME_72US;  //设置每个写间隙时间为70us,释放总线的时间2us,数据手册上:写间隙大于60us
                    flag_cnt<=1;      
               end
               else begin              //判断第一位是0还是1
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;     
				        dq_out<=1;       
                    end
					else if(cnt<TIME_3US)begin //拉低总线1us以上,在15us之内释放总线,我是拉低2us左右,释放总线
						dq_dir<=1;      
				        dq_out<=0;        
					end
                    else  begin
                      dq_dir<=0;       //包括释放总线
                    end
				    Time<=TIME_72US; 
                    flag_cnt<=1;      
               end       	  
			end
         CONVERT_TEMP_DATA:begin   
                if(CONVERT_CMD[cnt1]==0) begin
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;     
				           dq_out<=1;       
                    end
				    else if(cnt<TIME_70US)begin
						     dq_dir<=1;    
				           dq_out<=0;        
						  end
                    else  begin
                      dq_dir<=0;       
                    end
				       Time<=TIME_72US; 
                   flag_cnt<=1;      
               end
               else begin
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;     
				           dq_out<=1;       
                    end
				    else if(cnt<TIME_3US)begin
						     dq_dir<=1;    
				           dq_out<=0;        
						  end
                    else  begin
                      dq_dir<=0;      
                    end
				    Time<=TIME_72US; 
                    flag_cnt<=1;      
               end       	  
			end
         WAITING_500MS:begin
                 dq_dir<=1;       
				     dq_out<=1;          
				     Time<=TIME_500MS; 
                 flag_cnt<=1;    
          end
            AGAIN_RST_PULSE:begin
                 dq_dir<=1;   
                 dq_out<=0;   
                 Time<=TIME_600US;
                 flag_cnt<=1; 
            end
				DS18B20_WAIT1:begin
				     dq_dir<=0;  
					  Time<=TIME_10US;
                 flag_cnt<=1; 
				end
            AGAIN_ANSWER_PULSE:begin
                 dq_dir<=0;    
                 flag_cnt<=0;  
            end
            AGAIN_SKIP_ROM_DATA:begin   
                if(ROM_SKIP_CMD[cnt1]==0) begin
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;     
				           dq_out<=1;       
                    end
						  else if(cnt<TIME_70US)begin
						     dq_dir<=1;   
				           dq_out<=0;        
						  end
                    else  begin
                      dq_dir<=0;  //释放总线,2us    
                    end
				       Time<=TIME_72US; 
                   flag_cnt<=1;      
               end
               else begin
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;      
				           dq_out<=1;       
                    end
						  else if(cnt<TIME_3US)begin
						     dq_dir<=1;    
				           dq_out<=0;        
						  end
                    else  begin
                      dq_dir<=0;       //释放总线,2us
                    end
				        Time<=TIME_72US; 
                    flag_cnt<=1;      
               end       	  
			end
            READ_TEMP_DATA:begin  
            if(READ_TEMP[cnt1]==0) begin
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;     
				           dq_out<=1;       
                    end
						  else if(cnt<TIME_70US)begin
						     dq_dir<=1;      
				           dq_out<=0;        
						  end
                    else  begin
                      dq_dir<=0;      
                    end
				       Time<=TIME_72US; 
                   flag_cnt<=1;      
               end
               else begin
                    if(cnt<TIME_1US) begin
                       dq_dir<=1;       
				           dq_out<=1;       
                    end
						  else if(cnt<TIME_3US)begin
						     dq_dir<=1;      
				           dq_out<=0;        
						  end
                    else  begin
                      dq_dir<=0;       
                    end
				        Time<=TIME_72US; 
                    flag_cnt<=1;      
               end       	  
			end
            READ_DATA:begin       
               if(cnt<TIME_70US) begin
					    if(cnt<TIME_2US) begin 
						    dq_dir<=1;         
				            dq_out<=1;         
						 end
                   else if(cnt<TIME_3US) begin  //拉低最少1us
                          dq_dir<=1;         
				              dq_out<=0;        
                  end
                  else begin
						    dq_dir<=0;       //释放总线
                      if(cnt==TIME_10US) begin  //在15us以内读取数据,我是在10us的时刻,读取从机发过来的数据
                          temp_data[cnt2]<=dq_in;
                      end
                  end
               end
               else  begin
                     dq_dir<=0;       //释放总线2us
               end
				Time<=TIME_72US;   
                flag_cnt<=1;      	  
            end
            default:; 
          endcase
    end
end    

//计算时间
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
end
assign add_cnt = flag_cnt;       
assign end_cnt = add_cnt && cnt==Time-1;   

//计算器
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt  && write_flag ;      
assign end_cnt1 = add_cnt1 && cnt1==8-1 ;  //八个数据,发送出去了,结束标志 

//计数器,
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt2 <= 0;
    end
    else if(add_cnt2)begin
        if(end_cnt2)
            cnt2 <= 0;
        else
            cnt2 <= cnt2 + 1;
    end
end

assign add_cnt2 = end_cnt  && read_flag;       
assign end_cnt2 = add_cnt2 && cnt2==16-1 ; //16个数据读进来,结束标志  


//写8位数据的标志位
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
       write_flag<=0;
    end
    else  if(state_c==SKIP_ROM_DATA||state_c==CONVERT_TEMP_DATA||state_c==AGAIN_SKIP_ROM_DATA||state_c==READ_TEMP_DATA)   begin
       write_flag<=1;   
    end
    else begin
       write_flag<=0;   
    end
end


//读8位数据的标志
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        read_flag<=0;
    end
    else if(state_c==READ_DATA) begin
        read_flag<=1;
    end
    else begin
        read_flag<=0;
    end
end


//数据转换
//判断符号位
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sign  <=  1'b0;
        data1 <= 11'b0;
    end
    else if(temp_data[15] == 1'b0) begin
        sign  <= 1'b0;
        data1 <= temp_data[10:0];
    end
    else if(temp_data[15] == 1'b1) begin
        sign  <= 1'b1;
        data1 <= ~temp_data[10:0] + 1'b1;
    end
end

//对采集到的温度进行转换
assign data2 = (data1 * 11'd625)/ 7'd100;

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
       data<=123;
    end
    else begin
      data<=data2;  //将读出的值显示到数码管上
    end
end


//测试
assign  neg_key_in1 =  key_in1_d1 & (~key_in1_d0);  
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_in1_d0<=1'b0;
        key_in1_d1<=1'b0;
    end
    else  begin
        key_in1_d0<=key[0];
        key_in1_d1<=key_in1_d0;
    end
end

assign  neg_key_in2 =  key_in2_d1 & (~key_in2_d0);  

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_in2_d0<=1'b0;
        key_in2_d1<=1'b0;
    end
    else  begin
        key_in2_d0<=key[1];
        key_in2_d1<=key_in2_d0;
    end
end


//测试
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        start_flag1<=0;
    end
    else if(neg_key_in1) begin
       start_flag1<=1;
    end
end


//测试
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        start_flag2<=0;
    end
    else if(neg_key_in2) begin
       start_flag2<=1;
    end
end

//测试
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        led[0]<=1;
    end
    else if(start_flag1) begin
        led[0]<=0;
    end 
end


//测试
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        led[1]<=1;
    end
    else if(start_flag2) begin
        led[1]<=0;
    end 
end

endmodule


 

 

  • 8
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值