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

article/2025/9/27 8:41:22

身份证读取设备开发解决方案: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的话需要确认一下定时器和系统延时时间是否混用,这可能会导致异常中断。

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

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

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


http://chatgpt.dhexx.cn/article/UA7pfwCa.shtml

相关文章

身份证阅读器读卡器React网页方法实现身份证的读取

支持产品型号东信EST-100GS谷歌版本身份证读卡器USB免驱接口&#xff0c;支持谷歌火狐360 IE 遨游 搜狗 等等多种web浏览器使用。 官方网址&#xff1a;www.eastcoms.com 以下为React源码示例&#xff1a; import React, {PureComponent} from react; import "./agreemen…

身份证读取

在找读取身份证动态库&#xff08;dll&#xff09; 文件时&#xff0c;发现这么一篇好文章&#xff0c;有兴趣学习一下&#xff01; ---------------- 这份代码&#xff0c;主要是根据api文档来写的&#xff0c;文档上提供了各个方法的使用说明&#xff0c;并且有一个示例代码。…

身份证读取设备开发解决方案:1、Windows下开发Qt程序demo读取身份证信息

身份证读取设备开发解决方案&#xff1a;1、Windows下开发Qt程序demo读取身份证信息 文章目录 身份证读取设备开发解决方案&#xff1a;1、Windows下开发Qt程序demo读取身份证信息1. 前言2. 身份证读取模块3. Qt5开发简单上位机读取身份证信息1. 注意的点2. 部分源码3. 结果展示…

最新web/java/jsp实现发送手机短信验证码和邮箱验证码的注册登录功能(详细)

最新web/java/jsp实现发送手机短信验证码和邮箱验证码的注册登录功能&#xff08;详细&#xff09; 最近几天有人需要帮忙做一个关于发送验证码的功能&#xff0c;之前没有做过&#xff0c;于是我鼓捣一阵子&#xff0c;记录一下关于web项目中注册登录常用的手机验证码和邮箱验…

uniapp中注册手机号短信验证码

一、效果图 二、输入手机号页面 <template><view><view classlogin-tel><view classtel-main><view classlogin-from><view classlogin-user><text classuser-text>手机号</text><input type"number" focustrue…

阿里云手机验证码注册(可以使用阿里云提供的测试模板,不用个人申请)

目录 打开阿里云&#xff1a;&#xff08;绑定手机号码&#xff09;&#xff0c;不用申请模板和签名手机验证码注册流程&#xff1a;实现流程创建springboot工程&#xff0c;添加依赖编写applicatioin配置文件编写controller&#xff0c;根据手机号发送短信编写service&#xf…

抖音实战~手机号验证码一键注册登录流程(限制手机终端登录)

文章目录 一、手机号验证码二、前端2.1. 点击登陆流程2.2. 点击登录源码 三、后端登录3.1. 登录流程图3.2. 流程简述3.3. 手机号验证码登录流程 一、手机号验证码 二、前端 2.1. 点击登陆流程 1.先校验手机号是否合法&#xff1f;不合法&#xff0c;则提示“请输入正确的手机…

会话——验证码注册与记住密码登录

文章目录 1、需求分析2、用户登录功能2.1、流程分析2.2、代码实现2.3、结果演示 3、登录记住密码功能3.1、流程分析3.2、代码实现3.3、结果演示 4、用户注册功能4.1、流程分析4.2、代码实现4.3、结果演示 5、注册验证码功能5.1、流程分析5.2、代码实现5.3、结果演示 1、需求分析…

关于烧写ESP8285核心板的相关事项

首先需要一个CH340的usb 转ttl 板子 然后连接到8285 脚管对应 TTL ---- 8285 VCC----VCC RXD----TX0 TXD----RX0 GND----GND 在通电之前先要把8285的GND 和io 0 连在ttl 板子的 GND上 烧写软件要设置正确否则无法启动

esp32 esp8285 wf6000OTA升级小记

近期做了3个IOT芯片的OTA升级&#xff0c;记录下&#xff1a; 最开始做完的是ESP32,升级流程也简单&#xff0c;初始烧录到固定区&#xff0c;然后OTA升级就会在user1,user2两个区内来回升&#xff0c;升级的文件是同一个&#xff08;即同一个文件&#xff0c;先升级就是user1…

Ubuntu18.04 上 ESP8285 的 esp-at release_v2.2.0.0 编译环境搭建

1 环境搭建前提 1.1 安装编译 ESP-IDF 需要的软件包&#xff1a; sudo apt-get install git wget flex bison gperf python3 python3-pip python3-setuptools cmake ninja-build ccache libffi-dev libssl-dev dfu-util1.2 安装 Python 3.8 sudo apt-get install python3.8-…

探索ESP8285(3)通过EMQX服务器点亮一个LED灯

CCC_122&#xff1a;博客只用于学习交流&#xff0c;不涉及任何商业用途&#xff0c;如果有错误之处&#xff0c;欢迎指正。 在上一个博客的基础上 探索ESP8285&#xff08;2&#xff09;搭建Windows版MQTT服务器 我们来通过EMQX服务器点亮ESP8285模块上的LED灯。 首先查得E…

乐鑫esp8266学习rtos3.0笔记第10篇:内置仅1M的Esp8285,如何攻破最棘手的OTA问题,大大节省资源成本开发产品;

本系列博客学习由非官方人员 半颗心脏 潜心所力所写&#xff0c;仅仅做个人技术交流分享&#xff0c;不做任何商业用途。如有不对之处&#xff0c;请留言&#xff0c;本人及时更改。 1、 Esp8266之 搭建开发环境&#xff0c;开始一个“hellow world”串口打印。 2、 Esp8266之…

晶科鑫 | 国产26MHz晶振匹配Espressif(乐鑫) ESP8285/ESP8266芯片案例

【应用】国产26MHz频率晶振应用于物联网WIFI物联网模块&#xff08;串口转WiFi模块&#xff09;&#xff0c;Espressif(乐鑫) ESP8285/ESP8266芯片匹配测试OK ESP8285其实是ESP8266的升级版本&#xff0c;两者可以共用同一套SDK&#xff0c;只是ESP8285内部集成了1MB Flash&…

ESP8285烧写问题备忘

1 问题现象 ESP8285 烧写了固件&#xff0c;怎么都跑不起来&#xff0c;串口打印如下信息&#xff1a; ets Jan 8 2013,rst cause:2, boot mode:(3,7)load 0x4010f000, len 1384, room 16 tail 8 chksum 0xef csum 0xef csum err ets_main.c 2 问题原因 上乐鑫官网查了 ESP…

ESP8285 多个bin文件合并烧录

可通过两种方式烧录固件&#xff0c;一种是基于esp-idf开发时&#xff0c;中命令终端执行make flash命令烧录&#xff1b;二是使用ESPFlashDownloadTool工具。 bin文件说明 ESP8285/ESP8266的固件一般包含4个bin文件。 查看各bin文件的路径 以带OTA的固件为例&#xff0c;在…

ESP8285+WS2812+MAX9814制作的音乐律动氛围灯

该项目主要参考了立创EDA开源广场的项目&#xff1a; esp8285芯片ESP-01F模块为主控&#xff0c;MAX9814音频采集模块&#xff0c;WS2812 2020rgb灯珠&#xff0c;Arduino编程环境简单制作一个律动灯条。 因为正在学习硬件PCB&#xff0c;所以只能算是一个仿照。 硬件&#x…

探索ESP8285(2)搭建Windows版MQTT服务器

CCC_122&#xff1a;博客只用于学习交流&#xff0c;不涉及任何商业用途&#xff0c;如果有错误之处&#xff0c;欢迎指正。 MQTT服务器有多个选择&#xff0c;例如EMQX&#xff0c;Mosquitto&#xff0c;Apollo&#xff0c;以下我们选择比较简单的EMQX来搭建MQTT的服务器。 一…

峥果智能连接不到服务器,峥果浴霸 ESP8285版本 固件

ESP ZINGUO 峥果智能浴霸个人固件. 作者声明 注意: 本项目主要目的为作者本人自己学习及使用峥果智能浴霸而开发&#xff0c;本着开源精神及造福网友而开源&#xff0c;仅个人开发&#xff0c;可能无法做到完整的测试&#xff0c;所以不承担他人使用本项目照成的所有后果。 严禁…

ESP8266 简单研究 ESP8285 研究

网址&#xff1a; http://wiki.ai-thinker.com/esp8266 ESP8266 系列模组专题 概述 ESP8266 系列模组是深圳市安信可科技有限公司开发的一系列基于乐鑫ESP8266的超低功耗的UART-WiFi模块的模组&#xff0c;可以方便地进行二次开发&#xff0c;接入云端服务&#xff0c;实现…