proteus仿真下载:
(仿真电路连接和实际硬件电路图有所不同,见文中说明)
链接:https://pan.baidu.com/s/1lE3fXhxlXvlca3QdciRIIw
提取码:z9xa
手上有一个韦根26协议的读头,想把它利用起来。
我购买的是UID IC卡,配合这种读头使用。
什么是韦根26协议:
Wiegand(韦根)协议是由摩托罗拉公司制定的一种通讯协议,它适用于涉及门禁控制系统的读卡器和卡片的许多特性,其协议并没有定义通讯的波特率、也没有定义数据长度。
韦根格式主要定义是数据传输方式:Data0和Data1两根数据线分别传输0和1,现在应用最多的是26bit,34bit,36bit,44bit等等,其中标准26-bit 格式是一个开放式的格式,任何人都可以购买某一特定格式的HID卡,并且这些特定格式的种类是公开可选的,26-Bit格式就是一个广泛使用的工业标准,并且对所有HID的用户开放,现在几乎所有的门禁控制系统都接受26-Bit格式的标准。
韦根数据是如何输出的:
韦根码在数据的传输中只需两条数据线,一条为DATA0,另一条为DATA1。协议规定,两条数据线在无数据时均为高电平。DATA0为低电平代表数据0,DATA1为低电平代表数据1。(低电平信号低于1V,高电平信号大于4V),数据信号波形如图所示。
韦根数据的输出格式
标准韦根输出是由26位二进制数组成,例如有数据:01000110111000001001010101
各位的含义如下:
最高位,也就是序号为1的那位,是第2-13位的偶校验位。
最低位,也就是序号为26的那位,是第14-25位的奇校验位。
第2-9位对应与电子卡HID码的低8位
第10-25位对应电子卡的PID号码
以上数据从左至右顺序发送。
关于奇偶校验:
偶校验就是加上校验位后,使得数据中1的个数为偶数。例如,在上面的韦根26数据中,第1位是第2-13位的偶校验位。第2-13位一共有6个1,算上校验位,要使1的个数为偶数,那么偶校验位必须为0。
奇校验就是加上校验位后,使得数据中1的个数位奇数。例如,在上面的韦根26数据中,第26位是第14-25位的奇校验位。第14-25位一共有4个1,算上校验位,要使1的个数为奇数,那么奇校验位必须为1。
关于HID和PID号码:
HID即Hidden ID code 隐含码,PID即Public ID code 公开码。PID很容易在读出器的输出结果中找到,但HID在读出器的输出结果中部分或者全部隐藏。
韦根26接收
为了保证数据接收的实时性,需要在中断服务程序中接收数据,而不是通过查询的方式接收。
以上就是韦根26协议的相关知识。
下面来看一下手中的读头
我购买的韦根26协议读头是读IC卡的,卡扣是UID类型的IC卡。它的各个引脚功能为:
读头需要提供9-16V的电源电压。韦根协议规定,无信号的时候是高电平,有信号的时候是低电平,那么,D0,D1上的电平是多少呢?使用15V电源供电的情况下,用万用表测量了一下是5V。
读头读卡后输出了些什么数据呢?按照图示接好电源,将D0和D1连接到逻辑分析仪的两路上,将逻辑分析仪的捕获电平设置为自定义:0.2V,读卡后得到的波形如下:
经测量,每个低电平的时间是404us,两个数据间的间隔是2ms。但是和韦根26协议规定的低电平持续时间:20us-200us略有不同。
图中,粉色的是D0,每个负脉冲代表0,蓝色的是D1每个负脉冲代表1。图中上下两行数据加起来正好是26个。
解读一下这些数据:
注意一下,这个时间轴是从左往右的,也就是左边数据是先发送的,右边数据是后发送的。
那么读头发送的原始数据应该是:01000110111000001001010101 一共26位。
用proteus模拟韦根26读头的数据发送
使用定时器T1,采用16位定时器方式。
//8051 T1初始化
void Timer1_init()
{
TMOD=0x10; //T1 16位定时器模式
ET1=0; //关闭定时器中断
TR1=0; //关闭定时器
TF1=0; //清除TF1标志
}
例如,就发送上面的这个数据:01000110111000001001010101 十进制的18580053
发送数据0的时候,就是将数据线D0拉低404us,发送数据1的时候,就是将数据线D1拉低404us。
首先设置定时器初值,用STC的下载器计算404us的预装入值。
拉低数据线,等待404us到时,之后抬高数据线,再等待2ms的时间,一位数据就发送完成了。
void Send_bit(bit bD)
{
//拉低数据线D0 404us
TL1 = 0x8C; //设置定时初值
TH1 = 0xFE; //设置定时初值
if(bD==0)
Send_D0=0;
else
Send_D1=0;
TR1=1; //开启定时器
while(TF1 ==0); //等待溢出
//时间到抬高数据线
if(bD==0)
Send_D0=1;
else
Send_D1=1;
TF1=0; //清溢出标志
TR1=0; //关定时器
//下面是数据位的间隔 2ms
TL1 = 0xCD; //设置定时初值
TH1 = 0xF8; //设置定时初值
TR1=1; //开启定时器
while(TF1 ==0); //等待溢出
TF1=0; //清溢出标志
TR1=0; //关定时器
}
将韦根26协议的数据装入一个无符号长整型变量里:
//二进制 0 100011011100000100101010 1 头尾两位为奇偶校验位,十进制是18580053
unsigned long WG26=18580053;
无符号长整型是四个字节32位,装入26位的数据,则最前面的6位是无效的,循环移位6次,把无效数据移除。
//000000 01000110111000001001010101
for(i=0; i<6; i++)
{
WGdata=WGdata<<1;
}
//现在WGdata中的数据是 01000110111000001001010101 000000,后面多了6个0。
有效数据已经移动到最前面,可以开始发送了,循环26次发送数据
for(i=0; i<26; i++)
{
if( (WGdata & 0x80000000) == 0x80000000 )
Send_bit(1); //如果最高位为1,发送1
else
Send_bit(0); //如果最高位为0,发送0
WGdata=WGdata<<1; //左移1位
}
}
完整发送函数:
//发送韦根26数据,用4个字节保存,一共32位
void SendWG26(unsigned long WGdata)
{
uchar data i;
//从最高位开始发送数据,将开头的6个无效数据位隔过去
//18580053
//000000 01000110111000001001010101
//01000110111000001001010101 000000
for(i=0; i<6; i++)
{
WGdata=WGdata<<1;
}
//有效数据位已经移到了开头,开始发送数据
for(i=0; i<26; i++)
{
if( (WGdata & 0x80000000) == 0x80000000 )
Send_bit(1);
else
Send_bit(0);
WGdata=WGdata<<1;
}
}
数据的接收
将数据线D0,D1连接到与门74HC08上,两条数据线上有数据发送时会产生INT0的下降沿中断。
(这只是仿真图,实际硬件连接有所不同)
在中断服务程序中接收数据:
还是用一个4字节的无符号长整型数据WG26,将收到的数据记入其最低位。每接到一位数据,左移一次。当接收到26个数据时,认为收到了读头发来的完整数据。设置接收完成标志ReceiveFlag=1;供主程序查询。
这里设置了一个超时检测,就是接收到的两位数据之间的时间间隔如果大于5ms就认为数据超时,(因为读头发来的数据每位之间的间隔是2ms)。这样,如果有意外的脉冲干扰,引起计数数据位的count值错误,也只会产生一次数据接收错误,将各种标志和变量全部清零后,不会影响下一次的数据接收。
在中断服务程序退出之前,一定要清除中断标志IE0,以免响应了无效数据的中断标志,产生接收错误。
void INT0_ISR(void) interrupt 0 //外部中断0服务程序
{
//如果接到的两位数据之间间隔超过5ms,定时器溢出标志TF1置位
//超时检测使用定时器T1,16位定时方式
EX0=0; //关中断
//如果有定时器超时标志置位
if(TF1==1)
//数据有误,放弃数据
{
LCD_StrDisp(0x00,"Try Again ");
LCD_StrDisp(0x40,"TimeOut Error ");
Beep(10);
//隔过至少一个数据包的时间,以便放弃不完整的数据
//延时100ms
Delay50ms();
Delay50ms();
TR1=0; //关定时
TF1=0; //清标志
TL1 = 0x00; //设置定时初值 5ms 溢出
TH1 = 0xEE; //设置定时初值 5ms 溢出
count=0;
WG26=0;
ReceiveFlag=0;
}
//如果数据位间隔未超时
else
{
WG26=WG26<<1;
if(RD0==0)
//接收到了0
WG26=WG26&0xFFFFFFFE;
else if(RD1==0)
//接收到了1
WG26=WG26|0x00000001;
count++;
if(count==26)
{
count=0;
ReceiveFlag=1;
TR1=0; //关定时
TF1=0; //清标志
}
else
{
//为接收下一位做准备
TR1 = 0; //关定时
TF1 = 0; //清除TF1标志
TL1 = 0x00; //设置定时初值
TH1 = 0xEE; //设置定时初值
//超过5ms溢出标志被置位
TR1 = 1; //定时器1开始计时
}
}
IE0=0; //清除INT0中断标志,很重要!
EX0=1; //开中断
}
在主程序查询到接收完成标志后,开始对数据进行奇偶校验位的核对。
得到奇校验位,记入odd=1
将无效的6位移除
得到偶校验位,记入even=0
将偶校验位移除,统计前12位有几个1
100011011100 000100101010 1
有6个1,因此偶校验位应为0,核对接收到的偶校验位even是否为0
再统计后12位中有几个1
100011011100 000100101010 1
有4个1,因此奇校验位应为1,核对接收到的奇校验位odd是否位1
如果奇偶校验正确,显示数据,如果错误,给出错误提示。
完整检查数据函数:
bit DataCheck()
{
uchar data count=0,i;
unsigned long xdata temp;
uchar idata odd,even;
temp=WG26;
//得到奇校验位
if( (temp&0x00000001) ==0x00000001)
odd=1;
else
odd=0;
//统计第2-13位1的个数
//把没用的6位移除
for(i=0; i<6; i++)
temp=temp<<1;
//得到偶校验位
if( (temp&0x80000000) ==0x80000000)
even=1;
else
even=0;
//将偶校验位移除
temp=temp<<1;
//开始统计第2-13位有几个1
for(i=0; i<12; i++)
{
if( (temp&0x80000000) == 0x80000000)
count++;
temp=temp<<1;
}
//判断偶校验位的正确性
if( (count%2==0 && even==0) || (count%2==1 && even==1) )
{
//接着判断奇校验位 14-25位
count=0;
for(i=0; i<12; i++)
{
if( (temp&0x80000000) ==0x80000000)
count++;
temp=temp<<1;
}
if( (count%2==0 && odd==1) || (count%2==1 && odd==0 ) )
{
//数据正确
return 1;
}
else
{
return 0;
}
}
else
return 0; //数据有错误
}
实际硬件连接:
实际硬件连接和仿真有所不同,为了稳定接收数据,需要通过一片总线驱动芯片74LS573再连接74LS08到单片机,见图: