DS1991 多密钥iButton加密芯片(单线通信)
一、单总线协议(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************************************/
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)