原创 两段式DS1820 verilog的实现

2011-7-3 23:10 848 0 分类: FPGA/CPLD

    写DS1820的时候发现网上的都是一段式状态机,就想写一个两段式的,可是花了一个星期才写好,编译的时候总是有组合环合锁存器的警告,怎么都去不掉,一直在想是不是两段式根本写不了,都快放弃了,可是书上说没有什么是两段式写不了的。

    搞了好久,后来看了特权的《两段式不可能完成的状态机》,才知道问题出在哪,就是敏感变量不能出现作为输出出现在第二段的always段中,也不能改变其值,如果要改变,必须设一个中间变量,最后在板子上调通了,下来是代码,我的注释应该还算是详细吧,我是菜鸟,有问题帮我指出来吧,

module DS1820_ctrl (
      CLK,RSTn,
      DQ,
      readtemp,
      clk1us
     );
     
    
input   CLK;   //50MHz
input   RSTn;   //复位信号,低有效

inout   DQ;    //一线数据信号
output[7:0]  readtemp;  //读出的温度并送给7段数码管
output   clk1us;

 

reg[7:0]  counter_1us;
reg    clk_1us;

parameter  DELAY_1US  = 49;
parameter  DELAY_800MS  = 800000;
parameter  DELAY_1MS   = 1000;
parameter  DELAY_2MS   = 2000;
parameter  DELAY_10MS   = 10000;
parameter  DELAY_20MS   = 20000;
parameter  DELAY_30MS   = 30000;
parameter  DELAY_40MS   = 40000;
parameter  DELAY_50MS   = 50000;
parameter  DELAY_100MS  = 100000;
parameter  DELAY_200MS  = 200000;
parameter  DELAY_300MS  = 300000;
parameter  DELAY_400MS  = 400000;
parameter  DELAY_500MS  = 500000;
parameter  DELAY_600MS  = 600000;
parameter  DELAY_1S   = 1000000;
parameter  DELAY_2S   = 2000000;
parameter  DELAY_10S   = 10000000;
parameter  DELAY_100US  = 100;
parameter  DELAY_200US  = 200;
parameter  DELAY_300US  = 300;
parameter  DELAY_400US  = 400;
parameter  DELAY_500US  = 500;
parameter  DELAY_600US  = 600;
parameter  DELAY_550US  = 550;
parameter  DELAY_2US   = 2;
parameter  DELAY_4US   = 4;
parameter  DELAY_6US   = 6;
parameter  DELAY_8US   = 8;
parameter  DELAY_12US   = 12;
parameter  DELAY_15US   = 15;
parameter  DELAY_20US  = 20;
parameter  DELAY_25US   = 25;
parameter  DELAY_30US  = 30;
parameter  DELAY_40US  = 40;
parameter  DELAY_70US  = 70;
parameter  DELAY_850US  = 850;

always@(posedge CLK or negedge RSTn) begin
 if(!RSTn) counter_1us<=8'd0;
 else if(counter_1us==DELAY_1US) begin
  counter_1us<=8'd0;
  clk_1us<=1'b1;
 end
 else begin
  counter_1us<=counter_1us+1'b1;
  clk_1us<=1'b0;
 end
end


reg[23:0]  counter;  //
reg[23:0]  counter1;
reg    clk1us_start;

always@(posedge CLK or negedge RSTn) begin
 if(!RSTn) counter<=24'd0;
 else if(!clk1us_start) begin
  counter<=24'd0; //只要允许位置0则counter归0
 end
 else if(clk_1us) begin
  counter<=counter1+1'b1;
 end
end


parameter CMD_SKIP = 8'b1100_1100; //
parameter CMD_READ = 8'b1011_1110;
parameter CMD_CONVERT = 8'b0100_0100;

parameter RESET1  = 3'b000;
parameter WRITE_BYTE1 = 3'b001;
parameter WAIT_CONVERT = 3'b010;
parameter RESET2  = 3'b011;
parameter WRITE_BYTE2 = 3'b100;
parameter READ_BYTE = 3'b101;
parameter WAIT1S  = 3'b110;

//第一段:状态转移

reg[2:0] cstate;  //current state
reg[2:0] nstate;  //next state

reg[7:0] temp_data;  //
reg[3:0] data_bit;

//reg[7:0] read_temp;  //
//reg[7:0] read_temp1;  //

reg[7:0] temp_data1;  //
reg[3:0] data_bit1;

reg   dq_ctrl;  //dq_ctrl=1'b1;output    dq_ctrl=1'b0;high Z  
reg   dq_r;

reg   dq_ctrl1;  //dq_ctrl=1'b1;output    dq_ctrl=1'b0;high Z  
reg   dq_r1;

always @ (posedge CLK or negedge RSTn)  //异步复位
 if(!RSTn) begin
  cstate <= RESET1;
 end
 else begin
  cstate <= nstate;//注意,使用的是非阻塞赋值
  temp_data<=temp_data1;
  data_bit<=data_bit1;
  dq_ctrl<=dq_ctrl1;
  dq_r<=dq_r1;
 end


//第二段;状态逻辑判断
always@(cstate or DQ or counter or data_bit or temp_data or dq_r or dq_ctrl) begin  //任何一个敏感变量变化都引起变化才不会产生锁存器
 nstate=cstate;
 temp_data1=temp_data;
 data_bit1=data_bit;
 dq_ctrl1=dq_ctrl;
 dq_r1=dq_r;
 counter1=counter;
 case(cstate)
  //=========================================================
  /*主机拉低DQ到0至少480US,然后释放DQ,这时DQ会被上拉电阻从0上拉到1,一旦DS1820检测到
  DQ引脚从0->1这个上升沿,DS1820就会等待15-60US然后发送应答脉冲(一个60-240US的低电平)*/    
  RESET1:  begin
      clk1us_start=1'b1; //启动1us时钟计数
      case(counter)
       DELAY_2US:  begin
            dq_ctrl1=1'b1; //set to output
            dq_r1=1'b0;  //pull down to 0 for at least 480us
            nstate=RESET1;
           end
       DELAY_500US: begin//拉低数据线至少480US,这里设为500US
            dq_ctrl1=1'b0; //enable input
            dq_r1=1'b1;
            nstate=RESET1;
           end
       DELAY_550US: begin//DS1820在0->1上升沿等待15-60US再应答,这里设为50US
            if(DQ==1'b1) begin
             nstate=WAIT1S;//如果接收到的数据线电平是1,则复位错误
             //nstate=RESET1;
            end
            else if(DQ==1'b0) begin
             nstate=RESET1;
            end
           end
       DELAY_850US: begin//收到应答信号后等待270US才进入发数据状态
            temp_data1=CMD_SKIP;
            nstate=WRITE_BYTE1;
            clk1us_start=1'b0;  //停止1us时钟计数
            dq_ctrl1=1'b1;  //set to output
            dq_r1=1'b1;
            data_bit1=0; 
           end
       default:  nstate=RESET1;
      endcase
     end
  //=========================================================
  //写1要先拉低1-15us,写0要拉低一直到写0结束,每一位的1到0作为读周期的开始
  //写0写1都要保持60us
  //2个写时序之间至少1us恢复期
  //主机把数据线从1拉到0告诉DS1820写数据开始
  WRITE_BYTE1: begin
       clk1us_start=1'b1; //启动1us时钟计数
       case(counter)
        DELAY_20US:  begin
             dq_r1=1'b0; //不论写0写1都要拉低数据
             nstate=WRITE_BYTE1;
            end
        DELAY_30US:  begin
             case(data_bit)
              4'd0:  dq_r1=temp_data[0]; //from LSB
              4'd1:  dq_r1=temp_data[1];
              4'd2:  dq_r1=temp_data[2];
              4'd3:  dq_r1=temp_data[3];
              4'd4:  dq_r1=temp_data[4];
              4'd5:  dq_r1=temp_data[5];
              4'd6:  dq_r1=temp_data[6];
              4'd7:  dq_r1=temp_data[7];
              default: ;
             endcase
             nstate=WRITE_BYTE1;
            end
        DELAY_100US:  begin  //写0写1都要保持60us
             clk1us_start=1'b0;//counter清0
             if(data_bit<7) begin
              data_bit1=data_bit+1'b1;
              dq_r1=1'b1; //写完1位拉高数据
              nstate=WRITE_BYTE1;
             end
             else if(temp_data==CMD_SKIP) begin
              data_bit1=0;
              dq_r1=1'b1; //写完1位拉高数据,第7位虽然写完,但在循环语句中没有拉高
              temp_data1=CMD_CONVERT;          
              nstate=WRITE_BYTE1;
             end
             else if(temp_data==CMD_CONVERT) begin
              data_bit1=0;
              dq_r1=1'b1; //写完1位拉高数据,第7位虽然写完,但在循环语句中没有拉高
              nstate=WAIT_CONVERT; 
             end
            end
        default:  nstate=WRITE_BYTE1;
       endcase 
      end     
  //=========================================================
  WAIT_CONVERT: begin
       clk1us_start=1'b1; //启动1us时钟计数
       if(counter==DELAY_800MS) begin
        clk1us_start=1'b0;
        nstate=RESET2;
       end
       else begin
        nstate=WAIT_CONVERT;
       end
      end
  //========================================================= 
  RESET2:   begin
       clk1us_start=1'b1; //启动1us时钟计数
       case(counter)
        DELAY_2US:  begin
             dq_ctrl1=1'b1; //set to output
             dq_r1=1'b0;  //pull down to 0 for at least 480us
             nstate=RESET2;
            end
        DELAY_500US: begin//拉低数据线至少480US,这里设为500US
             dq_ctrl1=1'b0; //set to input
             nstate=RESET2;
            end
        DELAY_550US: begin//DS1820在0->1上升沿等待15-60US再应答,这里设为50US
             if(DQ==1'b1) begin
              nstate=WAIT1S;//如果接收到的数据线电平是1,则复位错误
              //nstate=RESET2;
             end
             else if(DQ==1'b0) begin
              nstate=RESET2;
             end
            end
        DELAY_850US: begin//收到应答信号后等待270US才进入发数据状态
             temp_data1=CMD_SKIP;
             nstate=WRITE_BYTE2;
             clk1us_start=1'b0;  //停止1us时钟计数
             dq_ctrl1=1'b1;  //set to output
             dq_r1=1'b1;
             data_bit1=0; 
            end
        default:  nstate=RESET2;
       endcase
      end 
  //========================================================= 
  WRITE_BYTE2: begin
       clk1us_start=1'b1; //启动1us时钟计数
       case(counter)
        DELAY_20US:  begin
             dq_r1=1'b0; //不论写0写1都要拉低数据
             nstate=WRITE_BYTE2;
            end
        DELAY_30US:  begin
             case(data_bit)
              4'd0:  dq_r1=temp_data[0]; //from LSB
              4'd1:  dq_r1=temp_data[1];
              4'd2:  dq_r1=temp_data[2];
              4'd3:  dq_r1=temp_data[3];
              4'd4:  dq_r1=temp_data[4];
              4'd5:  dq_r1=temp_data[5];
              4'd6:  dq_r1=temp_data[6];
              4'd7:  dq_r1=temp_data[7];
              default: ;
             endcase
             nstate=WRITE_BYTE2;
            end
        DELAY_100US: begin  //写0写1都要保持60us
        //DELAY_6US:  begin  //写0写1都要保持60us
             clk1us_start=1'b0;//counter清0
             if(data_bit<7) begin
              data_bit1=data_bit+1'b1;
              dq_r1=1'b1; //写完1位拉高数据
              nstate=WRITE_BYTE2;
             end
             else if(temp_data==CMD_SKIP) begin
              data_bit1=0;
              dq_r1=1'b1; //写完1位拉高数据
              temp_data1=CMD_READ;          
              nstate=WRITE_BYTE2;
             end
             else if(temp_data==CMD_READ) begin 
              data_bit1=0;
              dq_r1=1'b1; //写完1位拉高数据
              nstate=READ_BYTE; 
             end
            end
        default:  nstate=WRITE_BYTE2;
       endcase
      end   
  
  //=========================================================
  //每一位的1到0作为读周期的开始
  //读完1位数至少要延迟60us
  //要右移9位 
  READ_BYTE: begin
      clk1us_start=1'b1; //启动1us时钟计数
      case(counter)
       DELAY_12US:  begin
            dq_r1=1'b0; //不论写0写1都要拉低数据
            nstate=READ_BYTE;
           end
       DELAY_15US:  begin //主机拉低数据后至少要保持1US的低电平
            dq_ctrl1=1'b0;//set to high Z
            nstate=READ_BYTE;
           end
       DELAY_25US:  begin//DS1820在主机拉低后开始的15US内都保持数据有效
            case(data_bit)
             //4'd0:  temp_data1[0]=DQ; //from LSB
             4'd1:  temp_data1[0]=DQ; 
             4'd2:  temp_data1[1]=DQ; 
             4'd3:  temp_data1[2]=DQ; 
             4'd4:  temp_data1[3]=DQ; 
             4'd5:  temp_data1[4]=DQ; 
             4'd6:  temp_data1[5]=DQ; 
             4'd7:  temp_data1[6]=DQ;
             4'd8:  temp_data1[7]=DQ;
             default: ;
            endcase
            nstate=READ_BYTE;
           end
       DELAY_100US: begin
            if(data_bit<8) begin
             data_bit1=data_bit+1'b1;
             dq_ctrl1=1'b1; //set to output
             clk1us_start=1'b0;//counter清0,开始下一位
             nstate=READ_BYTE;
            end
            else begin
             data_bit1=0;
             dq_ctrl1=1'b1; //set to output
             dq_r1=1'b1;
             clk1us_start=1'b0;
             nstate=WAIT1S;
            end
           end
       default:  nstate=READ_BYTE;
      endcase
     end
  //========================================================= 
  WAIT1S:  begin
      clk1us_start=1'b1; //启动1us时钟计数
      if(counter==DELAY_800MS) begin
       clk1us_start=1'b0;//此处少了一句清0,致使在RESET时候要等counter计数溢出才会执行
       nstate=RESET1;
      end
      else begin
       nstate=WAIT1S;
      end
     end      
  //=========================================================
  default: nstate=RESET1;
 endcase 
end

assign DQ = dq_ctrl ? dq_r:1'bz;
assign readtemp = temp_data;
assign clk1us = clk_1us;

endmodule

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
0
关闭 站长推荐上一条 /3 下一条