鬼谷左原 发表于 2016-3-8 01:09:39

DS18B20分析和VHDL代码实现

DS18B20简介
DS18B20是一种单总线数字温度传感器。测试温度范围-55℃-125℃,温度数据位可配置为9、10、11、12位,对应的刻度值分别为0.5℃、0.25℃、0.125℃、0.0625℃,对应的最长转换时间分别为93.75ms、187.5ms、375ms、750ms。出厂默认配置为12位数据,刻度值为0.0625℃,最长转换时间为750ms。从以上数据可以看出,DS18B20数据位越低、转换时间越短、反应越快、精度越低。
单总线,意味着没有时钟线,只有一根通信线。单总线读写数据是靠控制起始时间和采样时间来完成,所以时序要求很严格,这也是DS18B20驱动编程的难点。
作为一个传感器来说,对于MCU或者FPGA大都是通过设备地址和寄存器地址来访问我们想要读取的数据,但是DS18B20这个芯片这里没有地址,下面给出DS18B20 的寄存器及地址,只要照着datasheet上的时序对DS18B20进行读寄存器的内容,DS18B20就会自动完成温度的模数转化,更新到寄存器。下面是DS18B20面向MCU的寄存器内容
DSfile:///C:/Users/yao/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif
18B20重要寄存器
Configuration Register
对于一个器件,一般都会有可以配置的寄存器,DS18B20也不例外。这个寄存器就是配置转化时间间隔和位数的这里不进行配置,使用默认的模式。file:///C:/Users/yao/AppData/Local/Temp/msohtmlclip1/01/clip_image004.gif

Temperature Register
高位字节前5位为符号位,其余的权重和16进制数使用方法一致。
代码实现:
Temperature_1
读取到的高字节(前面定义读取);
temperature_buf_low
读取到的低字节(前面定义读取);
neg_flag
负温度指示(输出)
if(temperature_1and "11111000") = "11111000"then   --如果温度是负数则需要转换下,高字节
         temperature_buf_low<=(nottemperature_buf_low )+1;--低字节取反再加1
         temperature_1:=(not temperature_1);
         iftemperature_buf_low="0000000" then --低字节取反时进位处理
         temperature_1:=temperature_1+1;
         end if;
         neg_flag<='1';--负温度指示
         else neg_flag<='0';
         end if;
         temperature<=temperature_1(2 downto0)&temperature_buf_low;--温度值是templ_value2的低3位和templ_value1
DS18B20使用过程:
       (1)初始化;
       (2)发送ROMCommand(33h:只读rom,不用寻址过程);
       (3)发送FunctionCommand(22h: 开始转换温度);
       (4)初始化;
(5)发送ROMCommand(33h:只读rom,不用寻址过程);
       (6)发送FunctionCommand(7dh: 开始读暂存器);
       (7)读取温度寄存器低字节;
       (8)读取温度寄存器高字节;
时序实现:file:///C:/Users/yao/AppData/Local/Temp/msohtmlclip1/01/clip_image006.gif

(1)初始化时序:
a.主机拉低480us总线,
b.主机释放总线,输出高阻,或者输出高电平60us
c.DS18B20会拉低总线240us,主机等待240us时间后拉高就完成了初始化过程。
代码实现:同样使用把时序整合到状态机里面的想法。
wheninitial_1 =>
         initial_count <=initial_count + '1';
         if initial_count=10#50# then
         dq<='1';
         elsif initial_count=10#100# then   --拉低
                   dq<='0';
         elsif initial_count= 10#25100#then   --初始化时要求,低电平至少保持480us,这里取500us
                   dq<='1';
         elsif initial_count=10#26500# then   --要求15us~60us,拉高,这里取30us(530),再释放总线,以让DS18B20发出存在脉冲
                   dq<='Z';
         elsif initial_count= 10#41500#then   --存在脉冲为60us~240us,取 300 再拉高
                   dq<='1';
         elsif initial_count>=10#51000#then   --整个时序要求960us,这里取1020us
         initial_count<=(others =>'0');   --释放总线电阻拉高,初始化标志置1,即下次不进行初始化,计数器归零
         current_state<=wr_cmd1;
         dq<='Z';
       (2)写一位的时序:写时序分写0和写“1”
       a.主机拉低总线15us;
       b.主机根据输出0和1来决定拉低或者拉高总线至少60us;
       c.最后主机总线至少拉高1us;
file:///C:/Users/yao/AppData/Local/Temp/msohtmlclip1/01/clip_image008.gif

       代码实现:
whenwr_cmd1 =>
         wr_count<= wr_count +1;
         if wr_count = 10#50# then
                   dq <= '0';--将总线拉低
         elsif wr_count = 10#500# then--在15us内向总线写一比特数值
                   dq <= cmd1(cmd_bit_cnt);
         elsif wr_count=10#4500# then   --在写时序的15us~60us内,DS18B20对总线采样,所以取90us
                   dq<='1';
         elsif wr_count = 10#4600# then
         --在大于1us之后,总线拉低,产生下一写时序 取2us
                   dq<='0';
                   wr_count<=(others =>'0');--计数器归零
                   cmd_bit_cnt := cmd_bit_cnt-1;--写下一bit 的命令
                   if cmd_bit_cnt = -1 then --写完命令,命令计数值“归零”(倒数),状态转为1,即将写命令2
                   cmd_bit_cnt :=15;
                   current_state <=initial_2;
                   end if;
         end if;
(3)读取一位的时序:
a.主机拉低总线至少1us;
      b.主机释放总线file:///C:/Users/yao/AppData/Local/Temp/msohtmlclip1/01/clip_image010.gif

c.最后主机在15us时刻读取总线电平;

代码实现:
whenrd_temp_1=>--读取第一字节温度(低字节)
rd_count<=rd_count+1;
         if rd_count = 10#50# then
         dq <= '0';
         elsif rd_count = 10#200# then --低电平至少1us,在释放总线
                   dq <= 'Z';
         elsif rd_count = 10#650# then --要求在15us内读取温度
                   temperature_1(rd_temp_bit_cnt):=dq;
         elsif rd_count = 10#4000# then--读时序至少60us,这里取80us
                   dq<='1';
         elsif rd_count = 10#4100# then
                   rd_count <= (others =>'0');
                   rd_temp_bit_cnt:=rd_temp_bit_cnt+1;--读取下个比特
                   if(rd_temp_bit_cnt = 8) then--读取完第一个字节
                                     rd_temp_bit_cnt:=0;--归零
                                     current_state<=rd_temp_2;
                                     temperature_buf_low<=temperature_1;
                   end if;
         end if;
         3.3.5DS18B20驱动设计总结
只要按照上述的时序拼合,经验证,可以读取到温度的温度数据。由于需要给数码管显示,需要把数据转化为BCD码,这里使用整数转化的方法,使用STD_LOGIC_ARITH库的转化运算:
CONV_INTEGER:把向量转化为整数
Conv_Std_Logic_Vector:把整数转化为向量
代码实现:       --温度转化为BCD码
       process(temperature)
         begin
         --if(neg_flag = '0') then --温度为正数
         var4<=CONV_INTEGER(temperature)/1600;   --百位
         var3<=CONV_INTEGER(temperature)/160rem 10;   --十位
         var2<=CONV_INTEGER(temperature)/16rem 10;   --个位
         var1<=CONV_INTEGER(temperature)*10/16rem 10;   --小数点后一位
         --end if;
         bcd_data(15 downto12)<=Conv_Std_Logic_Vector(var4,4);
         bcd_data(11 downto8)<=Conv_Std_Logic_Vector(var3,4);
         bcd_data(7 downto4)<=Conv_Std_Logic_Vector(Var2,4);
         bcd_data(3 downto0)<=Conv_Std_Logic_Vector(Var1,4);
         end process;

鬼谷左原 发表于 2016-3-8 01:12:01

对于上述的图片,请自行查看datasheet,我黏贴时黏贴不上来,不好意思

鬼谷左原 发表于 2016-3-8 01:12:24

对于上述的图片,请自行查看datasheet,我黏贴时黏贴不上来,不好意思

鬼谷左原 发表于 2016-3-8 01:12:48

对于上述的图片,请自行查看datasheet,我黏贴时黏贴不上来,不好意思
页: [1]
查看完整版本: DS18B20分析和VHDL代码实现