身份证读取设备开发解决方案:3、单片机读取身份证信息的demo


文章目录

  • 身份证读取设备开发解决方案:3、单片机读取身份证信息的demo
    • 一、前言
    • 二、部分代码及结果展示
      • 1. 准备
      • 2. 部分代码
      • 3. 结果展示
    • 三、常见错误
      • 错误1
      • 错误2
    • 四、注意事项
      • 1. 确认串口线焊接正常
      • 2. 其它

一、前言

前面我们已经在Windows(https://blog.csdn.net/weixin_39510813/article/details/118579865?spm=1001.2014.3001.5501)、Android(https://blog.csdn.net/weixin_39510813/article/details/118707701?spm=1001.2014.3001.5501)上确认过读取身份证模块的可行性,对应封装模块串口协议可以正常使用且比较方便,但是在某些情况下部分控制门磁等的旧的功能是已经在stm32单片机上开发了,目前的新功能只想叠加而不想重构,所以方案设计上为了可能会将读取身份证、nfc卡等功能都还是放到单片机上处理,为了满足这种方案设计,我们在单片机上也开发一个demo来做下测试。理论上已经有了Android设备,那么将外设接口都放在这个Android设备上即可,再多一个单片机的设计有些浪费,而且结构设计、通信方案都是处于一个冗余的状态,理想和现实总是有差距的,我们这里只确认该方案的可行性即可,合理性只做反馈,至于使用何种方案最终还是根据实际情况决定。

二、部分代码及结果展示

1. 准备

  • 串口连接:

目前身份证模块和stm32是通过串口通信,stm32和Android也是通过串口通信,这也是串口的一个好处,可以和spi等接口复用,因此一个小的单片机可以使用多个串口,像我们使用的stm32L471RE里面目前有5个串口,一个调试串口,一个用于和Android通信,目前再接一个和身份证模块通信,还剩余两个串口。这块的串口需要让硬件工程使结合原理图焊接一下(由于我目前只调试stm32和身份证模块的通信,因此stm32和Android通信的串口暂时不需要):

我们单片机上使用的是FreeRTOS,而且串口这块已经封装好了,因此加下来直接调用封装好的接口进行串口初始化后再进行读写即可。

  • 开发工具:

IAR,目前项目组不是使用的mdk,使用的是iar,因此开发工具需要准备好,这个我之前总结过了(https://blog.csdn.net/weixin_39510813/article/details/116065838?spm=1001.2014.3001.5501)。

  • 调试工具:

串口调试工具、stlink/v2

2. 部分代码

注意:涉及协议的部分去掉了。

#include "readIDCardSerial.h"
#include "serialSend.h"
#include "beep.h"
#include "serial.h"
#include "modules_init.h"
#include "timer.h"
#include "unit.h"
#include "open_log.h"
#include "sysCfg.h"
#include "open_door.h"
#include "permi_list.h"
#include "tempwd.h"
#include "sysCntSave.h"
#include "BleDataHandle.h"
#include "config.h"
#include "crc32.h"static portBASE_TYPE             xHigherPriorityTaskWoken = pdFALSE;
static xTaskHandle               readIDCardSerialTask;
void                             *readIDCardSerialPort = NULL;
__IO uint8_t                     readIDCardSerialreceiveTimeStart = TRUE;
readIDCardSerialReceiveDataType  readIDCardSerialReceiveData;
enum readIDCard_action {BUZZER,FIND_IDCARD,SELECT_IDCARD,READ_IDCARD,OTHER,
};
static enum readIDCard_action g_action = FIND_IDCARD;
static uint8_t g_readIDCardInfoFlag    = false;/void readIDCard_serial_receive_byte(uint8_t ch)
{readIDCardSerialReceiveData.buff[readIDCardSerialReceiveData.len++] = ch;if ( readIDCardSerialReceiveData.len >= SERIAL_RECEIVE_MAX){log(ERR,"readIDCardSerial recv too length\n");memset(&readIDCardSerialReceiveData , 0x00, sizeof(readIDCardSerialReceiveDataType));}
}void readIDCard_serial_send( uint8_t *pData , uint16_t len)
{   serial.puts(readIDCardSerialPort , pData , len);log_arry(DEBUG,"send data:" ,pData , len);xSemaphoreGiveFromISR( xReadIDCardSerialSemaphore, &xHigherPriorityTaskWoken );portEND_SWITCHING_ISR(xHigherPriorityTaskWoken );
}static void readIDCard_send_buzzer()
{//蜂鸣器unsigned char buzzer[9] = {...};log_arry(DEBUG, "send buzzer data:" ,(unsigned char *)buzzer, 9);readIDCard_serial_send(buzzer, 9);
}static void readIDCard_send_findIDCard()
{//身份证寻卡unsigned char findIDCard[9] = {...};log_arry(DEBUG, "send findIDCard data:" ,(unsigned char *)findIDCard, 9);readIDCard_serial_send(findIDCard, 9);
}static void readIDCard_send_selectIDCard()
{//身份证选卡unsigned char selectIDCard[9] = {...};log_arry(DEBUG, "send selectIDCard data:" ,(unsigned char *)selectIDCard, 9);readIDCard_serial_send(selectIDCard, 9);
}static void readIDCard_send_readIDCard()
{//身份证读卡unsigned char readIDCard[9] = {...};log_arry(DEBUG, "send readIDCard data:" ,(unsigned char *)readIDCard, 9);readIDCard_serial_send(readIDCard, 9);
}void readIDCard_serial_timer_isr(void)
{    if(readIDCardSerialreceiveTimeStart){readIDCardSerialreceiveTimeStart = FALSE;             readIDCardSerialReceiveData.receiveFinsh = TRUE;switch(g_action) {case BUZZER:readIDCard_send_buzzer();break;case FIND_IDCARD:readIDCard_send_findIDCard();break;case SELECT_IDCARD:readIDCard_send_selectIDCard();break;case READ_IDCARD:readIDCard_send_readIDCard();break;case OTHER:break;default:break;}}HAL_IWDG_Refresh(&hiwdg);
}void readIDCardSerial_sendbyte( uint8_t ch)
{serial.putc(readIDCardSerialPort , ch );
}void readIDCard_serial_open_door(void)
{uint8_t openVoiceFlag = config.read(CFG_SYS_OPEN_VOICE_ENABLE,NULL);if(openVoiceFlag == TRUE){  beep.write(BEEP_NORMAL);}    open_door();
}static void getName(unsigned char * idCardInfo) {unsigned short name[15];for(int i = 0; i < 15; i++) {name[i] = idCardInfo[8+2*i+1] << 8;name[i] = name[i] + (idCardInfo[8+2*i] & 0x00ff);}log_arry(DEBUG, "name data:" ,(unsigned char *)name, 15);
}static void getSex(unsigned char * idCardInfo) {unsigned short sex[1];for(int i = 0; i < 1; i++){sex[i] = idCardInfo[8+30+2*i+1] << 8;sex[i] = sex[i] + (idCardInfo[8+30+2*i] & 0x00ff);}log_arry(DEBUG, "sex data:" ,(unsigned char *)sex, 1);
}static void getNation(unsigned char * idCardInfo) {unsigned short nation[2];for(int i = 0; i < 2; i++) {nation[i] = idCardInfo[8+30+2+2*i+1] << 8;nation[i] = nation[i] + (idCardInfo[8+30+2+2*i] & 0x00ff);}log_arry(DEBUG, "nation data:" ,(unsigned char *)nation, 2);
}static void getBirth(unsigned char * idCardInfo) {unsigned short birth[8];for(int i = 0; i < 8; i++) {birth[i] = idCardInfo[8+30+2+4+2*i+1] << 8;birth[i] = birth[i] + (idCardInfo[8+30+2+4+2*i] & 0x00ff);}log_arry(DEBUG, "birth data:" ,(unsigned char *)birth, 8);
}static void getAddress(unsigned char * idCardInfo) {unsigned short address[35];for(int i = 0; i < 35; i++) {address[i] = idCardInfo[8+30+2+4+16+2*i+1] << 8;address[i] = address[i] + (idCardInfo[8+30+2+4+16+2*i] & 0x00ff);}log_arry(DEBUG, "address data:" ,(unsigned char *)address, 35);
}static void getIDCardNum(unsigned char * idCardInfo) {unsigned short idCardNum[18];for(int i = 0; i < 18; i++) {idCardNum[i] = idCardInfo[8+30+2+4+16+70+2*i+1] << 8;idCardNum[i] = idCardNum[i] + (idCardInfo[8+30+2+4+16+70+2*i] & 0x00ff);}log_arry(DEBUG, "idCardNum data:" ,(unsigned char *)idCardNum, 18);
}static void getIssuingAuthority(unsigned char * idCardInfo) {unsigned short issuingAuthority[15];for(int i = 0; i < 15; i++) {issuingAuthority[i] = idCardInfo[8+30+2+4+16+70+36+2*i+1] << 8;issuingAuthority[i] = issuingAuthority[i] + (idCardInfo[8+30+2+4+16+70+36+2*i] & 0x00ff);}log_arry(DEBUG, "issuingAuthority data:" ,(unsigned char *)issuingAuthority, 15);
}static void getEffectiveStartDate(unsigned char * idCardInfo) {unsigned short effectiveStartDate[8];for(int i = 0; i < 8; i++) {effectiveStartDate[i] = idCardInfo[8+30+2+4+16+70+36+30+2*i+1] << 8;effectiveStartDate[i] = effectiveStartDate[i] + (idCardInfo[8+30+2+4+16+70+36+30+2*i] & 0x00ff);}log_arry(DEBUG, "effectiveStartDate data:" ,(unsigned char *)effectiveStartDate, 8);
}static void getEffectiveEndDate(unsigned char * idCardInfo) {unsigned short effectiveEndDate[8];for(int i = 0; i < 8; i++) {effectiveEndDate[i] = idCardInfo[8+30+2+4+16+70+36+30+16+2*i+1] << 8;effectiveEndDate[i] = effectiveEndDate[i] + (idCardInfo[8+30+2+4+16+70+36+30+16+2*i] & 0x00ff);}log_arry(DEBUG, "effectiveEndDate data:" ,(unsigned char *)effectiveEndDate, 8);
}void readIDCard_serial_data_deal(void)
{uint16_t dataofs = 0;readIDCardSerialReceiveDataType serialData;memset(&serialData  , 0x00 , sizeof(readIDCardSerialReceiveDataType));memcpy(&serialData , &readIDCardSerialReceiveData , sizeof(readIDCardSerialReceiveDataType));         memset( &readIDCardSerialReceiveData ,0x00 ,  sizeof(readIDCardSerialReceiveDataType));int systime = HAL_GetTick();while(dataofs < serialData.len && (serialData.buff != 0x00)){log_arry(DEBUG,"ReadIDCardSerial data:" ,(unsigned char *)serialData.buff, serialData.len);if((serialData.buff[dataofs] == 0xEA) && (serialData.buff[dataofs+1] == 0xEB) && (serialData.buff[dataofs+2] == 0xEC) && (serialData.buff[dataofs+3] == 0xED)){switch(serialData.buff[dataofs+7]) {case 0xA4:log(INFO, "蜂鸣器\n");g_action = FIND_IDCARD;break;case 0xB0:log(INFO, "身份证寻卡\n");if(serialData.buff[dataofs+6] == 0) {log(INFO, "身份证寻卡成功,开始选卡\n");g_action = SELECT_IDCARD;} else {log(WARN, "身份证寻卡失败\n");g_action = FIND_IDCARD;}break;case 0xB1:log(INFO, "身份证选卡\n");if(serialData.buff[dataofs+6] == 0) {log(INFO, "身份证选卡成功,开始读卡\n");g_action = READ_IDCARD;} else {log(WARN, "身份证选卡失败\n");g_action = FIND_IDCARD;}break;case 0xB4:log(INFO, "身份证读取\n");if(serialData.buff[dataofs+6] == 0) {log(INFO, "身份证读取成功,开始处理读取到的数据\n");g_readIDCardInfoFlag = true;} else {log(WARN, "身份证读取失败\n");g_action = FIND_IDCARD;}break;default:log(WARN, "unknown");break;}}if(g_readIDCardInfoFlag) {log(INFO, "读取到的数据长度:%u\n", serialData.len);if(serialData.len == 1290) {getName((unsigned char *)serialData.buff);getSex((unsigned char *)serialData.buff);getNation((unsigned char *)serialData.buff);getBirth((unsigned char *)serialData.buff);getAddress((unsigned char *)serialData.buff);getIDCardNum((unsigned char *)serialData.buff);getIssuingAuthority((unsigned char *)serialData.buff);getEffectiveStartDate((unsigned char *)serialData.buff);getEffectiveEndDate((unsigned char *)serialData.buff);g_action = BUZZER;g_readIDCardInfoFlag = false;}if(serialData.len > 1290) {g_readIDCardInfoFlag = false;g_action = FIND_IDCARD;}}memset(&serialData, 0x00 , sizeof(readIDCardSerialReceiveDataType));}log(WARN,"readIDCardSerial 处理时间:%d\n",HAL_GetTick()-systime);readIDCardSerialreceiveTimeStart = TRUE;
}void readIDCardSerial_init( void )
{readIDCardSerialPort = serial.open("serial2");if( readIDCardSerialPort == NULL){beep.write(BEEP_ALARM);return ;}serial.init(readIDCardSerialPort, 115200, readIDCard_serial_receive_byte);timer.creat(1000, TRUE, readIDCard_serial_timer_isr);
}static void readIDCard_serial_task( void const *pvParameters)
{readIDCardSerial_init();memset(&readIDCardSerialReceiveData , 0x00 ,sizeof(readIDCardSerialReceiveDataType));for( ; ; ){if( xSemaphoreTake(xReadIDCardSerialSemaphore, 2000) == pdTRUE ){readIDCard_serial_data_deal();}}
} void creat_readIDCardSerial_task( void )
{osThreadDef( readIDCardSerial, readIDCard_serial_task , osPriorityHigh, 0, configMINIMAL_STACK_SIZE*3);readIDCardSerialTask = osThreadCreate(osThread(readIDCardSerial), NULL);configASSERT(readIDCardSerialTask);
}readIDCardSerialFunsType readIDCardSerial =
{.send = readIDCard_serial_send,
};

3. 结果展示

创建定时器一直进行寻卡,寻卡成功后进行选卡和读卡,读卡成功后调用蜂鸣器,上面的代码为了便于测试定时器时间设置为1000,读取阻塞时间设置为2000,当读取身份证成功后会发出“滴”的声音,没有身份证时则轮询寻卡。

由于没有GUI,所以这里我们通过串口调试工具确认收发串口满足协议即可(涉及协议的部分不便展示):

三、常见错误

错误1

找不到头文件:

Fatal Error[Pe1696]: cannot open source file "readIDCardSerial.h" D:\code\a9a10_reader_shanghai\Src\freertos.c 76

如下图所示(右键齿轮出现Option,添加绝对路径后可选取使用相对路径):

错误2

Error[Lp021]: the destination for compressed initializer batch "P6-1" is placed at an address that is dependent on the size of the batch, which is not allowed

这个是内存不足造成的问题:https://blog.csdn.net/weixin_42381351/article/details/103677495

修改SRAM的大小即可,我这里目前修改为65字节(搜索configTOTAL_HEAP_SIZE修改即可)。

四、注意事项

1. 确认串口线焊接正常

和硬件同事确认焊接的串口收发线(tx、rx)正常,我们可以使用蜂鸣器来确定发送正常,接收正常则需要读取接收值确认,读取不到时可以和硬件工程师一起使用示波器调试一下。(stm32通过和身份证模块的串口连接线进行收发,之后通过和usb转ttl的串口进行日志数据打印,当我们和Android设备连接后还需要确认和Android的串口收发正常)

2. 其它

再者如果使用了freertos的话需要确认一下定时器和系统延时时间是否混用,这可能会导致异常中断。

单片机中可以控制串口的缓存,因此我们可以一次性读取到所有的身份证信息,不用分几段来拼接。

单片机中串口收发需要注意收发的时序,这个和上层应用不太一样的地方,调时序是个令人崩溃的活,如果你看到有工程师在调时序,千万不要打扰,他可能会因此突然变的暴躁。

单片机的性能相对较弱,要注意不要在循环中打印。

身份证读取设备开发解决方案:3、单片机读取身份证信息的demo相关推荐

  1. 身份证读取设备开发解决方案:2、Android下通过usb转串口读取身份证信息

    身份证读取设备开发解决方案:2.Android下通过usb转串口读取身份证信息 文章目录 身份证读取设备开发解决方案:2.Android下通过usb转串口读取身份证信息 1. 前言 2. 准备 3. ...

  2. 关于app.config不能即时保存读取的解决方案

    关于app.config不能即时保存读取的解决方案 参考文章: (1)关于app.config不能即时保存读取的解决方案 (2)https://www.cnblogs.com/homezzm/p/35 ...

  3. STM32F4单片机读取光电编码器的小理解

    STM32F4单片机读取光电编码器的小理解 大家好! 我是木兔同学,这是我第一次发CSDN的博客,本人接触的单片机主要是STM32f407,今天谈一谈STM32F4单片机读取光电编码器的小理解,希望大 ...

  4. STM32F407单片机读取USR-WIFI232-B2模块的MAC地址

    最近工程项目需要单片机读取USR-WIFI232-B2(WIFI)模块的MAC地址,研究了一下,并成功获取了MAC地址.步骤如下: 1.单片机上电.USR-WIFI232-B2(WIFI)模块先延时1 ...

  5. 安卓微信浏览器返回上一页默认读取缓存解决方案

    安卓微信浏览器返回上一页默认读取缓存解决方案 参考文章: (1)安卓微信浏览器返回上一页默认读取缓存解决方案 (2)https://www.cnblogs.com/AlexBlogs/p/577727 ...

  6. 空光盘复制后到另外计算机无法读取,解决方法:无法读取计算机CD的解决方案...

    我相信大多数人都遇到过无法读取计算机CD,计算机无法读取CD驱动器中的CD,CD驱动器发出"吱吱"声,无法读取某些CD文件和目录或整个CD的情况.我看不懂它,甚至有时时间驱动器发出 ...

  7. U盘无法访问文件或目录损坏且无法读取的解决方案

    U盘无法访问文件或目录损坏且无法读取的解决方案 参考文章: (1)U盘无法访问文件或目录损坏且无法读取的解决方案 (2)https://www.cnblogs.com/yueansixing/arti ...

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

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

  9. access通过身份证号提取性别_Access计算根据身份证号码字段计算年龄和性别的表达式,最好是还能确定户籍地址,该在什么地方输入?...

    谢谢你的帮助,我已经提高了分数,相信非你莫属了,也许是我不知道用法,我也是在OFFICE 2003 ACCESS 中用的,总之让你费心了,能够直接发个已经通过测试的文件给我就好了 自己改了一下,成功了 ...

  10. stm32读取驾驶模拟器数据 stm32F407读取joystick数据

    需求 实习工作,老板要求用单片机读取驾驶模拟器(Joystick)返回的数据,驾驶模拟器usb输出,输出信息包括:方向盘转角.左右拨杆.按键等. 硬件 采用正点原子探索者开发板,即插即用,硬件不需要改 ...

最新文章

  1. Python Qt GUI设计:QComboBox下拉列表框类(基础篇—14)
  2. %config InlineBackend.figure_format=svg#矢量图设置
  3. python训练模型函数参数_一步步亲手用python实现Logistic Regression
  4. 算法导论--python--插入排序
  5. 【转】ABAP的坑2
  6. python递归实现_Python-递归实现
  7. SQLServer中的死锁的介绍
  8. wordpress url index.php,WordPress对URL的路由解析过程详解
  9. notepad快捷键大全
  10. (转载)linux中shell变量
  11. 用资源管理器右键编译 Visual Studio 解决方案文件
  12. hdu3081 Marriage Match II
  13. mysql 序列号_mysql 序列号生成器(转)
  14. C++11常见编译与链接错误解决总结
  15. monkeyrunner之环境搭建及实例(三)
  16. linux免费邮件服务器,Linux 电子邮件服务器的搭建
  17. api 接口简单调用
  18. 手机qq邮箱html,手机QQ邮箱在哪里找
  19. Fabric.js IText 手动设置斜体
  20. 零基础如何学习计算机语言

热门文章

  1. 推荐几款好用的富文本编辑器
  2. SQL Server的下载和安装
  3. 怎样才能查到4S店保养记录,4S店维修保养记录怎样查询,Javascript学习指南
  4. PyCharm修改主题和修改背景
  5. java dateutils工具类_Java日期工具类DateUtils详解(转)
  6. SQL如何删除重复数据
  7. 【离散数学】图论 第七章(8) 运输网络、流和割、Ford-Fulkerson定理
  8. 精通innodb引擎_《MySQL技术内幕:InnoDB存储引擎》PDF 下载
  9. MCP2515收发程序 CAN总线 CAN程序 CAN通信 5K-1M波特率 STM32+MCP2515
  10. Asp.net MVC3 WebGrid查询绑定