DS18B20 HDL
第一节 DS18B20通讯协议
DS18B20采用Dallas公司1-WIRE网络协议通讯,这里就围绕着DS18B20介绍1-WIRE网络协议。
一、命令序列
典型的单总线命令序列如下:
第一步初始化;
第二步ROM 命令(跟随需要交换的数据);
第三步功能命令(跟随需要交换的数据)。
每次访问单总线器件,必须严格遵守这个命令序列,如果出现序列混乱,则单总线器件不会响应主机。但是,这个准则对于搜索ROM 命令和报警搜索命令例外,在执行两者中任何一条命令之后,主机不能执行其后的功能命令,必须返回至第一步。
1、初始化
基于单总线上的所有传输过程都是以初始化开始的,初始化过程由主机发出的复位脉冲和从机响应的应答脉冲组成。应答脉冲使主机知道总线上有从机设备且准备就绪。复位和应答脉冲的时间详见单总线信号部分。
2、ROM 命令
在主机检测到应答脉冲后,就可以发出ROM 命令。这些命令与各个从机设备的唯一64 位ROM 代码相关,允许主机在单总线上连接多个从机设备时,指定操作某个从机设备。这些命令还允许主机能够检测到总线上有多少个从机设备以及其设备类型,或者有没有设备处于报警状态。从机设备可能支持5 种ROM 命令(实际情况与具体型号有关),每种命令长度为8 位。主机在发出功能命令之前,必须送出合适的ROM 命令。下面将简要地介绍各个ROM 命令的功能以及使用在何种情况下。
(1)搜索ROM[F0h]
当系统初始上电时,主机必须找出总线上所有从机设备的ROM 代码,这样主机就能够判断出从机的数目和类型。主机通过重复执行搜索ROM 循环(搜索ROM 命令跟随着位数据交换),以找出总线上所有的从机设备。如果总线只有一个从机设备,则可以采用读ROM命令来替代搜索ROM 命令。在每次执行完搜索ROM 循环后,主机必须返回至命令序列的第一步(初始化)。
(2)读ROM[33h](仅适合于单节点)
该命令仅适用于总线上只有一个从机设备。它允许主机直接读出从机的64 位ROM 代码,而无须执行搜索ROM 过程。如果该命令用于多节点系统,则必然发生数据冲突,因为每个从机设备都会响应该命令。
(3)匹配ROM[55h]
匹配ROM 命令跟随64 位ROM 代码,从而允许主机访问多节点系统中某个指定的从机设备。仅当从机完全匹配64 位ROM 代码时,才会响应主机随后发出的功能命令;其它设备将处于等待复位脉冲状态。
(4)跳越ROM[CCh](仅适合于单节点)
主机能够采用该命令同时访问总线上的所有从机设备,而无须发出任何ROM 代码信息。例如,主机通过在发出跳越ROM 命令后跟随转换温度命令[44h], 就可以同时命令总线上所有的DS18B20 开始转换温度,这样大大节省了主机的时间。值得注意,如果跳越ROM命令跟随的是读暂存器[BEh]的命令(包括其它读操作命令),则该命令只能应用于单节点系统,否则将由于多个节点都响应该命令而引起数据冲突。
(5)报警搜索[ECh](仅少数1-wire 器件支持)
除那些设置了报警标志的从机响应外,该命令的工作方式完全等同于搜索ROM 命令。该命令允许主机设备判断那些从机设备发生了报警(如最近的测量温度过高或过低等)。同搜索ROM 命令一样,在完成报警搜索循环后,主机必须返回至命令序列的第一步。
3、 功能命令
在主机发出ROM 命令,以访问某个指定的DS18B20 ,接着就可以发出DS18B20 支持的某个功能命令。这些命令允许主机写入或读出DS18B20 暂存器、启动温度转换以及判断从机的供电方式。DS18B20 的功能命令总结于表1中。
表1 DS18B20功能命令集
|
命令 |
描述 |
命令代码 |
发送命令后单线上的响应信息 |
注释 |
|
温度转换命令 |
||||
|
转换温度 |
启动温度转换 |
44h |
无 |
1 |
|
存储器命令 |
||||
|
读暂存器 |
读全部的暂存器内容包括CRC 字节 |
BEh |
DS18B20传输多达9 个字节至主机 |
2 |
|
写暂存器 |
写暂存器第2 、3 和4 个字节的数据(即TH、TL和配置寄存器) |
4Eh |
主机传输3 个字节数据至DS18B20 |
3 |
|
复制暂存器 |
将暂存器中的TH、TL和配置字节复制到EEPROM中 |
48h |
无 |
1 |
|
回读EEPROM |
将TH、TL和配置字节从EEPROM回读至暂存器中 |
B8h |
DS18B20传送回读状态至主机 |
|
注释:
1.在温度转换和复制暂存器数据至EEPROM 期间,主机必须在单总线上允许强上拉。并且在此期间,总线上不能进行其它数据传输;
2.通过发出复位脉冲,主机能够在任何时候中断数据传输;
3.在复位脉冲发出前,必须写入全部的三个字节。
二、信号方式
所有的单总线器件要求采用严格的通信协议,以保证数据的完整性。该协议定义了几种信号类型:复位脉冲、应答脉冲、写0、写1、读0、和读1。所有这些信号除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前,这一点与多数串行通信格式不同(多数为字节的高位在前)。
1、初始化序列复位和应答脉冲
单总线上的所有通信都是以初始化序列开始,包括主机发出的复位脉冲及从机的应答脉冲,如图1所示。当从机发出响应主机的应答脉冲时,即向主机表明它处于总线上,且工作准备就绪。在主机初始化过程,主机通过拉低单总线至少480μs,以产生(Tx)复位脉冲。接着主机释放总线,并进入接收模式(Rx)。当总线被释放后,5k上拉电阻将单总线拉高。在单总线器件检测到上升沿后,延时15-60μs,接着通过拉低总线60-240μs,以产生应答脉冲。

图1 单总线初始化时序图
2、读/写时隙
在写时隙期间,主机向单总线器件写入数据;而在读时隙期间,主机读入来自从机的数据。在每一个时隙,总线只能传输一位数据。
(1)写时隙
存在两种 写时隙:“写1”和“写0”。主机采用写1时隙向从机写入1,而采用写0。时隙向从机写入0所有写时隙至少需要60μs,且在两次独立的写时隙之间至少需要1μs的恢复时间。两种写时隙均起始于主机拉低总线(图2所示)。产生写1时隙的方式:主机在拉低总线后,接着必须在15μs之内释放总线,由5k上拉电阻将总线拉至高电平;而产生写0时隙的方式:在主机拉低总线后,只需在整个时隙期间保持低电平即可(至少60μs)。
写时隙起始后15-60μs期间,单总线器件采样总线电平状态。如果在此期间采样为高电平,则逻辑1被写入该器件,如果为0,则写入逻辑0。
(2)读时隙
单总线器件仅在主机发出读时隙时,才向主机传输数据,所以在主机发出读数据命令后,必须马上产生读时隙,以便从机能够传输数据。所有读时隙至少需要60μs,且在两次独立的读时隙之间至少需要1μs的恢复时间。每个读时隙都由主机发起,至少拉低总线1μs(图2所示)。在主机发起读时隙之后,单总线器件才开始在总线上发送0或1。若从机发送1则保持总线为高电平,若发送0则拉低总线。当发送0时,从机在该时隙结束后释放总线,由上拉电阻将总线拉回至空闲高电平状态。从机发出的数据在起始时隙之后,保持有效时间15μs,因而主机在读时隙期间必须释放总线,并且在时隙起始后的15μs之内采样总线状态。

图2 单总线主机读写时序示意图
第二节 FPGA与DS18B20实现通讯
本设计中与DS18B20通讯步骤如下:
1、初始化;
2、ROM 命令:跳越ROM命令[CCh](由于在本设计中只有DS18B20一个单总线协议芯片);
3、功能命令:温度转换命令[44h];
4、延时780ms(在本设计中对DS18B20的设置采用默认,即输出数据为12位有效数据,因此一次转换至少750ms);
5、初始化;
6、ROM 命令:跳越ROM命令[CCh];
7、功能命令:读暂存器命令[BEh]。
为了方便以后各类1_wire网络协议芯片,本设计中首先编写了一个1_wire网络协议的.VHD文件(dallas_1wire.VHD)。由于各芯片的功能命令比一样,在本文件中只有初始化和ROM 命令两步,功能命令在下一个.VHD文件(ds18b20_ctl.VHD)中介绍,dallas_1wire.VHD程序如下:
PROCESS(nreset,clk)
BEGIN
IF(nreset = '0') THEN --复位
state_index <= S0_wait;
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
pcmd_start <= '0';
bus_1wire <= 'Z';
return_init <= '0';
ack <= '1';
ELSIF(clk'EVENT AND clk = '1') THEN
pcmd_start <= cmd_start;
CASE state_index IS
WHEN S0_wait =>
IF(pcmd_start = '0' AND cmd_start = '1') THEN --判断cmd_start上升沿
state_index <= S1_init_wr; --进入初始化写状态
ELSE
state_index <= state_index;
END IF;
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
bus_1wire <= 'Z';
return_init <= '0';
ack <= '1';
WHEN S1_init_wr => --初始化写
IF(cnt_1us >= delay_us) THEN --延时1us
cnt_1us <= 0;
IF(cnt_delay >= 500) THEN --延时500个1us(至少480us)
state_index <= S2_init_rd;--进入初始化读状态
cnt_delay <= 0;
ELSE
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
END IF;
ELSE
state_index <= state_index;
cnt_1us <= cnt_1us + 1;
cnt_delay <= cnt_delay;
END IF;
bus_1wire <= '0'; --拉低总线,发送复位脉冲
cnt_bit <= 0;
return_init <= '0';
ack <= '1';
WHEN S2_init_rd => --初始化读
IF(cnt_1us >= delay_us) THEN --延时1us
cnt_1us <= 0;
CASE cnt_delay IS --(仪器检测到上升沿后延时15~60us拉低总线60~240us)
WHEN 70 => --延时70个1us
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
return_init <= return_init;
ack <= bus_1wire; --读总线有无仪器存在状态
WHEN 500 => --读完总线后延时(500-70=)430个1us
IF(ack = '0') THEN
state_index <= S3_rom_wr; --总线上有仪器则进入ROM写状态
return_init <= return_init;
ELSE
state_index <= S0_wait; --总线上无仪器则进入等待状态
return_init <= '1';
END IF;
cnt_delay <= 0;
ack <= ack;
WHEN OTHERS =>
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
return_init <= return_init;
ack <= ack;
END CASE;
ELSE
state_index <= state_index;
cnt_1us <= cnt_1us + 1;
cnt_delay <= cnt_delay;
return_init <= return_init;
ack <= ack;
END IF;
cnt_bit <= 0;
bus_1wire <= 'Z';
WHEN S3_rom_wr => --ROM命令写
IF(cnt_1us >= delay_us) THEN --延时1us
cnt_1us <= 0;
CASE cnt_delay IS --(每一位数据先将总线拉低15us以内以示起始,然后写数据至少45us:写1释放总线,写0保持;写时隙之间至少1us恢复)
WHEN 0 => --延时10个1us(起始)
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= '0';
return_init <= '0';
cnt_bit <= cnt_bit;
WHEN 10 => --延时100个1us(数据)
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= cmd_rom(cnt_bit);
return_init <= '0';
cnt_bit <= cnt_bit;
WHEN 110 => --延时10个1us(位间隙)
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= 'Z';
return_init <= '0';
cnt_bit <= cnt_bit;
WHEN 120 =>
cnt_delay <= 0;
IF(cnt_bit >= 7) THEN
cnt_bit <= 0;
CASE cmd_rom IS
WHEN X"F0" => --ROM搜索命令
state_index <= S4_rom_search; --进入ROM搜索状态
return_init <= '0';
WHEN X"33" => --ROM读命令
state_index <= S5_rom_rd; --进入ROM读状态
return_init <= '0';
WHEN OTHERS => --异常状态
state_index <= S0_wait; --进入初始化写状态
return_init <= '1';
END CASE;
ELSE
cnt_bit <= cnt_bit + 1;
state_index <= state_index;
return_init <= '0';
END IF;
bus_1wire <= 'Z';
WHEN OTHERS =>
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= bus_1wire;
return_init <= '0';
cnt_bit <= cnt_bit;
END CASE;
ELSE
state_index <= state_index;
cnt_1us <= cnt_1us + 1;
cnt_delay <= cnt_delay;
bus_1wire <= bus_1wire;
return_init <= '0';
cnt_bit <= cnt_bit;
END IF;
ack <= '1';
WHEN S7_fun_wr => --功能命令写
IF(cnt_1us >= delay_us) THEN --延时1us
cnt_1us <= 0;
CASE cnt_delay IS --(每一位数据先将总线拉低15us以内以示起始,然后写数据至少45us:写1释放总线,写0保持;写时隙之间至少1us恢复)
WHEN 0 => --延时10个1us(起始)
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= '0';
return_init <= '0';
cnt_bit <= cnt_bit;
WHEN 10 => --延时100个1us(数据)
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= cmd_fun(cnt_bit);
return_init <= '0';
cnt_bit <= cnt_bit;
WHEN 110 => --延时10个1us(位间隙)
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= 'Z';
return_init <= '0';
cnt_bit <= cnt_bit;
WHEN 120 =>
cnt_delay <= 0;
IF(cnt_bit >= 7) THEN
cnt_bit <= 0;
state_index <= S0_wait; --进入初始化写状态
return_init <= '1';
ELSE
cnt_bit <= cnt_bit + 1;
state_index <= state_index;
return_init <= '0';
END IF;
bus_1wire <= 'Z';
WHEN OTHERS =>
state_index <= state_index;
cnt_delay <= cnt_delay + 1;
bus_1wire <= bus_1wire;
return_init <= '0';
cnt_bit <= cnt_bit;
END CASE;
ELSE
state_index <= state_index;
cnt_1us <= cnt_1us + 1;
cnt_delay <= cnt_delay;
bus_1wire <= bus_1wire;
return_init <= '0';
cnt_bit <= cnt_bit;
END IF;
ack <= '1';
WHEN OTHERS =>
state_index <= S0_wait;
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
pcmd_start <= '0';
bus_1wire <= 'Z';
return_init <= '1';
ack <= '1';
END CASE;
END IF;
END PROCESS;
dallas_1wire.VHD文件中只有初始化和ROM 命令两步,ds18b20_ctl.VHD中实现了功能命令以及对DS18B20的总体时序控制,ds18b20_ctl.VHD程序如下:
PROCESS(nreset,clk)
BEGIN
IF(nreset = '0') THEN --复位
state_index <= S0_start;
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
data_fun <= X"0000";
data_fun_tmp <= X"0000";
cmd_start <= '0';
cmd_rom <= X"00";
cmd_fun <= X"BE";
bus_1wire <= 'Z';
ELSIF(clk'EVENT AND clk = '1') THEN
CASE state_index IS
WHEN S0_start => --开始一个命令序列
cnt_bit <= 0;
data_fun <= data_fun;
data_fun_tmp <= data_fun_tmp;
CASE cmd_fun IS
WHEN X"44" => --转换温度命令
--进入延时状态(至少750ms)
IF(cnt_1us >= delay_us) THEN
cnt_1us <= 0;
IF(cnt_delay >= 850000) THEN --延时850ms
cnt_delay <= 0;
state_index <= S1_wait;
cmd_start <= '1';
cmd_fun <= X"BE"; --读暂存器命令
ELSE
cnt_delay <= cnt_delay + 1;
state_index <= state_index;
cmd_start <= '0';
cmd_fun <= cmd_fun;
END IF;
ELSE
cnt_1us <= cnt_1us + 1;
cnt_delay <= cnt_delay;
state_index <= state_index;
cmd_start <= '0';
cmd_fun <= cmd_fun;
END IF;
cmd_rom <= X"CC"; --ROM跳越命令
WHEN X"BE" => --读暂存器命令
state_index <= S1_wait; --进入等待ROM写/读以及功能写结束状态
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
data_fun <= data_fun_tmp;
data_fun_tmp <= data_fun_tmp;
cmd_start <= '1';
cmd_rom <= X"CC"; --ROM跳越命令
cmd_fun <= X"44"; --转换温度命令
WHEN OTHERS => --异常状态
state_index <= S0_start;
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
data_fun <= data_fun;
data_fun_tmp <= X"0000";
cmd_start <= '0';
cmd_rom <= X"CC"; --ROM跳越命令
cmd_fun <= X"BE"; --读暂存器命令
END CASE;
bus_1wire <= 'Z';
WHEN S1_wait => --等待ROM写/读以及功能写结束
cnt_1us <= 0;
cnt_delay <= 0;
cnt_bit <= 0;
cmd_start <= '0';
cmd_rom <= cmd_rom;
cmd_fun <= cmd_fun;
data_fun <= data_fun;
data_fun_tmp <= data_fun_tmp;
IF(return_init = '1') THEN
CASE cmd_fun IS
WHEN X"44" => --转换温度命令
state_index <= S0_start;
WHEN X"BE" => --读暂存器命令
state_index <= S2_fun_rd;--进入暂存器读状态
WHEN OTHERS => --异常状态
state_index <= S0_start;
END CASE;
ELSE
state_index <= state_index;
END IF;
bus_1wire <= 'Z';
WHEN S2_fun_rd => --暂存器读
--读时隙由主机拉低总线至少1us发起,然后释放,在15us内采样总线状态
IF(cnt_1us >= delay_us) THEN
cnt_1us <= 0;
CASE cnt_delay IS
WHEN 0 =>
state_index <= state_index;
bus_1wire <= '0';
cnt_delay <= cnt_delay + 1;
cnt_bit <= cnt_bit;
data_fun_tmp <= data_fun_tmp;
WHEN 3 =>
state_index <= state_index;
bus_1wire <= 'Z';
cnt_delay <= cnt_delay + 1;
cnt_bit <= cnt_bit;
data_fun_tmp <= data_fun_tmp;
WHEN 13 =>
state_index <= state_index;
bus_1wire <= 'Z';
cnt_delay <= cnt_delay + 1;
cnt_bit <= cnt_bit;
data_fun_tmp(14 DOWNTO 0) <= data_fun_tmp(15 DOWNTO 1);
data_fun_tmp(15) <= bus_1wire;
WHEN 100 =>
bus_1wire <= 'Z';
cnt_delay <= 0;
cnt_bit <= cnt_bit + 1;
data_fun_tmp <= data_fun_tmp;
IF(cnt_bit >= 15) THEN
state_index <= S0_start;
ELSE
state_index <= state_index;
END IF;
WHEN OTHERS =>
state_index <= state_index;
bus_1wire <= bus_1wire;
cnt_delay <= cnt_delay + 1;
cnt_bit <= cnt_bit;
END CASE;
ELSE
state_index <= state_index;
cnt_1us <= cnt_1us + 1;
cnt_delay <= cnt_delay;
bus_1wire <= bus_1wire;
cnt_bit <= cnt_bit;
END IF;
cmd_start <= '0';
cmd_rom <= cmd_rom;
cmd_fun <= cmd_fun;
data_fun <= data_fun;
END CASE;
END IF;
END PROCESS;

浙公网安备 33010602011771号