FPM383驱动

FPM383C模组通信协议_V1.2.pdf这是官方文档如有需要在这里查看

一. 简介

  • 面阵式指纹传感器

  • 传感器表面覆盖保护涂层

  • RGB三色LED灯

  • UART接口

  • 1.0mm连接器,支持可靠连接

  • 指纹模组支持3.3V供电

  • ESD防护等级>15KV

  • 假率FAR(FalseAcceptanceRate):<1/1000000

  • 拒真率FRR(FalseRejectionRate):<2%

  • 响应速度:特征提取时间<0.30s,单枚匹配时间<0.025s

  • 支持存储 50枚指纹特征(拼接12次) (实测我的模块最大只支持4次拼接)

二. 关键参数

  • 串口波特率为 57.6Kbps,数据格式:8 位数据位,1 位停止位,无校验位;

  • UART 波特率可以通过指令进行设置,范围从 9600 至115200;

  • 模组中断信号由Sensor芯片产生,中断输出管脚内部默认加下拉电阻,休眠时中断输出脚会维持低电平,直到有手指按压指纹传感器,此时中断输出高电平,sensor再次进入休眠后变为低电平。

  • 建议用户端MCU在模组唤醒之后,屏蔽中断引脚的中断触发功能,模组进入休眠之后,再开启中断触发,并且设置为上升沿触发。(任何一条指令都可以唤醒芯片)

  • 如果主控是 MCU(3.3V),则直接与 UART_TD 和UART_RD 连接;如果主控是 PC,则需要挂接 RS232 电平转换设备。

  • 指令包格式

    image-20250811204705917

    • 包头固定为0xEF01
    • 设备地址在没有生成之前为缺省的 0xffffffff,一旦主控通过指令生成了设备地址,则所 有的数据包都必须按照生成的地址收发。
    • 包标识固定为0x01
    • 包长度 = 包长度至校验和(指令、参数或数据)的总字节数,包含校验和,但不包含包长 度本身的字节数。
    • 指令为各指令名一个字节
    • 参数根据各指令决定
    • 校验和 = 从包标识至校验和之间所有字节之和,包含包标识不包含校验和,超出 2 字节的进位忽略。
    • 对于多字节的高字节在前低字节在后(如2bytes 的 00 06 表示0006,而不是0600)
  • 指令应答格式

    image-20250811205304705

    • 包头到包标识和指令包一样
    • 确认码:(只看两个常用的,只有0x00为接收成功其均为失败,其他确认码在官方文档19页)
      • 00H:表示指令执行完毕或OK;
      • 01H:表示数据包接收错误

三. 串口连接

代码中使用的宏和枚举类型

#define FPM383_BAUDRATE             57600
#define FPM383_UART_NUM             UART_NUM_1
#define FPM383_TX_GPIO              GPIO_NUM_21
#define FPM383_RX_GPIO              GPIO_NUM_20
#define FPM383_INT_GPIO             GPIO_NUM_10

typedef enum
{
    CMD_SUCCESS,
    CMD_FAILED,
} Command_Status;

typedef enum
{
    switch_ON,
    switch_OFF,
} switch_type;

如需移植只需实现下面这几个函数即可

1. 初始化串口和中断引脚

中断引脚设置为上升沿触发,gpio下拉输入。串口波特率57600,8位数据位,1位停止位,不使用校验位。

/**
 * @brief 初始化FPM383
 *
 * @param 无
 *
 * @return 无
 */
void FPM383_Init(void)
{
    /* 创建按下手指二值信号量 */
    g_xBinarySemaphorePressFinger = xSemaphoreCreateBinary();
    /* 中断引脚配置 */
    gpio_config_t FPM383_gpio_config = 
    {
        .intr_type = GPIO_INTR_POSEDGE,           /* 上升沿触发 */
        .mode = GPIO_MODE_INPUT,                  /* 输入模式 */
        .pin_bit_mask = (1 << FPM383_INT_GPIO),   /* GPIO引脚掩码 */
        .pull_down_en = GPIO_PULLDOWN_ENABLE,     /* 下拉输入 */
        .pull_up_en = GPIO_PULLUP_DISABLE,    
    };
    gpio_config(&FPM383_gpio_config);
    /* 安装中断服务驱动 */
    gpio_install_isr_service(ESP_INTR_FLAG_LEVEL2);

    /* 添加中断服务程序 */
    gpio_isr_handler_add(FPM383_INT_GPIO,FPM383_isr,(void*)FPM383_INT_GPIO);

    uart_config_t FPM383_uart_config = 
    {
        .baud_rate = FPM383_BAUDRATE,             /* 波特率57600*/
        .data_bits = UART_DATA_8_BITS,            /* 数据位8位 */
        .stop_bits = UART_STOP_BITS_1,            /* 停止位1位 */
        .parity = UART_PARITY_DISABLE,            /* 不校验 */
        .source_clk = UART_SCLK_DEFAULT,          /* 使用默认时钟源APB*/
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,    /* 不使用硬件流控制 */
    };
    /* 安装串口驱动 */
    uart_driver_install(FPM383_UART_NUM,UART_RX_BUFF_SIZE,UART_TX_BUFF_SIZE,0,NULL,0);

    /* 配置串口1 */
    uart_param_config(FPM383_UART_NUM,&FPM383_uart_config);

    /* 设置串口引脚 */
    uart_set_pin(FPM383_UART_NUM,FPM383_TX_GPIO,FPM383_RX_GPIO,UART_PIN_NO_CHANGE,UART_PIN_NO_CHANGE);

    /* 睡眠 */
    FPM383_sleep();
}

2. 中断回调函数

这里使用信号量也可以使用全局变量表示手指是否按下。

/* 按下手指二值信号量 */
SemaphoreHandle_t g_xBinarySemaphorePressFinger = NULL;

/* 表示是否按下手指 */
// uint8_t pressFinger = 0;

/**
 * @brief 中断回调函数
 *
 * @param arg: 设置中断引脚时传递的参数
 *
 * @return 无
 */
void FPM383_isr(void *arg)
{
    /* 信号量+1 */
    xSemaphoreGiveFromISR(g_xBinarySemaphorePressFinger,NULL);
    //uint8_t pressFinger = 1;
    /* 唤醒后关中断 */
    intr_switch(switch_OFF);
}

3. 串口发送数据

/**
 * @brief 串口发送数据
 *
 * @param Txbuff: 发送的数据
 * @param Txbuff_size: 发送的数据长度
 *
 * @return 无
 */
static void FPM383_send_data(uint8_t *Txbuff,uint8_t Txbuff_size)
{
    uint8_t len = uart_write_bytes(FPM383_UART_NUM,Txbuff,Txbuff_size);
    if(len == Txbuff_size)
    {
        // printf("发送成功\r\n");
    }
    else
    {
        printf("发送失败\r\n");
    }
}

4. 串口接收数据

/* 使用全局数据作为接收缓冲区 */
static uint8_t g_rx_buff[100];

/**
 * @brief 串口接收数据
 *
 * @param Rxbuff_size: 应答包的数据长度
 * @param timeout: 超时时间
 *
 * @return 无
 */
static void FPM383_receive_data(uint8_t Rxbuff_size,uint16_t timeout)
{
    /* 先清空数据 因为0为接收成功,所以将数组全部置1 */
    memset(g_rx_buff,1,sizeof(g_rx_buff));
    uint8_t len = uart_read_bytes(FPM383_UART_NUM,g_rx_buff,Rxbuff_size,timeout);
    /* 接收到长度等于应答包的长度接收成功 */
    if(len == Rxbuff_size)
    {
        // printf("接收成功\r\n");
    }
    else
    {
        printf("接收失败\r\n");
    }
}

5. 开关中断

/**
 * @brief 开关中断
 *
 * @param type: 开关
 *
 * @return 无
 */
void intr_switch(switch_type type)
{
    if(type == switch_ON)
    {
        gpio_intr_enable(FPM383_INT_GPIO);
    }
    else
    {
        gpio_intr_disable(FPM383_INT_GPIO);
    }
}

6. 延迟函数

/**
 * @brief 延迟函数
 *
 * @param ms: 延迟时间  单位:ms
 *
 * @return 无
 */
void delay_ms(uint32_t ms)
{
    vTaskDelay(pdMS_TO_TICKS(ms));
}

四. 业务类指令集

1 通用指令集

1.1 休眠指令PS_Sleep

image-20250811212758356

代码

/**
 * @brief 睡眠
 *
 * @param 无
 *
 * @return 无
 */
void FPM383_sleep(void)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                             /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                   /* 设备地址 */
        0X01,                                  /* 包标识 */
        0X00,0X03,                             /* 包长度 */
        0X33,                                  /* 指令码 */
        0X00,0X37,                             /* 校验和 */
    };
    /* 确保睡眠指令成发出,否则后续无法执行 */
    do
    {
        delay_ms(500);
        /* 发送指令 */
        FPM383_send_data(cmd,sizeof(cmd));
        /* 接收应答包 */
        FPM383_receive_data(12,500);
    } while (g_rx_buff[9] != 0);

    /* 开中断 */
    intr_switch(switch_ON);
    printf("睡眠成功\r\n");
}

1.2 获取芯片唯一序列号PS_GetChipSN

image-20250811220439829

代码

/**
 * @brief 获取芯片ID
 *
 * @param 无
 *
 * @return 无
 */
void FPM383_get_chipID(void)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                             /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                   /* 设备地址 */
        0X01,                                  /* 包标识 */
        0X00,0X04,                             /* 包长度 */
        0X34,                                  /* 指令码 */
        0X00,                                  /* 参数 */
        0X00,0X39,                             /* 校验和 */
    };

    /* 发送指令 */
    FPM383_send_data(cmd,sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(44,3000);
    
    if(g_rx_buff[9] == 0)
    {
        printf("ID:%.32s\r\n",&g_rx_buff[10]);
    }    
    else
    {
        printf("ID获取失败 确认码: %x\r\n",g_rx_buff[9]);
        printf("获取ID失败\r\n");
    }
    FPM383_sleep();
}

1.3 验证用获取图像PS_GetImage

image-20250811210153518

代码

/**
 * @brief 获取图像
 *
 * @param 无
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_get_image(void)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                             /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                   /* 设备地址 */
        0X01,                                  /* 包标识 */
        0X00,0X03,                             /* 包长度 */
        0X01,                                  /* 指令码 */
        0X00,0X00,                             /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd,sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd,sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(12,500);

    if(g_rx_buff[9] == 0)
    {
        printf("获取图像成功\r\n");
        return CMD_SUCCESS;
    }    
    else
    {
        printf("获取图像失败 确认码: %x\r\n", g_rx_buff[9]);
        return CMD_FAILED;
    }
}

1.4 生成特征PS_GenChar

image-20250811214426192

代码

/**
 * @brief 生成特征
 *
 * @param buffID: 缓冲区号
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_generate_feature(uint8_t buffID)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                             /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                   /* 设备地址 */
        0X01,                                  /* 包标识 */
        0X00,0X04,                             /* 包长度 */
        0X02,                                  /* 指令码 */
        buffID,                                /* 缓冲区ID */
        0X00,0X00,                             /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd,sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd,sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(12,500);

    if(g_rx_buff[9] == 0)
    {
        printf("生成特征成功\r\n");
        return CMD_SUCCESS;
    }    
    else
    {
        printf("生成特征失败 确认码: %x\r\n",g_rx_buff[9]);
        return CMD_FAILED;
    }
}

1.5 合并特征(生成模板)PS_RegMode

image-20250811214706741

代码

/**
 * @brief 合并特征
 *
 * @param 无
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_merge_feature(void)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                             /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                   /* 设备地址 */
        0X01,                                  /* 包标识 */
        0X00,0X03,                             /* 包长度 */
        0X05,                                  /* 指令码 */
        0X00,0X00,                             /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd,sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd,sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(12, 500);

    if(g_rx_buff[9] == 0)
    {
        printf("合并特征成功\r\n");
        return CMD_SUCCESS;
    }    
    else
    {
        printf("合并特征失败 确认码: %x\r\n", g_rx_buff[9]);
        return CMD_FAILED;
    }
}

1.6 储存模板PS_StoreChar

image-20250811214858475

代码

/**
 * @brief 存储模板
 *
 * @param 无
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_storage_template(uint16_t pageID)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                            /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                  /* 设备地址 */
        0X01,                                                 /* 包标识   */
        0X00,0X06,                                            /* 包长度   */
        0X06,                                                 /* 指令码   */
        0x01,                                                 /* 缓冲区ID */
        pageID >> 8,pageID & 0xFF,                            /* 位置号   */
        0X00,0X00,                                            /* 校验和   */
    };  

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    uart_receive_data(12, 500);

    if(0 == g_rx_buff[9])
    {
        printf("储存指纹成功\r\n");
        return COM_OK;
    }    
    else
    {
        printf("储存指纹失败 确认码: %x\r\n", g_rx_buff[9]);
        return COM_ERROR;
    }
}

image-20250811221240428

代码

/**
 * @brief 搜索指纹
 *
 * @param pageID: 指纹所在页码
 * @param matchScore: 和指纹指纹对比得分
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_search_fingerprint(uint16_t *pageID, uint16_t *matchScore)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                             /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                   /* 设备地址 */
        0X01,                                  /* 包标识 */
        0X00,0X08,                             /* 包长度 */
        0X04,                                  /* 指令码 */
        0x01,                                  /* 缓冲区ID */
        0x00,0x00,                             /* 起始页 */
        0x00,0x00,                             /* 页数0x00为搜索整个指纹库 */
        0X00,0X00,                             /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(16, 500);

    if(g_rx_buff[9] == 0)
    {
        *pageID = ((g_rx_buff[10] << 8) | g_rx_buff[11]);
        *matchScore = ((g_rx_buff[12] << 8) | g_rx_buff[13]);
        //printf("搜索指纹成功\r\n");
        return CMD_SUCCESS;
    }    
    else
    {
        printf("搜索指纹失败 确认码: %x\r\n", g_rx_buff[9]);
        return CMD_FAILED;
    }
}

1.8 删除模板PS_DeletChar

image-20250811224001630

代码

/**
 * @brief 删除指纹
 *
 * @param pageID: 指纹所在页码
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_delete_fingerprint(uint16_t pageID)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                            /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                  /* 设备地址 */
        0X01,                                 /* 包标识 */
        0X00,0X07,                            /* 包长度 */
        0X0C,                                 /* 指令码 */
        pageID >> 8,pageID & 0xFF,            /* 页码 */
        0x00,0x01,                            /* 删除个数 */
        0X00,0X00,                            /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(12, 500);

    if(g_rx_buff[9] == 0)
    {
        printf("删除指纹成功\r\n");
        FPM383_sleep();
        return CMD_SUCCESS;
    }    
    else
    {
        printf("删除指纹失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return CMD_FAILED;
    }
}

1.9 删除指纹库

image-20250811234708846

代码

/**
 * @brief 清空指纹库
 *
 * @param 无
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_clear_Fingerprint_database(void)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                    /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                          /* 设备地址 */
        0X01,                                         /* 包标识 */
        0X00,0X03,                                    /* 包长度 */
        0X0D,                                         /* 指令码 */
        0X00,0X00,                                    /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(12, 500);

    if(g_rx_buff[9] == 0)
    {
        printf("清空指纹库成功\r\n");
        FPM383_sleep();
        return CMD_SUCCESS;
    }    
    else
    {
        printf("清空指纹库失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return CMD_FAILED;
    }
}

1.10 写系统寄存器PS_WriteReg

image-20250812105345062

代码

/**
 * @brief 写系统寄存器
 *
 * @param RegID: 寄存器号
 * @param RegData: 寄存器数据
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_write_system_register(uint8_t RegID, uint8_t *RegContent)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                              /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                    /* 设备地址 */
        0X01,                                                   /* 包标识   */
        0X00,0X05,                                              /* 包长度   */
        0X0E,                                                   /* 指令码   */
        RegID,                                                  /* 寄存器号 */
        *RegContent,                                            /* 内容     */
        0X00,0X00,                                              /* 校验和   */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    uart_receive_data(12, 500);

    if(0 == g_rx_buff[9])
    {
        printf("写寄存器成功\r\n");
        FPM383_sleep();
        return COM_OK;
    }    
    else
    {
        printf("写寄存器失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return COM_ERROR;
    }
}

1.11 读模组基本参数PS_ReadSysPara

WPS拼图0

代码

/**
 * @brief 读模组基本参数
 *
 * @param 无
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_read_module_basics_params(void)
{
    uint16_t registerCount;                                /* 注册次数     */ 
    uint16_t fingerprintTemplateSize;                      /* 指纹模板大小 */
    uint16_t fingerprintDatabaseSize;                      /* 指纹库大小   */
    uint16_t scoreLevel;                                   /* 分数等级     */
    uint8_t deviceAddress[4];                              /* 设备地址     */
    uint16_t packetSize;                                   /* 数据包大小   */
    uint16_t baudRate;                                     /* 波特率设置   */
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                         /* 包头         */
        0XFF,0XFF,0XFF,0XFF,                               /* 设备地址     */
        0X01,                                              /* 包标识       */
        0X00,0X03,                                         /* 包长度       */
        0X0F,                                              /* 指令码       */
        0X00,0X00,                                         /* 校验和       */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    uart_receive_data(28, 500);

    registerCount = (g_rx_buff[10] << 8) | g_rx_buff[11];  /* 注册次数    */ 
    fingerprintTemplateSize = (g_rx_buff[12] << 8)
                                 | g_rx_buff[13];          /* 指纹模板大小*/
    fingerprintDatabaseSize = (g_rx_buff[14] << 8)
                                 | g_rx_buff[15];          /* 指纹库大小  */
    scoreLevel = (g_rx_buff[16] << 8) | g_rx_buff[17];     /* 分数等级    */
    deviceAddress[0] = g_rx_buff[18];                      /* 设备地址    */
    deviceAddress[1] = g_rx_buff[19];
    deviceAddress[2] = g_rx_buff[20];
    deviceAddress[3] = g_rx_buff[21];
    packetSize = (g_rx_buff[22] << 8) | g_rx_buff[23];     /* 数据包大小  */
    baudRate = (g_rx_buff[24] << 8) | g_rx_buff[25];       /* 波特率设置  */

    if(0 == packetSize)
    {
        packetSize = 32;
    }
    else if(1 == packetSize)
    {
        packetSize = 64;
    }
    else if(2 == packetSize)
    {
        packetSize = 128;
    }
    else if(3 == packetSize)
    {
        packetSize = 256;
    }
                     
    if(0 == g_rx_buff[9])
    {
        printf("读模组基本参数成功\
                \r\n 注册次数: %d\
                \r\n 指纹模板大小: %d\
                \r\n 指纹库大小: %d\
                \r\n 分数等级: %d\
                \r\n 设备地址: %x %x %x %x\
                \r\n 数据包大小: %dbytes\
                \r\n 波特率设置: %d\r\n",registerCount, 
                                        fingerprintTemplateSize,
                                        fingerprintDatabaseSize,
                                        scoreLevel, 
                                        deviceAddress[0],
                                        deviceAddress[1],
                                        deviceAddress[2],
                                        deviceAddress[3],
                                        packetSize, 
                                        baudRate * 9600);
        FPM383_sleep();
        return COM_OK;
    }    
    else
    {
        printf("读模组基本参数失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return COM_ERROR;
    }
}

1.12 LED控制灯指令PS_ControlBLN

WPS拼图0

代码(一般指示灯可以使用,我的七彩呼吸灯不能使用)

/* LED灯功能码 */
typedef enum
{
    LED_MODE_NORMAL_BREATHING = 1, // 普通呼吸灯
    LED_MODE_BLINKING,             // 闪烁灯
    LED_MODE_ON,                   // 常开灯
    LED_MODE_OFF,                  // 常闭灯
    LED_MODE_FADE_IN,              // 渐开灯
    LED_MODE_FADE_OUT,             // 渐闭灯
    LED_MODE_RAINBOW_BREATHING     // 七彩编程呼吸灯
} LED_mode_type;

/* 颜色类型 */
typedef enum
{
    COLOR_CODE_NONE,         // 无颜色
    COLOR_CODE_BLUE,         // 蓝色
    COLOR_CODE_GREEN,        // 绿色
    COLOR_CODE_CYAN,         // 青色
    COLOR_CODE_RED,          // 红色
    COLOR_CODE_MAGENTA,      // 品红色
    COLOR_CODE_YELLOW,       // 黄色
    COLOR_CODE_WHITE,        // 白色
} color_type;

/* 一般指示灯 */
typedef struct 
{
    led_mode_t mode;                  /* 指示灯模式                       */
    uint8_t start_color;              /* 起始颜色                         */
    uint8_t finish_color;             /* 结束颜色                         */
    uint8_t cycles_num;               /* 循环次数                         */
}led_config_t;

/**
 * @brief 一般指示灯
 *
 * @param led_config: 指示灯配置
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_general_indicate_light(led_config_t led_config)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                           /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                 /* 设备地址 */
        0X01,                                                /* 包标识   */
        0X00,0X07,                                           /* 包长度   */
        0X3C,                                                /* 指令码   */
        led_config.mode,                                     /* 功能码   */
        led_config.start_color,                              /* 起始颜色 */
        led_config.finish_color,                             /* 结束颜色 */
        led_config.cycles_num,                               /* 循环次数 */
        0X00,0X00,                                           /* 校验和   */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    uart_receive_data(12, 500);

    if(0 == g_rx_buff[9])
    {
        printf("指示灯设置成功\r\n");
        FPM383_sleep();
        return COM_OK;
    }    
    else
    {
        printf("指示灯设置失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return COM_ERROR;
    }
}

/* 七彩呼吸灯 */
typedef struct 
{
    uint8_t timebit;                  /* 时间位(0.1s~10.0s,范围1~100)     */
    uint8_t colors[5];                /* 颜色码数组,每个字节分为2个4位单元 */
    uint8_t cycles_num;               /* 循环次数                          */
}colorful_breathing_config_t;

/**
 * @brief 七彩呼吸灯
 *
 * @param colorful_breathing_config: 七彩呼吸灯配置
 * 每个字节的颜色码分成 2 个单元,每个单元有 4位,从高位开始分为一个有效位,和 3 个颜色控制位。
 * 每个单元控制某种颜色的灯从灭到亮,再到灭的过程。
 * 另外,编程后的呼吸灯 1 次循环点亮顺序是从颜色 1 的单元 1 开始,
 * 然后是颜色 1的单元 2,接着是颜色 2 的单元 1,以此类推。
 * @param cyclesNum: 循环次数
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_colorful_breathing_light(colorful_breathing_config_t colorful_breathing_config)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                           /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                 /* 设备地址 */
        0X01,                                                /* 包标识   */
        0X00,0X0B,                                           /* 包长度   */
        0X3C,                                                /* 指令码   */
        LED_MODE_RAINBOW_BREATHING,                          /* 功能码   */
        colorful_breathing_config.timebit,                   /* 时间位   */
        colorful_breathing_config.colors[0],                 /* 颜色1    */
        colorful_breathing_config.colors[1],                 /* 颜色2    */
        colorful_breathing_config.colors[2],                 /* 颜色3    */
        colorful_breathing_config.colors[3],                 /* 颜色4    */
        colorful_breathing_config.colors[4],                 /* 颜色5    */
        colorful_breathing_config.cycles_num,                /* 循环次数 */
        0X00,0X00,                                           /* 校验和   */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));
    /* 接收应答包 */
    uart_receive_data(12, 500);

    if(0 == g_rx_buff[9])
    {
        printf("七彩呼吸灯设置成功\r\n");
        FPM383_sleep();
        return COM_OK;
    }    
    else
    {
        printf("七彩呼吸灯设置失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return COM_ERROR;
    }
}

1.13 读索引表PS_ReadIndexTable

image-20250812142250588

代码

/**
 * @brief 读取索引表
 *
 * @param pageID: 页码
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_read_index_table(uint8_t pageID)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                    /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                          /* 设备地址 */
        0X01,                                         /* 包标识 */
        0X00,0X04,                                    /* 包长度 */
        0X1F,                                         /* 指令码 */
        pageID,                                       /* 页码 */
        0X00,0X00,                                    /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd,sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd,sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(44,3000);

    if(g_rx_buff[9] == 0)
    {
        printf("读取索引表成功\r\n");
        memcpy(IDIndxTable, &g_rx_buff[10], 32);  
        // for(uint8_t i = 0;i < 32;i++)
        // {
        //     printf("模板%d-%d的录取情况: %x\r\n",i * 8,(i + 1) * 8 - 1, 
        //                                     g_rx_buff[10 + i]);
        // } 
        FPM383_sleep();
        return CMD_SUCCESS;
    }    
    else
    {
        printf("读取索引表失败 确认码: %x\r\n",g_rx_buff[9]);
        FPM383_sleep();
        return CMD_FAILED;
    }
}

2.模块指令集

2.1 自动注册模板PS_AutoEnro

WPS拼图0

代码

/**
 * @brief 自动注册指纹
 *
 * @param 无
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_auto_register_fingerprint(void)
{
    uint8_t i = 1;
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                           /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                 /* 设备地址 */
        0X01,                                                /* 包标识   */
        0X00,0X08,                                           /* 包长度   */
        0X31,                                                /* 指令码   */
        g_pageID_inidx >> 8,g_pageID_inidx & 0xFF,           /* ID号     */
        ENTRY_NUM,                                           /* 录入次数 */
        0X00,0X12,                                           /* 参数     */
        0X00,0X00,                                           /* 校验和   */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));

    /* 指令合法验证 */
    FPM383_auto_verify_reply(0x00, 0x00);

    while(i < ENTRY_NUM + 1)
    {
        /* 获取指纹图像 */
        if(COM_OK != FPM383_auto_verify_reply(0x01, i))
            goto err;
        /* 生成特征 */
        if(COM_OK != FPM383_auto_verify_reply(0x02, i))
            goto err;
        /* 手指离开 */
        if(COM_OK != FPM383_auto_verify_reply(0x03, i))
            goto err;    
        i++;
    }

    /* 合成模板 */
    if(COM_OK != FPM383_auto_verify_reply(0x04, 0xF0))
        goto err; 
    /* 指纹重复检测 */
    if(COM_OK != FPM383_auto_verify_reply(0x05, 0xF1))
        goto err; 
    /* 登记模板成功 */
    if(COM_OK != FPM383_auto_verify_reply(0x06, 0xF2))
        goto err; 

    printf("注册指纹成功");
    FPM383_sleep();
    return COM_ERROR;
err:
    printf("注册指纹失败");
    FPM383_sleep();
    return COM_ERROR;
}

2.2 自动验证指纹PS_AutoIdentify

WPS拼图0

代码

/**
 * @brief 自动验证指纹
 *
 * @param 无
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_auto_verify_fingerprint(void)
{
    uint16_t pageID = 0;
    uint8_t matchScore = 0;
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                           /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                 /* 设备地址 */
        0X01,                                                /* 包标识   */
        0X00,0X08,                                           /* 包长度   */
        0X32,                                                /* 指令码   */
        0x03,                                                /* 分数等级 */
        0xFF,0xFF,                                           /* ID号     */
        0X00,0X02,                                           /* 参数     */
        0X00,0X00,                                           /* 校验和   */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));

    /* 指令合法验证 */
    FPM383_auto_verify_reply(0x00, 0x00);

    /* 获取指纹图像成功 */
    if(COM_OK != FPM383_auto_verify_reply(0x00, 0x01))
        goto err;
    /* 生成特征成功,指纹对比成功 */
    if(COM_OK != FPM383_auto_verify_reply(0x00, 0x05))
        goto err;

    pageID = ((g_rx_buff[10] << 8) | (g_rx_buff[11] & 0xFF));
    matchScore = g_rx_buff[12];

    printf("指纹验证成功 指纹所在页: %x,\
            和指纹库对比得分: %x\r\n",pageID,
                                     matchScore);
    FPM383_sleep();
    return COM_ERROR;
err:
    printf("验证指纹失败");
    FPM383_sleep();
    return COM_ERROR;
}

2.3 取消指令PS_Cancel

image-20250812104837605

代码

/**
 * @brief 取消指令
 *
 * @param 无
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_cancel_cmd(void)
{
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                    /* 包头 */
        0XFF,0XFF,0XFF,0XFF,                          /* 设备地址 */
        0X01,                                         /* 包标识 */
        0X00,0X03,                                    /* 包长度 */
        0X30,                                         /* 指令码 */
        0X00,0X00,                                    /* 校验和 */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd,sizeof(cmd));
    /* 发送指令 */
    FPM383_send_data(cmd,sizeof(cmd));
    /* 接收应答包 */
    FPM383_receive_data(12, 500);

    if(g_rx_buff[9] == 0)
    {
        printf("取消指令成功\r\n");
        FPM383_sleep();
        return CMD_SUCCESS;
    }    
    else
    {
        printf("取消指令失败 确认码: %x\r\n", g_rx_buff[9]);
        FPM383_sleep();
        return CMD_FAILED;
    }
}

五. 通用指令通信流程

1. 通用指令注册指纹流程

image-20250811215349597

代码

/**
 * @brief 注册指纹
 *
 * @param 无
 *
 * @return Command_Status: 成功或失败
 */
Command_Status FPM383_register_fingerprint(void)
{
    uint8_t BufferID = 1;
    while( BufferID < 6)
    {
        /* 获取指纹图像 */
        if(FPM383_get_image() != CMD_SUCCESS)
            goto err;
        /* 生成特征 */
        if(FPM383_generate_feature(BufferID++) != CMD_SUCCESS)
            goto err;
    }
    /* 合并特征 */
    if(FPM383_merge_feature() != CMD_SUCCESS)
        goto err;
    /* 存储模板 */
    if(FPM383_storage_template() != CMD_SUCCESS)
        goto err;

    printf("指纹录入成功\r\n");
    /* 睡眠 */
    FPM383_sleep();
    return CMD_SUCCESS;
err:
    printf("指纹录入失败\r\n");
    FPM383_sleep();
    return CMD_FAILED;
}

2. 通用指令验证指纹流程

image-20250811222544631

代码

/**
 * @brief 验证指纹
 *
 * @param 无
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_verify_fingerprint(uint16_t *pageID)
{
    uint16_t matchScore = 0;
    /* 获取指纹图像 */
    if(COM_OK != FPM383_get_image())
        goto err;
    /* 生成特征 */
    if(COM_OK != FPM383_generate_feature(0x01))
        goto err;

    /* 搜索指纹 */
    if(COM_OK != FPM383_search_fingerprint(pageID, &matchScore))
        goto err;

    printf("指纹验证成功 指纹所在页: %x,\
            和指纹库对比得分: %x\r\n", *pageID, 
                                      matchScore);
    /* 睡眠 */
    FPM383_sleep();
    return COM_OK;
err:
    printf("指纹验证失败");
    FPM383_sleep();
    return COM_ERROR;
}

6. 模块指令通信流程

1. 自动注册模板流程

image-20250811235104160

代码

/**
 * @brief 自动注册指纹
 *
 * @param 无
 *
 * @return com_status_t: 成功或失败
 */
com_status_t FPM383_auto_register_fingerprint(void)
{
    uint8_t i = 1;
    uint8_t cmd[] = 
    {
        0xEF,0X01,                                           /* 包头     */
        0XFF,0XFF,0XFF,0XFF,                                 /* 设备地址 */
        0X01,                                                /* 包标识   */
        0X00,0X08,                                           /* 包长度   */
        0X31,                                                /* 指令码   */
        g_pageID_inidx >> 8,g_pageID_inidx & 0xFF,           /* ID号     */
        ENTRY_NUM,                                           /* 录入次数 */
        0X00,0X12,                                           /* 参数     */
        0X00,0X00,                                           /* 校验和   */
    };

    /* 计算校验和 */
    FPM383_checkSum(cmd, sizeof(cmd));
    /* 发送指令 */
    uart_send_data(cmd, sizeof(cmd));

    /* 指令合法验证 */
    FPM383_auto_verify_reply(0x00, 0x00);

    while(i < ENTRY_NUM + 1)
    {
        /* 获取指纹图像 */
        if(COM_OK != FPM383_auto_verify_reply(0x01, i))
            goto err;
        /* 生成特征 */
        if(COM_OK != FPM383_auto_verify_reply(0x02, i))
            goto err;
        /* 手指离开 */
        if(COM_OK != FPM383_auto_verify_reply(0x03, i))
            goto err;    
        i++;
    }

    /* 合成模板 */
    if(COM_OK != FPM383_auto_verify_reply(0x04, 0xF0))
        goto err; 
    /* 指纹重复检测 */
    if(COM_OK != FPM383_auto_verify_reply(0x05, 0xF1))
        goto err; 
    /* 登记模板成功 */
    if(COM_OK != FPM383_auto_verify_reply(0x06, 0xF2))
        goto err; 

    printf("注册指纹成功");
    FPM383_sleep();
    return COM_ERROR;
err:
    printf("注册指纹失败");
    FPM383_sleep();
    return COM_ERROR;
}

七. 常见错误

1. 卡死在睡眠

  1. 两条睡眠指令发送时间不能太进,否则会进不去睡眠,卡死在睡眠
  2. 断电重插一下,解决%99问题。

2.接收失败错误码0x01

  1. 检查校验和失是否错误

    校验和 = 从包标识至校验和之间所有字节之和,包含包标识不包含校验和,超出 2 字节的进位忽略

image-20250811225749379

  1. 接收应答包数据长度错误

    image-20250811230754553

  2. 接收数据等待时间太短,增加等待时间。

image-20250811230030578

posted @ 2025-10-08 20:27  2666666  阅读(285)  评论(0)    收藏  举报