stm32收发 wiegand 韦根协议开发详解

stm32收发 wiegand 韦根协议开发详解在刚开始接触到韦根接口时,知道这是一种门禁相关的传输协议。其中有两种比较常用的韦根数据格式,韦根26和韦根34,其中韦根26是开放的,韦根34开不开放我不知道(看样子不开放),但是在网上还是能看到韦根34的代码协议,下面介绍一下韦根26以及韦根34的相关内容。Wiegand26格式:各数据位的含义:第1位: 为输出数据2—13位的偶校验位第2-9位:…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

     在刚开始接触到韦根接口时,知道这是一种门禁相关的传输协议。其中有两种比较常用的韦根数据格式,韦根26和韦根34,其中韦根26是开放的,韦根34开不开放我不知道(看样子不开放),但是在网上还是能看到韦根34的代码协议,下面介绍一下韦根26以及韦根34的相关内容。

Wiegand 26格式:

stm32收发 wiegand 韦根协议开发详解

各数据位的含义:

第 1   位: 为输出数据2—13位的偶校验位

第 2 – 9 位:  ID卡的HID码的低8位

第10 – 25位:  ID卡的PID号码

第 26 位: 为输出数据14-25位的奇校验位

检验位1为偶校验位:对于WG26来说,如果前1-8位有偶数个1,那么检验位1=0,反之为1

检验位2为奇校验位:对于WG26来说,如果后14-25位有奇数个1,那么检验位2=0,反之为1

数据输出顺序:

HID码和PID码均为高位在前,低位在后

例:一张ID卡内容为:

HID:32769   PID:34953      ( 卡面印:2147584137   001, 34953 )

相应的二进制为:

HID:1000 0000 0000 0001       ( 只输出低8位 )

PID:1000 1000 1000 1001

输出如下:

1  0 0 0 0 0 0 0 1   1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1    0

    |   HID_L        |   |       PID                      |

 

Wiegand 34格式:

stm32收发 wiegand 韦根协议开发详解

各数据位的含义:

第 1   位: 为输出第2—17位的偶校验位

第 2-17 位:  ID卡的HID码

第18-33位:  ID卡的PID号码

第 34 位: 为输出第18-33位的奇校验位

检验位1为偶校验位:对于WG34来说,如果前16位有数个1,那么检验位1=0,反之为1

检验位2为奇校验位:对于WG34来说,如果前16位有数个1,那么检验位2=0,反之为1

 

数据输出顺序:

HID码和PID码均为高位在前,低位在后

例:一张ID卡内容为:

HID:32769   PID:34953       ( 卡面印:2147584137   001, 34953 )

相应的二进制为:

HID:1000 0000 0000 0001

PID:1000 1000 1000 1001

输出如下:

0  1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1      1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1   0

   |       HID_L                    |                 |        PID                     |

在空闲时间,两个线保持的是高电平+5V,两根线分别为DATA0和DATA1。其中,DATA0用来传输‘0’,DATA1用来传输‘1’,原本处于高电平状态的DATA0拉低一个脉冲宽度W,便发出了‘0’,DATA1原本处于高电平状态拉低一个脉冲宽度W,便发出了一个‘1’。上述脉冲宽度W=100us-200us较为合适,两输出之间间隔T=1ms-3ms较为合适。

 

韦根连续发送两张卡的电平最小时间间隔T为0.25s,因此如果要连续接收多张电子卡数据时,可判断脉冲间隔T是否大于240ms,以此判断前一张卡片数据是否已经接收完成,韦根的接收程序一般是用中断方式完成,然后使用定时器进行计数以判断是否接受完一帧数据。

下图所示为,韦根时序图。

stm32收发 wiegand 韦根协议开发详解

以下为韦根26和韦根34发送代码:

wiegand.h文件

#define WG_DATA0(x) {if(0==(x))	HAL_GPIO_WritePin(WGD0_GPIO_Port, WGD0_Pin,GPIO_PIN_RESET); else HAL_GPIO_WritePin(WGD0_GPIO_Port, WGD0_Pin,GPIO_PIN_SET);} 
#define WG_DATA1(x) {if(0==(x))	HAL_GPIO_WritePin(WGD1_GPIO_Port, WGD1_Pin,GPIO_PIN_RESET); else HAL_GPIO_WritePin(WGD1_GPIO_Port, WGD1_Pin,GPIO_PIN_SET);} 


typedef struct
{
	INT8U ucRxRingBuffer[WG_RX_BUFFERSIZE];//接收环形缓冲区
	INT16U usReadPos;					//环形接收缓冲区读位置
	INT16U usWritePos;					//环形接收缓冲区写位置
	INT8S usFrameCount;					//帧数
}WieGand_MSG_ST;						//韦根数据接收结构

Jetbrains全家桶1年46,售后保障稳定

wiegand.c 

int WG_Send26(unsigned char *str)
{
	unsigned char one_num	= 0;
	unsigned char even 		= 0;
	unsigned char odd 		= 0;
	unsigned char check_temp,i;

	if(NULL == str)
		return -1;

	/*首先计算2-13位共12位的奇偶*/
	check_temp = *str;
	for(i = 0;i < 8;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	
	check_temp = *(str + 1);
	for(i = 0;i < 4;i++)
	{
		if(check_temp & 0x80)
			one_num++;
		check_temp <<= 1;
	}
	if(one_num % 2 )
		even = 0;
	else
		even = 1;

	
	/*然后计算14-25位共12位的奇偶*/
	one_num = 0;
	check_temp = *(str + 1);
	for(i = 0;i < 4;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	check_temp = *(str + 2);
	for(i = 0;i < 8;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	if(one_num % 2 )
		odd = 1;
	else
		odd = 0;

	/*保持高电平准备发送数据*/
	WG_DATA0(1);
	WG_DATA1(1);
	HAL_Delay(5);

	/*发送第一位校验位*/
	if(even)
	{
		WG_DATA1(0);					
		myDelay_us(300);
		WG_DATA1(1);
	}
	else
	{			
		WG_DATA0(0);				   
		myDelay_us(300);
		WG_DATA0(1);
	}
	HAL_Delay(2);

	/*发送24位数据*/
	for(i = 0;i < 24;i++)
	{
		WG_DATA0(1);
		WG_DATA1(1);
		if(str[0] & 0x80)
		{
			WG_DATA1(0);
			myDelay_us(300);
			WG_DATA1(1);
		}
		else
		{
			WG_DATA0(0);
			myDelay_us(300);
			WG_DATA0(1);
		}
		(*(long*)&str[0]) <<= 1;
		HAL_Delay(2);			   
	}
	WG_DATA0(1); //拉高两条数据线电平
	WG_DATA1(1);

	/*发送最后一位校验位*/
	if(odd)
	{
		WG_DATA1(0);
		myDelay_us(300);
		WG_DATA1(1);
	}
	else
	{			
		WG_DATA0(0);
		myDelay_us(300);
		WG_DATA0(1);
	}
	WG_DATA0(1); //拉高两条数据线电平
	WG_DATA1(1);

	return 0;
}
int WG_Send34(unsigned char *str)
{
	unsigned char one_num 	= 0;
	unsigned char even 		= 0;
	unsigned char odd 		= 0;
	unsigned char check_temp,i;

	if(NULL == str)
		return -1;
	
	check_temp = *str; //第一个字节
	for(i = 0;i < 8;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	
	check_temp = *(str + 1);//第二个字节
	for(i = 0;i < 8;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	
	if(one_num % 2 )
		even = 0;
	else
		even = 1;
	
	one_num = 0;
	check_temp = *(str + 2);//第三个字节
	for(i = 0;i < 8;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	check_temp = *(str + 3);//第三个字节
	for(i = 0;i < 8;i++)
	{
		if(check_temp & 0x01)
			one_num++;
		check_temp >>= 1;
	}
	if(one_num % 2 )
		odd = 1;
	else
		odd = 0;
	
	WG_DATA0(1); //拉高两条数据线电平
	WG_DATA1(1);
	HAL_Delay(5);

	/*发送第一位*/
	if(even)
	{
		WG_DATA1(0);
		myDelay_us(300);
		WG_DATA1(1);
	}
	else
	{
		WG_DATA0(0);
		myDelay_us(300);
		WG_DATA0(1);
	}
	HAL_Delay(2);

	/*发送32字节数据*/
	for(i = 0;i < 32;i++)
	{
		WG_DATA0(1);
		WG_DATA1(1);
		if(str[0] & 0x80)
		{
			WG_DATA1(0);
			myDelay_us(300);
			WG_DATA1(1);
		}
		else
		{
			WG_DATA0(0);
			myDelay_us(300);
			WG_DATA0(1);
		}
		(*(long*)&str[0]) <<= 1;
		HAL_Delay(2);
	}
	WG_DATA0(1); //拉高两条数据线电平
	WG_DATA1(1);

	/*发送最后一位校验位*/
	if(odd)
	{
		WG_DATA1(0);
		myDelay_us(300);
		WG_DATA1(1);
	}
	else
	{
		WG_DATA0(0);
		myDelay_us(300);
		WG_DATA0(1);
	}
	WG_DATA0(1); //拉高两条数据线电平
	WG_DATA1(1);

	return 0;
}

在韦根信号接收方面,我使用了一个循环缓冲数组进行接收,在接收代码编写过程中,之前有一个疑问是,似乎采用中断接收时,是不用判断脉冲宽度的,然后只增加了对于一帧数据是否接收完的超时判断,这个超时计数是通过定时器做的,判断是否大于240ms还没有接收到脉冲,如果超过,则认为一帧接收完成了。

 

WieGand_MSG_ST stWG_Receive;		//韦根数据接收结构
INT8U u_DataBits 		= 0;		//当前接收数据位数
INT16S us_FirstBitPos	= 0;		//记录存放帧格式的位置
extern	TIMER_WG_ST st_Timer_WG;	//韦根接收定时结构


void WG_Receive(unsigned char dataLineType)
{		
	//Data0-> 低电平表示1位0
	if(WG_DATA0_LINE == dataLineType)
	{
		/*接收的第一位*/
		if (st_Timer_WG.usTIM_WgRxTimeCount == 0xffff)
		{
			/*接收位数清零*/
			u_DataBits = 0;
			
			st_Timer_WG.usTIM_WgRxTimeCount = 0;/*开始计时*/
			/*记录存放本次接收位数的位置*/
			us_FirstBitPos = stWG_Receive.usWritePos;
			/*更新保存的位置*/
			stWG_Receive.usWritePos++;
			if(stWG_Receive.usWritePos == WG_RX_BUFFERSIZE)
			{
				stWG_Receive.usWritePos = 0;
			}
			
			/*增加--若写指针赶上了读指针,需要处理*/
			if(stWG_Receive.usWritePos == stWG_Receive.usReadPos)
			{
				/*读指针往后偏移一位*/
				if(stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE26 || stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE34)
				{
					stWG_Receive.usReadPos++;
					if(stWG_Receive.usReadPos == WG_RX_BUFFERSIZE)
					{
						stWG_Receive.usReadPos = 0;
					}
				}
				else /*读指针往后偏移一帧*/
				{
					if((WG_RX_BUFFERSIZE - stWG_Receive.usReadPos)> (stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos]+1))
					{
						stWG_Receive.usReadPos +=stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] +1;
					}
					else
					{
						stWG_Receive.usReadPos = (stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos]+1) - (WG_RX_BUFFERSIZE - stWG_Receive.usReadPos);
					}
					if(stWG_Receive.usFrameCount > 0) 
						stWG_Receive.usFrameCount--;
				}
			}
			
			/*保存接收到的0*/
			stWG_Receive.ucRxRingBuffer[stWG_Receive.usWritePos] = 0;
			
			stWG_Receive.usWritePos++;
			if(stWG_Receive.usWritePos == WG_RX_BUFFERSIZE)
			{
				stWG_Receive.usWritePos = 0;
			}
		}
		else/*不是接收第一位*/
		{
			/*增加--若写指针赶上了读指针,需要处理*/
			if(stWG_Receive.usWritePos == stWG_Receive.usReadPos)
			{
				/*读指针往后偏移一位*/
				if(stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE26 || stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE34)
				{
					stWG_Receive.usReadPos++;
				}
				else /*读指针往后偏移一帧*/
				{
					if((WG_RX_BUFFERSIZE - stWG_Receive.usReadPos)> stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos]+1)
						stWG_Receive.usReadPos +=stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] +1;
					else
					{
						stWG_Receive.usReadPos = (WG_RX_BUFFERSIZE - stWG_Receive.usReadPos) +1;
					}
					if(stWG_Receive.usFrameCount > 0) 
						stWG_Receive.usFrameCount--;
				}
			}
			/*保存接收到的0*/
			stWG_Receive.ucRxRingBuffer[stWG_Receive.usWritePos] = 0;
			
			stWG_Receive.usWritePos++;
			if(stWG_Receive.usWritePos == WG_RX_BUFFERSIZE)
			{
				stWG_Receive.usWritePos = 0;
			}
		}

		/*累计接收到的位数*/
		u_DataBits++;
		st_Timer_WG.usTIM_WgRxTimeCount = 0;
		stWG_Receive.ucRxRingBuffer[us_FirstBitPos] = u_DataBits; //存放接收到WG的位数
		
	}
	//Data1 -> 低电平表示1位1
	else if(WG_DATA1_LINE == dataLineType)
	{
		/*接收的第一位*/
		if (st_Timer_WG.usTIM_WgRxTimeCount == 0xffff)
		{
			/*接收位数清零*/
			u_DataBits = 0;
			/*开始计时*/
			st_Timer_WG.usTIM_WgRxTimeCount = 0;
			/*记录存放本次接收位数的位置*/
			us_FirstBitPos = stWG_Receive.usWritePos;
			/*更新保存的位置*/
			stWG_Receive.usWritePos++;
			if(stWG_Receive.usWritePos == WG_RX_BUFFERSIZE)
			{
				stWG_Receive.usWritePos = 0;
			}
			
			/*增加--若写指针赶上了读指针,需要处理*/
			if(stWG_Receive.usWritePos == stWG_Receive.usReadPos)
			{
				/*读指针往后偏移一位*/
				if(stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE26 || stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE34)
				{
					stWG_Receive.usReadPos++;
					if(stWG_Receive.usReadPos == WG_RX_BUFFERSIZE)
					{
						stWG_Receive.usReadPos = 0;
					}
				}
				else /*读指针往后偏移一帧*/
				{
					if((WG_RX_BUFFERSIZE - stWG_Receive.usReadPos)> (stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos]+1))
					{
						stWG_Receive.usReadPos +=stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] +1;
					}
					else
					{
						stWG_Receive.usReadPos = (stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos]+1) - (WG_RX_BUFFERSIZE - stWG_Receive.usReadPos);
					}
					if(stWG_Receive.usFrameCount > 0) 
						stWG_Receive.usFrameCount--;
				}
			}
			
			/*保存接收到的值*/
			stWG_Receive.ucRxRingBuffer[stWG_Receive.usWritePos] = 1;
			
			stWG_Receive.usWritePos++;
			if(stWG_Receive.usWritePos == WG_RX_BUFFERSIZE)
			{
				stWG_Receive.usWritePos = 0;
			}
		}
		else/*不是接收第一位*/
		{
			

			/*增加--若写指针赶上了读指针,需要处理*/
			if(stWG_Receive.usWritePos == stWG_Receive.usReadPos)
			{
				/*读指针往后偏移一位*/
				if(stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE26|| stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] != WG_TYPE34)
				{
					stWG_Receive.usReadPos++;
				}
				else /*读指针往后偏移一帧*/
				{
					if((WG_RX_BUFFERSIZE - stWG_Receive.usReadPos)> stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos]+1)
						stWG_Receive.usReadPos +=stWG_Receive.ucRxRingBuffer[stWG_Receive.usReadPos] +1;
					else
					{
						stWG_Receive.usReadPos = (WG_RX_BUFFERSIZE - stWG_Receive.usReadPos) +1;
					}
					if(stWG_Receive.usFrameCount > 0) 
						stWG_Receive.usFrameCount--;
				}
			}
			/*保存接收到的值*/
			stWG_Receive.ucRxRingBuffer[stWG_Receive.usWritePos] = 1;
			
			stWG_Receive.usWritePos++;
			if(stWG_Receive.usWritePos == WG_RX_BUFFERSIZE)
			{
				stWG_Receive.usWritePos = 0;
			}
		}

		/*累计接收到的位数*/
		u_DataBits++;
		st_Timer_WG.usTIM_WgRxTimeCount = 0;
		stWG_Receive.ucRxRingBuffer[us_FirstBitPos] = u_DataBits; //存放接收到WG的位数
		
	}	 
}

定时器计时代码timer.c

typedef struct
{
	INT16U		usTIM_WgRxTimeCount;	//接收韦根超时的信息结构体
}TIMER_WG_ST;

TIMER_WG_ST st_Timer_WG;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	/*韦根接收计时*/
	if (htim->Instance == htim3.Instance)
	{
		if(st_Timer_WG.usTIM_WgRxTimeCount != 0xffff)
		{
			st_Timer_WG.usTIM_WgRxTimeCount++;
			if(st_Timer_WG.usTIM_WgRxTimeCount == WG_RXTIMEOUT)
			{
				//到达20ms延时,读取按键状态
				st_Timer_WG.usTIM_WgRxTimeCount = 0xffff;
				stWG_Receive.usFrameCount++; //接收到帧数+1
			}
		}
	}
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215875.html原文链接:https://javaforall.net

(0)
上一篇 2025年7月12日 下午11:43
下一篇 2025年7月13日 上午7:22


相关推荐

  • 分布式熔断机制_服务器熔断是什么意思啊

    分布式熔断机制_服务器熔断是什么意思啊#服务熔断-“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器(hystrix)的故障监控,某个异常条件被触发,直接熔断整个服务。向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。如果目标服务情况好转则恢复调用。服务熔断是解决服务雪崩的重要手段。#服务熔断图示…

    2022年8月31日
    4
  • scrapy安装步骤_scrapy官网

    scrapy安装步骤_scrapy官网安装scrapy过程中出现各种包安装错误,所以自己一直看教程知道scrapy安装需要准备好各种环境。这些包按照从下到上的顺序下载,lxml这个包按下文教程安装。不想看过多文字和图片的懒人们可看教程视频:http://www.iqiyi.com/w_19rz36pjft.html利用pipinstall命令安装pywin32,pyopenssl.这两个包可在cmd安装成功pip…

    2026年1月17日
    4
  • JavaScript判断类型的方法

    JavaScript判断类型的方法1 操作符 1 typeof 操作符格式 type typeofvariab 能判断类型有 number string boolean symbol undefined function object array null 的类型都返回 object 返回值为字符串 一共七种 number string boolean undefined symbol function object 判断时也应该加上引号 表示字符串 typeoff function typeof

    2026年3月20日
    2
  • win10网络重置命令net_win10 重置网络设置

    win10网络重置命令net_win10 重置网络设置经常遇到局域网无法互相访问、网络连接上但无法上网的问题。如果找不到愿意,可以暴力重置一下网络:win+x,管理员方式打开cmd;输入netshwinsockreset命令;可能需要重启操作系统。

    2025年7月3日
    5
  • win7打印共享无访问权限_win7连不上共享打印机

    win7打印共享无访问权限_win7连不上共享打印机平时我们在设置网络共享打印机的时候,可能会遇到没有权限访问的情况,下面和大家分析一下如何解决该问题。一、系统环境WIN7二、没有权限访问的原因原因就是:安装打印机的那台WIN7电脑,设置了开机的帐户和密码,所以导致其它计算机没有权限访问。所以得使用下面的办法解决。三、解决办法在安装打印机的电脑上,进行如下的操作即可。首先,在运行那里输入gpedit.msc并回车。如下图。在打开的界面中,如下图一样…

    2025年6月26日
    3
  • kotlin与java区别_Java和Kotlin的区别

    kotlin与java区别_Java和Kotlin的区别1 继承用 冒号 代替 java MainActivity MaiActivity Activity 2 interface 用 逗号 代替 Java AinterfaceB kotlin B 3 override 不现进注解 而是用到了方法中 overridefuno savedInstanc Bund

    2026年3月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号