单片机读取ID卡(EM4100卡)/ 125KHz RFID 曼彻斯特码 读卡程序


【本文发布于https://blog.csdn.net/Stack_/article/details/116952678,未经许可不得转载,转载须注明出处】


一、读卡电路原理图

在这里插入图片描述
这个图是别人画的,不是太懂原理。


MCU产生125K 方波,经过74HC04和4.7欧电阻后到达铜质线圈,ID卡靠近线圈,LM358会输出“曼彻斯特编码”的数据。MCU采集后校验通过得到卡号。


二、曼彻斯特码

【参考资料】


曼彻斯特编码的ID卡每次输出64bit数据,其载波编码为曼彻斯特码。

在这里插入图片描述

曼彻斯特码调制方式下,EM4100卡片每传送一位数据的时间是64个振荡周期。


125KHZ载波时,卡片传送一bit数据理论频率为125KHz / 64 = 1.953125KHz。


得一个周期为1 000 000us / 1.953125KHz = 512us。


曼彻斯特码一个完整的数据跳变为一个周期(512us),


存在空跳则半个跳变为半个周期(256us)。


假设MCU的PC5产生125K方波,PD2作检波。PD2配置为外部中断输入,且中断触发方式为上升沿&下降沿。

  1. 如果捕获到一个边沿,距离上一个边沿时间为512us,则读取此时的PD2电平
低电平此bit为1
高电平此bit为0
  1. 如果捕获到一个边沿,距离上一个边沿时间为256us,则此次不作判断;
    再次捕获到一个边沿时,判断:
上次bit = 1此次bit为1
上次bit = 0此次bit为0
或者
PD2 = 0此次bit为1
PD2 = 1此次bit为0

三、接收解析程序

以STM8S103为例,主频16MHz

/**
  * @brief  定时器
  * @note   
  * @param  None
  * @retval None
  * @author PWH @ CSDN Tyrion.Mon
  * @date   2020
  */
void Tim1_Init(void)
{
	Tim1_DeInit();
	TIM1_TimeBaseInit(1, TIM1_COUNTERMODE_UP, 9999, 0); //2分频,计数模式-向上,溢出值,重载值
	TIM1_ARRPreloadConfig(ENABLE);//使能数值自动重装载功能
	TIM1_Cmd(ENABLE);
}

根据此配置得

计数器频率16MHz / 2 = 8MHz
计数周期1000000us / 8MHz = 0.125us
一个周期(512us)计数值为512us / 0.125 = 4096
半个周期(256us)计数值为256us / 0.125 = 2048



因为一次传输64bit,所以一次接收128bit数据,其中肯定包含完整的一组数据。
用一个数组存储采集的数据

uint8_t ManchesterCodeBits[16] = {0}; //125KhzID卡 曼彻斯特码(Manchester code)  一次输出64位数据信息  ,采集两组 64bit*2 = 128bit  128/8=16Bytes
/**
  * @brief  外部中断配置
  * @note   
  * @param  None
  * @retval None
  * @author PWH @ CSDN Tyrion.Mon
  * @date   2020
  */
void Exti_init(void)
{
    GPIO_Init(GPIOD,GPIO_PIN_2,GPIO_MODE_IN_PU_IT);
    EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_RISE_FALL);
}
uint8_t CodeBitsRecComplete = 0;	//为0接收,为1校验
/**
  * @brief  PD中断服务函数
  * @note   
  * @param  None
  * @retval None
  * @author PWH  @ CSDN Tyrion.Mon
  * @date   2020
  */
INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6)
{
	static uint8_t BitsCnt = 0;   //接收到的bit计数,接收完128bit后暂停接收,进入校验步骤
	uint8_t Value = 0;
	uint16_t temp = 0;
	static uint8_t flag = 0;
	if (!CodeBitsRecComplete)
	{
		temp |= TIM1->CNTRH;
		temp <<= 8;
		temp |= TIM1->CNTRL;    //temp为计数器当前值
		if (temp > 3000 && temp < 5000) // 1个周期计数值约4096
		{
			TIM1->CR1 &= 0xFE;    //关定时器
			TIM1->CNTRH = 0;      //清零计数器
			TIM1->CNTRL = 0;
			TIM1->CR1 |= 0x01;    //开定时器
			if (flag)             //上一个跳变为空跳
			{
				Value = ((ManchesterCodeBits[(BitsCnt - 1) / 8] >> (7 - (BitsCnt - 1) % 8)) & 0x01) ? 1 : 0;    //上一bit为1则为1,否则为0
			}
			else
				Value = (GPIOD->IDR) & 0x04 ? 0 : 1;
			flag = 0;
			ManchesterCodeBits[BitsCnt / 8] |= (Value << (7 - BitsCnt % 8));
			BitsCnt++;
			if (BitsCnt > 127)
			{
				CodeBitsRecComplete = 1;
				BitsCnt = 0;
			}
		}
		else if (temp > 1000 && temp < 3000) // 1/2个周期计数值约2048
		{
			flag = 1;     //检测到一个空跳
		}
	}
}

四、EM4100卡片64位数据存储格式和发送格式

在这里插入图片描述

引导码1 1111 1111 (9bit)
D00 - D13版本号之类的(8bit)
D20 - D93卡号(32bit)
Px行校验 (10bit)
PCx列校验 (4bit)
S0停止位 (1bit)


传输顺序

bit 0 - bit 8引导位 1 1111 1111 (9bit)
bit 9 - bit 13D00 D01 D02 D03 P0
bit 14 - bit 18D10 D11 D12 D13 P1
bit 54 - bit 58D90 D91 D92 D93 P9
bit 59 - bit 62PC0 PC1 PC2 PC3
bit 63S0


五、数据校验程序

uint8_t GetBitValue(uint8_t bitsCnt)
{
	return (ManchesterCodeBits[bitsCnt / 8] >> (7 - bitsCnt % 8)) & 0x01;
}

/**
  * @brief  数据校验
  * @note   此函数在主循环中执行,或放在过滤函数(例如连续多次读到的数据均无错误才认为读到卡)中执行
  * @param  pBuff 存储卡号的数组
  * @retval None
  * @author PWH   @ CSDN Tyrion.Mon
  * @date   2020
  */
uint8_t GetCardNO(uint8_t * pBuff)
{
	uint8_t bitsCnt = 0;
	uint8_t i = 0;
	uint8_t j = 0;
	uint16_t Value = 0;
	uint8_t status = CARDFALSE;
	if (!CodeBitsRecComplete) return CARDREADING;     //有卡时,1秒满13次
	//无卡,1秒4 - 6次
	status = CARDFALSE;
	
	/* 找 0 1 1111 1111 (1停止位+9引导位) 如果bit63-bit72仍不是,则没必要继续找 */
	while (bitsCnt <= 63) 
	{
		Value = 0;
		for (j = 0; j <= 9; j++)
		{
			Value <<= 1;
			Value |= GetBitValue(bitsCnt + j);
		}
		if ( Value == 0x01ff ) //找到 0 1 1111 1111
		{
			bitsCnt += 10;	//跳过停止位和引导位
			break;
		}
		else				//没 找到 0 1 1111 1111
		{
			bitsCnt++;
		}
	}
	/* 找到了 0 1 1111 1111 */
	if (bitsCnt != 64)
	{     /* 数据行0校验 */
		if ( (GetBitValue(bitsCnt) + GetBitValue(bitsCnt + 1) + GetBitValue(bitsCnt + 2) + GetBitValue(bitsCnt + 3)) % 2 == GetBitValue(bitsCnt + 4) )
		{     /* 数据行1校验 */
			if ( (GetBitValue(bitsCnt + 5) + GetBitValue(bitsCnt + 6) + GetBitValue(bitsCnt + 7) + GetBitValue(bitsCnt + 8)) % 2 == GetBitValue(bitsCnt + 9) )  
			{     /* 数据行2校验 */
				if ( (GetBitValue(bitsCnt + 10) + GetBitValue(bitsCnt + 11) + GetBitValue(bitsCnt + 12) + GetBitValue(bitsCnt + 13)) % 2 == GetBitValue(bitsCnt + 14) )
				{     /* 数据行3校验 */
					if ( (GetBitValue(bitsCnt + 15) + GetBitValue(bitsCnt + 16) + GetBitValue(bitsCnt + 17) + GetBitValue(bitsCnt + 18)) % 2 == GetBitValue(bitsCnt + 19) )
					{     /* 数据行4校验 */
						if ( (GetBitValue(bitsCnt + 20) + GetBitValue(bitsCnt + 21) + GetBitValue(bitsCnt + 22) + GetBitValue(bitsCnt + 23)) % 2 == GetBitValue(bitsCnt + 24) )
						{     /* 数据行5校验 */
							if ( (GetBitValue(bitsCnt + 25) + GetBitValue(bitsCnt + 26) + GetBitValue(bitsCnt + 27) + GetBitValue(bitsCnt + 28)) % 2 == GetBitValue(bitsCnt + 29) )
							{     /* 数据行6校验 */
								if ( (GetBitValue(bitsCnt + 30) + GetBitValue(bitsCnt + 31) + GetBitValue(bitsCnt + 32) + GetBitValue(bitsCnt + 33)) % 2 == GetBitValue(bitsCnt + 34) )
								{     /* 数据行7校验 */
									if ( (GetBitValue(bitsCnt + 35) + GetBitValue(bitsCnt + 36) + GetBitValue(bitsCnt + 37) + GetBitValue(bitsCnt + 38)) % 2 == GetBitValue(bitsCnt + 39) )
									{     /* 数据行8校验 */
										if ( (GetBitValue(bitsCnt + 40) + GetBitValue(bitsCnt + 41) + GetBitValue(bitsCnt + 42) + GetBitValue(bitsCnt + 43)) % 2 == GetBitValue(bitsCnt + 44) )
										{     /* 数据行9校验 */
											if ( (GetBitValue(bitsCnt + 45) + GetBitValue(bitsCnt + 46) + GetBitValue(bitsCnt + 47) + GetBitValue(bitsCnt + 48)) % 2 == GetBitValue(bitsCnt + 49) )
											{     /* 数据列0校验 */
												if ( (GetBitValue(bitsCnt) + GetBitValue(bitsCnt + 5) + GetBitValue(bitsCnt + 10) + GetBitValue(bitsCnt + 15) + GetBitValue(bitsCnt + 20) + GetBitValue(bitsCnt + 25) + GetBitValue(bitsCnt + 30) + GetBitValue(bitsCnt + 35) + GetBitValue(bitsCnt + 40) + GetBitValue(bitsCnt + 45)) % 2 == GetBitValue(bitsCnt + 50) )
												{     /* 数据列1校验 */
													if ( (GetBitValue(bitsCnt + 1) + GetBitValue(bitsCnt + 6) + GetBitValue(bitsCnt + 11) + GetBitValue(bitsCnt + 16) + GetBitValue(bitsCnt + 21) + GetBitValue(bitsCnt + 26) + GetBitValue(bitsCnt + 31) + GetBitValue(bitsCnt + 36) + GetBitValue(bitsCnt + 41) + GetBitValue(bitsCnt + 46)) % 2 == GetBitValue(bitsCnt + 51) )
													{     /* 数据列2校验 */
														if ( (GetBitValue(bitsCnt + 2) + GetBitValue(bitsCnt + 7) + GetBitValue(bitsCnt + 12) + GetBitValue(bitsCnt + 17) + GetBitValue(bitsCnt + 22) + GetBitValue(bitsCnt + 27) + GetBitValue(bitsCnt + 32) + GetBitValue(bitsCnt + 37) + GetBitValue(bitsCnt + 42) + GetBitValue(bitsCnt + 47)) % 2 == GetBitValue(bitsCnt + 52) )
														{     /* 数据列3校验 */
															if ( (GetBitValue(bitsCnt + 3) + GetBitValue(bitsCnt + 8) + GetBitValue(bitsCnt + 13) + GetBitValue(bitsCnt + 18) + GetBitValue(bitsCnt + 23) + GetBitValue(bitsCnt + 28) + GetBitValue(bitsCnt + 33) + GetBitValue(bitsCnt + 38) + GetBitValue(bitsCnt + 43) + GetBitValue(bitsCnt + 48)) % 2 == GetBitValue(bitsCnt + 53) )
															{     /* 停止位 */
																if (GetBitValue(bitsCnt + 54) == 0)
																{
																	status = CARDTRUE;
	                                                                /* 4字节卡号 */
																	pBuff[0] = GetBitValue(bitsCnt + 10) << 7 | GetBitValue(bitsCnt + 11) << 6 | GetBitValue(bitsCnt + 12) << 5 | GetBitValue(bitsCnt + 13) << 4 |  GetBitValue(bitsCnt + 15) << 3 | GetBitValue(bitsCnt + 16) << 2 | GetBitValue(bitsCnt + 17) << 1 | GetBitValue(bitsCnt + 18);
																	pBuff[1] = GetBitValue(bitsCnt + 20) << 7 | GetBitValue(bitsCnt + 21) << 6 | GetBitValue(bitsCnt + 22) << 5 | GetBitValue(bitsCnt + 23) << 4 |  GetBitValue(bitsCnt + 25) << 3 | GetBitValue(bitsCnt + 26) << 2 | GetBitValue(bitsCnt + 27) << 1 | GetBitValue(bitsCnt + 28);
																	pBuff[2] = GetBitValue(bitsCnt + 30) << 7 | GetBitValue(bitsCnt + 31) << 6 | GetBitValue(bitsCnt + 32) << 5 | GetBitValue(bitsCnt + 33) << 4 |  GetBitValue(bitsCnt + 35) << 3 | GetBitValue(bitsCnt + 36) << 2 | GetBitValue(bitsCnt + 37) << 1 | GetBitValue(bitsCnt + 38);
																	pBuff[3] = GetBitValue(bitsCnt + 40) << 7 | GetBitValue(bitsCnt + 41) << 6 | GetBitValue(bitsCnt + 42) << 5 | GetBitValue(bitsCnt + 43) << 4 |  GetBitValue(bitsCnt + 45) << 3 | GetBitValue(bitsCnt + 46) << 2 | GetBitValue(bitsCnt + 47) << 1 | GetBitValue(bitsCnt + 48);
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	for (i = 0; i < 16; i++)
	{
		ManchesterCodeBits[i] = 0;
	}
	CodeBitsRecComplete = 0;
	return status;
}


在理解校验原理的基础上用循环结构对校验进行简写

if (bitsCnt != 64)
{
    status = CARDTRUE;
    /* 对10组行数据进行校验 */
    for (i = bitsCnt; i <= (bitsCnt + 45); i += 5)
    {
        Value = 0;
        for (j = 0; j <= 3; j++)
        {
            Value +=  GetBitValue(i + j);   //每行4bit数据累加
        }
        if ( Value % 2 != GetBitValue(i + 4) )
        {
            status = CARDFALSE;
            break;
        }
    }
    if (status == CARDTRUE)
    {
        /* 对4组列数据进行校验 */
        for (i = bitsCnt; i <= (bitsCnt + 3); i++)
        {
            Value = 0;
            for (j = 0; j <= 45; j += 5)
            {
                Value +=  GetBitValue(i + j);   //每列10bit数据累加
            }
            if ( Value % 2 != GetBitValue(i + 50) )
            {
                status = CARDFALSE;
                break;
            }
        }
    }
    if (status == CARDTRUE)
    {
        /* 停止位 */
        if (GetBitValue(bitsCnt + 54) == 0)
        {
            status = CARDTRUE;
            /* 4字节卡号 */
            for (i = 0; i < 4; i++)
            {
                pBuff[i] = 0;
                for (j = 0; j <= 8; j++)
                {
                    if (j != 4)
                    {
                        pBuff[i] <<= 1;
                        pBuff[i] |= GetBitValue(bitsCnt + (i + 1) * 10 + j);
                    }
                }
            }
        }
        else
            status = CARDFALSE;
    }
}



这个只是简单的实现,识别速度慢,偶有识别错误。可以通过优化大大提高识别速度以及达到接近百分百的准确率。
  • 6
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
RFID读卡模块原理图是ORCAD的,有PDF预览,PCB是PADS的,还有BOM烧写说明,标注图等。其中UART输出是量产过的 韦根输出的只是看示波器波形是对的,没有实际应用;里面有源代,编写前提是是CODE小于1K RAM小于64字节。 原理图原理就是CD4060产生125KHZ的方波,经过推挽电路进行功率放大,高频电流进入LC串联谐振电路,345uH和4.7NF的谐振频率正好是125KHZ,这时电容两端电压会到十几伏,如果用CBB电容,会到二十多伏,这时读卡距离会到10CM以上。当有接近线圈时,线圈两端会有曼彻斯特的调幅波。通过二极管以及电容的检波和滤波,产生的小信号送入LM358进行放大和整形,变成单片机可以读取曼彻斯特信号。 关于硬件电路设计上,CD4060在3.3V时 4M以下的晶振都可以起振,但2M的晶振体积很大,所以用了4M。实验发现ATTINY13的频率随着工作电压的变化会有很大的变化,所以不能用RC校准了,正好CD4060会输出4M,所以用来当系统时钟,保证时序的精确性。这样模块在3.3V 5V都能工作。 ATTINY13接不了无缘晶振 只能接有源的~ 它只有一个CLKIN脚~有源晶振价格就贵了。 另外ATTINY13只有5个IO口 1个用于中断 1个用于UART输出 1个用于CLKIN 1个用于上电波特率配置 1个用于曼彻斯特输入 正好用完了 RFID读卡模块 模块正反面 串口收到5个字节 前4个字节就是程序中,根据曼彻斯特原理,找到长电平和短电平,根据跳变沿分析出0数据还是1数据。然后寻找消息头,后面的数据进行 行 列奇偶校验,从而分析出号。 程序上电时,通过ADC引脚读取外部电阻的分压配置,从而初始化出4中波特率。然后进行读卡操作。实际应用中,要保证读卡的稳定,当靠近线圈时,要只发一次数据。要很好的去抖。代不能超过1K。现在代正好1024个字节。关于奇偶校验算法,异或和要比对2取余简洁,但我测试时发现异或和代长度大于对2取余,所以用了对2取余。 RFID读卡模块原理图+PCB+代+调试至附件下载
### 回答1: 51单片机是一种非常常用的单片机,它的核心是Intel公司制造的8051系列芯片。125kHz ID是一种低频射频,它采用125kHz的频率进行通信。而解曼彻斯特是将曼彻斯特进行解的过程。 在51单片机中使用125kHz ID的C程序,主要分为两部分:读取ID信息和解曼彻斯特。 在读取ID信息的过程中,首先需要使用51单片机的GPIO口连接到电磁感应模块,然后通过串行通信的方式进行数据交互。通过控制GPIO口的高低电平变化,实现与ID的通信。读取ID的信息之后,可以将其存储在单片机的内存中,或者通过串口输出。 解曼彻斯特的过程比较复杂。曼彻斯特是一种基于波形来表示数字的编方式,通过观察波形的升降沿变化来判断传输的数据。解曼彻斯特的过程需要对接收到的波形进行解,根据波形的升降沿变化来确定数字的0和1。解的结果可以根据应用的需求进行处理,例如将其转换为二进制数据进行处理或者输出。 总之,使用51单片机读取125kHz ID的C程序中,需要对串行通信进行控制以及解曼彻斯特的过程。这些步骤需要对硬件和软件进行相应的配置和编程,并根据具体的需求进行调试和优化。 ### 回答2: 51单片机是一种高性能、低功耗的单片机芯片,广泛应用于嵌入式系统开发中。125kHz ID是一种低频射频识别,常用于门禁系统、停车场管理等场景。解曼彻斯特是将二进制数据编曼彻斯特信号的过程,用于数据传输中的同步和识别。 在51单片机上编写C程序实现125kHz ID的解曼彻斯特过程,需要以下步骤: 1. 配置IO口和中断:使用51单片机的IO口连接到读卡器,配置IO口为输入模式,并启用外部中断。 2. 初始化串口通信:通过串口与电脑进行数据传输,需要初始化串口的参数,如波特率、数据位数、停止位等。 3. 中断服务程序:设置中断服务程序,当IO口检测到信号变化时,触发中断,并执行相应的处理函数。 4. 读取曼彻斯特:在中断处理函数中,读取IO口信号的变化情况,并根据曼彻斯特原理解析出对应的二进制数据。 5. 数据处理和验证:根据ID的协议,解析出有效的二进制数据后,对数据进行处理和验证,如校验位校验、数据位截取等。 6. 串口通信:最后,将解析出来的数据通过串口进行传输,可以通过调用串口发送函数实现。 以上是简单的步骤,具体实现还需要根据实际需求和硬件环境进行调整。需要注意的是,51单片机编写C程序时需要熟悉相关的编程语言和51单片机的开发环境,同时了解曼彻斯特ID的相关协议。 ### 回答3: 51单片机是一款常见的单片机系列,适用于各种嵌入式应用场景。而125khz ID是一种低频无线射频识别,常用于门禁、考勤等领域。 解曼彻斯特是指将数据编的一种技术,它通过改变信号的波形来表示0和1两个状态。曼彻斯特中,每一位数据都分为两个时钟周期,根据波形的变化来表示0和1。 在51单片机中使用125khz ID进行读取的C程序可以通过以下步骤实现解曼彻斯特: 1. 设置串口通信参数,包括波特率、数据位、停止位等。 2. 确定曼彻斯特的时钟周期,通常为两个时钟周期表示一位数据。 3. 通过串口接收数据,并根据波形的变化判断0和1的状态。 4. 解析数据,获取ID的信息。 5. 进行相应的处理,比如验证ID的合法性,执行相应的操作等。 需要注意的是,曼彻斯特的解析是基于正确的波形变化来判断的,因此在实际应用中需要考虑噪声、干扰等因素对波形的影响,并采取相应的错误处理机制。 总的来说,通过编写C程序实现51单片机125khz ID读取和解析曼彻斯特,可以实现对ID的识别和相应的操作。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值