一、单总线协议(1-wire)

1.定义:主机和从机通过1根线进行通信,在一条总线上可挂接的从器件数量几乎不受限制。

2.特点:它采用单根信号线,既可传输时钟,又能传输数据,而且数据传输是双向的。

3.优点:单总线技术具有线路简单,硬件开销少,成本低廉,便于总线扩展和维护等。
二、单总线通信过程

1.初始化

初始化过程 = 复位脉冲 + 从机应答脉冲。

主机通过拉低单总线480 ~ 960 us产生复位脉冲,然后释放总线,进入接收模式。主机释放总线时,会产生低电平跳变为高电平的上升沿,单总线器件检测到上升沿之后,延时15 ~ 60 us,单总线器件拉低总线60 ~ 240 us来产生应答脉冲。主机接收到从机的应答脉冲说明单总线器件就绪,初始化过程完成。

初始化时序图如下所示:

2.写间隙

写间隙有两种,包括写0的时间隙和写1的时间隙。

当数据线拉低后,在15 ~ 60 us的时间窗口内对数据线进行采样。如果数据线为低电平,就是写0,如果数据线为高电平,就是写1。一般一个数据位的周期60us到120us,特别是写1时,周期拉高后要等待一会
主机要产生一个写1时间隙,就必须把数据线拉低,在写时间隙开始后的15 us内允许数据线拉高。 
主机要产生一个写0时间隙,就必须把数据线拉低并保持60 us以上。

写时间隙时序图如下所示:

3.读时间隙

主机把总线拉低是,并保持至少1 us后释放总线,必须在15 us内读取数据。

读时间隙时序图如下所示

总的来说,需要严格按照时序写程序,主要分 "复位时序","写入一字节","读出一字节 " 基础操作, 之后就是按芯片手册具体操作控制

DS1991 官方手册下载:https://www.maximintegrated.com/cn/products/ibutton-one-wire/ibutton/DS1991.html#modalDatasheet

DS1991 官方手册下载:https://datasheets.maximintegrated.com/cn/ds/DS1991_cn.pdf
——————————————————————————————————————————————————————————————————————————————————————————————————————————————

 DS1991.c

/*
************************************* Copyright ******************************
*
*                 (C) Copyright 2021,Wenkic,China, GCU.
*                            All Rights Reserved
*
*                     By(Wenkic)
*                      https://blog.csdn.net/Wekic
*
* FileName     : DS1991.c
* Version      : v1.0
* Author       : Wenkic
* Date         : 2021-04-27
* Description  :
* Function List:
******************************************************************************
*/
/********************************End of Head************************************/
#include "DS1991.h"
#include "intrins.h"
//单线管脚 P3.4

// typedef struct _DS_Pass
// {
//     u8 Identifier[8]; //识别码
//     u8 Key[8];        //密钥
// }DS1991_Type;

//显示错误并停止
void DS_ErrMsg(u8*str)
{
	LCD_Printf(1, 0, "                    "); //器件没有
	LCD_Printf(2, 0, "                    ");
	LCD_Printf(3, 0, "                    ");
	LCD_Printf(4, 0, "                    ");
	LCD_Printf(1, 0, "%s",str); //器件没有
	while(1);
}
void DS_1991Err(void)
{
	DS_ErrMsg("DS1991 Err!");
}


//
DS1991_Type  DS_PassWord[3]=
{
	{{0x96,0x8F,0x67,0x57,0x14,0x38,0x5E,0x5E},{0x27,0xA9,0x66,0xA6,0x24,0x70,0xE8,0x22}},
	{{0x61,0xF5,0x32,0xA4,0x0F,0x7A,0x2E,0xB8},{0x5A,0x5D,0x4E,0xE6,0x72,0x44,0x38,0x12}},
	{{0x29,0x26,0x17,0x09,0xA4,0x96,0xD2,0x74},{0x58,0x2F,0x7B,0x15,0xDA,0x29,0x04,0x8D}},
};

volatile u8 DS_Dat[48]= {0};


#define _1Wire P3_4

void DS_delayus(u16 nus)
{
	while(nus--)
	{

	}
}


// DS器件复位+检查器件是否存在
u8  DS_Reset(void)
{
	u16 i = 0, k = 0;
	_1Wire = 1;
	_1Wire = 0;
	DS_delayus(250);    // >480us  拉低480us以上
	_1Wire = 1;
	i = 100;
	do   /*等待回应脉冲*/
	{
		if(_1Wire == 0)    //信号拉低
		{
			i = 0;
			k = 1;
			do
			{
				if(_1Wire == 1) //等待信号置1
				{
					break;
				}
			}
			while(i++ < 0x30);
			break;
		}
	}
	while(i--);
	DS_delayus(100);
	return k;
}
//02 C8 E4 00 10 00 00 CB
/******************************
函数功能:DS1991写数据
*byte1为数据指针,
num表示写的数据个数
********************************/
static void DS_wbyte(unsigned char Dat)
{
	unsigned char j = 0, temp = 0;
	for(j = 0; j < 8; j++)
	{
		temp = (Dat >> j) & 0x01; //7us
		if(temp == 1)   //写1    15us < 拉低信号 < 60us
		{
			_1Wire = 0;
			DS_delayus(10);      //(大概20us)
		}
		else           //写0     60us < 拉低信号 < 120us
		{
			_1Wire = 0;
			DS_delayus(32);     //(大概64us)
		}
		_1Wire = 1;
		DS_delayus(5);          //(大概10us)
	}

}
/**
 *  @ 读取DS1991器件数据
 */
static u8 DS_rbyte(void)
{
	unsigned char i, Dat = 0;
	for(i = 0; i < 8; i++)
	{
		//启动输出信号
		_1Wire = 0;
		DS_delayus(8); //中间大概16us
		_1Wire = 1;
		//在检测到总线=0开始15us后,将数据放在总线上,持续时间0~45us
		//然后释放总线
		Dat >>= 1;
		if(_1Wire == 1)   //读取数据
		{
			Dat |= 0x80;
		}
		DS_delayus(25);  //(大概32us)
	}
	return Dat;
}

//读取 DS1991 序列号ID(8字节)
//返回0 器件存在  1 器件不存在
u8  DS_ReadSerialNumber(u8 *pdat)
{
	u8  temp[8] = 0;
	u8  i = 0;
	u32 Dat = 0;
	if(DS_Reset())
	{
		DS_wbyte(0xCC);
		DS_wbyte(0x33); //写入
		//读取8字节==1字节(家族码)+6字节(序列号)+1字节(CRC校验)
		pdat[0] = DS_rbyte();
		pdat[1] = DS_rbyte();
		pdat[2] = DS_rbyte();
		pdat[3] = DS_rbyte();
		pdat[4] = DS_rbyte();
		pdat[5] = DS_rbyte();
		pdat[6] = DS_rbyte();
		pdat[7] = DS_rbyte();
	}
	return DS_Reset();
}

//获得机器编号
//DS_SerialID:DS1991 芯片序列号
//返回 机器的序列号
u32  DS_GetMachineID(u8 *DS_SerialID)
{
	u32 Dat = 0;
	Dat <<=8;
	Dat |=DS_SerialID[4];
	Dat <<=8;
	Dat |=DS_SerialID[3];
	Dat <<=8;
	Dat |=DS_SerialID[2];
	Dat <<=8;
	Dat |=DS_SerialID[1];
	return Dat%100000000;
}

//数据写入缓存器
void DS_WriteCacheAreaByte(u8 addr,u8 dat)
{
	addr &=0x3F;
	addr |=0xC0;
	DS_Reset();
	DS_wbyte(0xCC);          //跳过验证ROM
	DS_wbyte(0x96);          //写入缓存区
	DS_wbyte(addr);          //地址
	DS_wbyte(0xFF-addr);         //地址反码
	DS_wbyte(dat);           //放入数据
	DS_Reset();
}
//数据读出缓存区
u8 DS_ReadCacheAreaByte(u8 addr)
{
	addr &=0x3F;
	addr |=0xC0;
	DS_Reset();
	DS_wbyte(0xCC);          //跳过验证ROM
	DS_wbyte(0x69);          //写入缓存区
	DS_wbyte(addr);          //地址
	DS_wbyte(0xFF-addr);     //地址反码
	return DS_rbyte();       //读出数据
}

//获取识别码
//PartitionAddr 分区 pIdentifier识别码
//返回 0:器件不存在 1:器件正常
u8 DS_GetIdentifier(u8 PartitionAddr,u8 *pIdentifier)
{
	u8 temp[8]= {0};
	u8 i = 0;
	u8 addr = (PartitionAddr<<6)|0x10;
	DS_Reset();
	DS_wbyte(0xCC);
	DS_wbyte(0x99);
	DS_wbyte(addr );
	DS_wbyte(~addr);
	for ( i = 0; i < 8; i++)
	{
		pIdentifier[i]=DS_rbyte();
	}
	return DS_Reset();
}

//写入新的识别码,和密钥 (取地址,识别码,密钥)
//PartitionAddr 数据区地址:0,1,2
//返回 0:器件不存在 1:器件正常
u8 DS_ChangePassword(u8 PartitionAddr,u8* pIdentifier,u8*pKey)
{
	u8 temp[8]= {0};
	u8 i = 0;
	u8 flag = 0;   //是否修改密钥
	u8 addr = (PartitionAddr<<6);
	DS_Reset();
	DS_wbyte(0xCC);
	DS_wbyte(0x5A);
	DS_wbyte(addr );
	DS_wbyte(~addr);
	for ( i = 0; i < 8; i++) //读出识别码
	{
		temp[i]=DS_rbyte();
	}
	for ( i = 0; i < 8; i++) //写入读出的识别码
	{
		DS_wbyte(temp[i]);
	}
	for ( i = 0; i < 8; i++) //写入新识别码
	{
		DS_wbyte(pIdentifier[i]);
	}
	for ( i = 0; i < 8; i++) //写入新密钥
	{
		DS_wbyte(pKey[i]);
	}
	return DS_Reset();
}

//写入加密数据    (数据区,识别码,密钥,数据区,开始地址(0-0x2F),数据长度)
//PartitionAddr 数据区地址:0,1,2
//startAddr     开始地址:0x10-0x3F
//len           读取的长度
//0:器件不存在 1:器件正常 2:识别码不正确 3:区地址溢出
u8 DS_WriteSubDat(u8 PartitionAddr,u8* pIdentifier,u8*pKey,u8 *dat,u8 startAddr,u8 len)
{
	u8 temp[8]= {0};
	u8 i = 0;
	u8 addr = 0;
	if(PartitionAddr>=3)return 3;
	addr    = (PartitionAddr<<6)|startAddr;
	if(DS_Reset()==0)return 0; //器件不存在
	DS_wbyte(0xCC);
	DS_wbyte(0x99);            //写入加密数据
	DS_wbyte(addr );
	DS_wbyte(~addr);
	for ( i = 0; i < 8; i++)  //读出识别码(判断)
	{
		temp[i]=DS_rbyte();
		if(pIdentifier[i] != temp[i])
		{
			DS_Reset();
			return 2;  //识别码错误
		}
	}
	for ( i = 0; i < 8; i++) //写入密钥
	{
		DS_wbyte(pKey[i]);
	}
	for ( i = 0; i < len; i++)//写入数据
	{
		DS_wbyte(dat[i]);
	}
	return DS_Reset();
}
//读出加密数据
//PartitionAddr 数据区地址:0,1,2
//startAddr     开始地址:0x10-0x3F
//len           读取的长度
//0:器件不存在 1:器件正常 2:识别码不正确 3:区地址溢出
u8 DS_ReadSubDat(u8 PartitionAddr,u8* pIdentifier,u8*pKey, u8 *dat,u8 startAddr,u8 len)
{
	u8 temp[8]= {0};
	u8 i = 0;
	u8 addr = 0;
	if(PartitionAddr>=3)return 3;
	addr    = (PartitionAddr<<6)|startAddr;
	if(DS_Reset()==0)return 0;
	DS_wbyte(0xCC);  //跳过ROM
	DS_wbyte(0x66);  //写入加密数据
	DS_wbyte(addr );
	DS_wbyte(~addr);
	for ( i = 0; i < 8; i++)  //读出识别码(判断)
	{
		temp[i]=DS_rbyte();
		if(pIdentifier[i] != temp[i])
		{
			return 2;  //识别码错误
		}
	}
	for ( i = 0; i < 8; i++)  //写入密钥
	{
		DS_wbyte(pKey[i]);
	}
	for ( i = 0; i < len; i++)//读取数据
	{
		dat[i] = DS_rbyte();
	}
	return DS_Reset();
}

//比较识别码(8字节)
//返回0 失败 1成功
u8 DS_CompareIdentifier(u8*ReadIdentifier,u8*Password)
{
	u8 i=0;
	for ( i = 0; i < 8; i++)
	{
		if(ReadIdentifier[i]!=Password[i])return 0;
	}
	return 1;
}
//void OutDat(u8*str,u8*dat)
//{
//   u8 i =0;
//   DebugOut(5,"%s :",str);
//   for ( i = 0; i < 8; i++)
//   {
//       DebugOut(5,"%02X ",(u16)dat[i]);
//   }
//   DebugOut(5,"\r\n");
//}

/********************************End of File************************************/

DS1991.h

/**
  ************************************* Copyright ******************************   
  *                 (C) Copyright 2021,Wenkic,China, GCU.
  *                            All Rights Reserved
  *                              
  *                     By(Wenkic)
  *                    https://blog.csdn.net/Wekic
  *      
  * FileName     : DS1991.h   
  * Version      : v1.0     
  * Author       : Wenkic           
  * Date         : 2021-04-27         
  * Description  :    
  * Function List:  
  ******************************************************************************
 */ 
 /********************************End of Head************************************/
#ifndef _DS1991_H
#define _DS1991_H

/* C++ detection */
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"

typedef struct _DS_Pass
{
    u8 Identifier[8]; //识别码
    u8 Key[8];        //密钥
}DS1991_Type;
extern DS1991_Type  DS_PassWord[3];
extern volatile u8 DS_Dat[48];

u8  DS_Reset(void);             //复位+加检测器件是否存在
//读取 DS1991 序列号ID(8字节)
//返回0 器件存在  1 器件不存在
u8  DS_ReadSerialNumber(u8 *pdat);
//获得机器编号
//DS_SerialID:DS1991 芯片序列号
//返回 机器的序列号
u32  DS_GetMachineID(u8 *DS_SerialID);

//数据写入缓存器
void DS_WriteCacheAreaByte(u8 addr,u8 dat);
//数据读出缓存区
u8 DS_ReadCacheAreaByte(u8 addr);

//获取识别码
//PartitionAddr 分区 pIdentifier识别码
//返回 0:器件不存在 1:器件正常
u8 DS_GetIdentifier(u8 PartitionAddr,u8 *pIdentifier);

//写入新的识别码,和密钥 (取地址,识别码,密钥)
//PartitionAddr 数据区地址:0,1,2
u8 DS_ChangePassword(u8 PartitionAddr,u8* pIdentifier,u8*pKey);
//写入加密数据    (数据区,识别码,密钥,数据区,开始地址(0-0x2F),数据长度)
//PartitionAddr 数据区地址:0,1,2
//startAddr     开始地址:0x10-0x3F
//len           读取的长度
//0:器件不存在 1:器件正常 2:识别码不正确 3:取地址溢出
u8 DS_WriteSubDat(u8 PartitionAddr,u8* pIdentifier,u8*pKey,u8 *dat,u8 startAddr,u8 len);
//读出加密数据
//PartitionAddr 数据区地址:0,1,2
//startAddr     开始地址:0x10-0x3F
//len           读取的长度
//0:器件不存在 1:器件正常 2:识别码不正确 3:取地址溢出
u8 DS_ReadSubDat(u8 PartitionAddr,u8* pIdentifier,u8*pKey, u8 *dat,u8 startAddr,u8 len);
//比较识别码(8字节)
//返回0 失败 1成功
u8 DS_CompareIdentifier(u8*ReadIdentifier,u8*Password);

//void OutDat(u8*str,u8*dat);
//显示错误并停止
void DS_ErrMsg(u8*str);
void DS_1991Err(void);
/* C++ detection */
#ifdef __cplusplus
}
#endif


#endif
/********************************End of File************************************/

 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐