本帖最后由 A571157242 于 2022-5-6 21:45 编辑
基于STM32F103C8T6的指纹门锁(超简单)注:本页面全部复制于本人所做的Markdown笔记,因此在该网页中显示排班会比较乱,建议下载附件下面的PDF文件进行查看。另一附件为最终的程序源码。
实验目的:用指纹模块来达到开门的目的。 实验条件: STM32板子一块指纹模块一个(ATK_301) 舵机一个(SG90) 杜邦线若干
实验环境:
因为在外租房打工,宿舍里没人,有时候会老是忘记带钥匙,叫房东又慢有麻烦,所以利用手头上的东西做个指纹解锁来开门。 这次实验用的板子的芯片是STM32F103C8T6,当然并不是说只能用这个芯片,你有其他的任何的芯片都可以,哪怕是51单片机。这次使用STM32CuBeMX直接生成工程模板,然后在工程模板上编写指纹的驱动代码。如果不想用CuBeMX也是完全可以的,自己找个自己板子的工程模板,然后再据此工程编写功能代码即可。 目录一、建立工程模板 二、编写功能代码 三、实验效果 一.建立工程模板新建项目 选择芯片 选择高速外部时钟 开启SWD下载接口 开启串口和串口中断,修改串口波特率。 开启TIM1的通道1的PWM 因为我的32板子上有一个按键,后面我们要用到按键,这个按键接的就是PA0,这里我配置PA0为外部中断,并且按键另一端接的是GND,因此这里配置引脚上拉,下降沿触发中断。 板子上有一个LED,后面用来当提示使用,接在PC13上,因此配置为输出,命名为LED。 配置时钟树,将系统主频配置为72M。 修改工程信息,最后生成工程。
二、编写功能代码这里我直接给出需要添加修改的代码,根据步骤添加到自己的工程里面即可。 本来打算写一下如何编写这些代码,以及思路和原理的,后来发现写注释已经表述的差不多了,就不再写代码讲解了。可以根据注释来理解程序代码。 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输出
​t++;
if(t>=200)
{
t=0;
searchTouch(); //搜索指纹
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); //LED翻转,用来观察程序是否正常运行。
}touch.c: #include "touch.h"
/*—————————————————指纹处理部分代码—————————————————————————*/
​
uint16_t Head=0xEF01; //指纹模块包头
uint32_t ICaddress=0xFFFFFFFF; //指纹模块芯片地址
uint8_t PackID; //指令包标识
uint16_t PackLen; //指令包长度
uint8_t CmdID; //指令码
uint16_t Checksum; //校验和
​
/**
函数功能:搜索指纹是否正确,正确则开门
*/
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();//取消指令 会导致指纹锁的灯灭掉
}
​
/**
函数功能:创建指纹,无需输入指纹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();//取消指令 会导致指纹锁的灯灭掉
}
​
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;
}
​
/**
函数功能:计算校验和
参数解释:
data:要计算的数据
len:数据字节长度
*/
void checksum(uint8_t *data,uint8_t len)
{
uint16_t i;
for (i = 0; i < len; i++)
Checksum += *data++;
}
​
/**
函数功能:指纹注册
参数解释:
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))); //发送校验和
}
​
/**
函数功能:自动验证指纹
*/
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))); //发送校验和
​
}
​
/**
函数功能:清空指纹库
*/
void PS_Empty()
{
​
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))); //计算校验和
​
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))); //发送校验和
​
}
/**
函数功能:取消正在运行的指令
*/
void PS_Cancel()
{
​
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))); //发送校验和
​
}
/**
函数功能:读取指纹模块的索引表,获取已录入指纹的ID号
*/
void PS_ReadIndexTable()
{
​
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))); //发送校验和
​
}
​
​
/*—————————————————串口部分代码—————————————————————————*/
​
uint8_t RxBuffer[1]; //HAL库串口接收中断函数缓冲区
​
​
uint16_t volatile USART_RX_STA=0; //接收状态标记,用以表示串口接收长度
​
uint8_t USART_RX_BUF[1024]; //接收缓冲,最大1024个字节.
​
/**
函数功能:串口接受中断回调函数
参数解释:
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); //触发中断后会自动关闭中断,因此在退出接收中断回调函数的时候要重新开启接收中断
}
​
/**
函数功能:串口发送函数。反序发送
正常的串口发送函数都是正序发送的,先发低位,再发高位。例如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--; //发送完后指针移置低一位
}
}
​
/*—————————————————电机控制部分代码—————————————————————————*/
​
/**
函数功能:控制电机转动角度
参数解释:
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;
}
}
​
/**
函数功能:电机转动开门
*/
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输出
}
​
​
/**
函数功能:电机转动关门
*/
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输出
}
/*—————————————————按键部分代码—————————————————————————*/
​
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) //按键的外部中断回调函数
{
​
HAL_Delay(10); //延时10ms,重新判断是否按下,消除抖动
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0)
createTouch();
}touch.h: #ifndef _TOUCH_H_
#define _TOUCH_H_
​
#include "usart.h"
#include "tim.h"
​
extern uint8_t RxBuffer[1]; //HAL库串口接收中断函数缓冲区
extern volatile uint16_t USART_RX_STA; //接收状态标记
extern uint8_t USART_RX_BUF[1024]; //接收缓冲,最大USART_REC_LEN个字节.
​
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);
​
#endif
​
​
三、实验效果接线: 开发板 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度关门。
|