基于R303电容指纹模块的MCU串口通信详解(NodeJs及51单片机实现)

前期准备

R303电容指纹简介

R303指纹模块采用台湾产电容大面状采集芯片,集成了指纹算法芯片,具有指纹录入、图像处理、特征提取、模板生成、模板存储、指纹比对(1:1)或指纹搜索(1:N)等功能;工作电流低。
R303作为下位机,可以使用UART与上位机通信,可以在9600-115200波特率通信。对于MCU,R303可以直接使用RXD和TXD两个引脚与上位机通信,工作电压5V;上位机为x86架构下的windows时,有dll和lib库封装的SDK,插上USB转接线,可以直接使用,本文不做详细介绍,其本质也是通过USB转TTL后的串口通信。

R303的全部使用方式,可以参考我上传的官方文档资料:
https://share.weiyun.com/b94427dcf3ffa37217d6bb1587b873e7

R303常用功能命令

  • ①采集指纹到图像缓冲区
  • ②从图像缓冲区生成特征码到N号寄存器(N in [01, 02])
  • ③合并1号寄存器和2号寄存中的特征码,并同时复制给1号、2号寄存器
  • ④将N号寄存器中的特征码保存到指纹库PAGE位置(PAGE in [0,1028))
  • ⑤对指纹库BEGIN-END位置搜索验证第N号寄存器特征码(BEGIN,END in[0,1028); N in [01, 02])

上述命令为最常用的命令,此外,还有一些删除,清空指纹库的命令,读取指纹库有效特征码个数的命令没有写上。

不如要执行录入指纹的操作,就可以按照1->2->1->2->3->4的步骤来实现,要实现验证指纹的操作,就可以按照1->2->5。

指令集

上述所有命令,以及本文中没有列举的命令,R303都对应一系列的串口指令集,同时R303在接受到指令后会给出一个相应的反馈信息,从而实现通信的闭环。

指令集的结构为:包头 + 收发标志(发01/收07) + 2byte的后续包长度 + 1byte命令码 + Nbyte参数(N可以等于0) + 校验和

包头:如果MCU只挂载一个指纹模块的话,我们可以基本认为16进制的EF 01 FF FF FF FF是一个包头,后4个字节其实是设备物理地址。

收发标志:发01/收07

2byte后续包长度:指令包从下个字节开始到结束时的长度,如果大于2个byte,高位舍去,2个byte就是16个bit,就是65535个长度,几乎不可能发送或者接受这么长的指令的。

1byte命令码:相当于函数名,对应不同的指令,1byte够了,能表示255种命令。

Nbyte参数:相当于函数参数,调用不同指令时可能会传入参数,也可能没有参数。

校验和:从长度字节开始到最后一个参数之间所有的byte相加,得到校验和。

下面整理罗列一下指令集对应的串口数据:

录入图像

发送:EF 01 FF FF FF FF 01 00 03 01 00 05
接受:EF 01 FF FF FF FF 07 00 03 RE S U M

RE:应答码,1byte,没有错误一般都是0x00,有异常时抛出不同的异常状态码。
SUM:2byte校验和

生成特征码

将ImageBuffer中的图像生成指纹特征,存于cb1或cb2寄存器
发送:EF 01 FF FF FF FF 01 00 04 02 01/02 S U M
接受:EF 01 FF FF FF FF 07 00 03 RE S U M

合并指纹

将cb1和cb2的特征码进行合并,如果指纹出入太大的话有可能合并不成功
发送:EF 01 FF FF FF FF 01 00 03 05 00 09
接受:EF 01 FF FF FF FF 07 00 03 RE S-U-M

搜索指纹

将cb1或者cb2中的特征码与指纹库中的数据进行对比
发送:EF 01 FF FF FF FF 01 00 08 04 CB ST-PG PG-ED S-U-M
接受:EF 01 FF FF FF FF 07 00 07 RE PAGE= =SC== S-U-M

CB: 特征码寄存器
ST-PG: 2byte 搜索起始位
PG-ED: 2byte 搜索截止位
如果想整个指纹库搜索,上面两个可以设置为00 00 03 E7,代表从0搜到1000位。
RE:0表示找到了,其他值表示找不到
PAGE: 2byte 如果指纹模块搜索到了匹配指纹,则表示找到的位置
SC:匹配得分

存储特征码到指纹库

将cb1或者cb2中的特征码存储到pageID指纹库位置
发送:EF 01 FF FF FF FF 01 00 08 06 CB PAGE= S-U-M
接受:EF 01 FF FF FF FF 07 00 07 RE S-U-M

NodeJS编码测试

在进行R303指纹模块PC端编程测试时,我选择使用刚学的NodeJS。串口通信是一种异步的通信,它的发送和接受是靠RXD和TXD两根线的高低电平变化同时产生的。而NodeJS这个语言,天生可以通过Promis机制非常有序的处理这种异步过程,将这种异步过程通过then堵塞变成同步操作。

下面是我用NodeJS编写的模块测试代码:

const readline = require('readline');
var SerialPort = require("serialport");
var Q = require('q');
var EventEmitter = require('events').EventEmitter;

var fingureEmitter = new EventEmitter();            //指纹接受消息弹射器
var keyboardEmitter = new EventEmitter();           //键盘输入事件的弹射器

var MessageOpenError = 'open port error : ';        //串口打开失败的提示

var PackageHeaderBuffer = new Buffer('EF01FFFFFFFF01', 'hex');      //UART发送包包头

var cmdReceiveBuffer = new Buffer('');      //指令串,包含EF01FFFFFFFF07
var packageLength = 0;                      //接受包长度,不包含长度位,但包含校验和
var waiteCount = 100;                       //录入指纹时的失败控制次数
var inputFingureTimmer;                     //控制录入指纹循环定时器
var pos = 0;                                //读入指纹地址列表的坐标
var fingureAddressList = [];                //指纹地址列表

var port = new SerialPort(
    "COM3",
    {
        baudRate: 9600,
        parser: SerialPort.parsers.byteLength(1),
        autoOpen: false,
    }
);

fingureEmitter.on('processEnd', () => showMenu());

port.on('error', err => {console.log(err); rl.close();});

port.on('data', data => {
    let dataBuffer = new Buffer(data, 'hex');

    //console.log('data received: =========' + dataBuffer.toString('hex'));

    cmdReceiveBuffer = Buffer.concat([cmdReceiveBuffer, dataBuffer]);

    if (cmdReceiveBuffer.length == 9) {
        //提取包长度信息, packageLength = buffer[8,9]
        packageLength = (cmdReceiveBuffer[7] << 8) + cmdReceiveBuffer[8];
        //console.log('packageLength: ' + packageLength);
    } else if (cmdReceiveBuffer.length == 9 + packageLength) {
        //结束一轮消息接受,清空buffer,解析命令
        //console.log('cmd end.cmd is: ' + cmdReceiveBuffer.toString('hex'));
        let checkSum = (cmdReceiveBuffer[7 + packageLength] << 8) + cmdReceiveBuffer[8 + packageLength];
        let cfmCode = cmdReceiveBuffer[9];
        let params = [];
        for (kk = 0; kk < (packageLength - 3) / 2; kk++) {
            params.push((cmdReceiveBuffer[10 + kk * 2] << 8) + cmdReceiveBuffer[11 + kk * 2]);
        }

        //console.log('checkSum is:' + checkSum);
        //console.log('cfmCode  is:' + cfmCode);
        //console.log('params are:' + params.toString());

        let tempSum = 0;
        for (kk = 6; kk < cmdReceiveBuffer.length - 2; kk++) {
            tempSum += cmdReceiveBuffer[kk];
        }
        if (tempSum == checkSum) {
            //校验正确,发射消息响应
            fingureEmitter.emit('dataReceived', cfmCode, params);
        }
        //清空指令buffer
        cmdReceiveBuffer = new Buffer('');
    }
});

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

//菜单0:与指纹模块握手
function ackFingure() {
    sendCmd('13', '00000000').then(data => {
        if (data.cfmCode == 0)
            console.log('握手成功');
        else
            console.log('握手失败');
    }).catch(err => { console.log('握手失败') }).finally(() =>{fingureEmitter.emit('processEnd');});
}

//通过输入的权限得到下一个存放位置
function generatePower(answer){
    let startPos = (answer - 1) * 100;
    while(fingureAddressList.indexOf(startPos) != -1){
        startPos++;
        if(startPos >= answer * 100){
            startPos = -1;
            break;
        }
    }
    return startPos;
}


//菜单1:录入指纹
function inputFingure() {
    showQuestion('请输入指纹录入者的权限(1-10):').then(answer => {
        if (answer < 1 || answer > 10) {
            console.log('权限输入有误');
            showMenu();
            return;
        }
        let pow = generatePower(answer);
        if(pow == -1){
            console.log('该权限存储位置已满,无法录入。');
            fingureEmitter.emit('processEnd');
        }
        console.log(`指纹将存放在${pow}位置。`);
        waiteCount = 100;
        console.log('开始采集第一次指纹,请将手指放到采集器');
        inputFingureTimmer = setInterval(() => {
            if (waiteCount == 0) {
                clearInterval(inputFingureTimmer);
                console.log('长时间没有采集到指纹,已终止采集');
                fingureEmitter.emit('processEnd');
            }
            sendCmd('01').then(data => {
                if (data.cfmCode == 0) {
                    clearInterval(inputFingureTimmer);
                    console.log('指纹采集成功');
                    console.log('准备生成特征码到cb1');
                    return sendCmd('02', '01');
                } else if (data.cfmCode == 2) {
                    //console.log('没有指纹,等待录入:' + waiteCount);
                    waiteCount--;
                } else {
                    console.log('录入失败, 等待继续录入:' + waiteCount);
                    waiteCount--;
                }
            }).then(data => {
                if (data.cfmCode == 0) {
                    console.log('生成特征码到cb1成功');
                    console.log('开始采集第二次指纹,请将手指放到采集器');
                    waiteCount = 100;
                    inputFingureTimmer = setInterval(() => {
                        if (waiteCount == 0) {
                            clearInterval(inputFingureTimmer);
                            console.log('长时间没有采集到指纹,已终止采集');
                            fingureEmitter.emit('processEnd');
                        }
                        sendCmd('01').then(data => {
                            if (data.cfmCode == 0) {
                                clearInterval(inputFingureTimmer);
                                console.log('指纹采集成功');
                                console.log('准备生成特征码到cb2');
                                return sendCmd('02', '02');
                            } else if (data.cfmCode == 2) {
                                //console.log('没有指纹,等待录入:' + waiteCount);
                                waiteCount--;
                            } else {
                                console.log('录入失败, 等待继续录入:' + waiteCount);
                                waiteCount--;
                            }
                        }).then(data => {
                            if (data.cfmCode == 0) {
                                console.log('生成特征码到cb2成功');
                                console.log('准备合并特征码');
                                return sendCmd('05');
                            } else {
                                console.log('生成特征码到cb2失败');
                                fingureEmitter.emit('processEnd');
                            }
                        }).then(data => {
                            if (data.cfmCode == 0) {
                                console.log('合并成功');
                                console.log(`准备将合并后的特征码保存到指纹库${pow}位置`);
                                let hPow = pow>>8;
                                let lPow = pow&0x00FF;
                                return sendCmd('06', '02' + new Buffer([hPow, lPow]).toString('hex'));
                            } else if (data.cfmCode == 10) {
                                console.log('合并失败,两个指纹不是同一手指');
                                fingureEmitter.emit('processEnd');
                            }
                        }).then(data => {
                            if (data.cfmCode == 0) {
                                console.log('保存成功,流程结束!');
                                getFingureDBList();
                            } else {
                                console.log('保存失败!code:' + data.cfmCode);
                                fingureEmitter.emit('processEnd');
                            }
                        });
                    }, 1000);
                }else {
                    console.log('生成特征码到cb1失败');
                    fingureEmitter.emit('processEnd');
                }
            });
        }, 1000);
    });
}

//菜单2:检索指纹
function searchFingure() {
    console.log('开始采集指纹,请将手指放到采集器');
    waiteCount = 100;
    inputFingureTimmer = setInterval(() => {
        if (waiteCount <= 0) {
            clearInterval(inputFingureTimmer);
            console.log('长时间没有采集到指纹,已终止采集');
            fingureEmitter.emit('processEnd');
        }
        sendCmd('01')
            .then(data => {
                if (data.cfmCode == 0) {
                    clearInterval(inputFingureTimmer);
                    console.log('指纹采集成功');
                    console.log('准备生成特征码到cb1');
                    return sendCmd('02', '01');
                } else {
                    //console.log('没有指纹,等待录入:' + waiteCount);
                    waiteCount--;
                }
            }).then(data => {
                if (data.cfmCode == 0) {
                    console.log('生成特征码到cb1成功,准备匹配指纹库');
                    return sendCmd('1b', '01000003E7');
                } else {
                    console.log('生成特征码到cb1失败');
                    fingureEmitter.emit('processEnd');
                }
            }).then(data => {
                if (data.cfmCode == 0) {
                    console.log('比对成功,指纹ID:' + data.params[0] + ' 匹配度:' + data.params[1] + '。流程结束!');
                    fingureEmitter.emit('processEnd');
                } else if (data.cfmCode == 9) {
                    console.log('没有匹配指纹,流程结束!');
                    fingureEmitter.emit('processEnd');
                } else {
                    console.log('其他错误,code: ' + data.cfmCode);
                    fingureEmitter.emit('processEnd');
                }
            });
    }, 1000);
}

//菜单3:获取指纹库有效个数
function getFingureDBCount() {
    console.log('正在获取指纹库有效指纹个数');
    sendCmd('1d').then(data => {
        if (data.cfmCode == 0) {
            console.log('指纹库个数提取成功:' + data.params[0]);
        } else {
            console.log('指纹库个数提取失败。');
        }
    }).finally(() => {fingureEmitter.emit('processEnd');});
}

function countValue(value) {
    let h8 = value >> 8;
    let l8 = value;
    for (i = 0; i < 8; i++) {
        if (h8 & 0x01) {
            fingureAddressList.push(pos);
        }
        h8 >>= 1;
        pos++;
    }
    for (i = 0; i < 8; i++) {
        if (l8 & 0x01) {
            fingureAddressList.push(pos);
        }
        l8 >>= 1;
        pos++;
    }
}

//菜单4:获取指纹库
function getFingureDBList() {
    pos = 0;
    fingureAddressList.splice(0, fingureAddressList.length);    //清空数组
    console.log('准备读取指纹库page0页列表');
    sendCmd('1F', '00').then(data => {
        if (data.cfmCode == 0) {
            console.log('page0列表读取完毕:');
            data.params.forEach(value => {
                countValue(value);
            });
            console.log('准备读取指纹库page1页列表');
            return sendCmd('1F', '01');
        } else {
            console.log('page0列表读取失败:' + data.cfmCode);
        }
    }).then(data => {
        if (data.cfmCode == 0) {
            console.log('page1列表读取完毕:');
            data.params.forEach(value => {
                countValue(value);
            });
            console.log('准备读取指纹库page2页列表');
            return sendCmd('1F', '02');
        } else {
            console.log('page1列表读取失败:' + data.cfmCode);
        }
    }).then(data => {
        if (data.cfmCode == 0) {
            console.log('page2列表读取完毕:');
            data.params.forEach(value => {
                countValue(value);
            });
            console.log('准备读取指纹库page3页列表');
            return sendCmd('1F', '03');
        } else {
            console.log('page2列表读取失败:' + data.cfmCode);
        }
    }).then(data => {
        if (data.cfmCode == 0) {
            console.log('page2列表读取完毕:');
            data.params.forEach(value => {
                countValue(value);
            });
            console.log('指纹库所有页的数据读取完毕,有效地址列表:');
            console.log(fingureAddressList.toString());
        } else {
            console.log('page3列表读取失败:' + data.cfmCode);
        }
    }).catch(err => console.trace(err)).finally(() => {fingureEmitter.emit('processEnd');});
}

//键盘输入事件监听
rl.on("line", answer => {
    keyboardEmitter.emit('keyboardInputed', answer);
});

//内部发送串口通信函数
function sendCmdFn(cmd, params) {
    let cmdBuffer = new Buffer(cmd, 'hex');
    let paramsBuffer = null;
    if (params && params != '')
        paramsBuffer = new Buffer(params, 'hex');
    let length = (paramsBuffer ? paramsBuffer.length : 0) + 3;           //计算长度,指令1byte + 参数nbyte + 校验和2byte
    let checkSum = cmdBuffer[0] + (paramsBuffer ? paramsBuffer.reduce((x, y) => x + y) : 0) + 1 + length;    //求校验和,使用了数组的高阶函数reduce

    let l1 = length >> 8;
    let lengthBuffer = Buffer.from([l1, length]);
    let c1 = checkSum >> 8;
    let checkSumBuffer = Buffer.from([c1, checkSum]);

    let resultBuffer = paramsBuffer ? Buffer.concat([PackageHeaderBuffer, lengthBuffer, cmdBuffer, paramsBuffer, checkSumBuffer]) : Buffer.concat([PackageHeaderBuffer, lengthBuffer, cmdBuffer, checkSumBuffer]);

    //console.log('send: ' + resultBuffer.toString('hex'));
    port.write(resultBuffer);
}

//对外公开的发送串口指令函数
function sendCmd(cmd, params) {
    var deferred = Q.defer();
    if (port.isOpen()) {
        sendCmdFn(cmd, params);
    } else {
        port.open(err => {
            if (err)
                deferred.reject(err);
            else
                sendCmdFn(cmd, params);
        })
    }
    fingureEmitter.once('dataReceived', (cfmCode, params) => {
        //console.log(cfmCode + "\nParams's length:" + params.length);
        deferred.resolve({ 'cfmCode': cfmCode, 'params': params });
    });
    return deferred.promise;
}

function closePort() {
    if (port.isOpen())
        port.close();
}

function showQuestion(question) {
    console.log(question);
    rl.prompt();
    var keyboardDiferred = Q.defer();
    keyboardEmitter.once('keyboardInputed', answer => {
        keyboardDiferred.resolve(answer);
    });
    return keyboardDiferred.promise;
}

function showMenu() {
    showQuestion(
        `What do you want to do?
        0.与指纹模块握手验证.
        1.录入指纹.
        2.验证指纹.
        3.获得数据库特征个数
        4.获得数据库特征列表
        5.退出\n`
    ).then(answer => {
        switch (answer) {
            case '0':
                ackFingure();
                break;
            case '1':
                inputFingure();
                break;
            case '2':
                searchFingure();
                break;
            case '3':
                getFingureDBCount();
                break;
            case '4':
                getFingureDBList();
                break;
            case '5':
                closePort();
                while (!port.isOpen())
                    process.exit(0);
            default:
                showMenu();
        }
    });
}

getFingureDBList();

51单片机进行MCU移植

指纹模块不可能一直连着x86的PC用,现在我们来将上面的逻辑用51单片机实现:

在编写单片机程序时,我参考借鉴了windows编程中的消息响应机制,在大循环中优先判断是否需要响应消息。

/*
 * @CreateTime: Mar 3, 2017 4:25 PM 
 * @Author: Smith Ding 
 * @Contact: newflydd@gmail.com 
 * @Last Modified By: Smith Ding
 * @Last Modified Time: Mar 3, 2017 4:46 PM
 * @Description: 本软件用来将面板与指纹模块进行通信
 * @Company: JIANGSU SAIYANG MECHANICAL & ELECTRICAL TECHNOLOGY CO.,LTD
 */

#include <reg52.h>
#include <intrins.h>
#include "uart.h"
#include "fingure.h"
#include "event.h"

void delay(uint num){
    while(num--);
}

//启动初始化
void initMain(){
    //初始化发送指令前7个byte
    for(ucharTemp = 0; ucharTemp < 7; ucharTemp ++){
        sendBuffer[ucharTemp] = sendPackageHeader[ucharTemp];
    }

    //将串口接受寄存器的数据长度置0
    receiveBufferLength = 0;

    //接受到了串口通知开关置0
    receiveCmdNotify = 0;
    //等待串口消息开关置0
    waitForReceive = 0;

    EA = 1;     //总中断开关打开
}

void main(){    
    uchar fingureReadable = 1;      //下位机指纹模块连接性,0时代表可准确连接
    delay(65535);
    delay(65535);
    delay(65535);
    delay(65535);
    delay(65535);
    delay(65535);
    delay(65535);
    delay(65535);

initPrograme:
    initUart();
    initMain();

    fingureReadable = getAddressListFunction();

    if(fingureReadable){
        //@TODO: 异常警报
        showWarning();
        goto initPrograme;
    }else{
        //P1 = 0x40;
    }

    //初始化指令为:0x11,向指纹模块发送采集指纹用来验证的命令
    sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;

    while(1){
        /* 主循环中首先判断是否有串口响应,如果有,则本次循环用来处理消息响应 */
        if(receiveCmdNotify == 1){
            receiveCmdNotify = 0;

            receiveEventFunction();     //接住消息

            continue;
        }

        /*
         * 如果没有串口响应
         * 首先判断是否在等待下位机反馈,如果在等待,就开始延时计数
         */   
        if(waitForReceive == 1){
            waitForReceive = 0;
            if(waitTimes < 3)
                waitForReceiveFunction();
            else
                resetFingureFunction();
            continue;
        }

        /* 
         * 继续判断P2口有没有录入通知
         * 如果有录入通知,并且当前状态为验证状态,则发送录入命令
         * 如果没有录入通知,则发送验证命令
         */
        inputSignal = checkInputSignal();
        if((sendCmdStatus ==  ACTION_GET_IMAGE_FOR_CHECK) && inputSignal){
            newFingureAddressIndex = getNewAddressIndexByPower(inputSignal);    //通过权限来构造一个空位置
            noFingureTimesWhenInput = 0;                                        //将录入指纹时的重复次数归零
            sendCmdStatus = ACTION_GET_IMAGE_FOR_INPUT1;
        }

        //uartSendByte(sendCmdStatus);

        if(receiveCmdNotify == 0)
            sendCmdFunction();
        delay(2000);
    }
}

/**
 * 串口中断函数,用来接收下位机数据
 */
void serialInterruptCallback() interrupt 4 {
    if(RI){                //本次中断是接受中断
        RI = 0;            //接受完了清零

        //将接受到的字节追加到指令接受缓冲区
        receiveByte = SBUF;
        receiveBuffer[receiveBufferLength] = receiveByte;
        receiveBufferLength++;

        if(receiveBufferLength == 1 && receiveByte!=0xEF){
            receiveBufferLength = 0;
            return;
        }

        if(receiveBufferLength == 2 && receiveByte!=0x01){
            receiveBufferLength = 0;
            return;
        }

        if(receiveBufferLength == 3 && receiveByte!=0xFF){
            receiveBufferLength = 0;
            return;
        }

        if(receiveBufferLength == 4 && receiveByte!=0xFF){
            receiveBufferLength = 0;
            return;
        }

        if(receiveBufferLength == 5 && receiveByte!=0xFF){
            receiveBufferLength = 0;
            return;
        }

        if(receiveBufferLength == 6 && receiveByte!=0xFF){
            receiveBufferLength = 0;
            return;
        }

        if(receiveBufferLength == 9){
            //如果指令接受到9个byte,这时候包长度信息有了
            receivePackageLength = (receiveBuffer[7] << 8) + receiveBuffer[8];
        }else if (receiveBufferLength == 9 + receivePackageLength) {
            //结束一轮消息接受,清空buffer,解析命令

            //获取校验和
            receiveCheckSum = (receiveBuffer[7 + receivePackageLength] << 8) + receiveBuffer[8 + receivePackageLength];
            //获取确认码
            cfmCode = receiveBuffer[9];
            //获取参数
            for (ucit = 0; ucit < receivePackageLength - 3; ucit++) {
                receiveParams[ucit] = receiveBuffer[10 + ucit];
            }

            //验证校验和
            uiit = 0;
            for (ucit = 6; ucit < receiveBufferLength - 2; ucit++) {
                uiit += receiveBuffer[ucit];
            }

            if (uiit == receiveCheckSum) {
                //校验正确,发射消息响应
                receiveCmdNotify = 1;
                receiveEventStatus = sendCmdStatus - 100;    //事件类型根据发送类型对应
                waitForReceive = 0;                          //解除等待锁
                waitTimes = 0;                               //等待响应次数复位
            }
            //清空指令buffer
            receiveBufferLength = 0;
        }            
    }
}

/**
 * 给sendBuffer变量构建发送指令
 * 命令和参数来自于全局变量sendCmdAndParams
 * @param capLength [命令+参数的长度]
 */
void buildSendCmd(uchar capLength){
    uint checkSum;
    uchar packageLength = capLength + 2;    //包长度 = 指令集长度 + 校验和长度
    sendBufferLength = 11 + capLength;      // 7 + 2 + capLength + 2

    sendBuffer[7] = 0;                      //包长度高位为0x00
    sendBuffer[8] = packageLength;          //包长度低位为实际包长度

    //构建发送指令串口消息中的指令和参数
    for(ucharTemp = 0; ucharTemp < capLength; ucharTemp++){
        sendBuffer[9+ucharTemp] = sendCmdAndParams[ucharTemp];
    }

    checkSum = getCheckSum(packageLength, sendCmdAndParams, capLength);
    sendBuffer[9 + capLength]  = checkSum>>8;
    sendBuffer[10 + capLength] = checkSum;
}

/**
 * [计算校验和]
 * @param  packageLength [包长度]
 * @param  cmdAndParams  [命令和参数数组]
 * @param  capLength     [命令和参数长度]
 * @return               [2byte 校验和]
 */
uint getCheckSum(uchar packageLength, uchar* cmdAndParams, uchar capLength){
    uintTemp = packageLength;
    for(ucharTemp = 0; ucharTemp < capLength; ucharTemp++){
        uintTemp += cmdAndParams[ucharTemp];
    }
    return uintTemp + 1;
}

/**
 * @TODO:临时模拟信号
 * 检查输入端口有没有录入信号(P2高4位),录入信号需要阶段时间内维持稳定信号
 * @return 如果没有,返回0,如果有,返回P2高4位的低三位作为权限数据
 */
uchar checkInputSignal(){
    uchar inputValue = GPIO_INPUT;
    uintTemp = 500;

    while(uintTemp--){
        ucharTemp = (inputValue<<1);
        if((ucharTemp & 0x80) == 0x80)
            return 0x00;
    }

    return 0x05;
}

/* 根据receiveEventStatus,解析串口响应 */
void receiveEventFunction(){
    switch(receiveEventStatus){
        case EVENT_GET_IMAGE_FOR_CHECK:
            if(cfmCode == 0){
                //传感器采集到指纹
                sendCmdStatus = ACTION_BUILD_CB1_FOR_CHECK;
            }else if(cfmCode == 2){ 
                //传感器上没有手指,延时后继续发送采集命令
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(3000);
            }
            break;
        case EVENT_BUILD_CB1_FOR_CHECK:
            if(cfmCode == 0){
                //生成特征码成功
                sendCmdStatus = ACTION_SEARCH;
                delay(3000);
            }else{
                //生成特征码失败
                showWarning();
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(3000);
            }
            break;
        case EVETN_SEARCH:
            if(cfmCode == 0){
                //@TODO:搜索到匹配指纹
                uartSendByte(receiveParams[0]);
                uartSendByte(receiveParams[1]);
                uintTemp = receiveParams[0];
                uintTemp = uintTemp<<8;
                uintTemp += receiveParams[1];
                uintTemp = uintTemp / 100;
                P1 = display_code[uintTemp + 1];
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(65535);
            }else{
                //搜索失败
                showWarning();
                P1 = 0x3F;
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(3000);    
            }
            break;
        case EVENT_GET_IMAGE_FOR_INPUT1:
            if(cfmCode == 0){
                //传感器采集到指纹
                sendCmdStatus = ACTION_BUILD_CB1_FOR_INPUT;
                noFingureTimesWhenInput = 0;        //录入时没有指纹的次数归零
            }else if(cfmCode == 2){ 
                //传感器上没有手指,延时后继续发送采集命令,计数30次后重置为验证指纹
                noFingureTimesWhenInput++;
                if(noFingureTimesWhenInput > NO_FINGURE_WHEN_INPUT_MAX_TIME)
                    sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                else    
                    sendCmdStatus = ACTION_GET_IMAGE_FOR_INPUT1;
                delay(3000);
            }
            break;
        case EVENT_BUILD_CB1_FOR_INPUT:
            if(cfmCode == 0){
                //生成特征码成功
                sendCmdStatus = ACTION_GET_IMAGE_FOR_INPUT2;
                delay(3000);
            }else{
                //生成特征码失败
                showWarning();
                sendCmdStatus = ACTION_GET_IMAGE_FOR_INPUT1;
                delay(3000);
            }
            break;
        case EVENT_GET_IMAGE_FOR_INPUT2:
            if(cfmCode == 0){
                //传感器采集到指纹
                sendCmdStatus = ACTION_BUILD_CB2_FOR_INPUT;
                noFingureTimesWhenInput = 0;
            }else if(cfmCode == 2){ 
                //传感器上没有手指,延时后继续发送采集命令,计数30次后重置为验证指纹
                noFingureTimesWhenInput++;
                if(noFingureTimesWhenInput > NO_FINGURE_WHEN_INPUT_MAX_TIME)
                    sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                else    
                    sendCmdStatus = ACTION_GET_IMAGE_FOR_INPUT2;
                delay(3000);
            }
            break;
        case EVENT_BUILD_CB2_FOR_INPUT:
            if(cfmCode == 0){
                //生成特征码成功
                sendCmdStatus = ACTION_MEARGE_CODE;
                delay(3000);
            }else{
                //生成特征码失败
                showWarning();
                sendCmdStatus = ACTION_GET_IMAGE_FOR_INPUT2;
                delay(3000);
            }
            break;
        case EVENT_MEARGE_CODE:
            if(cfmCode == 0){
                //特征码合并成功
                sendCmdStatus = ACTION_SAVE_ADDRESS;
                delay(3000);
            }else{
                //特征码合并失败
                showWarning();
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(3000);
            }
            break;
        case EVENT_SAVE_ADDRESS:
            if(cfmCode == 0){
                //指纹特征保存成功
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(3000);
                updateFingureAddress(newFingureAddressIndex);
            }else{
                //特征码合并失败
                showWarning();
                sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
                delay(3000);
            }
            break;
    }
}

/* 根据sendCmdStatus,构造发送命令 */
void sendCmdFunction(){
    /* 串口接受的相关状态复位 */
    receiveEventStatus = 0;
    waitForReceive = 1;
    receiveBufferLength = 0;

    switch(sendCmdStatus){
        case ACTION_GET_IMAGE_FOR_CHECK:
            P1 = display_code[10];
            sendCmdAndParams[0] = 0x01;
            buildSendCmd(1);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_BUILD_CB1_FOR_CHECK:
            sendCmdAndParams[0] = 0x02;
            sendCmdAndParams[1] = 0x01;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_SEARCH:
            sendCmdAndParams[0] = 0x04;
            sendCmdAndParams[1] = 0x01;
            sendCmdAndParams[2] = 0x00;
            sendCmdAndParams[3] = 0x00;
            sendCmdAndParams[4] = 0x03;
            sendCmdAndParams[5] = 0xE7;
            buildSendCmd(6);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_GET_FINGURE_ADDRESS_LIST0:
            sendCmdAndParams[0] = 0x1F;
            sendCmdAndParams[1] = 0x00;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            break;
        case ACTION_GET_FINGURE_ADDRESS_LIST1:
            sendCmdAndParams[0] = 0x1F;
            sendCmdAndParams[1] = 0x01;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            break;
        case ACTION_GET_FINGURE_ADDRESS_LIST2:
            sendCmdAndParams[0] = 0x1F;
            sendCmdAndParams[1] = 0x02;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            break;
        case ACTION_GET_FINGURE_ADDRESS_LIST3:
            sendCmdAndParams[0] = 0x1F;
            sendCmdAndParams[1] = 0x03;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            break;
        case ACTION_GET_IMAGE_FOR_INPUT1:
            P1 = display_code[11];
            sendCmdAndParams[0] = 0x01;
            buildSendCmd(1);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_GET_IMAGE_FOR_INPUT2:
            sendCmdAndParams[0] = 0x01;
            buildSendCmd(1);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_BUILD_CB1_FOR_INPUT:
            sendCmdAndParams[0] = 0x02;
            sendCmdAndParams[1] = 0x01;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_BUILD_CB2_FOR_INPUT:
            sendCmdAndParams[0] = 0x02;
            sendCmdAndParams[1] = 0x02;
            buildSendCmd(2);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_MEARGE_CODE:
            sendCmdAndParams[0] = 0x05;
            buildSendCmd(1);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
        case ACTION_SAVE_ADDRESS:
            sendCmdAndParams[0] = 0x06;
            sendCmdAndParams[1] = 0x01;
            sendCmdAndParams[2] = (uchar)(newFingureAddressIndex>>8);
            sendCmdAndParams[3] = (uchar)newFingureAddressIndex;
            buildSendCmd(4);
            uartSendBuffer(sendBuffer, sendBufferLength);
            delay(65535);
            break;
    }
}

/* 等待下位机反馈的延时计数函数 */
void waitForReceiveFunction(){
    waitTimes++;
    delay(500);
}

/* 对指纹模块的复位函数 */
void resetFingureFunction(){
    waitTimes = 0;

    //@TODO:控制引脚让指纹下位机复位

    delay(65535);
    delay(65535);
    delay(65535);
    delay(65535);

    initMain();
    sendCmdStatus = ACTION_GET_IMAGE_FOR_CHECK;
}

/**
 * 获取指纹模块的有效指纹列表
 * @return [0:成功,1:超时失败,2:反馈错误]
 */
uchar getAddressListFunction(){
    sendCmdStatus = ACTION_GET_FINGURE_ADDRESS_LIST0;

    for(ut1 = 0; ut1 < 4; ut1++){
        sendCmdFunction();

        if(waitForReceive)
            delay(65535);
        if(waitForReceive)
            delay(65535);
        if(waitForReceive)
            delay(65535);
        if(waitForReceive)
            delay(65535);
        //等待4 * 65535个机器步骤,如果依然没有反馈则失败

        if(waitForReceive)
            return 1;
        if(cfmCode || receivePackageLength != 35)
            return 2;

        //从串口反馈参数中提取列表索引数据
        for(uintTemp = 0; uintTemp < 33; uintTemp++){
            fingureAddressIndex[ ut1 * 32 + uintTemp] = receiveParams[uintTemp];
        }
        sendCmdStatus++;    //将指令移到下一页
    }

    return 0;
}

/**
 * 根据内存中的指纹库,和传入的权限,构造一个新的未使用的指纹索引
 * @param  uchar [权限,1-10]
 * @return       [未使用的指纹库索引]
 */
uint  getNewAddressIndexByPower(uchar power){
    uintTemp = (power - 1) * 100;
    ucharTemp = fingureAddressIndex[uintTemp / 8]<<(uintTemp % 8);
    while((ucharTemp & 0x80) == 0x80){
        uintTemp++;
        ucharTemp = fingureAddressIndex[uintTemp / 8]<<(uintTemp % 8);
    }
    return uintTemp;
}

/**
 * 更新指纹库,将指定位置的bit置为1
 * @param  address [指定地址]
 * @return         []
 */
void  updateFingureAddress(uint address){
    ucharTemp = fingureAddressIndex[address/8];
    ut1 = 0x80 >> (address % 8);
    ucharTemp = ucharTemp | ut1;
    fingureAddressIndex[address/8] = ucharTemp;
}

/* @TODO:交互反馈 */
void showWarning(){

}

丁丁生于 1987.07.01 ,30岁,英文ID:newflydd

  • 现居住地 江苏 ● 泰州 ● 姜堰
  • 创建了 Jblog 开源博客系统
  • 坚持十余年的 独立博客 作者
  • 大学本科毕业后就职于 中国电信江苏泰州分公司,前两年从事Oracle数据库DBA工作,两年后公司精简技术人员,被安排到农村担任支局长(其本质是搞销售),于2016年因志向不合从国企辞职,在小城镇找了一份程序员的工作。
  • Git OSChina 上积极参与开源社区