Contributed by WT_0213, organized by Ai-Thinker
[Peripheral Porting] Ai-WB2+FPM383C Fingerprint
) The FPM383C fingerprint module has been introduced in detail in a previous post; you can take a look at this post: > 【外设移植】FPM383C指纹模块+Ai-M61-32S > ) This post mainly covers some problems encountered when porting the FPM383C fingerprint module to the Ai-WB2, as well as a completed porting example. First, set up the environment. > 【Ai-WB2入门篇】搭建windows+eclipse环境 > Following the tutorial from the garden director (园长), the environment setup basically goes smoothly and everything can run normally. If it doesn't run, check where your setup differs from the tutorial. There is a small pitfall to note here. Use the clone command to clone the project rather than downloading the zip directly. > git clone --recursive [https://gitee.com/Ai-Thinker-Open/Ai-Thinker-WB2](https://gitee.com/Ai-Thinker-Open/Ai-Thinker-WB2) If you are not familiar with the configuration files, try to create your project following the existing directory structure so that you minimize changes to the configuration files. > 如果在其他目录下创建项目,需要注意的是BL60X_SDK_PATH这个值要能找到sdkpath {width=600} There are some warnings during compilation; you can ignore them if they are not from your own code. If compilation passes, a bin file will be generated. {width=600} The next step is the flashing process. > Ai-WB2烧录软件 > > [[https://docs.ai-thinker.com/](https://docs.ai-thinker.com/)\_media/bl602\_flash\_download\_tool.zip]([https://docs.ai-thinker.com/_media/bl602_flash_download_tool.zip](https://docs.ai-thinker.com/_media/bl602_flash_download_tool.zip)) For the flashing process, refer to: > 【Ai-WB2入门篇】新建工程和烧录指导 Here is another pitfall to note. {width=600} > 1、如果出现COM口拒绝访问,检查下时候开启的串口调试工具。 > > 2、如果提示BFLB IMG LOAD SHAKEHAND FAIL应该是没有进入烧录模式,点击Create & Download 然后按住开发板右侧的按钮,再点按左侧EN按钮松手就可以了。 **There is a big pitfall to note here** {width=600} When flashing the compiled bin, you need to use this flashing method; otherwise the program will not run properly after flashing. > 序号1、3、4选择的都是烧录工具目录下的文件 > > 序号 2 选择编译的bin即可 After flashing, open the serial debugging tool. Set the baud rate to 115200.  Here the log is printed using the printf method; I didn't dig deeper into why blog_info doesn't take effect. Next is the code section.
Click to expand full code
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#include "fpm383c.h"
#include <blog.h>
int is_register = 1;
int main(void) {
printf("\r\n");
// 初始化FPM383C指纹模块
fpm383c_init();
printf("clear \r\n");
// 清空指纹库
fpm383c_empty(2000);
printf("random id\r\n");
// 指纹id
int fpm383cPageId = 10;
while (1) {
if (is_register) {
// 开启注册指纹,指纹ID:0—59, 超时时间尽量在 10秒左右,需要录入四次
fpm383c_enroll(fpm383cPageId, 10000);
// 休息600毫秒进行下次注册
vTaskDelay(pdMS_TO_TICKS(600));
// 模块休眠一下
fpm383c_sleep();
is_register = 0;
printf("register OK\r\n");
} else {
// printf("identify\r\n");
// 开启自动识别
fpm383c_identify();
}
vTaskDelay(pdMS_TO_TICKS(10));
}
return 0;
}Note here that the int main(void) function has a return value, and its return type is int. This function runs in a FreeRTOS task; you can check out this analysis post:
wb2项目启动流程分析 fpm383c.h
Click to expand full code
#ifndef __FPM383C_H
#define __FPM383C_H
#include "stdint.h"
void fpm383c_init(void);
void fpm383c_senddata(int length, uint8_t buffer[]);
void fpm383c_sleep(void);
uint8_t fpm383c_getimage(uint32_t timeout);
uint8_t fpm383c_genchar(uint32_t timeout);
uint8_t fpm383c_search(uint32_t timeout);
uint8_t fpm383c_empty(uint32_t timeout);
uint8_t fpm383c_delete(uint16_t pageID,uint32_t timeout);
uint8_t fpm383c_controlled(uint8_t PS_ControlLEDBuf[],uint32_t timeout);
void fpm383c_identify(void);
void fpm383c_enroll(uint16_t pageID,uint16_t timeout);
#endiffpm383c.c
Click to expand full code
#include "stdlib.h"
#include "string.h"
#include "bl_sys.h"
#include <stdio.h>
#include <cli.h>
#include <hosal_uart.h>
#include <blog.h>
#include <hosal_uart.h>
#include <stdint.h>
#include <FreeRTOS.h>
#include <bl_gpio.h>
#include <blog.h>
#include "fpm383c.h"
#define GPIO_LED_PIN 14
/**
* 控制模块LED灯颜色
*/
uint8_t ps_blueledbuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x03,0x01,0x01,0x00,0x00,0x49};
uint8_t ps_redledbuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x02,0x04,0x04,0x02,0x00,0x50};
uint8_t ps_greenledbuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x02,0x02,0x02,0x02,0x00,0x4C};
/**
* 休眠指令-设置传感器进入休眠模式
*/
uint8_t ps_sleepbuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x33,0x00,0x37};
/**
* 清空指纹库-删除 flash 数据库中所有指纹模板。
*/
uint8_t ps_emptybuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x0D,0x00,0x11};
/**
* 取消指令-取消自动注册模板和自动验证指纹。如表 2-1 中加密等级设置为 0 或 1 情况下支持此功能
*/
uint8_t ps_cancelbuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x30,0x00,0x34};
/**
* 自动注册模板-一站式注册指纹,包含采集指纹、生成特征、组合模板、存储模板等功能。加密等级设置为 0 或 1 情况下支持此功能。
*/
uint8_t ps_autoenrollbuf[17] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x31,'\0','\0',0x04,0x00,0x16,'\0','\0'};
/**
* 验证用获取图像-验证指纹时,探测手指,探测到后录入指纹图像存于图像缓冲区。返回确认码表示:录入成功、无手指等。
*/
uint8_t ps_getimagebuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x01,0x00,0x05};
/**
* 生成特征值-将图像缓冲区中的原始图像生成指纹特征文件存于模板缓冲区
*/
uint8_t ps_getcharbuf[13] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x04,0x02,0x01,0x00,0x08};
/**
* 搜索指纹-以模板缓冲区中的特征文件搜索整个或部分指纹库。若搜索到,则返回页码。加密等级设置为 0 或 1 情况下支持
*/
uint8_t ps_searchbuf[17] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x04,0x01,0x00,0x00,0xFF,0xFF,0x02,0x0C};
/**
* 删除模板-删除 flash 数据库中指定 ID 号开始的N 个指纹模板
*/
uint8_t ps_deletebuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x0C,'\0','\0',0x00,0x01,'\0','\0'};
/**
* 设置名为fpm383c_uart的外设句柄,用来执行串口指令的发送
*/
hosal_uart_dev_t fpm383c_uart = {
.config = {
.uart_id = 1,
.tx_pin = 4, // TXD GPIO
.rx_pin = 3, // RXD GPIO
.baud_rate = 57600,
.data_width = HOSAL_DATA_WIDTH_8BIT,
.parity = HOSAL_NO_PARITY,
.stop_bits = HOSAL_STOP_BITS_1,
.mode = HOSAL_UART_MODE_POLL,
},
};
/**
* 指纹ID和验证指纹的分数
*/
uint16_t pageID,score;
/**
* USART串口接收缓冲数组
*/
uint8_t uart_receivebuf[20];
/**
* 主循环状态标志位
*/
uint8_t scanstatus = 0;
/**
* @brief 获取状态
*
* @param timeout
*/
void fpm383c_receivedata(uint16_t timeout, uint8_t lenght)
{
// 轮询fpm383c_uart接收到的字符
int ret;
uint8_t uart_receive_len = 0;
uint8_t receivebuf_cache[20];
memset(uart_receivebuf, 0xFF, sizeof(uart_receivebuf));
while(true){
ret = hosal_uart_receive(&fpm383c_uart, receivebuf_cache, sizeof(receivebuf_cache));
if (ret > 0) {
memcpy(uart_receivebuf + uart_receive_len, receivebuf_cache, ret);
uart_receive_len += ret;
/* Uart send poll */
if(uart_receive_len >= lenght){
uart_receive_len=0;
break;
}
vTaskDelay(1);
}
/**
* 初始化FPM383C指纹模块
*/
void fpm383c_init(){
/* Uart init device */
hosal_uart_init(&fpm383c_uart);
printf("fpm383c_init\r\n");
}
/**
* USART串口发送数据
* @param length 发送数组长度
* @param fpm383c_databuf 需要发送的功能数组
*/
void fpm383c_senddata(int length, uint8_t fpm383c_databuf[])
{
hosal_uart_send(&fpm383c_uart, fpm383c_databuf, length);
}
/**
* 发送休眠指令 确认码=00H 表示休眠设置成功。确认码=01H 表示休眠设置失败。
*/
void fpm383c_sleep(void)
{
fpm383c_senddata(12, ps_sleepbuf);
}
/**
* 验证用获取图像
* @param timeout接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_getimage(uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(12,ps_getimagebuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 将图像缓冲区中的原始图像生成指纹特征文件存于模板缓冲区
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_genchar(uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(13,ps_getcharbuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 发送搜索指纹指令
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_search(uint32_t timeout)
{
fpm383c_senddata(17,ps_searchbuf);
fpm383c_receivedata(timeout, 16);
return (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
}
/**
* 删除指定指纹指令
* @param pageID 需要删除的指纹
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_delete(uint16_t pageID,uint32_t timeout)
{
uint8_t tmp;
ps_deletebuf[10] = (pageID>>8);
ps_deletebuf[11] = (pageID);
ps_deletebuf[14] = (0x15+ps_deletebuf[10]+ps_deletebuf[11])>>8;
ps_deletebuf[15] = (0x15+ps_deletebuf[10]+ps_deletebuf[11]);
fpm383c_senddata(16,ps_deletebuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 清空指纹库
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_empty(uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(12,ps_emptybuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 发送控制灯光指令
* @param ps_controlledbuf 不同颜色的协议数据
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_controlled(uint8_t ps_controlledbuf[],uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(16,ps_controlledbuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 验证指纹是否注册
*/
void fpm383c_identify(void)
{
if(fpm383c_getimage(2000) == 0x00)
{
if(fpm383c_genchar(2000) == 0x00)
{
bl_gpio_enable_output(GPIO_LED_PIN, 0, 0);
bl_gpio_output_set(GPIO_LED_PIN, 0);
if(fpm383c_search(2000) == 0x00)
{
score = (int)((uart_receivebuf[10] << 8) + uart_receivebuf[11]);
printf("success ID: %d \r\n",(int)score);
fpm383c_controlled(ps_greenledbuf,1000);
bl_gpio_output_set(GPIO_LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
bl_gpio_output_set(GPIO_LED_PIN, 0);
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return;
}else{
printf("fail \r\n");
bl_gpio_output_set(GPIO_LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
bl_gpio_output_set(GPIO_LED_PIN, 0);
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return;
}
/**
* 自动注册
* @param pageID 输入需要注册的指纹ID号,取值范围0—59
* @param timeout 设置注册指纹超时时间,因为需要按压四次手指,建议大于10000(即10s)
*/
void fpm383c_enroll(uint16_t pageID,uint16_t timeout)
{
blog_info("注册指纹ID: %d\r\n", pageID);
ps_autoenrollbuf[10] = (pageID>>8);
ps_autoenrollbuf[11] = (pageID);
ps_autoenrollbuf[15] = (0x54+ps_autoenrollbuf[10]+ps_autoenrollbuf[11])>>8;
ps_autoenrollbuf[16] = (0x54+ps_autoenrollbuf[10]+ps_autoenrollbuf[11]);
fpm383c_senddata(17,ps_autoenrollbuf);
fpm383c_receivedata(timeout, 14);
if(uart_receivebuf[9] == 0x00)
{
blog_info("指纹注册完成\r\n");
// 亮绿灯2秒
fpm383c_controlled(ps_greenledbuf,2000);
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return;
}
else if(timeout == 0)
{
// 超时取消注册
fpm383c_senddata(12,ps_cancelbuf);
vTaskDelay(pdMS_TO_TICKS(50));
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
}
// 亮红灯2秒
fpm383c_controlled(ps_redledbuf,2000);
}The above is the main logic code.
Wiring: 
| Ai-WB2-12F | FPM383C |
|---|---|
| 3V3 | PIN3 |
| GND | PIN6 |
| IO3 | TX |
| IO4 | RX |
| Because IO3 is multiplexed, the blue LED will light up. | |
| For other Ai-WB2 related knowledge, you can check out this article compiled by the garden director (园长): | |
| [Ai-WB2 Tutorial Collection] One article is enough! |
)
The FPM383C fingerprint module has been introduced in detail in a previous post; you can take a look at this post:
【外设移植】FPM383C指纹模块+Ai-M61-32S
)
This post mainly covers some problems encountered when porting the FPM383C fingerprint module to the Ai-WB2, as well as a completed porting example.
First, set up the environment.
【Ai-WB2入门篇】搭建windows+eclipse环境
Following the tutorial from the garden director (园长), the environment setup basically goes smoothly and everything can run normally.
If it doesn't run, check where your setup differs from the tutorial.
There is a small pitfall to note here.
Use the clone command to clone the project rather than downloading the zip directly.
git clone --recursive <https://gitee.com/Ai-Thinker-Open/Ai-Thinker-WB2>
If you are not familiar with the configuration files, try to create your project following the existing directory structure so that you minimize changes to the configuration files.
如果在其他目录下创建项目,需要注意的是BL60X_SDK_PATH这个值要能找到sdkpath

There are some warnings during compilation; you can ignore them if they are not from your own code.
If compilation passes, a bin file will be generated.

The next step is the flashing process.
Ai-WB2烧录软件
[<https://docs.ai-thinker.com/>_media/bl602_flash_download_tool.zip](https://docs.ai-thinker.com/_media/bl602_flash_download_tool.zip)
For the flashing process, refer to:
【Ai-WB2入门篇】新建工程和烧录指导
Here is another pitfall to note.

1、如果出现COM口拒绝访问,检查下时候开启的串口调试工具。
2、如果提示BFLB IMG LOAD SHAKEHAND FAIL应该是没有进入烧录模式,点击Create & Download 然后按住开发板右侧的按钮,再点按左侧EN按钮松手就可以了。
There is a big pitfall to note here

When flashing the compiled bin, you need to use this flashing method; otherwise the program will not run properly after flashing.
序号1、3、4选择的都是烧录工具目录下的文件
序号 2 选择编译的bin即可
After flashing, open the serial debugging tool.
Set the baud rate to 115200.

Here the log is printed using the printf method; I didn't dig deeper into why blog_info doesn't take effect.
Next is the code section.
Click to expand full code
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <bl_gpio.h>
#include "fpm383c.h"
#include <blog.h>
int is_register = 1;
int main(void) {
printf("\r\n");
// 初始化FPM383C指纹模块
fpm383c_init();
printf("clear \r\n");
// 清空指纹库
fpm383c_empty(2000);
printf("random id\r\n");
// 指纹id
int fpm383cPageId = 10;
while (1) {
if (is_register) {
// 开启注册指纹,指纹ID:0—59, 超时时间尽量在 10秒左右,需要录入四次
fpm383c_enroll(fpm383cPageId, 10000);
// 休息600毫秒进行下次注册
vTaskDelay(pdMS_TO_TICKS(600));
// 模块休眠一下
fpm383c_sleep();
is_register = 0;
printf("register OK\r\n");
} else {
// printf("identify\r\n");
// 开启自动识别
fpm383c_identify();
}
vTaskDelay(pdMS_TO_TICKS(10));
}
return 0;
}Note here that the int main(void) function has a return value, and its return type is int.
This function runs in a FreeRTOS task; you can check out this analysis post:
wb2项目启动流程分析
fpm383c.h
Click to expand full code
#ifndef __FPM383C_H
#define __FPM383C_H
#include "stdint.h"
void fpm383c_init(void);
void fpm383c_senddata(int length, uint8_t buffer[]);
void fpm383c_sleep(void);
uint8_t fpm383c_getimage(uint32_t timeout);
uint8_t fpm383c_genchar(uint32_t timeout);
uint8_t fpm383c_search(uint32_t timeout);
uint8_t fpm383c_empty(uint32_t timeout);
uint8_t fpm383c_delete(uint16_t pageID,uint32_t timeout);
uint8_t fpm383c_controlled(uint8_t PS_ControlLEDBuf[],uint32_t timeout);
void fpm383c_identify(void);
void fpm383c_enroll(uint16_t pageID,uint16_t timeout);
#endiffpm383c.c
Click to expand full code
#include "stdlib.h"
#include "string.h"
#include "bl_sys.h"
#include <stdio.h>
#include <cli.h>
#include <hosal_uart.h>
#include <blog.h>
#include <hosal_uart.h>
#include <stdint.h>
#include <FreeRTOS.h>
#include <bl_gpio.h>
#include <blog.h>
#include "fpm383c.h"
#define GPIO_LED_PIN 14
/**
* 控制模块LED灯颜色
*/
uint8_t ps_blueledbuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x03,0x01,0x01,0x00,0x00,0x49};
uint8_t ps_redledbuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x02,0x04,0x04,0x02,0x00,0x50};
uint8_t ps_greenledbuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x02,0x02,0x02,0x02,0x00,0x4C};
/**
* 休眠指令-设置传感器进入休眠模式
*/
uint8_t ps_sleepbuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x33,0x00,0x37};
/**
* 清空指纹库-删除 flash 数据库中所有指纹模板。
*/
uint8_t ps_emptybuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x0D,0x00,0x11};
/**
* 取消指令-取消自动注册模板和自动验证指纹。如表 2-1 中加密等级设置为 0 或 1 情况下支持此功能
*/
uint8_t ps_cancelbuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x30,0x00,0x34};
/**
* 自动注册模板-一站式注册指纹,包含采集指纹、生成特征、组合模板、存储模板等功能。加密等级设置为 0 或 1 情况下支持此功能。
*/
uint8_t ps_autoenrollbuf[17] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x31,'\0','\0',0x04,0x00,0x16,'\0','\0'};
/**
* 验证用获取图像-验证指纹时,探测手指,探测到后录入指纹图像存于图像缓冲区。返回确认码表示:录入成功、无手指等。
*/
uint8_t ps_getimagebuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x01,0x00,0x05};
/**
* 生成特征值-将图像缓冲区中的原始图像生成指纹特征文件存于模板缓冲区
*/
uint8_t ps_getcharbuf[13] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x04,0x02,0x01,0x00,0x08};
/**
* 搜索指纹-以模板缓冲区中的特征文件搜索整个或部分指纹库。若搜索到,则返回页码。加密等级设置为 0 或 1 情况下支持
*/
uint8_t ps_searchbuf[17] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x04,0x01,0x00,0x00,0xFF,0xFF,0x02,0x0C};
/**
* 删除模板-删除 flash 数据库中指定 ID 号开始的N 个指纹模板
*/
uint8_t ps_deletebuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x0C,'\0','\0',0x00,0x01,'\0','\0'};
/**
* 设置名为fpm383c_uart的外设句柄,用来执行串口指令的发送
*/
hosal_uart_dev_t fpm383c_uart = {
.config = {
.uart_id = 1,
.tx_pin = 4, // TXD GPIO
.rx_pin = 3, // RXD GPIO
.baud_rate = 57600,
.data_width = HOSAL_DATA_WIDTH_8BIT,
.parity = HOSAL_NO_PARITY,
.stop_bits = HOSAL_STOP_BITS_1,
.mode = HOSAL_UART_MODE_POLL,
},
};
/**
* 指纹ID和验证指纹的分数
*/
uint16_t pageID,score;
/**
* USART串口接收缓冲数组
*/
uint8_t uart_receivebuf[20];
/**
* 主循环状态标志位
*/
uint8_t scanstatus = 0;
/**
* @brief 获取状态
*
* @param timeout
*/
void fpm383c_receivedata(uint16_t timeout, uint8_t lenght)
{
// 轮询fpm383c_uart接收到的字符
int ret;
uint8_t uart_receive_len = 0;
uint8_t receivebuf_cache[20];
memset(uart_receivebuf, 0xFF, sizeof(uart_receivebuf));
while(true){
ret = hosal_uart_receive(&fpm383c_uart, receivebuf_cache, sizeof(receivebuf_cache));
if (ret > 0) {
memcpy(uart_receivebuf + uart_receive_len, receivebuf_cache, ret);
uart_receive_len += ret;
/* Uart send poll */
if(uart_receive_len >= lenght){
uart_receive_len=0;
break;
}
}
vTaskDelay(1);
}
}
/**
* 初始化FPM383C指纹模块
*/
void fpm383c_init(){
/* Uart init device */
hosal_uart_init(&fpm383c_uart);
printf("fpm383c_init\r\n");
}
/**
* USART串口发送数据
* @param length 发送数组长度
* @param fpm383c_databuf 需要发送的功能数组
*/
void fpm383c_senddata(int length, uint8_t fpm383c_databuf[])
{
hosal_uart_send(&fpm383c_uart, fpm383c_databuf, length);
}
/**
* 发送休眠指令 确认码=00H 表示休眠设置成功。确认码=01H 表示休眠设置失败。
*/
void fpm383c_sleep(void)
{
fpm383c_senddata(12, ps_sleepbuf);
}
/**
* 验证用获取图像
* @param timeout接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_getimage(uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(12,ps_getimagebuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 将图像缓冲区中的原始图像生成指纹特征文件存于模板缓冲区
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_genchar(uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(13,ps_getcharbuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 发送搜索指纹指令
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_search(uint32_t timeout)
{
fpm383c_senddata(17,ps_searchbuf);
fpm383c_receivedata(timeout, 16);
return (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
}
/**
* 删除指定指纹指令
* @param pageID 需要删除的指纹
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_delete(uint16_t pageID,uint32_t timeout)
{
uint8_t tmp;
ps_deletebuf[10] = (pageID>>8);
ps_deletebuf[11] = (pageID);
ps_deletebuf[14] = (0x15+ps_deletebuf[10]+ps_deletebuf[11])>>8;
ps_deletebuf[15] = (0x15+ps_deletebuf[10]+ps_deletebuf[11]);
fpm383c_senddata(16,ps_deletebuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 清空指纹库
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_empty(uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(12,ps_emptybuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 发送控制灯光指令
* @param ps_controlledbuf 不同颜色的协议数据
* @param timeout 接收数据的超时时间
* @return 确认码
*/
uint8_t fpm383c_controlled(uint8_t ps_controlledbuf[],uint32_t timeout)
{
uint8_t tmp;
fpm383c_senddata(16,ps_controlledbuf);
fpm383c_receivedata(timeout, 12);
tmp = (uart_receivebuf[6] == 0x07 ? uart_receivebuf[9] : 0xFF);
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return tmp;
}
/**
* 验证指纹是否注册
*/
void fpm383c_identify(void)
{
if(fpm383c_getimage(2000) == 0x00)
{
if(fpm383c_genchar(2000) == 0x00)
{
bl_gpio_enable_output(GPIO_LED_PIN, 0, 0);
bl_gpio_output_set(GPIO_LED_PIN, 0);
if(fpm383c_search(2000) == 0x00)
{
score = (int)((uart_receivebuf[10] << 8) + uart_receivebuf[11]);
printf("success ID: %d \r\n",(int)score);
fpm383c_controlled(ps_greenledbuf,1000);
bl_gpio_output_set(GPIO_LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
bl_gpio_output_set(GPIO_LED_PIN, 0);
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return;
}else{
printf("fail \r\n");
bl_gpio_output_set(GPIO_LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
bl_gpio_output_set(GPIO_LED_PIN, 0);
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return;
}
}
}
}
/**
* 自动注册
* @param pageID 输入需要注册的指纹ID号,取值范围0—59
* @param timeout 设置注册指纹超时时间,因为需要按压四次手指,建议大于10000(即10s)
*/
void fpm383c_enroll(uint16_t pageID,uint16_t timeout)
{
blog_info("注册指纹ID: %d\r\n", pageID);
ps_autoenrollbuf[10] = (pageID>>8);
ps_autoenrollbuf[11] = (pageID);
ps_autoenrollbuf[15] = (0x54+ps_autoenrollbuf[10]+ps_autoenrollbuf[11])>>8;
ps_autoenrollbuf[16] = (0x54+ps_autoenrollbuf[10]+ps_autoenrollbuf[11]);
fpm383c_senddata(17,ps_autoenrollbuf);
fpm383c_receivedata(timeout, 14);
if(uart_receivebuf[9] == 0x00)
{
blog_info("指纹注册完成\r\n");
// 亮绿灯2秒
fpm383c_controlled(ps_greenledbuf,2000);
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
return;
}
else if(timeout == 0)
{
// 超时取消注册
fpm383c_senddata(12,ps_cancelbuf);
vTaskDelay(pdMS_TO_TICKS(50));
// 重置接收数据缓存
memset(uart_receivebuf,0xFF,sizeof(uart_receivebuf));
}
// 亮红灯2秒
fpm383c_controlled(ps_redledbuf,2000);
}The above is the main logic code.

Wiring:

| Ai-WB2-12F | FPM383C |
|---|---|
| 3V3 | PIN3 |
| GND | PIN6 |
| IO3 | TX |
| IO4 | RX |
Because IO3 is multiplexed, the blue LED will light up.
For other Ai-WB2 related knowledge, you can check out this article compiled by the garden director (园长):
[Ai-WB2 Tutorial Collection] One article is enough!

