【自学嵌入式:51单片机】单总线
所有自学嵌入式:51单片机系列的操作相关器件的代码都汇总到了:https://gitee.com/qin-ruiqian/Qin51
单总线
单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线。其特点如下:
- 一根通信线:DQ
- 异步、半双工
单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电通信只需要DQ和GND两根线。
单总线电路规范
- 设备的DQ均要配置成开漏输出模式
- DQ添加一个上拉电阻,阻值一般为5.7kΩ左右
- 若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路。(下图,接了一个MOS管)
![image]()
单总线时序结构
初始化
主机将总线拉低至少480us,然后释放总线,等待15-60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线。(使用绝对时间,而不是上升沿或者下降沿)

图中弯地上升是电阻弱上拉过去的。
发送一位
主机将总线拉低60-120us,然后释放总线,表示发送0;主机将总线拉低1-15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值),整个时间片应大于60us

阴影是指在阴影这个时间段内都可以释放总线
接收一位
主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1,整个时间片应大于60us。

当主机拉低准备读取,假设从机发的是低,那么整个总线还是低电平,15us以内读取,然后总线又被弱上拉;如果从机发的高,那会在主机拉低后,会被上拉。
发送或接收一个字节

代码实现
OneWrite.h:
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
unsigned char OneWire_Init(void); // 初始化单总线通信
void OneWire_SendBit(bit Bit); //发送一位
bit OneWire_ReceiveBit(); //接收一位
void OneWire_SendByte(unsigned char Byte); //发送一个字节
unsigned char OneWire_ReceiveByte(void); //接收一个字节
#endif
OneWire.c:
#include <REGX52.H>
#include <intrins.h> // 延时空语句
sbit OneWire_DQ = P3^7;
void Delay500us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 227;
while (--i);
}
void Delay70us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 29;
while (--i);
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void Delay50us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 20;
while (--i);
}
void Delay5us() //@11.0592MHz
{
}
unsigned char OneWire_Init(void)
{
bit AckBit = 0; //确认帧
OneWire_DQ = 1;
OneWire_DQ = 0;
Delay500us(); //根据单总线通信协议,主机拉低至少480微秒,这里用500微秒
OneWire_DQ = 1;//然后再释放
Delay70us(); //等待15-60微秒后,存在的从机会拉低总线60-240微秒以响应主机,所以要延迟60~(60+240)微秒
//延迟70微秒,包括了等待,和从机响应主机的时间
AckBit = OneWire_DQ;
//要求主机将总线拉低至少480微秒,继续延迟500微秒
Delay500us();
return AckBit;
}
//发送一位
void OneWire_SendBit(bit Bit)
{
//主机将总线拉低60~120us,然后释放总线,表示发送0
//主机将总线拉低1~15us,然后释放总线,表示发送1
//从机将在总线拉低30us后(典型值)读取电平
//整个时间片应大于60us
OneWire_DQ = 0; //先拉低
//在10us的时候检测一下,如果是0,那就一直是0
//如果是1,那就拉高(释放总线)
//直接写OneWire_DQ = Bit;
//Bit是0就是0,Bit为1,就在1-15us(也就是我设定的10us)拉高为1
//但是考虑到调用一个函数是4us
//江科大版本在这里直接把函数体的内容拿过来
//比如软件延时功能写出来的函数,延迟10us
//是算上了调用函数的时间,实际函数体6us,调用函数6us
//江科大这里是把函数体的内容拿过来
//这样不能用生成的延迟10us的函数(因为函数体6us)
//所以他在视频中用延迟14us的函数的函数体
//但是我这里还是正常调用延迟10us函数,包括函数体和调用过程
Delay10us();
OneWire_DQ = Bit;
//整个时间片应大于60us(至少60us)
//所以再延迟个50us,这个时序就结束了(50+10=60)us
Delay50us();
//通信结束,弱上拉拉回去
OneWire_DQ = 1;
}
//接收一位
bit OneWire_ReceiveBit()
{
//主机将总线拉低1~15us,然后释放总线
//并在拉低后15us内读取总线电平(尽量贴近15us的末尾)
//读取为低电平则为接收0
//读取为高电平则为接收1
//整个时间片应大于60us
bit Bit;
//先拉低
OneWire_DQ = 0;
Delay5us();
//再释放
OneWire_DQ = 1; //拉高就是释放总线,弱上拉,从机传来1就是1,从机传来是0瞬间拉低
Delay5us();
//15us内,目前是总共延迟10us内,读取总线电平
Bit = OneWire_DQ;
//时间片至少60us,之前有10us,还需要延迟50us
Delay50us();
//通信结束后,不用主机释放总线,从机释放总线
return Bit;
}
//发送一个字节
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i = 0; i <8 ;i++)
{
OneWire_SendBit(Byte & (0x01<<i)); //依次左移取位,从最低位开始取
}
}
//接收一个字节
unsigned char OneWire_ReceiveByte(void)
{
unsigned char Byte = 0x00;
unsigned char i;
for(i = 0; i <8 ;i++)
{
//如果传过来1位1,就和0x00做或运算,把1写入每一位
if(OneWire_ReceiveBit())
{
Byte |= (0x01 << i);
}
}
return Byte;
}

浙公网安备 33010602011771号