硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4704|回复: 3
收起左侧

DS18B20分析和VHDL代码实现

[复制链接]

8

主题

11

回帖

8

积分

新手上路

积分
8
发表于 2016-3-8 01:09:39 | 显示全部楼层 |阅读模式
DS18B20简介
DS18B20是一种单总线数字温度传感器。测试温度范围-55-125℃,温度数据位可配置为9101112位,对应的刻度值分别为0.5℃、0.25℃、0.125℃、0.0625℃,对应的最长转换时间分别为93.75ms187.5ms375ms750ms出厂默认配置为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)发送ROMCommand33h:只读rom,不用寻址过程);
       (3)发送FunctionCommand22h: 开始转换温度);
       (4)初始化;
(5)发送ROMCommand33h:只读rom,不用寻址过程);
       (6)发送FunctionCommand7dh: 开始读暂存器);
       (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,拉高,这里取30us530),再释放总线,以让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.主机根据输出01来决定拉低或者拉高总线至少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;
知行合一
回复

使用道具 举报

8

主题

11

回帖

8

积分

新手上路

积分
8
 楼主| 发表于 2016-3-8 01:12:01 | 显示全部楼层
对于上述的图片,请自行查看datasheet,我黏贴时黏贴不上来,不好意思
知行合一
回复

使用道具 举报

8

主题

11

回帖

8

积分

新手上路

积分
8
 楼主| 发表于 2016-3-8 01:12:24 | 显示全部楼层
对于上述的图片,请自行查看datasheet,我黏贴时黏贴不上来,不好意思
知行合一
回复

使用道具 举报

8

主题

11

回帖

8

积分

新手上路

积分
8
 楼主| 发表于 2016-3-8 01:12:48 | 显示全部楼层
对于上述的图片,请自行查看datasheet,我黏贴时黏贴不上来,不好意思
知行合一
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-19 12:11 , Processed in 0.168313 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表