前段时间在一个项目上使用了一个温度传感器,主控芯片实在是太局促了。需要8个温度传感器但是只剩了两个IO,就只能选择这种温度传感器了,所有的传感器只需要一个引脚就可以串起来。 总量一定的情况下,硬件越简单,程序就越复杂。建议不到紧急情况不要使用串联的方式,使用麻烦、开发麻烦、排故工作量大。在网上下载了很多代码,都是单个读取,没有符合使用要求的,也没有整个串联说明白的,于是就想写一篇他的文章。

长话短说,由于只有一根线,串联起来想要同时控制,必不可少的就是每个设备都要有编号,这样才可以一对一通信,要不然一个命令下去,从设备七嘴八舌的,总线就乱了。

分两个步骤

第一步:写一个工装程序,用来读取ID。然后将ID记录下来,有条件的贴上个胶带不要混淆。

第二部:写正式程序,程序里需要对系统内的每个18B20的ID进行宏定义。然后逐个读取。下面是开发过程中的程序,里面定义了四个串联的情况。

//--3段式状态机(Moore)实现的DS18B20驱动
// 初始化-跳过rom-温度转换-等待转换-初始化2-匹配rom-发送rom-读取温度。
//==================================================================//------------<模块及端口声明>----------------------------------------
module ds18b20_dri(
input                   sys_clk_n           , // 25MHz时钟信号
input                   sys_clk_p           ,       //系统时钟,50Minput              rst_n       ,       //低电平有效的复位信号    inout               dq          ,       //单总线(双向信号)output reg [19:0]   temp_data      ,       // 转换后得到的温度值output reg          sign                // 符号位
);INBUF_DIFF CLK                 //输入时钟缓存
(
.PADN (sys_clk_n),
.PADP (sys_clk_p),
.Y (clk)
);
//------------<参数定义>----------------------------------------------
//状态机状态定义
localparam  INIT1       = 6'b000001 ,WR_CMD      = 6'b000010 ,WAIT          = 6'b000100 ,INIT2        = 6'b001000 ,RD_CMD   = 6'b010000 ,TX_ROM      = 6'b100001 ,WR_READ_CMD = 6'b100011 ,RD_DATA    = 6'b100000;//时间参数定义
localparam  T_INIT = 1000      ,           //初始化最大时间,单位usT_WAIT = 780_000  ;           //转换等待延时,单位us//命令定义
localparam  WR_CMD_DATA = 16'h44cc,       //跳过 ROM 及温度转换命令,低位在前RD_CMD_DATA = 8'h55,      //匹配 ROM rom2 =64'h4606_2011_8814_9628,rom3 =64'ha006_2011_6342_3428,   rom1 =64'hfd06_2010_fe54_1028,  rom4 =64'hde12_2211_9440_ff28,//板上芯片idrom5 =64'h0005,READ_DATA=8'hbe;
//------------<reg定义>----------------------------------------------
//reg [2:0]num;
wire [63:0]ROM;
assign  ROM =rom1;
reg [5:0]   cur_state   ;                   //现态
reg [5:0]   next_state  ;                   //次态
reg [6:0]   cnt         ;                   //50分频计数器,1Mhz(1us)
reg         dq_out      ;                   //双向总线输出
reg         dq_en       ;                   //双向总线输出使能,1则输出,0则高阻态
reg         flag_ack    ;                   //从机响应标志信号
reg         clk_us      ;                   //us时钟
reg [19:0]  cnt_us      ;                   //us计数器,最大可表示1048ms
reg [5:0]   bit_cnt     ;                   //接收数据计数器
reg [15:0]  data_temp   ;                   //读取的温度数据寄存
reg [15:0]  data        ;                   //未处理的原始温度数据//------------<wire定义>----------------------------------------------
wire        dq_in       ;                   //双向总线输入//==================================================================
//===========================<main  code>===========================
//==================================================================//-----------------------------------------------------------------------
//--双向端口使用方式
//-----------------------------------------------------------------------
assign  dq_in = dq;                            //高阻态的话,则把总线上的数据赋给dq_in
assign  dq =  dq_en ? dq_out : 1'bz;      //使能1则输出,0则高阻态//-----------------------------------------------------------------------
//--us时钟生成,因为时序都是以us为单位,所以生成一个1us的时钟会比较方便
//-----------------------------------------------------------------------
//50分频计数
always @(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 7'd0;else if(cnt == 7'd49)                 //每25个时钟500ns清零cnt <= 7'd0;elsecnt <= cnt + 1'd1;
end
//生成1us时钟
always @(posedge clk or negedge rst_n)beginif(!rst_n)clk_us <= 1'b0;else  if(cnt == 7'd49)                 //每500nsclk_us <= ~clk_us;                  //时钟反转elseclk_us <= clk_us;
end//-----------------------------------------------------------------------
//--三段式状态机
//-----------------------------------------------------------------------//状态机第一段:同步时序描述状态转移
always @(posedge clk_us or negedge rst_n)beginif(!rst_n)   begincur_state <= INIT1;    endelsebegincur_state <= next_state;end
end//状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*)beginnext_state = INIT1;   case(cur_state)INIT1        :beginif(cnt_us == T_INIT&& flag_ack)             //满足初始化时间且接收到了从机的响应信号   begin next_state = WR_CMD;                     //跳转到写状态endelse next_state = INIT1;                            //不满足则保持原有状态end WR_CMD       :begin if(bit_cnt == 6'd15 && cnt_us == 20'd62)      //写完了16个数据,写跳过ROM和写温度转换命令    next_state = WAIT;                         //跳转到等待状态,等待温度转换完成 else  next_state = WR_CMD;                       //不满足则保持原有状态end WAIT  :begin    if(cnt_us == T_WAIT)                          //等待时间结束next_state = INIT2;    else    next_state = WAIT; end INIT2  :begin   if(cnt_us == T_INIT && flag_ack)              //再进行初始化,时序同INIT1next_state = RD_CMD;elsenext_state = INIT2;endRD_CMD  :beginif(bit_cnt == 6'd7 && cnt_us == 20'd62)     //写完了匹配ROM命令,8个数据。next_state = TX_ROM;                      //跳转到发送ROM状态else    next_state = RD_CMD;   end TX_ROM :beginif(bit_cnt == 6'd63 && cnt_us == 20'd62)     //写完了64个ROM数据,   next_state = WR_READ_CMD;                      //跳转到读取温度数据状态else   next_state = TX_ROM; end      WR_READ_CMD:beginif(bit_cnt == 6'd7 && cnt_us == 20'd62)       //写完了读取命令,8个数据。next_state = RD_DATA;                        //跳转到接受状态else   next_state = WR_READ_CMD;       endRD_DATA  :begin if(bit_cnt == 4'd15 && cnt_us == 20'd62)      //读取完了16个数据next_state = INIT1;                         //跳转到初始化状态,开始新一轮温度采集else next_state = RD_DATA;      enddefault:next_state = INIT1; //if(num==1)//begin next_state=INIT2;    //num<=num+1;ROM<=rom2;//end//else if(num==2)//begin next_state=INIT2;  //num<=num+1;ROM<=rom3;//end//else if(num==3)//begin next_state=INIT2;  //num<=num+1;ROM<=rom4;//end//else if(num==4)//begin next_state=INIT2;  //num<=num+1;ROM<=rom4;//end//elsenext_state=INIT1;   //num<=num;//end//else  //next_state = RD_DATA;    //end           //default:next_state = INIT1;                          //默认初始化状态endcase
end //状态机第三段:时序逻辑描述输出
always @(posedge clk_us or negedge rst_n)beginif(!rst_n)begin                                          //默认输出dq_en <= 1'b0;dq_out <= 1'b0;flag_ack <= 1'b0;cnt_us <= 20'd0;bit_cnt <= 6'd0;endelse begin  case(cur_state)INIT1    :beginif(cnt_us == T_INIT)begin                   //时间计数到最大值(初始化时间)cnt_us <= 20'd0;                        //计数器清零flag_ack <= 1'b0;                       //从机响应标志信号拉低endelse begin                                   //没有计数到最大值cnt_us <= cnt_us + 1'd1;                //计数器计数if(cnt_us <= 20'd499)begin              //小于500us时dq_en <= 1'b1;                       //控制总线dq_out <= 1'b0;                      //输出0,即拉低总线endelse begin                             //在500us处dq_en <= 1'b0;                        //释放总线,等待从机响应                        if (cnt_us == 20'd570 && !dq_in) //在570us处采集总线电平,如果为0则说明从机响应了flag_ack <= 1'b1;               //拉高从机响应标志信号end endendWR_CMD    :beginif(cnt_us == 20'd62)begin                      //一个写时隙周期63us,满足计时条件则cnt_us <= 20'd0;                           //清空计数器dq_en <= 1'b0;                              //释放总线if(bit_cnt == 6'd15)                       //如果数据已经写了15个bit_cnt <= 6'd0;                      //则清空else                                       //没写15个bit_cnt <= bit_cnt + 1'd1;             //则数据计数器+1,代表写入了一个数据end else begin                                      //一个写时隙周期63us未完成cnt_us <= cnt_us + 1'd1;                  //计数器一直计数if(cnt_us <= 20'd1)begin                  //0~1us(每两个写数据之间需要间隔2us)dq_en <= 1'b1;                           //拉低总线dq_out <= 1'b0;endelse begin                 if (WR_CMD_DATA[bit_cnt] == 1'b0)begin   //需要写入的数据为0dq_en <= 1'b1;                      //拉低总线dq_out <= 1'b0;                      //                              endelse if(WR_CMD_DATA[bit_cnt] == 1'b1)begindq_en <= 1'b0;                     //需要写入的数据为1dq_out <= 1'b0;                     //释放总线                      endend  end     end     WAIT    :begin                                      //等待温度转换完成dq_en <= 1'b1;                                   //拉低总线兼容寄生电源模式dq_out <= 1'b1;                                  if(cnt_us == T_WAIT)                          //计数完成cnt_us <= 20'd0;elsecnt_us <= cnt_us + 1'd1;end    INIT2   :begin                                      //第二次初始化,时序同INIT1if(cnt_us == T_INIT)begin                     cnt_us <= 20'd0;flag_ack <= 1'b0;endelse begincnt_us <= cnt_us + 1'd1;if(cnt_us <= 20'd499)begindq_en <= 1'b1;                        dq_out <= 1'b0;endelse begindq_en <= 1'b0;                                                if (cnt_us == 20'd570 && !dq_in)flag_ack <= 1'b1;end    endend  RD_CMD  :begin                                      //写8个数据,时序同WR_CMDif(cnt_us == 20'd62)begincnt_us <= 20'd0;dq_en <= 1'b0;if(bit_cnt == 6'd7)bit_cnt <= 6'd0;elsebit_cnt <= bit_cnt + 1'd1;endelse begincnt_us <= cnt_us + 1'd1;if(cnt_us <= 20'd1)begindq_en <= 1'b1;                          dq_out <= 1'b0;endelse begin                   if (RD_CMD_DATA[bit_cnt] == 1'b0)begindq_en <= 1'b1;                        dq_out <= 1'b0;                                                        endelse if(RD_CMD_DATA[bit_cnt] == 1'b1)begindq_en <= 1'b0;                     dq_out <= 1'b0;                                                endend  endend  TX_ROM  :begin                                      //写64个数据,时序同WR_CMDif(cnt_us == 20'd62)begincnt_us <= 20'd0;dq_en <= 1'b0;if(bit_cnt == 6'd63)bit_cnt <= 6'd0;elsebit_cnt <= bit_cnt + 1'd1;endelse begincnt_us <= cnt_us + 1'd1;if(cnt_us <= 20'd1)begindq_en <= 1'b1;                            dq_out <= 1'b0;endelse begin                   if (ROM[bit_cnt] == 1'b0)begindq_en <= 1'b1;                        dq_out <= 1'b0;                                                        endelse if(ROM[bit_cnt] == 1'b1)begindq_en <= 1'b0;                     dq_out <= 1'b0;                                                endend  endend  WR_READ_CMD :begin                                      //写8个数据,时序同WR_CMDif(cnt_us == 20'd62)begincnt_us <= 20'd0;dq_en <= 1'b0;if(bit_cnt == 6'd7)bit_cnt <= 6'd0;elsebit_cnt <= bit_cnt + 1'd1;endelse begincnt_us <= cnt_us + 1'd1;if(cnt_us <= 20'd1)begindq_en <= 1'b1;                          dq_out <= 1'b0;endelse begin                   if (READ_DATA[bit_cnt] == 1'b0)begindq_en <= 1'b1;                      dq_out <= 1'b0;                                                        endelse if(READ_DATA[bit_cnt] == 1'b1)begindq_en <= 1'b0;                       dq_out <= 1'b0;                                                endend  endend  RD_DATA :begin                                      //读16位温度数据if(cnt_us == 20'd62)begin                      //一个读时隙周期63us,满足计时条件则cnt_us <= 20'd0;                           //清空计数器dq_en <= 1'b0;                              //释放总线if(bit_cnt == 6'd15)begin                  //如果数据已经读取了15个bit_cnt <= 6'd0;                     //则清空data <= data_temp;                     //临时的数据赋值给dataendelse begin                                 //如果数据没有读取15个bit_cnt <= bit_cnt + 1'd1;               //则数据计数器+1,意味着读取了一个数据data <= data;endendelse begin                                      //一个读时隙周期还没结束cnt_us <= cnt_us + 1'd1;                 //计数器累加if(cnt_us <= 20'd1)begin                    //0~1us(每两个读数据之间需要间隔2us)dq_en <= 1'b1;                           //拉低总线dq_out <= 1'b0;endelse begin                                 //2us后dq_en <= 1'b0;                           //释放总掉线                 if (cnt_us == 20'd10)                    //在10us处读取总线电平data_temp <= {dq,data_temp[15:1]};    //读取总线电平end endenddefault:;     endcaseend
end//-----------------------------------------------------------------------
//--12位温度数据处理
//-----------------------------------------------------------------------
always @(posedge clk_us or negedge rst_n)beginif(!rst_n)begin                                                  //初始状态temp_data <= 20'd0;  sign  <= 1'b0; end else begin  if(!data[15])begin                                          //最高位为0则温度为正sign  <= 1'b0;                                         //标志位为正temp_data <= data[10:0] * 11'd625 /7'd100;             //12位温度数据处理end  else if(data[15])begin                                      //最高位为1则温度为负sign  <= 1'b1;                                         //标志位为负temp_data <= (~data[10:0] + 1'b1)* 11'd625 /7'd100;      //12位温度数据处理         endend
endendmodule

endmodule

ds18B20_verilog相关推荐

最新文章

  1. Leetcode - 144. Binary Tree Preorder Traversal (层次遍历)
  2. linux需要的GLIBCXX版本,GCC版本中没有GLIBCXX_3.4.15解决
  3. “高仿版拼多多”宣告破产!曾一年收割1.3亿用户,如今自救失败负债16亿
  4. Vue中引入css文件
  5. 门前异动监控、AI 人脸识别!360 发布新型智能门铃
  6. maven setting 设置jdk版本
  7. 傅里叶变换性质证明卷积_傅里叶变换2.系统属性和卷积公式的推导
  8. 如何提升固定资产盘点效率
  9. 3D游戏之投影矩阵算法技术实现
  10. MacBook Air响一声白屏故障情况说明及解决
  11. 实现正负值及多条Y轴 Echarts柱状图
  12. Git 读书笔记(二)
  13. 2015小米实习生笔试题1 求两个数的不同位的个数
  14. 2020年4月西安葡萄城挂经
  15. C语言——字符串指针-strcmp
  16. 机器学习之西瓜书绪论--关于机器学习的简单介绍
  17. python uiautomation_python+UIAutomation简介
  18. html中js隐藏div的高度,jQuery实现获取隐藏div高度的方法示例
  19. vue Popover 弹出框
  20. 数据库管理(库管理)

热门文章

  1. Linux-使用uboot命令将Linux镜像和设备树文件下载到DRAM中
  2. oracle图书管理项目案例,C#+oracle做的图书管理系统
  3. 如何用ps扣字体_如何用PS的字体扣出来,改变颜色.PS如何抠图?
  4. Excel如何快速将图片插入到批注中?
  5. EventBus介绍
  6. Vue响应式原理整理笔记
  7. c语言中函数floor用法,C 库函数
  8. 在Linux服务器root用户依然遇到删除不掉得文件如何办 ,宝塔用命令无法删除文件得解决问题,使用rm -rf删除命令提示Operation not permitted 如何解决
  9. pandas将dataframe的所有数据列的名称转化为大写形式(all column labels in dataframe to uppercase)
  10. JavaScript学习笔记 06、DOM元素—③定时器与延时器