【本文发布于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}elseValue = (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 13 D00 D01 D02 D03 P0
bit 14 - bit 18 D10 D11 D12 D13 P1
bit 54 - bit 58 D90 D91 D92 D93 P9
bit 59 - bit 62 PC0 PC1 PC2 PC3
bit 63 S0

五、数据校验程序

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);}}}}elsestatus = CARDFALSE;}
}

这个只是简单的实现,识别速度慢,偶有识别错误。可以通过优化大大提高识别速度以及达到接近百分百的准确率。

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

  1. 三、单片机读取ID卡(EM4100的数据格式)

    目录 一.单片机读取ID卡(工作原理及电路) 二.单片机读取ID卡(产生载波) 三.单片机读取ID卡(EM41000的数据格式) 四.单片机读取ID卡(硬件调试及读码) (一)EM4100的数据格式 ...

  2. 实验一. RFID自动读卡实验

    实验一. RFID自动读卡实验 实验目的 实验环境 实验内容 实验步骤 实验代码 1) 初始化MFRC531 2) 寻卡 硬件连接 编译.烧录并测试 实验结果 串口设置 实验目的 了解RFID相关知识 ...

  3. C#/.NET 电子口岸IC卡/UKEY用WebSocket方式进行读卡/数据加签

    一.握手成功 public static void Init()         {             websocket = new WebSocket("127.0.0.1:612 ...

  4. 125KHz RFID芯片模块及电路替代方案

    125K RFID芯片模块是否可以写数据? 以上是125KHz RFID芯片模块的供应商和芯片名称,频段.容量.功能.协议.18000-2.11784.11785协议. 集成 RFID 收发器可对 1 ...

  5. [STM32] Stm32f103c8t6+RC522 实现读卡写卡功能(超详细,零基础,小白)

    本篇文章内容总结下来就是 读卡            使用默认密码读卡所有扇区所有块的数据 写ID            使用默认密码读取卡一的0扇区的第一块数据并写入到卡二的0扇区的第一块里 密码读 ...

  6. Android使用NFC读卡实现 (一)

    Android使用NFC读卡实现 (一) Android使用NFC读卡实现 (二) 这是一篇写的用带NFC芯片的手机读卡的过程. 首先是创建Android工程.

  7. 裸板 nand flash K9F2G08U0C --- 读取ID

    上一节讲了 设置 nand时序 nand时序设置 存储类芯片操作步骤: 1.初始化        主控芯片对的nand flash 控制器 2.识别            读取id (判断芯片是否正常 ...

  8. IC卡读卡器调用php,IC卡读卡器如何进行IC卡的读卡?

    IC卡又称集成电路卡,它是在大小和普通信用卡相同的塑料卡片上嵌置一个或多个集成电路构成的.集成电路芯片可以是存储器或向处理器.带有存储器的IC卡又称为记忆卡或存储卡,带有微处理器的IC卡又称为智能卡或 ...

  9. C# 服务端(API)连接 安卓设备 读取 IC卡(仅读取ID),RFID卡(包含ID卡),获取卡号(10进制)

    处理安卓设备NFC读卡 C#WebAPI做服务端 APP使用 uni-app开发, 走了不少弯路,这里记录一下 获取 卡10进制ID的方法 商米POS.安卓移动设备(手机)通过NFC功能 读取 IC卡 ...

最新文章

  1. 大规模数据处理Apache Spark开发
  2. gitlab使用_如何在正确使用 Docker 搭建 GitLab
  3. getHibernateTemplate()的用法 (转)
  4. 解决fixed在苹果手机抖动问题/头部底部固定布局
  5. 基于silverlight4(beta)的摄像头应用(Beta2)发布
  6. Nginx防盗链、访问控制、Nginx解析PHP相关配置、Nginx代理
  7. ObjectOutputStream 和 ObjectInputStream的使用
  8. 理解 RXSwift:单元测试(四)
  9. 36产生用户恶情绪和报复情绪的原因
  10. Linux基础学习三:VMware和CentOS的安装详细图文教程
  11. 数据库-MySQL-数据库设计-表的关联
  12. No ‘Access-Control-Allow-Origin‘ header is present on the requested resource.
  13. 我优化多年的 C 语言竟然被 80 行 Haskell 打败了?
  14. OpenCV-Python实战(番外篇)——OpenCV、NumPy和Matplotlib直方图比较
  15. Redis分布式锁的正确实现方式
  16. ServletContextListener小小总结
  17. 拷贝构造函数什么时候调用?
  18. CocosCreator编辑器界面
  19. 计算机网络普遍采用什么传输方式,网络传输技术
  20. keras深度学习之猫狗分类二(数据增强)

热门文章

  1. 小游戏开发Laya对接主流平台(qq,抖音,vivo,oppo,微信)
  2. 计算机专业常用的学术搜索引擎
  3. 08-图9 关键活动 (30 分)
  4. IDEA添加子Module的正确姿势
  5. 学习使用Bing Maps Silverlight Control(七):自定义导航工具栏
  6. 连接mysql解决网络抖动_网络抖动时候,获取数据库连接等待15分钟后报错。
  7. 联想旭日C467M拆机
  8. 无法卸载Office2003,提示找不到Pro11.msi文件的解决办法
  9. 【数据结构初阶】链表(下)——带头双向循环链表的实现
  10. 地质雷达物理测量RADAN®7软件(Radan 7.6.19.11260)最新下载