OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5796|回复: 3

基于STM32F103C8T6的指纹门锁(超简单)

[复制链接]

4

主题

881

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4199
金钱
4199
注册时间
2019-9-4
在线时间
881 小时
发表于 2022-5-6 21:43:07 | 显示全部楼层 |阅读模式
本帖最后由 A571157242 于 2022-5-6 21:45 编辑

基于STM32F103C8T6的指纹门锁(超简单)
注:本页面全部复制于本人所做的Markdown笔记,因此在该网页中显示排班会比较乱,建议下载附件下面的PDF文件进行查看。另一附件为最终的程序源码。
实验目的:用指纹模块来达到开门的目的。
实验条件
  • STM32板子一块指纹模块一个(ATK_301)
  • 舵机一个(SG90)
  • 杜邦线若干

实验环境
  • MDK5
  • STM32CuBeMX(非必须)



因为在外租房打工,宿舍里没人,有时候会老是忘记带钥匙,叫房东又慢有麻烦,所以利用手头上的东西做个指纹解锁来开门。
这次实验用的板子的芯片是STM32F103C8T6,当然并不是说只能用这个芯片,你有其他的任何的芯片都可以,哪怕是51单片机。这次使用STM32CuBeMX直接生成工程模板,然后在工程模板上编写指纹的驱动代码。如果不想用CuBeMX也是完全可以的,自己找个自己板子的工程模板,然后再据此工程编写功能代码即可。
目录
一、建立工程模板
二、编写功能代码
三、实验效果
一.建立工程模板
  • 新建项目
  • 选择芯片
  • 选择高速外部时钟
  • 开启SWD下载接口
  • 开启串口和串口中断,修改串口波特率。
  • 开启TIM1的通道1的PWM
  • 因为我的32板子上有一个按键,后面我们要用到按键,这个按键接的就是PA0,这里我配置PA0为外部中断,并且按键另一端接的是GND,因此这里配置引脚上拉,下降沿触发中断。
  • 板子上有一个LED,后面用来当提示使用,接在PC13上,因此配置为输出,命名为LED。
  • 配置时钟树,将系统主频配置为72M。
  • 修改工程信息,最后生成工程。




二、编写功能代码
这里我直接给出需要添加修改的代码,根据步骤添加到自己的工程里面即可。
本来打算写一下如何编写这些代码,以及思路和原理的,后来发现写注释已经表述的差不多了,就不再写代码讲解了。可以根据注释来理解程序代码。
  • 在main.c里,main主函数下的/* USER CODE BEGIN 2 */和/* USER CODE END 2 */之间添加以下代码:

HAL_NVIC_DisableIRQ(EXTI0_IRQn);    //关闭按键外部中断
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0)
{
    HAL_Delay(2000);
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0) //判断按键长按
    {
        PS_Empty();                      //清空指纹库
        for(uint8_t i=0;i<20;i++)        //LED翻转2秒用以提示
        {
            HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
            HAL_Delay(100);
        }
    }
}
HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //开启按键外部中断
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);    //开启PWM输出
HAL_UART_Receive_IT(&huart1,RxBuffer,1);    //开启串口
CloseTheDoor();                             //关门
HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);    //关闭PWM输出
&#8203;
  • 在main.c里,main主函数下的while循环里添加以下代码:

t++;
if(t>=200)
{
    t=0;
    searchTouch();          //搜索指纹
    HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);  //LED翻转,用来观察程序是否正常运行。
}
  • 新建一个touch.c和touch.h并添加进工程文件里面。并将以下代码复制进文件里保存并且编译后把程序下载进板子里即可。

touch.c:
#include "touch.h"
/*—————————————————指纹处理部分代码—————————————————————————*/
&#8203;
uint16_t Head=0xEF01; //指纹模块包头
uint32_t ICaddress=0xFFFFFFFF; //指纹模块芯片地址
uint8_t PackID; //指令包标识
uint16_t PackLen; //指令包长度
uint8_t CmdID; //指令码
uint16_t Checksum; //校验和
&#8203;
/**
函数功能:搜索指纹是否正确,正确则开门
*/
void searchTouch()
{
    USART_RX_STA=0;//清空串口接收缓存标记
    PS_AutoIdentify();//发送搜索指纹指令
    USART_RX_STA=0;//清空串口接收缓存标记
    HAL_Delay(2000);//等待两秒
    if(USART_RX_STA==17&&USART_RX_BUF[9]==0x00&&USART_RX_BUF[10]==0x05) //成功应答包为17个字节,指令错误为12个字节
    {                                                                  //USART_RX_BUF 第9字节:确认码  第10字节:参数  第11~12字节:ID号
        OpenTheDoor();//开门
        HAL_Delay(2000);//等待两秒
        CloseTheDoor();//关门
    }
    PS_Cancel();//取消指令  会导致指纹锁的灯灭掉
}
&#8203;
/**
函数功能:创建指纹,无需输入指纹ID
*/
void createTouch()
{
    PS_Cancel();//取消指令  会导致指纹锁的灯灭掉
    HAL_Delay(100);
    uint16_t id=GetEmptyTouchID();  //获取没有空白的指纹ID
    if(id!=0XFFFF)
    {
        USART_RX_STA=0;
        PS_AutoEnroll(id);
        HAL_Delay(2000);//延迟两秒,等待指纹录入并且返回应答包。
        if(USART_RX_STA==14&&USART_RX_BUF[10]==0X06&&USART_RX_BUF[11]==0XF2)//成功录入指纹
        {
            for(uint8_t i=0;i<20;i++)           //LED翻转2秒用以提示
            {
                HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
                HAL_Delay(100);
            }
        }
    }
    PS_Cancel();//取消指令  会导致指纹锁的灯灭掉
}
&#8203;
uint16_t GetEmptyTouchID()
{
    uint8_t *p=&USART_RX_BUF[10];   //串口接收的数据包中,第10个数据包开始为索引表信息,10~42总共32个字节的索引表信息。
    uint16_t id=0XFFFF;
    USART_RX_STA=0;//清空串口接收缓存标记
   
    PS_ReadIndexTable();    //读取指纹模块索引表
    HAL_Delay(200);
    if(USART_RX_STA==44)         //成功应答包为44个字节
    {
        for(uint8_t i=0;i<32;i++)
        {
            if(*p++<255)        //32个字节,一个字节8bit,每bit代表一个指纹ID,0为空,1为已存储ID,如果8个字节都已存储则为255
            {
                --p;
                for(uint8_t j=0;j<8;j++)
                {
                    if(((*p)&(1<<j))==0)
                    {
                        id=j+i*8;
                        return id;
                    }
                }      
            }
        }
    }
    return id;
}
&#8203;
/**
函数功能:计算校验和
参数解释:
    data:要计算的数据
    len:数据字节长度
*/
void checksum(uint8_t *data,uint8_t len)
{
    uint16_t i;
    for (i = 0; i < len; i++)
        Checksum += *data++;
}
&#8203;
/**
函数功能:指纹注册
参数解释:
    id:指纹注册的ID
*/
void PS_AutoEnroll(uint16_t id)
{
    uint8_t InputNum = 0x04;                   //指纹录入次数
    uint16_t Parameter= 0x002F;               //指纹录入参数  
                                               /*
                                               1) bit0: 0-LED 长亮, 1-LED 获取图像成功后灭<只适用于光学指纹模组,半导体指纹模组默认 0>。
                                               2) bit1: 0-采图图像不预处理, 1-采图图像预处理;
                                               3) bit2 表示录入过程中是否要求模组在关键步骤返回当前状态: 0-要求返回, 1-不要求返回。
                                               4) bit3 表示新的指纹模板是否覆盖: 1-覆盖, 0-不覆盖;
                                               5) bit4 表示检测新的指纹模板是否已经存在: 1-检测, 0-不检测;
                                               6) bit5 表示录入过程中,是否要求手指离开才进入下一次指纹图像采集:0-要求离开,1-不要求离开。
                                               7) bit6~bit15:预留。
                                               */
    PackID=0x01;    //包标识
    PackLen=0x08;   //包长度
    CmdID=0x31;     //指令码
   
    Checksum=0;                                 //校验和清零
    checksum((uint8_t*)&PackID,sizeof((PackID)));                 //计算校验和
    checksum((uint8_t*)&PackLen,sizeof((PackLen)));               //计算校验和
    checksum((uint8_t*)&CmdID,sizeof((CmdID)));                   //计算校验和
    checksum((uint8_t*)&id,sizeof((id)));                         //计算校验和
    checksum((uint8_t*)&InputNum,sizeof((InputNum)));             //计算校验和
    checksum((uint8_t*)&Parameter,sizeof((Parameter)));           //计算校验和
   
    USART_SendData((uint8_t*)&Head,sizeof((Head)));               //发送指令
    USART_SendData((uint8_t*)&ICaddress,sizeof((ICaddress)));     //发送指令
    USART_SendData((uint8_t*)&PackID,sizeof((PackID)));           //发送指令
    USART_SendData((uint8_t*)&PackLen,sizeof((PackLen)));         //发送指令
    USART_SendData((uint8_t*)&CmdID,sizeof((CmdID)));             //发送指令
    USART_SendData((uint8_t*)&id,sizeof((id)));                   //发送指令
    USART_SendData((uint8_t*)&InputNum,sizeof((InputNum)));       //发送指令
    USART_SendData((uint8_t*)&Parameter,sizeof((Parameter)));     //发送指令
    USART_SendData((uint8_t*)&Checksum,sizeof((Checksum)));       //发送校验和
}
&#8203;
/**
函数功能:自动验证指纹
*/
void PS_AutoIdentify()
{
    uint8_t SafetyL=0x03;                   //安全等级
    uint16_t ID = 0xFFFF;                   //ID号,ID号为:FFFF时搜索全指纹库,否则为1:1匹配
    uint16_t Parameter= 0x0007;               //指纹验证参数
   
    PackID=0x01;    //包标识
    PackLen=0x08;   //包长度
    CmdID=0x32;     //指令码
   
    Checksum=0;  //校验和清零
    checksum((uint8_t*)&PackID,sizeof((PackID)));                 //计算校验和
    checksum((uint8_t*)&PackLen,sizeof((PackLen)));               //计算校验和
    checksum((uint8_t*)&CmdID,sizeof((CmdID)));                   //计算校验和
    checksum((uint8_t*)&SafetyL,sizeof((SafetyL)));               //计算校验和
    checksum((uint8_t*)&ID,sizeof((ID)));                            //计算校验和
    checksum((uint8_t*)&Parameter,sizeof((Parameter)));           //计算校验和
   
    USART_SendData((uint8_t*)&Head,sizeof((Head)));                 //发送指令
    USART_SendData((uint8_t*)&ICaddress,sizeof((ICaddress)));       //发送指令
    USART_SendData((uint8_t*)&PackID,sizeof((PackID)));             //发送指令
    USART_SendData((uint8_t*)&PackLen,sizeof((PackLen)));           //发送指令
    USART_SendData((uint8_t*)&CmdID,sizeof((CmdID)));               //发送指令
    USART_SendData((uint8_t*)&SafetyL,sizeof((SafetyL)));           //发送指令
    USART_SendData((uint8_t*)&ID,sizeof((ID)));                     //发送指令
    USART_SendData((uint8_t*)&Parameter,sizeof((Parameter)));       //发送指令
    USART_SendData((uint8_t*)&Checksum,sizeof((Checksum)));         //发送校验和
&#8203;
}
&#8203;
/**
函数功能:清空指纹库
*/
void PS_Empty()
{
&#8203;
    PackID=0x01;        //包标识
    PackLen=0x03;       //包长度
    CmdID=0x0d;         //指令码
   
    Checksum=0;  //校验和清零
    checksum((uint8_t*)&PackID,sizeof((PackID)));                 //计算校验和
    checksum((uint8_t*)&PackLen,sizeof((PackLen)));               //计算校验和
    checksum((uint8_t*)&CmdID,sizeof((CmdID)));                   //计算校验和
&#8203;
   
    USART_SendData((uint8_t*)&Head,sizeof((Head)));                     //发送指令
    USART_SendData((uint8_t*)&ICaddress,sizeof((ICaddress)));           //发送指令
    USART_SendData((uint8_t*)&PackID,sizeof((PackID)));                 //发送指令
    USART_SendData((uint8_t*)&PackLen,sizeof((PackLen)));               //发送指令
    USART_SendData((uint8_t*)&CmdID,sizeof((CmdID)));                   //发送指令
    USART_SendData((uint8_t*)&Checksum,sizeof((Checksum)));             //发送校验和
&#8203;
}
/**
函数功能:取消正在运行的指令
*/
void PS_Cancel()
{
&#8203;
    PackID=0x01;        //包标识
    PackLen=0x03;       //包长度
    CmdID=0x30;         //指令码
   
    Checksum=0;  //校验和清零
    checksum((uint8_t*)&PackID,sizeof((PackID)));                 //计算校验和
    checksum((uint8_t*)&PackLen,sizeof((PackLen)));               //计算校验和
    checksum((uint8_t*)&CmdID,sizeof((CmdID)));                   //计算校验和
   
    USART_SendData((uint8_t*)&Head,sizeof((Head)));                 //发送指令
    USART_SendData((uint8_t*)&ICaddress,sizeof((ICaddress)));       //发送指令
    USART_SendData((uint8_t*)&PackID,sizeof((PackID)));             //发送指令
    USART_SendData((uint8_t*)&PackLen,sizeof((PackLen)));           //发送指令
    USART_SendData((uint8_t*)&CmdID,sizeof((CmdID)));               //发送指令
    USART_SendData((uint8_t*)&Checksum,sizeof((Checksum)));         //发送校验和
&#8203;
}
/**
函数功能:读取指纹模块的索引表,获取已录入指纹的ID号
*/
void PS_ReadIndexTable()
{
&#8203;
    uint8_t Page=0x00;  //将要搜索的页码,指纹模块页码为0~3,每页可以存储256个指纹
   
    PackID=0x01;            //包标识
    PackLen=0x04;           //包长度
    CmdID=0x1f;             //指令码
   
    Checksum=0;  //校验和清零
    checksum((uint8_t*)&PackID,sizeof((PackID)));                 //计算校验和
    checksum((uint8_t*)&PackLen,sizeof((PackLen)));               //计算校验和
    checksum((uint8_t*)&CmdID,sizeof((CmdID)));                   //计算校验和
    checksum((uint8_t*)&Page,sizeof((Page)));                   //计算校验和
   
    USART_SendData((uint8_t*)&Head,sizeof((Head)));                 //发送指令
    USART_SendData((uint8_t*)&ICaddress,sizeof((ICaddress)));       //发送指令
    USART_SendData((uint8_t*)&PackID,sizeof((PackID)));             //发送指令
    USART_SendData((uint8_t*)&PackLen,sizeof((PackLen)));           //发送指令
    USART_SendData((uint8_t*)&CmdID,sizeof((CmdID)));               //发送指令
    USART_SendData((uint8_t*)&Page,sizeof((Page)));                 //发送指令
    USART_SendData((uint8_t*)&Checksum,sizeof((Checksum)));         //发送校验和
&#8203;
}
&#8203;
&#8203;
/*—————————————————串口部分代码—————————————————————————*/
&#8203;
uint8_t RxBuffer[1]; //HAL库串口接收中断函数缓冲区
&#8203;
&#8203;
uint16_t volatile USART_RX_STA=0;       //接收状态标记,用以表示串口接收长度
&#8203;
uint8_t USART_RX_BUF[1024];     //接收缓冲,最大1024个字节.
&#8203;
/**
函数功能:串口接受中断回调函数
参数解释:
    huart:回调的串口句柄
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance==USART1)//如果是串口1
    {
        USART_RX_BUF[USART_RX_STA]=RxBuffer[0] ;  //将接收到的串口数据存放到USART_RX_BUF串口缓存数组里面
        USART_RX_STA++;                           //串口接收长度加一
    }
    HAL_UART_Receive_IT(&huart1,RxBuffer,1);    //触发中断后会自动关闭中断,因此在退出接收中断回调函数的时候要重新开启接收中断
   
}
&#8203;
/**
函数功能:串口发送函数。反序发送
正常的串口发送函数都是正序发送的,先发低位,再发高位。例如0X1122,用HAL库的串口发送函数就是先发0X22,再发0X11。
而我们给模块发送的数据需要先发送高位再发送低位,因此需要自己编写一个反序的串口发送函数。
参数解释:
    data:发送的数据的指针
    len发送的数据的长度
*/
void USART_SendData(uint8_t *data,uint8_t len)
{
    uint8_t i;
    data+=len-1;    //将指针指向最高位。
    for(i=0;i<len;i++)
    {
        while ((USART1->SR & 0X40) == 0);   //等待串口发送完成标志位
        USART1->DR = *data--;       //发送完后指针移置低一位
    }
}
&#8203;
/*—————————————————电机控制部分代码—————————————————————————*/
&#8203;
/**
函数功能:控制电机转动角度
参数解释:
    angle:配置舵机转动的角度。
*/
void MotorControl(int angle)
{
     switch (angle)
    {
    case 0:
        __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, 50); //SG90舵机转动0度
        break;
    case 45:
        __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, 100);//SG90舵机转动45度
        break;
    case 90:
        __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, 150);//SG90舵机转动90度
        break;
    case 135:
        __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, 200);//SG90舵机转动135度
        break;
    case 180:
        __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, 250);//SG90舵机转动180度
        break;
   
    }
}
&#8203;
/**
函数功能:电机转动开门
*/
void OpenTheDoor()
{
    HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);    //开启PWM输出
    MotorControl(180);  //舵机转动置180度开门
    HAL_Delay(200);
    HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);    //关闭PWM输出
}
&#8203;
&#8203;
/**
函数功能:电机转动关门
*/
void CloseTheDoor()
{
    HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);    //开启PWM输出
    MotorControl(0);    //舵机转动置0度关门
    HAL_Delay(200);
    HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);    //关闭PWM输出
}
/*—————————————————按键部分代码—————————————————————————*/
&#8203;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)  //按键的外部中断回调函数
{
&#8203;
    HAL_Delay(10);                  //延时10ms,重新判断是否按下,消除抖动
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0)
        createTouch();
}
touch.h:
#ifndef _TOUCH_H_
#define _TOUCH_H_
&#8203;
#include "usart.h"
#include "tim.h"
&#8203;
extern uint8_t RxBuffer[1];   //HAL库串口接收中断函数缓冲区
extern  volatile uint16_t USART_RX_STA;       //接收状态标记   
extern uint8_t USART_RX_BUF[1024];     //接收缓冲,最大USART_REC_LEN个字节.
&#8203;
void MotorControl(int angle);
void USART_SendData(uint8_t *data,uint8_t len);
void PS_AutoEnroll(uint16_t id);
void PS_Cancel(void);
void PS_Empty(void);
void PS_AutoIdentify(void);
void checksum(uint8_t *data,uint8_t len);
void PS_VfyPwd(uint32_t Password);
void PS_SetPwd(uint32_t Password);
void PS_ReadIndexTable(void);
void searchTouch(void);
void CloseTheDoor(void);
void OpenTheDoor(void);
uint16_t GetEmptyTouchID(void);
void createTouch(void);
void searchTouch(void);
uint8_t Is_VfyPwd(uint32_t Password);
&#8203;
#endif
&#8203;
&#8203;

三、实验效果
接线
开发板
AKT-301指纹模块
SG90S舵机

PA9
RX

PA10
TX

3.3V
V_TOUCH

3.3V
VCC

GND
GND
GND

PA8
PWM信号线

5V
VCC
功能介绍
  • 清空指纹库:给板子复位时按住按键KEY不松2秒,将会清空指纹库,并且LED闪烁2秒
  • 添加指纹:短按一下KEY将会在两秒内录入指纹,两秒后将取消该操作。指纹录入成功则开关一下门锁。
  • 上电后,手指触摸指纹模块,若匹配指纹库成功,则舵机转动180度开门,2秒后转动回0度关门。



基于STM32F103C8T6的指纹门锁.zip

7.04 MB, 下载次数: 234

基于STM32F103C8T6的指纹识别设计.pdf

1.22 MB, 下载次数: 87

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

81

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
208
金钱
208
注册时间
2014-9-18
在线时间
21 小时
发表于 2022-5-7 10:07:28 | 显示全部楼层
回复 支持 反对

使用道具 举报

14

主题

821

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2035
金钱
2035
注册时间
2021-7-17
在线时间
636 小时
发表于 2022-5-9 10:16:39 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
10
金钱
10
注册时间
2023-3-18
在线时间
3 小时
发表于 2023-3-20 13:26:45 | 显示全部楼层
你好,我想做一个指纹识别的,但是写了你的程序不能用,我想让32直接控制继电器,指纹模块是FPM-383C带灯的,你能帮我写一下吗,有偿,希望你能看到
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-24 14:55

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表