【自学嵌入式: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以响应主机,之后从机将释放总线。(使用绝对时间,而不是上升沿或者下降沿)
image
图中弯地上升是电阻弱上拉过去的。

发送一位

主机将总线拉低60-120us,然后释放总线,表示发送0;主机将总线拉低1-15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值),整个时间片应大于60us
image
阴影是指在阴影这个时间段内都可以释放总线

接收一位

主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1,整个时间片应大于60us。
image
当主机拉低准备读取,假设从机发的是低,那么整个总线还是低电平,15us以内读取,然后总线又被弱上拉;如果从机发的高,那会在主机拉低后,会被上拉。

发送或接收一个字节

image

代码实现

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;
}
posted @ 2025-08-03 10:38  秦瑞迁  阅读(292)  评论(0)    收藏  举报