3.1 单总线通讯协议(One-Wire)
3.1.1 简介
* 定义:主机和从机通过1根线进行通信,在一条总线上可挂接的从器件数量几乎不受限制。
* 特点:这是由达拉斯半导体公司推出的一项通信技术。它采用单根信号线,既可传输时钟,又能传输数据,而且数据传输是双向的。
* 优点:单总线技术具有线路简单,硬件开销少,成本低廉,便于总线扩展和维护等。
3.1.2 通讯过程
(1) 初始化
基于单总线上的所有传输过程都是以初始化开始的,初始化过程由主机发出的复位脉冲和从机响应的应答脉冲组成。应答脉冲使主机知道,总线上有从机设备,且准备就绪。黑色实线代表系统主机拉低总线,灰色实线代表从机拉低总线,而黑色的虚线则代表上拉电阻将总线拉高。
初始化代码如下:
1. /*
2. *主机给从机发送复位脉冲
3. */
4. static void DS18B20_Rst(void)
5. {
6. /* 主机设置为推挽输出 */
7. DS_GPIO_Set_OUT_Mode();
8.
9. DS_PIN_CLR();
10. /* 主机至少产生480us的低电平复位信号 */
11. Delay_us(750);
12.
13. /* 主机在产生复位信号后,需将总线拉高 */
14. DS_PIN_SET();
15.
16. /*从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲*/
17. Delay_us(15);
18.}
初始化后监测从机发送的存在脉冲,判断当前是否存在单总线设备,示例以DS18B20为例:
1. /*
2. * 检测从机给主机返回的存在脉冲
3. * 0:成功
4. * 1:失败
5. */
6. static uint8_t DS18B20_Presence(void)
7. {
8. uint8_t pulse_time = 0;
9.
10. /* 主机设置为输入 */
11. DS_GPIO_Set_IN_Mode();
12.
13. /* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号
14. * 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
15. */
16. while( DS_PIN_READ() && pulse_time < 100 )
17. {
18. pulse_time++;
19. Delay_us(1);
20. }
21. /* 经过100us后,存在脉冲都还没有到来*/
22. if( pulse_time >=100 )
23. return 1;
24. else
25. pulse_time = 0;
26.
27. /* 存在脉冲到来,且存在的时间不能超过240us */
28. while( !DS_PIN_READ() && pulse_time < 240 )
29. {
30. pulse_time++;
31. Delay_us(1);
32. }
33. if( pulse_time >=240 )
34. return 1;
35. else
36. return 0;
37.}
(2) 写时序
存在两种写时隙:"写1" 和"写0"。主机采用写 1 时隙向从机写入 1,而采用写 0 时隙向从机写入 0。所有写时隙至少需要 60 us,且在两次独立的写时隙之间至少需要 1u s 的恢复时间。两种写时隙均起始于主机拉低总线(图5 所示)。产生写 1 时隙的方式:主机在拉低总线后,接着必须在 15u s 之内释放总线,由5k 上拉电阻将总线拉至高电平;而产生写0 时隙的方式:在主机拉低总线后,只需在整个时隙期间保持低电平即可( 至少60 us)。在写时隙起始后 15-60 us 期间,单总线器件采样总线电平状态。如果在此期间采样为高电平,则逻辑1 被写入该器件;如果为0 ,则写入逻辑0。时序图如下:
示例代码如下:
1. /*DS18B20写bit函数*/
2. static void DS_Write_Bit(ds_pin_level level)
3. {
4. if(level == HIGH) //写一
5. {
6. DS_PIN_CLR();
7. Delay_us(5);
8.
9. DS_PIN_SET(); //释放总线
10. Delay_us(50);
11. }
12.
13. else //写0
14. {
15. DS_PIN_CLR();
16. Delay_us(60);
17.
18. DS_PIN_SET(); //释放总线
19. Delay_us(2); //协议时间要求:1 < t < 无穷大,此处时间为10us
20. }
21.}
22.
23./*DS18B20写一个字节函数*/
24.static void DS_Write_Byte(uint8_t byte)
25.{
26. uint8_t i = 0;
27.
28. DS_GPIO_Set_OUT_Mode(); //配置为输出模式
29.
30. for(i = 0; i < 8; i++)
31. {
32. if(byte & 0x01) //先发送低位
33. {
34. DS_Write_Bit(HIGH);
35. }
36. else
37. {
38. DS_Write_Bit(LOW);
39. }
40.
41. byte >>= 1;
42.
43. Delay_us(2);
44. }
45.}
(3) 读时序
单总线器件仅在主机发出读时隙时,才向主机传输数, 所以, 在主机发出读数据命令后,必须马上产生读时隙,以便从机能够传输数据。所有读时隙至少需要 60us, 且在两次独立的读时隙之间至少需要 1us的恢复时间,每个读时隙都由主机发起,至少拉低总线 1us (图5 所示)。在主机发起读时隙之后,单总线器件才开始在总线上发送 0 或1。若从机发送1,则保持总线为高电平;若发送 0, 则拉低总线 。当发送 0 时,从机在该时隙结束后释放总线 。由上拉电阻将总线拉回至空闲高电平状态。从机发出的数据在起始时隙之后,保持有效时间 15us ,因而,主机在读时隙期间必须释放总线 ,并且在时隙起始后的 15 us 之内采样总线状态。时序图如下:
示例代码如下:
1. /*DS18B20读bit函数*/
2. static ds_pin_level DS_Read_Bit(void)
3. {
4. ds_pin_level temp;
5.
6. DS_GPIO_Set_OUT_Mode();
7.
8. DS_PIN_CLR();
9. Delay_us(10); //协议时间要求:1 < t < 无穷大,此处时间为5us
10.
11. DS_GPIO_Set_IN_Mode(); //配置DS18B20数据脚为输入模式
12.
13. if(DS_PIN_READ() == GPIO_PIN_SET) //读 1
14. {
15. temp = HIGH;
16. }
17. else //读0
18. {
19. temp = LOW;
20. }
21.
22. Delay_us(45); //协议时间要求为45us
23.
24. return temp;
25.}
26.
27./*DS18B20读字节函数*/
28.static uint8_t DS18B20_Read_Byte(void)
29.{
30. uint8_t i = 0;
31. uint8_t level = 0;
32. uint8_t dat = 0;
33.
34. for(i = 0; i < 8; i++)
35. {
36. level = DS_Read_Bit();
37. dat = (dat) | (level << i);
38. }
39.
40. return dat;
41.}
(4) 搜索ROM
ROM搜索过程只是一个简单的三步循环程序:读一位、读该位的补码 、写入一个期望的数据位。总线主机在 ROM的每一位上都重复这样的三步循环程序。当完成某个器件后,主机就能够知晓该器件的ROM信息。剩下的设备数量及其 ROM代码通过相同的过即可获得。
下面的ROM搜索过程实例假设四个不同的器件被连接至同一条总线上,它们的 ROM代码如下所示:
ROM1 00110101…
ROM2 10101010…
ROM3 11110101…
ROM4 00010001…
具体搜索过程如下:
a: 主机发出复位脉冲,启动初始化序列。从机设备发出响应的应答脉冲;
b: 接着主机在总线上发出ROM搜索命令 ;
c: 主机从总线上准备读入一个数据位,这时,每个响应设备分别将 ROM代码的第一位输出到单总线上。ROM1 和ROM4 输出 0 至总线,而ROM2 和ROM3 输出 1 至总线。线上的输出结果将是所有器件的逻辑”与“ ,所以,主机从总线上读到的将是0 。接着, 主机开始读另一位,即每个器件分别输出 ROM代码中第一位的补码,此时,ROM1 ROM4 输出 1至总线,而ROM2和ROM3 输出 0 至总线 。这样,主机读到的该位补码还是 0 。主机由此判定,总线上有些器件的ROM代码第一位为0, 有些则为1。
(5) 注意事项
在使用STM32进行调试的是否需要将DS18B20的数据脚配置为输入上拉,否则读出来的温度数据会有问题。