前言
这篇文章是本人通过学习51单片机中DS18B20模块中用到的单总线通讯协议做出的对单总线通信的理解,主要是针对单总线通信协议的详细理解,我们主要理解它的时序结构,就完全够写代码了,源码来自B站51单片机教学up主江协科技。
一、概述
单总线是Maxim全资子公司Dallas的一项专有技术。它采用单根信号线,既传输时钟,又传输数据,而且数据传输是双向的。它具有节省I/O口线资源、结构简单、成本低廉、便于总线扩展和维护等诸多优点。
二、单总线通信电路
单总线通信电路是一种较为简单的通讯电路,它只有一根线就能实现主机和从机的双向通信(也就是图中的1-Wire BUS),VDD和GND给DS18B20供电,DQ线就是连接单总线和DS18B20唯一的通信线,我们需要给DQ一个4.7KΩ上拉电阻并且将其配置成开漏模式。
三、单总线的时序结构
1.单总线初始化
主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us来响应主机,之后从机会释放总线 。(初始化这里的主机我们默认我们的单片机,从机就是DS18B20)(每句话都是对应下面的每句代码的)
//引脚定义
sbit OneWire_DQ=P3^7;
/**
* @brief 单总线初始化
* @param 无
* @retval 从机响应位,0为响应,1为未响应
*/
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1;
OneWire_DQ=0;
i = 247;while (--i); //Delay 500us
OneWire_DQ=1;
i = 32;while (--i); //Delay 70us
AckBit=OneWire_DQ;
i = 247;while (--i); //Delay 500us
return AckBit;
}
2.发送一个比特位
我们要发送一个字节的话,就需要一个比特位一个比特位这样发8次,并且全都采用2进制,这样我们只需要发“0”和“1”
主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后读取电平(因为30us的电平不可能处于变化状态,不是高就是低,所以说30us是一个典型值),整个时序为60us。
/**
* @brief 单总线发送一位
* @param Bit 要发送的位
* @retval 无
*/
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_DQ=0;
i = 4;while (--i); //Delay 10us
OneWire_DQ=Bit;
i = 24;while (--i); //Delay 50us
OneWire_DQ=1;
}
3.接收一个比特位
整个时序也是60us,我们分为三步。第一步,拉低总线(1~5us),第二步,释放总线(这里释放总线是一个时刻,我们在5us的时刻释放总线,然后再等5us),第三步,读取数据(我们等的第二个5us就是在等待从机是否会在这5us之内把总线拉下来,如果拉下来了就是0,没拉下来,因为我们主机已经释放了总线,那总线在这一时间段内也足够它恢复到高电平,所以在这10us末我们读取数据是很准确的,低电平就是0,高电平就是1,这样还有剩下的50us我们直接延时过去,一个时序就结束了)。需要注意的是,主机释放总线不一定就会变成高电平,主机释放总线后,从机如果发送的是0,他会继续拉下总线让总线保持低电平(这样即使主机释放了总线,总线也不会立刻回到高电平,而是被从机拉下来,继续保持低电平),如果发送的是1,我们可以认为从机没有做出任何动作响应,总线会随着主机的释放自动回到高电平。15us后如果发送的是0,从机会自动释放总线,这时主机和从机都释放了总线,总线才会回到高电平,至此一个时序结束。
/**
* @brief 单总线接收一位
* @param 无
* @retval 读取的位
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i = 2;while (--i); //Delay 5us
OneWire_DQ=1;
i = 2;while (--i); //Delay 5us
Bit=OneWire_DQ;
i = 24;while (--i); //Delay 50us
return Bit;
}
4.发送或接收一个字节
实际上我们的函数参数基本上都是一个字节,所以我们用一个函数嵌套,定义一个变量i,for循环,将发送和接收的一个比特位&(0x01<<i)循环八次就OK了。
/**
* @brief 单总线发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
/**
* @brief 单总线接收一个字节
* @param 无
* @retval 接收的一个字节
*/
unsigned char OneWire_ReceiveByte(void)
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
}
return Byte;
}
*以上仅个人对于但总线通信协议的理解,有疑问或者错误欢迎指出