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

本文档介绍了如何在STM32单片机上开发一个读取身份证信息的Demo,包括串口初始化、数据接收处理、错误排查以及注意事项。通过与身份证模块和Android设备的串口通信,展示了读取身份证信息的流程,并提供了部分关键代码。常见错误包括找不到头文件和内存不足问题,解决方法分别是添加头文件路径和调整SRAM大小。调试过程中应注意串口线焊接、系统延时和时序问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


一、前言

前面我们已经在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的话需要确认一下定时器和系统延时时间是否混用,这可能会导致异常中断。

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

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

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

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称系统有问题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值