Bootstrap

基于STM32的物联网门禁系统设计

最终效果

门禁射频卡

二维码

门禁指纹

项目介绍

该项目是“物联网实验室监测控制系统设计(仿智能家居)”项目中的“门禁系统设计”子项目,前者还包括“物联网设计”、“环境监测设计”、“家电控制设计”和“小程序设计”等内容。本文只介绍“门禁”部分。

项目功能实现的大致思路为:当单片机接收到MQTT服务器传来的身份识别信息录入指令时,驱动指纹识别、射频卡识别或扫码模块进行相关的信息录入;未收到录入指令时,单片机会自动比对传入的身份信息,并依据比对结果控制门锁的开闭。

硬件设计

接线

STM32

ESP-01S

AS608

XM1605

无源蜂鸣器

RFID-RC522

SYN6288

继电器

USB-TTL

3.3

3V3

Vi(红线)、Vt(绿线)

2

3.3V

VDD

DC+

G

GND

GND(黑线)

3

一个引脚

GND

G

DC-

GND

PA11

RST

PA6

MISO

PA7

MOSI

PA5

SCK

PA4

SDA

PB11

Tx(黄线)

PB10

Rx(白线)

PB1

WAK(蓝线)

PA10

TX

PA9

RX

PA3

5

PA1

10

9

另一个引脚

PA2

RXD

PC15

IN

PCB设计

此电路板仅是为了代替杜邦线而已,上面只有引脚排座,而没有任何电子元件。

未完待续

成本

未完待续

软件设计

本次主控制器STM32的开发环境为Keil5 IDE,使用C语言编写驱动程序。引入FreeRTOS实时操作系统。

工程文件:https://download.csdn.net/download/qq_44955826/90134721?spm=1001.2014.3001.5503

主程序

主程序主要负责执行各外设的初始化,其步骤如下:

①初始化led;

②初始化用于接收从ESP-01S传来的MQTT服务器发送的消息和打印提示信息的串口;

③初始化继电器模块;

④初始化指纹识别模块;

⑤初始化扫码模组;

⑥初始化IC卡感应模块。

在主程序执行完各外设的初始化后,系统会进入FreeRTOS。

main.c
#include "stm32f10x.h"                  // Device header
#include "FreeRTOS_demo.h"
#include "led.h"
#include "Serial.h"
#include "AS608.h"
#include "ScanCode.h"
#include "RC522.h"
#include "lock.h"


int main(void)
{	
	led_Init();
	
	Serial_Init();
	
	lock_Init();
	
	AS608_Init();
	
	ScanCode_Init();
	
	RC522_IO_Init();
	
	printf("这是QWy的“基于物联网的智能实验室监控系统设计”的环境监测部分的STM32代码\r\n\r\n");
	
	freertos_demo();
}

FreeRTOS程序

FreeRTOS中的任务分配如下图表所示:

任务名

任务优先级

堆栈大小

任务功能

start_task

1

128 Word

创建其他任务

task1

13

128 Word

处理串口1接收到的数据

task2

11

128 Word

录入指纹

task3

7

128 Word

识别指纹

task4

10

128 Word

录入二维码

task5

6

128 Word

识别二维码

task6

9

128 Word

录入射频卡

task7

5

128 Word

识别射频卡

task10

3

128 Word

led闪烁

start_task是开始任务函数,用于创建其他所有任务,优先级最低,在创建完其他任务后会删除自身。

task1主要是用于处理串口1从ESP-01S传来的MQTT服务器发送的消息,即处理用户通过小程序下达的身份信息录入指令,优先级最高。串口1的接收为中断响应。中断响应时,FreeRTOS停止工作。task1中的串口1接收标志位保证了只有当串口1接收到的数据被存入全局变量后,串口1才能接收新的数据。其流程如下图所示。

task10的现象为STM32板载led闪烁,优先级最低(在start_task删除自身后)。通过查看此任务的执行情况,可方便调试程序和检查系统工作状态。

所有身份信息录入任务的优先级均高于相应的识别任务,这是因为STM32在接收到身份信息录入指令时,要立刻执行相应的录入任务;而在未接收到身份信息录入指令时,才会执行相应的识别任务。

录入指纹、录入二维码和录入射频卡这三个任务的优先级顺序没有要求,这是因为录入身份信息时会进入临界区,临界区保证了即使有多个身份信息录入指令同时下达,同一时间也只会进行一个身份信息录入。且由于串口1接收标志位的存在,在录入一个身份信息时,最多只有一个身份信息待录入。识别指纹、识别二维码和识别射频卡这三个任务的优先级同理。

由于XM1605和RFID-RC522 IC卡感应模块没有身份信息存储功能,程序中使用char Entered_QR[3][20]和unsigned char Entered_RFID[5][4]这两个数组分别储存录入的二维码内容和射频卡卡号。

录入指纹、录入二维码和录入射频卡这三个任务储存的身份信息均有数量限制,超出数量后再录入的身份信息会按顺序覆盖之前录入的。

为避免接收到身份信息录入指令后,反复执行相应录入任务(因为串口1接收到的数据被存入全局变量后,此全局变量的值在被重新存入前不会被清空),特使用一个二值信号量,用以标记从串口1获得的字符串是否被使用过。

录入身份信息任务执行完后,会将对应的识别任务挂起一段时间,这是为了避免刚录完身份信息后,指纹、二维码和射频卡还未及时从本系统上移开,便立刻进行识别,从而导致门锁打开。

下图是task2录入指纹的流程图,其余两个身份信息录入任务的流程与之类似。

task10的现象为STM32板载led闪烁,优先级最低(在start_task删除自身后)。通过查看此任务的执行情况,可方便调试程序和检查系统工作状态。

所有身份信息录入任务的优先级均高于相应的识别任务,这是因为STM32在接收到身份信息录入指令时,要立刻执行相应的录入任务;而在未接收到身份信息录入指令时,才会执行相应的识别任务。

录入指纹、录入二维码和录入射频卡这三个任务的优先级顺序没有要求,这是因为录入身份信息时会进入临界区,临界区保证了即使有多个身份信息录入指令同时下达,同一时间也只会进行一个身份信息录入。且由于串口1接收标志位的存在,在录入一个身份信息时,最多只有一个身份信息待录入。识别指纹、识别二维码和识别射频卡这三个任务的优先级同理。

由于XM1605和RFID-RC522 IC卡感应模块没有身份信息存储功能,程序中使用char Entered_QR[3][20]和unsigned char Entered_RFID[5][4]这两个数组分别储存录入的二维码内容和射频卡卡号。

录入指纹、录入二维码和录入射频卡这三个任务储存的身份信息均有数量限制,超出数量后再录入的身份信息会按顺序覆盖之前录入的。

为避免接收到身份信息录入指令后,反复执行相应录入任务(因为串口1接收到的数据被存入全局变量后,此全局变量的值在被重新存入前不会被清空),特使用一个二值信号量,用以标记从串口1获得的字符串是否被使用过。

录入身份信息任务执行完后,会将对应的识别任务挂起一段时间,这是为了避免刚录完身份信息后,指纹、二维码和射频卡还未及时从本系统上移开,便立刻进行识别,从而导致门锁打开。

图3-46是task2录入指纹的流程图,其余两个身份信息录入任务的流程与之类似。

下图是task3识别指纹的流程图,其余两个身份信息识别任务的流程与之类似。

FreeRTOS_demo.c
/*
本文件中的“识别”还有判断与之前录入的是否相等之意
如任务七识别卡号,不仅会读出检测的卡号,还会判断当前的卡号是否存在于任务六录入的卡号集中
{input_fingerprints}{input_RFID}{input_QR},3个录入指令,放这方便复制
*/

#include "stm32f10x.h"                  // Device header
#include <string.h>		//字符串处理
#include "FreeRTOS_demo.h"
#include "Serial.h"
#include "led.h"
#include "AS608.h"
#include "ScanCode.h"
#include "RC522.h"
#include "USART2.h"
#include "lock.h"
#include "voice.h"

/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"//用于二值信号量

/*FreeRTOS配置******************************************************************************************************/

/* START_TASK 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    start_task_handler; 
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
void start_task( void * pvParameters );

/* TASK1串口1接收处理 任务配置 
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task1_handler;
#define TASK1_PRIO         13
#define TASK1_STACK_SIZE   128
void task1( void * pvParameters );

/* TASK2录入指纹 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task2_handler;
#define TASK2_PRIO         11
#define TASK2_STACK_SIZE   128
void task2( void * pvParameters );

/* TASK3识别指纹 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task3_handler;
#define TASK3_PRIO         7
#define TASK3_STACK_SIZE   128
void task3( void * pvParameters );

/* TASK4录入二维码 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task4_handler;
#define TASK4_PRIO         10
#define TASK4_STACK_SIZE   128
void task4( void * pvParameters );

/* TASK5识别二维码 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task5_handler;
#define TASK5_PRIO         6
#define TASK5_STACK_SIZE   128
void task5( void * pvParameters );

/* TASK6录入射频卡 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task6_handler;
#define TASK6_PRIO        9
#define TASK6_STACK_SIZE   128
void task6( void * pvParameters );

/* TASK7识别射频卡 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
TaskHandle_t    task7_handler;
#define TASK7_PRIO        5
#define TASK7_STACK_SIZE   128
void task7( void * pvParameters );

/* TASK10led闪烁 任务配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */
#define TASK10_PRIO         3
#define TASK10_STACK_SIZE   128
TaskHandle_t    task10_handler;
void task10( void * pvParameters );

QueueHandle_t Serial_RxData_BinarySemaphore;	//二值信号量句柄,标记(MQTT-esp01s)串口1获得的字符串是否被使用过

char Entered_QR[3][20];		//3行20列的数组,储存录入的二维码,再大可能会超过任务堆栈大小
unsigned char Entered_RFID[5][4];	//5行4列的数组,储存录入的射频卡卡号

/******************************************************************************************************/

/**
 * @brief       	FreeRTOS例程入口函数,用于创建start_task任务
 * @Parameter       无
 * @return_value    无
 */
void freertos_demo(void)
{   
	/* 创建二值信号量 */
	Serial_RxData_BinarySemaphore = xSemaphoreCreateBinary();
	//动态方式创建START_TASK任务
    xTaskCreate((TaskFunction_t         )   start_task,				//指向任务函数的指针
                (char *                 )   "start_task",			//任务名
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,	//任务堆栈大小,单位:字
                (void *                 )   NULL,					//传递给任务函数的参数
                (UBaseType_t            )   START_TASK_PRIO,		//任务优先级
                (TaskHandle_t *         )   &start_task_handler );	//任务句柄
    vTaskStartScheduler();	//启动任务调度器
}

/**
 * @brief       	开始任务函数,用于创建其他任务
 * @Parameter       pvParameters : 传入参数(未用到)???????????
 * @return_value    无
 */
void start_task( void * pvParameters )		
{
    taskENTER_CRITICAL();               /* 进入临界区 */
	
    /* 创建任务 1 */
	xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
	/* 创建任务 2 */
	xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
	/* 创建任务 3 */
	xTaskCreate((TaskFunction_t         )   task3,
                (char *                 )   "task3",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &task3_handler );
	/* 创建任务 4 */
	xTaskCreate((TaskFunction_t         )   task4,
                (char *                 )   "task4",
                (configSTACK_DEPTH_TYPE )   TASK4_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK4_PRIO,
                (TaskHandle_t *         )   &task4_handler );
	/* 创建任务 5 */
	xTaskCreate((TaskFunction_t         )   task5,
                (char *                 )   "task5",
                (configSTACK_DEPTH_TYPE )   TASK5_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK5_PRIO,
                (TaskHandle_t *         )   &task5_handler );
	/* 创建任务 6 */
	xTaskCreate((TaskFunction_t         )   task6,
                (char *                 )   "task6",
                (configSTACK_DEPTH_TYPE )   TASK6_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK6_PRIO,
                (TaskHandle_t *         )   &task6_handler );
	/* 创建任务 7 */
	xTaskCreate((TaskFunction_t         )   task7,
                (char *                 )   "task7",
                (configSTACK_DEPTH_TYPE )   TASK7_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK7_PRIO,
                (TaskHandle_t *         )   &task7_handler );
    /* 创建任务 10 */            
    xTaskCreate((TaskFunction_t         )   task10,
                (char *                 )   "task10",
                (configSTACK_DEPTH_TYPE )   TASK10_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK10_PRIO,
                (TaskHandle_t *         )   &task10_handler );
	
	vTaskDelete(NULL);					//删除自身
				
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一 串口1接收处理*/
void task1( void * pvParameters )
{
	while(1)
    { 
		//printf("正在等待接收esp01s传来的MQTT数据\r\n");
		
		taskENTER_CRITICAL();               /* 进入临界区 */
		if (Serial_RxFlag == 1)
		{
			printf("从esp01s那里收到的MQTT数据为:%s\r\n\r\n", Serial_RxData);
			Serial_RxFlag = 0;	
			xSemaphoreGive(Serial_RxData_BinarySemaphore);	 //释放二值信号量,使Serial_RxData[100]可被读取,释放后二值信号量为满,uxQueueMessagesWaiting()返回1
		}
		taskEXIT_CRITICAL();                /* 退出临界区 */
		
		//printf("%s\r\n\r\n", Serial_RxData);
		//printf("向MQTT发送的数据为:%s\r\n\r\n", transmit_to_MQTT);
		
		/*
		USART1_RX属于中断响应,只要有数据传来就一定会响应。
		不过传来的数据是否可以存在Serial_RxData[100]数组中是Serial_RxFlag这个标志位来控制的。
		所以可以通过控制Serial_RxFlag标志位来控制是否将得到的数据存在Serial_RxData[100]数组中。
		所以可以通过控制下面这个延时时间来间接控制串口数据储存到接收数组中的时间间隔。
		*/		
		vTaskDelay(2000);	
    }
}

/* 任务二 录入指纹
从MQTT收到录入指纹的指令后,无论录入结果如何,只会录入一次。
当再次收到录入指纹指令时,依然只会录一次。
*/
void task2( void * pvParameters )
{
	uint8_t ID = 0;
	uint8_t input_fingerprints_status = 2;  //0表示录入成功,1表示录入失败,2表示未放置手指
	uint8_t fingerprint_BinarySemaphore_Peek = 0;
	
	while(1)
    {	
		fingerprint_BinarySemaphore_Peek = uxQueueMessagesWaiting(Serial_RxData_BinarySemaphore);	
		//printf("%d\r\n\r\n",fingerprint_BinarySemaphore_Peek);
		if( fingerprint_BinarySemaphore_Peek == 1)	//如果二值信号量被释放了,即从esp01s那里收到了MQTT数据
		{
			input_fingerprints_status = 2;
		}
		
		if (strcmp(Serial_RxData, "{input_fingerprints}") == 0 && input_fingerprints_status == 2)
		{			
			//获取二值信号量, portMAX_DELAY:死等,获取后二值信号量为空,uxQueueMessagesWaiting()返回0
			/*当MQTT发送了指令,但不是录入指纹的指令时,不能获取二值信号量。
			二值信号量应由相应的任务获取,这也是此条语句必须要在if里的原因*/
			xSemaphoreTake(Serial_RxData_BinarySemaphore, portMAX_DELAY);	
			
			voice_output_fingerprint_input("请放手指");
			
			taskENTER_CRITICAL();               /* 进入临界区 */
			input_fingerprints_status = input_fingerprints(ID);	
			taskEXIT_CRITICAL();                /* 退出临界区 */
			
			vTaskSuspend(task3_handler);	//挂起识别指纹任务
			
			if(input_fingerprints_status == 0)	
			{				
				voice_output_fingerprint_input("指纹录入成功");
				
				if( ID < 241 )	//241是AS608.c中的save_template_to_buffer01_and_give_it_ID()函数的限制值
				{
					ID++;
				}
				else
				{
					ID = 0;		//之后再录入指纹会覆盖掉之前已录制的有相同ID的指纹
				}
			}
			if(input_fingerprints_status == 1)
			{
				voice_output_fingerprint_input("指纹录入失败");
			}
			if(input_fingerprints_status == 2)	
			{
				voice_output_fingerprint_input("手指接触不良");
			}
			
			vTaskDelay(3000);
			vTaskResume(task3_handler);		//延时后恢复识别指纹任务
		}
		
		vTaskDelay(1000);
	}
}

/* 任务三 识别指纹*/
void task3( void * pvParameters )
{
	while(1)
    { 
		identify_fingerprint(status_ID);
		
		if(status_ID[0] == 0)
		{
			voice_output_fingerprint_identify("指纹识别成功");
			printf("识别到的指纹ID为%d\r\n\r\n", status_ID[1]);
			unlock();	//开锁
		}
		if(status_ID[0] == 1)
		{
			voice_output_fingerprint_identify("无效指纹");
		}
		
		vTaskDelay(1000);
    }
}

/* 任务四 录入二维码*/
void task4( void * pvParameters )
{
	uint8_t input_QR_status = 0;  //1表示录入成功,0表示还未录入
	uint8_t QR_BinarySemaphore_Peek = 0;
	uint8_t QR_number= 0;	//录入的二维码编号
	
	while(1)	
    { 
		QR_BinarySemaphore_Peek = uxQueueMessagesWaiting(Serial_RxData_BinarySemaphore);	
		if( QR_BinarySemaphore_Peek == 1)	//如果二值信号量被释放了,即从esp01s那里收到了MQTT数据
		{
			input_QR_status = 0;
		}
	
		if (strcmp(Serial_RxData, "{input_QR}") == 0 && input_QR_status == 0)
		{
			xSemaphoreTake(Serial_RxData_BinarySemaphore, portMAX_DELAY);	//获取二值信号量, portMAX_DELAY:死等,获取后二值信号量为空,uxQueueMessagesWaiting()返回0	
				
			voice_output_QR("请提供二维码");
			
			while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)	//如果没有二维码输入,死等
			{}
			printf("等待二维码解码\r\n\r\n");	//???PA1引脚与串口接收完数据有时差,用此句来延时。
			if (USART2_RxFlag == 1)	 //防止数据被“任务五识别二维码”捕捉,即刚录入的二维码???
			{
				USART2_RxFlag = 0;				
			}
			strcpy(Entered_QR[QR_number], USART2_RxData);
			printf("Entered_QR[%d]: %s\r\n", QR_number, Entered_QR[QR_number]);  
			
			voice_output_QR("二维码录入成功");
			
			if( QR_number < 2 )	
			{
				QR_number++;
			}
			else
			{
				QR_number = 0;		//之后再录入二维码会覆盖掉之前已录制的有相同编号的二维码
			}
			
			input_QR_status = 1;
		}
			
		vTaskDelay(1000);
	}			
}
		
/* 任务五 识别二维码*/
void task5( void * pvParameters )
{
	uint8_t Get_QR_Flag = 2;	//0二维码识别失败,1二维码识别成功,2未识别到二维码
	
	while(1)
    { 			
		//taskENTER_CRITICAL();               //进入临界区
		Get_QR_Flag = get_QR(Entered_QR);
		//taskEXIT_CRITICAL();   				//退出临界区
		
		if(Get_QR_Flag == 1)
		{
			voice_output_QR("二维码识别成功");
			unlock();	//开锁
		}
		if(Get_QR_Flag == 0)
		{
			voice_output_QR("无效二维码");
		}
		
		vTaskDelay(1000);
    }
}

/* 任务六 录入射频卡*/
void task6( void * pvParameters )
{
	uint8_t input_RFID_status = 0;  //1表示录入成功,0表示还未录入
	uint8_t RFID_BinarySemaphore_Peek = 0;
	uint8_t RFID_number = 0;	//录入的射频卡编号
	unsigned char identify_Card_Flag = 0;
	
	while(1)	
    { 
		RFID_BinarySemaphore_Peek = uxQueueMessagesWaiting(Serial_RxData_BinarySemaphore);	
		if( RFID_BinarySemaphore_Peek == 1)	//如果二值信号量被释放了,即从esp01s那里收到了MQTT数据
		{
			input_RFID_status = 0;
		}
	
		if (strcmp(Serial_RxData, "{input_RFID}") == 0 && input_RFID_status == 0)
		{
			xSemaphoreTake(Serial_RxData_BinarySemaphore, portMAX_DELAY);	//获取二值信号量, portMAX_DELAY:死等,获取后二值信号量为空,uxQueueMessagesWaiting()返回0	
				
			vTaskSuspend(task7_handler);	//挂起识别射频卡任务
			
			voice_output_RFID("请放卡片");
			
			identify_Card_Flag = Input_Card(Entered_RFID[RFID_number]);
			
			if(identify_Card_Flag == 1)
			{
				printf("编号:%d\r\n\r\n", RFID_number);
				voice_output_RFID("射频卡录入成功");
			}
			
			if( RFID_number < 4 )	
			{
				RFID_number++;
			}
			else
			{
				RFID_number = 0;		//之后再录入射频卡会覆盖掉之前已录制的有相同编号的射频卡卡号
			}
			
			vTaskDelay(3000);
			vTaskResume(task7_handler);		//延时后恢复识别射频卡任务
			
			input_RFID_status = 1;
		}
			
		vTaskDelay(1000);
	}			
}

/* 任务七 识别射频卡*/
void task7( void * pvParameters )
{ 
	unsigned char identify_Card_Flag = 2;	//1识别成功,0识别失败,2未识别到卡片
		
	while(1)
    { 
		identify_Card_Flag = identify_Card(Entered_RFID);
		
		if (identify_Card_Flag == 1)
		{
			voice_output_RFID("射频卡识别成功");
			unlock();	//开锁
		}
		if (identify_Card_Flag == 0)
		{
			voice_output_RFID("无效卡片");
		}
		
		vTaskDelay(1000);
	}
}	

/* 任务十 led闪烁 用于调试 查看最低优先级任务的执行情况*/
void task10( void * pvParameters )
{
	while(1)
    { 
		led_flicker();
    }
}
FreeRTOS_demo.h
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H

#include "FreeRTOS.h"
#include "queue.h"

extern QueueHandle_t Serial_RxData_BinarySemaphore;

void freertos_demo(void);

#endif

辅控制器接入物联网

该功能实现的原理及流程可参考:利用ESP-01S中继实现STM32F103C8T6与MQTT服务器的串口双向通信-CSDN博客

ESP-01S开发板为本系统的辅助控制器。其功能是将从MQTT服务器传来的消息通过串口传给STM32。本次的开发环境为Arduino IDE,开发板类型为Generic ESP8266 Module。

ESP-01S开发板连接WiFi并接收MQTT服务器发布的特定主题信息的流程按照Arduino标准开发模式,分为初始化(setup)和循环(loop)两部分

初始化部分的步骤如下:

①引入ESP8266WiFi、PubSubClient库;

②设置ESP8266工作模式为无线终端模式,连接WiFi;

③配置MQTT服务器域名、端口号,加载MQTT订阅回调函数,连接MQTT服务器。

循环部分的流程如下图所示。

代码
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
 
// 设置wifi接入信息和MQTT服务器
const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";
const char* mqttServer = "broker.emqx.io";
 
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

 
void setup() 
{
  Serial.begin(9600);    // 启动串口通讯
  
  WiFi.mode(WIFI_STA);    //设置ESP8266工作模式为无线终端模式
  
  connectWifi();    // 连接WiFi
  
  mqttClient.setServer(mqttServer, 1883);   // 设置MQTT服务器和端口号
  mqttClient.setCallback(receiveCallback);    // 设置MQTT订阅回调函数
  connectMQTTserver();    // 连接MQTT服务器
}
 
void loop() 
{
  if (mqttClient.connected())   // 如果开发板成功连接服务器
  { 
    mqttClient.loop();          // 处理信息(收到信息后的回调函数)以及心跳
  } 
  else                          // 如果开发板未能成功连接服务器
  {                      
    connectMQTTserver();        // 则尝试连接服务器并订阅主题
  }
}

 
// 连接MQTT服务器并订阅主题
void connectMQTTserver()
{
  // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
  String clientId = "esp8266-" + WiFi.macAddress();
 
  if (mqttClient.connect(clientId.c_str()))     //如果成功连接MQTT服务器
  { 
    Serial.print("MQTT Server Has Connected. ");
    Serial.print("Server Address: ");
    Serial.println(mqttServer);
    Serial.print("ClientId: ");
    Serial.println(clientId);
    subscribeTopic(); // 订阅指定主题
  } 
  else 
  {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(3000);
  }   
}

// 收到信息后的回调函数
void receiveCallback(char* topic, byte* payload, unsigned int length) 
{
  Serial.print("Message with the topic of [ ");
  Serial.print(topic);
  Serial.println(" ] has been received.");

  Serial.print("Content: ");
  for (int i = 0; i < length; i++) 
  {
    Serial.print((char)payload[i]);
  }
  Serial.println("");
  
  Serial.print("Message Length (Bytes) :  ");
  Serial.println(length);
  Serial.println(" ");
}
 
// 订阅指定主题
void subscribeTopic()
{
  String topicString = "entranceGuard4";   // 订阅主题的名称
  char subTopic[topicString.length() + 1];  
  strcpy(subTopic, topicString.c_str());
  
  if(mqttClient.subscribe(subTopic))    //如果成功订阅主题
  {
    Serial.print("Subscrib Topic: ");
    Serial.println(subTopic);
    Serial.println("");
  } else 
  {
    Serial.print("Subscribe Fail...");
  }  
}
 
// ESP8266连接wifi
void connectWifi()  
{
  WiFi.begin(wifiname, password);
  
  Serial.println("Connecting to WiFi");
 
  while (WiFi.status() != WL_CONNECTED) //等待WiFi连接,当wifi未连接时,持续输出".";成功连接后输出连接成功信息
  {
    delay(1000);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.println("WiFi Connected!");  
  Serial.println(""); 
}

指纹相关

该功能实现的原理及流程可参考:基于STM32C8T6、ATK-AS608实现指纹的录入、识别与删除-CSDN博客

本系统通过串口通信驱动ATK-AS608光学指纹识别模块实现录入、识别删除指纹等功能。

AS608.c
#include "stm32f10x.h"
#include "AS608.h"
#include "USART3.h"
#include "serial.h"

uint8_t usart3_tx_hex[17];	//串口3发送的信息
uint8_t usart3_rx_hex[17];	//串口3接收的信息
uint8_t	check_code_flag = 0x00;		//校验和标志位,0表示校验正确,1表示校验错误
uint8_t status_ID[2];	//状态和ID号,status_ID[0]为0表示识别指纹成功,为1表示识别指纹失败,为2表示未检测到指纹;status_ID[1]为识别到的ID号
//当前文档均是0为成功,1为失败。原因是为了和校验码一致

void AS608_Init(void)
{
	USART3_Init();
	
	//用于判断手指是否已放置在采集处
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	delete_all_fingerprints();
}
//
void input_image(void)	//录入图像
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x03;
	usart3_tx_hex[9] = 0x01;
	usart3_tx_hex[10] = 0x00;
	usart3_tx_hex[11] = 0x05;
	
	printf("正在录入图像\r\n");
	
	USART3_SendArray(usart3_tx_hex, 12);
	//在发送与接收之间不能停留太长时间,必须在数据发过来之前就先进入USART3_ReceiveArray(),然后卡死,直到收完所有传过来的数据
	USART3_ReceiveArray(usart3_rx_hex, 12);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("录入图像成功\r\n");
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
}

void generate_feature_and_store_in_buffer(uint8_t buffer_address)	//生成特征并存在指定缓存区
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x04;
	usart3_tx_hex[9] = 0x02;
	if (buffer_address == 1)
	{	usart3_tx_hex[10] = 0x01;
		usart3_tx_hex[11] = 0x00;
		usart3_tx_hex[12] = 0x08;
		printf("正在生成特征并存在缓存区1\r\n");
	}
	if (buffer_address == 2)
	{	usart3_tx_hex[10] = 0x02;
		usart3_tx_hex[11] = 0x00;
		usart3_tx_hex[12] = 0x09;
		printf("正在生成特征并存在缓存区2\r\n");
	}	
	
	USART3_SendArray(usart3_tx_hex, 13);
	
	USART3_ReceiveArray(usart3_rx_hex, 12);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		if (buffer_address == 1)
		{
			printf("生成特征并存在缓存区1成功\r\n");
		}
		if (buffer_address == 2)
		{
			printf("生成特征并存在缓存区2成功\r\n");
		}
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
}

void accurately_compare_the_characteristics_of_two_fingerprints(void)	//精确比对两枚指纹特征
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x03;
	usart3_tx_hex[9] = 0x03;
	usart3_tx_hex[10] = 0x00;
	usart3_tx_hex[11] = 0x07;
	
	printf("正在精确比对两枚指纹特征\r\n");

	USART3_SendArray(usart3_tx_hex, 12);
	
	USART3_ReceiveArray(usart3_rx_hex, 14);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("%02X ", usart3_rx_hex[12]);
	printf("%02X ", usart3_rx_hex[13]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("精确比对两枚指纹特征成功\r\n");
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
}

void merge_features_and_generate_templates(void)	//合并特征并生成模板
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x03;
	usart3_tx_hex[9] = 0x05;
	usart3_tx_hex[10] = 0x00;
	usart3_tx_hex[11] = 0x09;
	
	printf("正在合并特征并生成模板\r\n");

	USART3_SendArray(usart3_tx_hex, 12);
	
	USART3_ReceiveArray(usart3_rx_hex, 12);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("合并特征并生成模板成功\r\n");
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
}

void save_template_to_buffer01_and_give_it_ID(uint8_t ID)	//储存模板到缓冲区01,并赋予ID。ID不要超过241
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x06;
	usart3_tx_hex[9] = 0x06;
	usart3_tx_hex[10] = 0x01;
	usart3_tx_hex[11] = 0x00;
	usart3_tx_hex[12] = ID;
	usart3_tx_hex[13] = 0x00;
	usart3_tx_hex[14] = 0x0E + ID;
	
	printf("正在储存模板到缓冲区01,并赋予ID:%d\r\n", ID);

	USART3_SendArray(usart3_tx_hex, 15);
	
	USART3_ReceiveArray(usart3_rx_hex, 12);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("储存模板到缓冲区01,并赋予ID:%d成功\r\n", ID);
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");

}

uint8_t search_fingerprint (void)		//搜索指纹,返回识别到的ID
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x08;
	usart3_tx_hex[9] = 0x04;
	usart3_tx_hex[10] = 0x02;
	usart3_tx_hex[11] = 0x00;
	usart3_tx_hex[12] = 0x00;
	usart3_tx_hex[13] = 0x01;
	usart3_tx_hex[14] = 0x2C;
	usart3_tx_hex[15] = 0x00;
	usart3_tx_hex[16] = 0x3C;
	
	printf("正在搜索指纹\r\n");

	USART3_SendArray(usart3_tx_hex, 17);
	
	USART3_ReceiveArray(usart3_rx_hex, 16);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("%02X ", usart3_rx_hex[12]);
	printf("%02X ", usart3_rx_hex[13]);
	printf("%02X ", usart3_rx_hex[14]);
	printf("%02X ", usart3_rx_hex[15]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("搜索指纹成功\r\n");
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
	
	return usart3_rx_hex[11];	//识别到的ID
}

void delete_fingerprint(uint8_t ID)		//删除指定ID的指纹,ID不要超过234
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x07;
	usart3_tx_hex[9] = 0x0C;
	usart3_tx_hex[10] = 0x00;
	usart3_tx_hex[11] = ID;
	usart3_tx_hex[12] = 0x00;
	usart3_tx_hex[13] = 0x01;
	usart3_tx_hex[14] = 0x00;
	usart3_tx_hex[15] = 0x15 + ID;
	
	printf("正在删除ID:%d的指纹\r\n", ID);

	USART3_SendArray(usart3_tx_hex, 16);
	
	USART3_ReceiveArray(usart3_rx_hex, 12);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("删除ID:%d的指纹成功\r\n", ID);
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
}

void delete_all_fingerprints(void)		//删除所有指纹
{
	usart3_tx_hex[0] = 0xEF;
	usart3_tx_hex[1] = 0x01;
	usart3_tx_hex[2] = 0xFF;
	usart3_tx_hex[3] = 0xFF;
	usart3_tx_hex[4] = 0xFF;
	usart3_tx_hex[5] = 0xFF;
	usart3_tx_hex[6] = 0x01;
	usart3_tx_hex[7] = 0x00;
	usart3_tx_hex[8] = 0x03;
	usart3_tx_hex[9] = 0x0D;
	usart3_tx_hex[10] = 0x00;
	usart3_tx_hex[11] = 0x11;
	
	printf("正在删除所有指纹\r\n");

	USART3_SendArray(usart3_tx_hex, 12);
	
	USART3_ReceiveArray(usart3_rx_hex, 12);		
	
	printf("收到的数据为:");
	printf("%02X ", usart3_rx_hex[0]);
	printf("%02X ", usart3_rx_hex[1]);
	printf("%02X ", usart3_rx_hex[2]);
	printf("%02X ", usart3_rx_hex[3]);
	printf("%02X ", usart3_rx_hex[4]);
	printf("%02X ", usart3_rx_hex[5]);
	printf("%02X ", usart3_rx_hex[6]);
	printf("%02X ", usart3_rx_hex[7]);
	printf("%02X ", usart3_rx_hex[8]);
	printf("%02X ", usart3_rx_hex[9]);
	printf("%02X ", usart3_rx_hex[10]);
	printf("%02X ", usart3_rx_hex[11]);
	printf("\r\n");
	
	if (usart3_rx_hex[9] == 0x00)
	{
		printf("删除所有指纹成功\r\n");
	}
	else 
	{
		check_code_flag = 0x01;
		printf("错误信息为:");
		printf("%02X \r\n", usart3_rx_hex[9]);
	}
	
	printf("\r\n");
}
/
uint8_t input_fingerprints(uint8_t ID)		//录入指纹,返回0表示录入成功,1表示录入失败,2表示未放置手指
{
	printf("请放手指,进行指纹录入\r\n\r\n");
	
	while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)	//如果手指已还未放置在采集处,死等
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1)	//如果手指已放置在采集处
	{		
		input_image();//录入图像
		generate_feature_and_store_in_buffer(1);//生成特征,存在缓存区1
		input_image();//再次录入图像
		generate_feature_and_store_in_buffer(2);//生成特征,存在缓存区2
		accurately_compare_the_characteristics_of_two_fingerprints();//精确比对两枚指纹特征
		merge_features_and_generate_templates();//合并特征并生成模板
		save_template_to_buffer01_and_give_it_ID(ID);	//储存模板到缓冲区01,并赋予ID 
		
		if (check_code_flag == 0x00)
		{
			printf("录入指纹成功\r\n\r\n");
			return 0;
		}
		else 
		{
			printf("录入指纹失败\r\n\r\n");
			check_code_flag = 0x00;
			return 1;
		}
	}
	
	return 2;
}

void identify_fingerprint (uint8_t* status_ID)	//识别指纹,以数组形式返回是否识别成功以及识别到的ID
{	
	printf("请放手指,进行指纹识别\r\n\r\n");
	
	status_ID[0] = 2;	//还未检测到手指
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1 )	//如果手指已放置在采集处
	{		
		input_image();//录入图像
		generate_feature_and_store_in_buffer(2);//生成特征,存在缓存区2
		status_ID[1] = search_fingerprint();	//搜索指纹
		
		status_ID[0] = check_code_flag;
		if (check_code_flag == 0x00)
		{
			printf("识别指纹成功\r\n");
		}
		else 
		{
			printf("识别指纹失败\r\n");
			check_code_flag = 0x00;
		}
		
		printf("\r\n");
	}
}
AS608.h
#ifndef __AS608_H
#define __AS608_H

#include "stm32f10x.h"
	
extern uint8_t status_ID[2];

void AS608_Init(void);

void input_image(void);
void generate_feature_and_store_in_buffer(uint8_t buffer_address);
void accurately_compare_the_characteristics_of_two_fingerprints(void);
void merge_features_and_generate_templates(void);
void save_template_to_buffer01_and_give_it_ID(uint8_t ID);
uint8_t search_fingerprint (void);

void delete_fingerprint(uint8_t ID);
void delete_all_fingerprints(void);

uint8_t input_fingerprints(uint8_t ID);
void identify_fingerprint (uint8_t* status_ID);

#endif

二维码相关

该功能实现的原理及流程可参考:基于STM32C8T6、XM1605实现二维码的扫描与信息读取-CSDN博客XM1605扫码模组使用介绍-CSDN博客

控制器通过XM1605扫码模组获取二维码内容的步骤如下:

①由10号引脚的电平状态判断是否有二维码输入,如有进入②步;

②通过串口读取模组识别到的二维码内容。

XM1605扫码模组只能输出识别到的二维码信息,没有二维码存储与比对功能。本系统中的二维码的录入与识别程序均是建立在控制器通过XM1605扫码模组获取二维码内容这段程序的基础上的。

规定录入的二维码格式遵循单片机、MQTT服务器与小程序之间所约定的文本数据通讯协议,且大小不超过20byte。

下图为二维码的录入流程。将符合格式和大小的二维码内容存入数组。

下图为二维码的识别流程。将返回是否为已录入的二维码。

ScanCode.c
#include "stm32f10x.h"
#include "ScanCode.h"
#include "USART2.h"
#include "serial.h"
#include <string.h>		//字符串处理

void ScanCode_Init(void)
{
	USART2_Init();
	
	//用于判断是否有二维码输入
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t get_QR(char Entered_QR[3][20])
{
	printf("正在扫码中,请提供二维码\r\n\r\n");

	uint8_t Get_QR_Flag = 2;	//0二维码识别失败,1二维码识别成功,2未识别到二维码
	
	if (USART2_RxFlag == 1)
	{
		printf("识别到的二维码为:%s\r\n\r\n", USART2_RxData);
		USART2_RxFlag = 0;
		
		if(strcmp(USART2_RxData, Entered_QR[0]) == 0)	
		{
			printf("二维码识别成功\r\n\r\n");
			Get_QR_Flag = 1;
		}
		else if (strcmp(USART2_RxData, Entered_QR[1]) == 0)
		{
			printf("二维码识别成功\r\n\r\n");
			Get_QR_Flag = 1;
		}
		else if (strcmp(USART2_RxData, Entered_QR[2]) == 0)
		{
			printf("二维码识别成功\r\n\r\n");
			Get_QR_Flag = 1;
		}
		else 
		{
			printf("二维码识别失败\r\n\r\n");
			Get_QR_Flag = 0;
		}
	}
	
	return Get_QR_Flag;
}
ScanCode.h
#ifndef __SCANCODE_H
#define __SCANCODE_H

void ScanCode_Init(void);
uint8_t get_QR(char Entered_QR[3][20]);

#endif

射频卡相关

该功能实现的原理及流程可参考:基于STM32C8T6、RFID_RC522模块读写Mifare One(S50)卡-CSDN博客

驱动RFID-RC522 IC卡感应模块读取射频卡(Mifare One(S50)卡)ID的流程如下图所示。对模块的操作和获取读取结果通过SPI通信实现。

RFID-RC522 IC卡感应模块只能输出识别到的射频卡内部数据,没有数据存储与射频卡ID比对功能。本系统中的射频卡(Mifare One(S50)卡)ID的录入与识别程序均是建立在驱动RFID-RC522 IC卡感应模块读取射频卡(Mifare One(S50)卡)ID这段程序的基础上的。

下图为射频卡ID录入流程,会将读到的ID存入数组。

下图为射频卡ID识别流程,会返回是否为已录入的射频卡。

RC522.c
#include "stm32f10x.h"
#include "RC522.h"
//#include "Delay.h"   //freertos中禁用
#include "spi_driver.h"
#include "stm32f10x_spi.h"
#include "Serial.h"
#include <string.h>		//字符串处理

void RC522_IO_Init(void) 
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   //开启AFIO时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //关闭JTAG因为要使用PB3和4
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_4;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOA, &GPIO_InitStruct);	
	
	SPI_Configuration(SPI1); 
	
	PcdReset();  //复位RC522
	PcdAntennaOff();  //关闭天线
	//Delay_ms(100);
	PcdAntennaOn();  //开启天线
	//Delay_ms(100);
	printf("RC522启动\r\n\r\n");
}

/*
/
//功    能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
//          value[IN]:写入的值
/
void WriteRawRC(unsigned char Address, unsigned char value)
{  
    unsigned char i, ucAddr;

    RC522_SCK_RESET();   //MF522_SCK = 0;
    RC522_NSEL_RESET();  //MF522_NSS = 0;
    ucAddr = ((Address<<1)&0x7E);
    RC522_Delay(10);
    for(i=8;i>0;i--)
    {
        //MF522_SI = ((ucAddr&0x80)==0x80);
			  if((ucAddr&0x80)==0x80)
				{
					RC522_MOSI_SET();
				}
				else
				{
					RC522_MOSI_RESET();
				}
        RC522_SCK_SET();  //MF522_SCK = 1;
        ucAddr <<= 1;
				RC522_Delay(10);
        RC522_SCK_RESET();  //MF522_SCK = 0;
				RC522_Delay(10);
    }

    for(i=8;i>0;i--)
    {
        //MF522_SI = ((value&0x80)==0x80);
			  if((value&0x80)==0x80)
				{
					RC522_MOSI_SET();
				}
				else
				{
					RC522_MOSI_RESET();
				}
				RC522_SCK_SET();  //MF522_SCK = 1;
        value <<= 1;
				RC522_Delay(10);
        RC522_SCK_RESET();  //MF522_SCK = 0;
				RC522_Delay(10);
//         MF522_SCK = 1;
//         value <<= 1;
//         MF522_SCK = 0;
    }
    RC522_NSEL_SET();  //MF522_NSS = 1;
    RC522_SCK_SET();    //MF522_SCK = 1;
}
/
//功    能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返    回:读出的值
/
unsigned char ReadRawRC(unsigned char Address)
{
     unsigned char i, ucAddr;
     unsigned char ucResult=0;

     RC522_SCK_RESET();   //MF522_SCK = 0;
     RC522_NSEL_RESET();  //MF522_NSS = 0;
     ucAddr = ((Address<<1)&0x7E)|0x80;
     RC522_Delay(10);
     for(i=8;i>0;i--)
     {
//          MF522_SI = ((ucAddr&0x80)==0x80);
//          MF522_SCK = 1;
//          ucAddr <<= 1;
//          MF522_SCK = 0;
			 
			  if((ucAddr&0x80)==0x80)
				{
					RC522_MOSI_SET();
				}
				else
				{
					RC522_MOSI_RESET();
				}
        RC522_SCK_SET();  //MF522_SCK = 1;
        ucAddr <<= 1;
				RC522_Delay(10);
        RC522_SCK_RESET();  //MF522_SCK = 0;
				RC522_Delay(10);
     }
     for(i=8;i>0;i--)
     {
         RC522_SCK_SET();  //MF522_SCK = 1;
         ucResult <<= 1;
			   RC522_Delay(10);
         //ucResult|=(bit)MF522_SO;
			 
// 			 	 if(RC522_MISO_STATUS==1)
// 				 {
// 					 ucResult|=0x01;
// 				 }
// 				 else
// 				 {
// 					 ucResult&=~0x01;
// 				 }
			   ucResult |=RC522_MISO_STATUS;
			 
         RC522_SCK_RESET();  //MF522_SCK = 0;
			   RC522_Delay(10);
     }

    RC522_NSEL_SET();   //MF522_NSS = 1;
    RC522_SCK_SET();    //MF522_SCK = 1;
     return ucResult;
}
*/
//#define MAXRLEN 18
                              
/
//功    能:寻卡
//参数说明: req_code[IN]:寻卡方式
//                0x52 = 寻感应区内所有符合14443A标准的卡
//                0x26 = 寻未进入休眠状态的卡
//          pTagType[OUT]:卡片类型代码
//                0x4400 = Mifare_UltraLight
//                0x0400 = Mifare_One(S50)
//                0x0200 = Mifare_One(S70)
//                0x0800 = Mifare_Pro(X)
//                0x4403 = Mifare_DESFire
//返    回: 成功返回MI_OK
/
char PcdRequest(unsigned char req_code,unsigned char *pTagType)
{
   char status;  
   unsigned int  unLen;
   unsigned char ucComMF522Buf[MAXRLEN]; 

   ClearBitMask(Status2Reg,0x08); //清RC522寄存位
   WriteRawRC(BitFramingReg,0x07); //写RC623寄存器
   SetBitMask(TxControlReg,0x03); //置RC522寄存位
	
   ucComMF522Buf[0] = req_code;

   status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);
//   UART_send_byte(status);
   if ((status == MI_OK) && (unLen == 0x10))
   {    
       *pTagType     = ucComMF522Buf[0];
       *(pTagType+1) = ucComMF522Buf[1];
   }
   else
   {   status = MI_ERR;   }
   
   return status;
}

/
//功    能:防冲撞
//参数说明: pSnr[OUT]:卡片序列号(传入时为空),4字节
//返    回: 成功返回MI_OK,并将卡片序列号写入pSnr
/  
char PcdAnticoll(unsigned char *pSnr)
{
    char status;
    unsigned char i,snr_check=0;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 
    

    ClearBitMask(Status2Reg,0x08);
    WriteRawRC(BitFramingReg,0x00);
    ClearBitMask(CollReg,0x80);
 
    ucComMF522Buf[0] = PICC_ANTICOLL1;
    ucComMF522Buf[1] = 0x20;

    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&unLen);

    if (status == MI_OK)
    {
    	 for (i=0; i<4; i++)
         {   
             *(pSnr+i)  = ucComMF522Buf[i];
             snr_check ^= ucComMF522Buf[i];
         }
         if (snr_check != ucComMF522Buf[i])
         {   status = MI_ERR;    }
    }
    
    SetBitMask(CollReg,0x80);
    return status;
}

/
//功    能:选定卡片
//参数说明: pSnr[IN]:卡片序列号,4字节
//返    回: 成功返回MI_OK
/
char PcdSelect(unsigned char *pSnr)
{
    char status;
    unsigned char i;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0] = PICC_ANTICOLL1;
    ucComMF522Buf[1] = 0x70;
    ucComMF522Buf[6] = 0;
    for (i=0; i<4; i++)
    {
    	ucComMF522Buf[i+2] = *(pSnr+i);
    	ucComMF522Buf[6]  ^= *(pSnr+i);
    }
    CalulateCRC(ucComMF522Buf,7,&ucComMF522Buf[7]);
  
    ClearBitMask(Status2Reg,0x08);

    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,9,ucComMF522Buf,&unLen);
    
    if ((status == MI_OK) && (unLen == 0x18))
    {   status = MI_OK;  }
    else
    {   status = MI_ERR;    }

    return status;
}

/
//功    能:验证卡片密码
//参数说明: auth_mode[IN]: 密码验证模式
//                 0x60 = 验证A密钥
//                 0x61 = 验证B密钥 
//          addr[IN]:块地址
//          pKey[IN]:密码
//          pSnr[IN]:卡片序列号,4字节
//返    回: 成功返回MI_OK
/                   
char PcdAuthState(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = auth_mode;
    ucComMF522Buf[1] = addr;
    for (i=0; i<6; i++)
    {    ucComMF522Buf[i+2] = *(pKey+i);   }
    for (i=0; i<6; i++)
    {    ucComMF522Buf[i+8] = *(pSnr+i);   }
    
    status = PcdComMF522(PCD_AUTHENT,ucComMF522Buf,12,ucComMF522Buf,&unLen);
    if ((status != MI_OK) || (!(ReadRawRC(Status2Reg) & 0x08)))
    {   status = MI_ERR;   }
    
    return status;
}

/
//功    能:读取M1卡一块数据
//参数说明: addr[IN]:块地址
//          pData [OUT]:读出的数据,16字节
//返    回: 成功返回MI_OK
/ 
char PcdRead(unsigned char addr,unsigned char *pData)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = PICC_READ;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
   
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
    if ((status == MI_OK) && (unLen == 0x90))
    {
        for (i=0; i<16; i++)
        {    *(pData+i) = ucComMF522Buf[i];   }
    }
    else
    {   status = MI_ERR;   }
    
    return status;
}

/
//功    能:写数据到M1卡一块
//参数说明: addr[IN]:块地址
//          pData [IN]:写入的数据,16字节
//返    回: 成功返回MI_OK
/                 
char PcdWrite(unsigned char addr,unsigned char *pData)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0] = PICC_WRITE;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
        
    if (status == MI_OK)
    {
        for (i=0; i<16; i++)
        {    ucComMF522Buf[i] = *(pData+i);   }
        CalulateCRC(ucComMF522Buf,16,&ucComMF522Buf[16]);

        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,18,ucComMF522Buf,&unLen);
        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {   status = MI_ERR;   }
    }
    
    return status;
}

/
//功    能:扣款和充值
//参数说明: dd_mode[IN]:命令字
//               0xC0 = 扣款
//               0xC1 = 充值
//          addr[IN]:钱包地址
//          pValue[IN]:4字节增(减)值,低位在前
//返    回: 成功返回MI_OK
/                
char PcdValue(unsigned char dd_mode,unsigned char addr,unsigned char *pValue)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0] = dd_mode;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
        
    if (status == MI_OK)
    {
        for (i=0; i<16; i++)
        {    ucComMF522Buf[i] = *(pValue+i);   }
        CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);
        unLen = 0;
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);
        if (status != MI_ERR)
        {    status = MI_OK;    }
    }
    
    if (status == MI_OK)
    {
        ucComMF522Buf[0] = PICC_TRANSFER;
        ucComMF522Buf[1] = addr;
        CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]); 
   
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {   status = MI_ERR;   }
    }
    return status;
}

/
//功    能:备份钱包
//参数说明: sourceaddr[IN]:源地址
//          goaladdr[IN]:目标地址
//返    回: 成功返回MI_OK
/
char PcdBakValue(unsigned char sourceaddr, unsigned char goaladdr)
{
    char status;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = PICC_RESTORE;
    ucComMF522Buf[1] = sourceaddr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
    
    if (status == MI_OK)
    {
        ucComMF522Buf[0] = 0;
        ucComMF522Buf[1] = 0;
        ucComMF522Buf[2] = 0;
        ucComMF522Buf[3] = 0;
        CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);
 
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);
        if (status != MI_ERR)
        {    status = MI_OK;    }
    }
    
    if (status != MI_OK)
    {    return MI_ERR;   }
    
    ucComMF522Buf[0] = PICC_TRANSFER;
    ucComMF522Buf[1] = goaladdr;

    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }

    return status;
}

/
//功    能:命令卡片进入休眠状态
//返    回: 成功返回MI_OK
/
char PcdHalt(void)
{
    //char status;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = PICC_HALT;
    ucComMF522Buf[1] = 0;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    //status = 
	  PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    return MI_OK;
}

/
//用MF522计算CRC16函数
/
void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData)
{
    unsigned char i,n;
    ClearBitMask(DivIrqReg,0x04);
    WriteRawRC(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);
    for (i=0; i<len; i++)
    {   WriteRawRC(FIFODataReg, *(pIndata+i));   }
    WriteRawRC(CommandReg, PCD_CALCCRC);
    i = 0xFF;
    do 
    {
        n = ReadRawRC(DivIrqReg);
        i--;
    }
    while ((i!=0) && !(n&0x04));
    pOutData[0] = ReadRawRC(CRCResultRegL);
    pOutData[1] = ReadRawRC(CRCResultRegM);
}

/
//功    能:复位RC522
//返    回: 成功返回MI_OK
/
char PcdReset(void)
{
    RC522_RESET_SET();     //RST522_1;
    //Delay_us(10);  //_NOP();
    RC522_RESET_RESET();   //RST522_0;
    //Delay_ms(60);  //_NOP();_NOP();
    RC522_RESET_SET();     //RST522_1;RST522_1;
    //Delay_us(500);  //_NOP();_NOP();
    WriteRawRC(CommandReg,PCD_RESETPHASE);
    //Delay_ms(2);  //_NOP();_NOP();
  
  WriteRawRC(ModeReg,0x3D);            //?Mifare???,CRC???0x6363
  WriteRawRC(TReloadRegL,30);         //?30?????           
  WriteRawRC(TReloadRegH,0);          
  WriteRawRC(TModeReg,0x8D);
  WriteRawRC(TPrescalerReg,0x3E);
  WriteRawRC(TxAutoReg,0x40);
  
  ClearBitMask(TestPinEnReg, 0x80);//off MX and DTRQ out
  WriteRawRC(TxAutoReg,0x40);
   
  return MI_OK;
}

/
//功    能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返    回:读出的值
/
unsigned char ReadRawRC(unsigned char Address)
{
	unsigned char ucAddr;
	unsigned char ucResult=0;
	ucAddr = ((Address<<1)&0x7E)|0x80;
	//Delay_ms(1);
	RC522_ENABLE;
	SPI_WriteNBytes(SPI1,&ucAddr,1);  //向总线写多个数据
	SPI_ReadNBytes(SPI1,&ucResult,1);  //向总线读多个数据
	RC522_DISABLE;
	return ucResult;
}

/
//功    能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
//          value[IN]:写入的值
/
void WriteRawRC(unsigned char Address, unsigned char value)
{  
	unsigned char ucAddr;
	uint8_t write_buffer[2]={0};
	ucAddr = ((Address<<1)&0x7E);
	write_buffer[0] = ucAddr;
	write_buffer[1] = value;
//	Delay_ms(1);
	RC522_ENABLE;
	SPI_WriteNBytes(SPI1,write_buffer,2);
	RC522_DISABLE;
}

/
//功    能:置RC522寄存器位
//参数说明:reg[IN]:寄存器地址
//          mask[IN]:置位值
/
void SetBitMask(unsigned char reg,unsigned char mask)  
{
    char tmp = 0x0;
    tmp = ReadRawRC(reg);  //读RC632寄存器
    WriteRawRC(reg,tmp | mask);  // set bit mask
}

/
//功    能:清RC522寄存器位
//参数说明:reg[IN]:寄存器地址
//          mask[IN]:清位值
/
void ClearBitMask(unsigned char reg,unsigned char mask)  
{
    char tmp = 0x0;
    tmp = ReadRawRC(reg);
    WriteRawRC(reg, tmp & ~mask);  // clear bit mask
} 

/
//功    能:通过RC522和ISO14443卡通讯
//参数说明:Command[IN]:RC522命令字
//          pIn [IN]:通过RC522发送到卡片的数据
//          InLenByte[IN]:发送数据的字节长度
//          pOut [OUT]:接收到的卡片返回数据
//          *pOutLenBit[OUT]:返回数据的位长度
/
char PcdComMF522(unsigned char Command, 
                 unsigned char *pInData, 
                 unsigned char InLenByte,
                 unsigned char *pOutData, 
                 unsigned int  *pOutLenBit)
{
    char status = MI_ERR;
    unsigned char irqEn   = 0x00;
    unsigned char waitFor = 0x00;
    unsigned char lastBits;
    unsigned char n;
    unsigned int i;
    switch (Command)
    {
       case PCD_AUTHENT:
          irqEn   = 0x12;
          waitFor = 0x10;
          break;
       case PCD_TRANSCEIVE:
          irqEn   = 0x77;
          waitFor = 0x30;
          break;
       default:
         break;
    }
   
    WriteRawRC(ComIEnReg,irqEn|0x80);
    ClearBitMask(ComIrqReg,0x80);
    WriteRawRC(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);
    
    for (i=0; i<InLenByte; i++)
    {   WriteRawRC(FIFODataReg, pInData[i]);    }
    WriteRawRC(CommandReg, Command);
   
    
    if (Command == PCD_TRANSCEIVE)
    {    SetBitMask(BitFramingReg,0x80);  }
    
    i = 800 ; //600;//????????,??M1???????25ms
    do 
    {
         n = ReadRawRC(ComIrqReg);
         i--;
    }
    while ((i!=0) && !(n&0x01) && !(n&waitFor));
    ClearBitMask(BitFramingReg,0x80);
	      
    if (i!=0)
    {    
         if(!(ReadRawRC(ErrorReg)&0x1B))
         {
             status = MI_OK;
             if (n & irqEn & 0x01)
             {   status = MI_NOTAGERR;   }
             if (Command == PCD_TRANSCEIVE)
             {
               	n = ReadRawRC(FIFOLevelReg);
              	lastBits = ReadRawRC(ControlReg) & 0x07;
                if (lastBits)
                {   *pOutLenBit = (n-1)*8 + lastBits;   }
                else
                {   *pOutLenBit = n*8;   }
                if (n == 0)
                {   n = 1;    }
                if (n > MAXRLEN)
                {   n = MAXRLEN;   }
                for (i=0; i<n; i++)
                {   pOutData[i] = ReadRawRC(FIFODataReg);    }
            }
         }
         else
         {   status = MI_ERR;   }
        
   }
   

   SetBitMask(ControlReg,0x80);           // stop timer now
   WriteRawRC(CommandReg,PCD_IDLE); 
   return status;
}

/
//开启天线  
//每次启动或关闭天险发射之间应至少有1ms的间隔
/
void PcdAntennaOn(void)
{
    unsigned char i;
    i = ReadRawRC(TxControlReg);
    if (!(i & 0x03))
    {
        SetBitMask(TxControlReg, 0x03);
    }
}

/
//关闭天线
/
void PcdAntennaOff(void)
{
    ClearBitMask(TxControlReg, 0x03);
}

void RC522_Config(unsigned char Card_Type)
{
	   ClearBitMask(Status2Reg,0x08);
     WriteRawRC(ModeReg,0x3D);//3F
     WriteRawRC(RxSelReg,0x86);//84
     WriteRawRC(RFCfgReg,0x7F);   //4F
   	 WriteRawRC(TReloadRegL,30);//tmoLength);// TReloadVal = 'h6a =tmoLength(dec) 
	   WriteRawRC(TReloadRegH,0);
     WriteRawRC(TModeReg,0x8D);
	   WriteRawRC(TPrescalerReg,0x3E);
//	   WriteRawRC(TxAutoReg,0x40);//???
//	   Delay_ms(5);//delay_10ms(1);
	
	
     PcdAntennaOn();
}

/
/
//识别卡片
/
/
unsigned char identify_Card(unsigned char Entered_RFID[5][4])
{
	unsigned char status;	//记录多种状态
	unsigned char identify_Card_Flag = 2;	//1识别成功,0识别失败,2未识别到卡片
	unsigned char Card_Type[2] = {0x04,0x00};  	//Mifare One(S50)卡
	unsigned char Card_ID[4];
	
	printf("请放置射频卡,进行卡号识别\r\n\r\n");
		
	status = PcdRequest(0x52, Card_Type);//寻卡函数,如果成功返回MI_OK
	if(status == MI_OK)  
	{
		printf("寻卡成功\r\n\r\n");
		
		status = PcdAnticoll(Card_ID);//防冲撞 如果成功返回MI_OK
		if(status == MI_OK)
		{
			printf("识别到的卡号:%.2x %.2x %.2x %.2x\r\n\r\n",Card_ID[0],Card_ID[1],Card_ID[2],Card_ID[3]);
		}
		else
		{
			printf("检测到多张卡片,识别失败\r\n");			
		}
		
		for (int i = 0; i < 5; i++) 
		{  
			if (Card_ID[0] == Entered_RFID[i][0] && Card_ID[1] == Entered_RFID[i][1] && Card_ID[2] == Entered_RFID[i][2] && Card_ID[3] == Entered_RFID[i][3]) 
			{ 
				printf("射频卡识别成功\r\n\r\n");  
				identify_Card_Flag = 1;  
				break; // 找到识别的卡后退出循环  
			}  
		}  
		
		if (identify_Card_Flag == 2) //到这一步还等于2,意味着寻卡成功,但却没识别成功
		{  
			printf("射频卡识别失败\r\n\r\n");  
			identify_Card_Flag = 0;
		}  
	}

	return identify_Card_Flag;		
}


/
/
//录入卡片
/
/
unsigned char Input_Card(unsigned char Card_ID[4])
{
	unsigned char status;
	unsigned char identify_Card_Flag = 0;
	unsigned char Card_Type[2] = {0x04,0x00};  	//Mifare One(S50)卡
	
	printf("正在进行卡片录入,请放置卡片\r\n\r\n");
	
	status = PcdRequest(0x52, Card_Type);//寻卡函数,如果成功返回MI_OK
	
	while(status != MI_OK)	//没找到卡,死等
	{
		status = PcdRequest(0x52, Card_Type);//寻卡函数,如果成功返回MI_OK
	}	

	if(status == MI_OK)  
	{
		printf("寻卡成功\r\n\r\n");
		
		status = PcdAnticoll(Card_ID);//防冲撞 如果成功返回MI_OK
		if(status == MI_OK)
		{
			printf("录入的卡号:%.2x %.2x %.2x %.2x, ",Card_ID[0],Card_ID[1],Card_ID[2],Card_ID[3]);
			identify_Card_Flag = 1;
		}
		else
		{
			printf("检测到多张卡片,录入失败\r\n");			
		}
	}

	return identify_Card_Flag;		
}

RC522.h
#include "stm32f10x.h"

#define RC522_RESET_PIN                GPIO_Pin_11                
#define RC522_RESET_GPIO_PORT          GPIOA                    
#define RC522_RESET_GPIO_CLK           RCC_APB2Periph_GPIOA
#define RC522_RESET_SET()              GPIO_SetBits(RC522_RESET_GPIO_PORT, RC522_RESET_PIN);      //1
#define RC522_RESET_RESET()            GPIO_ResetBits(RC522_RESET_GPIO_PORT, RC522_RESET_PIN);    //0

#define RC522_ENABLE 		GPIO_ResetBits(GPIOA,GPIO_Pin_4)
#define RC522_DISABLE 	GPIO_SetBits(GPIOA,GPIO_Pin_4)


/
//MF522命令字
/
#define PCD_IDLE              0x00               //取消当前命令
#define PCD_AUTHENT           0x0E               //验证密钥
#define PCD_RECEIVE           0x08               //接收数据
#define PCD_TRANSMIT          0x04               //发送数据
#define PCD_TRANSCEIVE        0x0C               //发送并接收数据
#define PCD_RESETPHASE        0x0F               //复位
#define PCD_CALCCRC           0x03               //CRC计算

/
//Mifare_One卡片命令字
/
#define PICC_REQIDL           0x26               //寻天线区内未进入休眠状态
#define PICC_REQALL           0x52               //寻天线区内全部卡
#define PICC_ANTICOLL1        0x93               //防冲撞
#define PICC_ANTICOLL2        0x95               //防冲撞
#define PICC_AUTHENT1A        0x60               //验证A密钥
#define PICC_AUTHENT1B        0x61               //验证B密钥
#define PICC_READ             0x30               //读块
#define PICC_WRITE            0xA0               //写块
#define PICC_DECREMENT        0xC0               //扣款
#define PICC_INCREMENT        0xC1               //充值
#define PICC_RESTORE          0xC2               //调块数据到缓冲区
#define PICC_TRANSFER         0xB0               //保存缓冲区中数据
#define PICC_HALT             0x50               //休眠

/
//MF522 FIFO长度定义
/
#define DEF_FIFO_LENGTH       64                 //FIFO size=64byte

/
//MF522寄存器定义
/
// PAGE 0
#define     RFU00                 0x00    
#define     CommandReg            0x01    
#define     ComIEnReg             0x02    
#define     DivlEnReg             0x03    
#define     ComIrqReg             0x04    
#define     DivIrqReg             0x05
#define     ErrorReg              0x06    
#define     Status1Reg            0x07    
#define     Status2Reg            0x08    
#define     FIFODataReg           0x09
#define     FIFOLevelReg          0x0A
#define     WaterLevelReg         0x0B
#define     ControlReg            0x0C
#define     BitFramingReg         0x0D
#define     CollReg               0x0E
#define     RFU0F                 0x0F
// PAGE 1     
#define     RFU10                 0x10
#define     ModeReg               0x11
#define     TxModeReg             0x12
#define     RxModeReg             0x13
#define     TxControlReg          0x14
#define     TxAutoReg             0x15
#define     TxSelReg              0x16
#define     RxSelReg              0x17
#define     RxThresholdReg        0x18
#define     DemodReg              0x19
#define     RFU1A                 0x1A
#define     RFU1B                 0x1B
#define     MifareReg             0x1C
#define     RFU1D                 0x1D
#define     RFU1E                 0x1E
#define     SerialSpeedReg        0x1F
// PAGE 2    
#define     RFU20                 0x20  
#define     CRCResultRegM         0x21
#define     CRCResultRegL         0x22
#define     RFU23                 0x23
#define     ModWidthReg           0x24
#define     RFU25                 0x25
#define     RFCfgReg              0x26
#define     GsNReg                0x27
#define     CWGsCfgReg            0x28
#define     ModGsCfgReg           0x29
#define     TModeReg              0x2A
#define     TPrescalerReg         0x2B
#define     TReloadRegH           0x2C
#define     TReloadRegL           0x2D
#define     TCounterValueRegH     0x2E
#define     TCounterValueRegL     0x2F
// PAGE 3      
#define     RFU30                 0x30
#define     TestSel1Reg           0x31
#define     TestSel2Reg           0x32
#define     TestPinEnReg          0x33
#define     TestPinValueReg       0x34
#define     TestBusReg            0x35
#define     AutoTestReg           0x36
#define     VersionReg            0x37
#define     AnalogTestReg         0x38
#define     TestDAC1Reg           0x39  
#define     TestDAC2Reg           0x3A   
#define     TestADCReg            0x3B   
#define     RFU3C                 0x3C   
#define     RFU3D                 0x3D   
#define     RFU3E                 0x3E   
#define     RFU3F		  0x3F

/
//和MF522通讯时返回的错误代码
/
#define MI_OK                          0
#define MI_NOTAGERR                    1 //(-1)
#define MI_ERR                         2 //(-2)

#define MAXRLEN                       18

void RC522_IO_Init(void);



char PcdRequest(unsigned char req_code,unsigned char *pTagType);
char PcdAnticoll(unsigned char *pSnr);
char PcdSelect(unsigned char *pSnr);
char PcdAuthState(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr);
char PcdRead(unsigned char addr,unsigned char *pData);
char PcdWrite(unsigned char addr,unsigned char *pData);
char PcdValue(unsigned char dd_mode,unsigned char addr,unsigned char *pValue);
char PcdBakValue(unsigned char sourceaddr, unsigned char goaladdr);
char PcdHalt(void);
void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData);
char PcdReset(void);
unsigned char ReadRawRC(unsigned char Address);
void WriteRawRC(unsigned char Address, unsigned char value);
void SetBitMask(unsigned char reg,unsigned char mask);
void ClearBitMask(unsigned char reg,unsigned char mask);
char PcdComMF522(unsigned char Command, 
                 unsigned char *pInData, 
                 unsigned char InLenByte,
                 unsigned char *pOutData, 
                 unsigned int  *pOutLenBit);
void PcdAntennaOn(void);
void PcdAntennaOff(void);
void RC522_Config(unsigned char Card_Type);

				 
				 
				 
unsigned char identify_Card(unsigned char Entered_RFID[5][4]);
unsigned char Input_Card(unsigned char Card_ID[4]);

语音播报相关

该功能实现的原理及流程可参考:STM32驱动SYN6288语音合成模块实现语音播报-CSDN博客SYN6288语音合成模块使用说明(MicroPython、STM32、Arduino)-CSDN博客

本系统所用到的中文语音播报内容依旧为固定的语音编码。

voice.c
#include<stdio.h>
#include "stm32f10x.h"                  // Device header
#include "USART2.h"
#include <string.h>		//字符串处理

/*
指纹录入中,请放手指
指纹录入成功
指纹录入失败
手指接触不良
指纹识别成功 
无效指纹 
二维码扫描中,请提供二维码
二维码录入成功
二维码识别成功 
无效二维码 
射频卡录入成功 
射频卡识别成功 
无效卡片 
*/

//写4个函数是因为1个函数查找太慢
 
void voice_output_fingerprint_input(char s[])
{	
	if(strcmp(s,"指纹录入成功") == 0 )
	{
		uint8_t Array[18]={253, 0, 15, 1, 0, 214, 184, 206, 198, 194, 188, 200, 235, 179, 201, 185, 166, 173};
		USART2_SendArray(Array, 18);
	}
	if(strcmp(s,"指纹录入失败") == 0 )
	{
		uint8_t Array[18]={253, 0, 15, 1, 0, 214, 184, 206, 198, 194, 188, 200, 235, 202, 167, 176, 220, 201};
		USART2_SendArray(Array, 18);
	}
	if(strcmp(s,"手指接触不良") == 0 )
	{
		uint8_t Array[18]={253, 0, 15, 1, 0, 202, 214, 214, 184, 189, 211, 180, 165, 178, 187, 193, 188, 138};
		USART2_SendArray(Array, 18);
	}
	if(strcmp(s,"请放手指") == 0 )
	{
		uint8_t Array[14]={253, 0, 11, 1, 0, 199, 235, 183, 197, 202, 214, 214, 184, 219};
		USART2_SendArray(Array, 14);
	}
}
 
void voice_output_fingerprint_identify(char s[])
{	
	if(strcmp(s,"指纹识别成功") == 0 )
	{
		uint8_t Array[18]={253, 0, 15, 1, 0, 214, 184, 206, 198, 202, 182, 177, 240, 179, 201, 185, 166, 205};
		USART2_SendArray(Array, 18);
	}
	if(strcmp(s,"无效指纹") == 0 )
	{
		uint8_t Array[14]={253, 0, 11, 1, 0, 206, 222, 208, 167, 214, 184, 206, 198, 246};
		USART2_SendArray(Array, 14);
	}
}

void voice_output_QR(char s[])
{	
	if(strcmp(s,"请提供二维码") == 0 )
	{
		uint8_t Array[18]={253, 0, 15, 1, 0, 199, 235, 204, 225, 185, 169, 182, 254, 206, 172, 194, 235, 225};
		USART2_SendArray(Array, 18);
	}
	if(strcmp(s,"二维码录入成功") == 0 )
	{
		uint8_t Array[20]={253, 0, 17, 1, 0, 182, 254, 206, 172, 194, 235, 194, 188, 200, 235, 179, 201, 185, 166, 214};
		USART2_SendArray(Array, 20);
	}
	if(strcmp(s,"二维码识别成功") == 0 )
	{
		uint8_t Array[20]={253, 0, 17, 1, 0, 182, 254, 206, 172, 194, 235, 202, 182, 177, 240, 179, 201, 185, 166, 182};
		USART2_SendArray(Array, 20);
	}
	if(strcmp(s,"无效二维码") == 0 )
	{
		uint8_t Array[16]={253, 0, 13, 1, 0, 206, 222, 208, 167, 182, 254, 206, 172, 194, 235, 149};
		USART2_SendArray(Array, 16);
	}
}

void voice_output_RFID(char s[])
{	
	if(strcmp(s,"请放卡片") == 0 )
	{
		uint8_t Array[14]={253, 0, 11, 1, 0, 199, 235, 183, 197, 191, 168, 198, 172, 212};
		USART2_SendArray(Array, 14);
	}
	if(strcmp(s,"射频卡录入成功") == 0 )
	{
		uint8_t Array[20]={253, 0, 17, 1, 0, 201, 228, 198, 181, 191, 168, 194, 188, 200, 235, 179, 201, 185, 166, 156};
		USART2_SendArray(Array, 20);
	}
	if(strcmp(s,"射频卡识别成功") == 0 )
	{ 
		uint8_t Array[20]={253, 0, 17, 1, 0, 201, 228, 198, 181, 191, 168, 202, 182, 177, 240, 179, 201, 185, 166, 252};
		USART2_SendArray(Array, 20);
	}
	if(strcmp(s,"无效卡片") == 0 )
	{
		uint8_t Array[14]={253, 0, 11, 1, 0, 206, 222, 208, 167, 191, 168, 198, 172, 237};
		USART2_SendArray(Array, 14);
	}
}
voice.h
#ifndef __VOICE_H
#define __VOICE_H

void voice_output_fingerprint_input(char s[]);
void voice_output_fingerprint_identify(char s[]);
void voice_output_QR(char s[]);
void voice_output_RFID(char s[]);

#endif

门锁相关

该功能实现的原理及流程可参考:未完待续

STM32通过控制引脚电平,进而控制继电器的吸合与断开,从而实现对门锁开关控制,其流程如下图所示。

lock.c
#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"

//PC15接继电器输入端

void lock_Init(void)		
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
}

void unlock(void)	//开锁
{
	GPIO_SetBits(GPIOC, GPIO_Pin_15);
	vTaskDelay(1500);
	GPIO_ResetBits(GPIOC, GPIO_Pin_15);
}	
lock.h
#ifndef __LOCK_H
#define __LOCK_H

void lock_Init(void);
void unlock(void);

#endif

板载led闪烁

该功能实现的原理及流程可参考:未完待续

led.c
#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"

//PC13是stm32c8t6板上自带led的引脚

void led_Init(void)		
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
}


void led_flicker(void)	//闪烁
{
	GPIO_ResetBits(GPIOC, GPIO_Pin_13);
	vTaskDelay(100);
	GPIO_SetBits(GPIOC, GPIO_Pin_13);
	vTaskDelay(100);
}	
led.h
#ifndef __LED_H
#define __LED_H

void led_Init(void);
void led_flicker(void);

#endif

串口1通信

该功能实现的原理及流程可参考:stm32c8t6实现串口输入输出_printf 串口输入-CSDN博客

串口1用于接收从ESP-01S传来的MQTT服务器发送的消息,同时打印log,方便查看系统运行状态和debug。

serial.c
//串口1打印和输入

#include "stm32f10x.h"                  
#include <stdio.h>
#include <stdarg.h>

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
		
	USART_Cmd(USART1, ENABLE);
}


void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//当其置1(SET) 标志着串口1接收完成
}

//重定向c库函数printf到串口
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

/*
uint8_t Serial_ReceiveByte(void)
{
	uint8_t Byte;
	Byte = USART_ReceiveData(USART1);
	while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);	//当其置1(SET) 标志着串口1发送完成
	return Byte;
}
*/
/*
//重定向c库函数scanf到串口,重写后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    uint8_t ch;
    ch = Serial_ReceiveByte();
    return ch;
}
*/
char Serial_RxData[100];		//全局变量,用于储存USART1获得的文本数据,最多100个字符
uint8_t Serial_RxFlag;		//全局变量,串口接收标志位

/*
下面的中断服务函数只会向Serial_RxData[]数组中储存第1对{}之间的内容
如  fdgh{"lamp": 77,"curtain": 3},"airConditioner": {2}frgtr  发送至USART1后,Serial_RxData[]数组中的内容为  {"lamp": 77,"curtain": 3}
如果只有 { 而没有 } ,Serial_RxData[]数组的'\0'结束符的位置就未被重置,后续处理就会出错
*/

void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;
	static uint8_t count = 0;	//Serial_RxData[]数组的下标
	
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1);
		
		if (RxState == 0)
		{
			if (RxData == '{' && Serial_RxFlag == 0)
			{
				RxState = 1;
				count = 0;
			}
		}
		if (RxState == 1)
		{
			if (RxData == '}')
			{
				Serial_RxData[count] = '}';
				Serial_RxData[count + 1] = '\0';	//标志数组的结束,方便后续处理
				RxState = 0;
				Serial_RxFlag = 1;
				count = 0;
			}			
			else
			{
				Serial_RxData[count] = RxData;
				count ++;	
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
serial.h
//串口1打印和输入

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
uint8_t Serial_ReceiveByte(void);

extern char Serial_RxData[100];
extern uint8_t Serial_RxFlag;

#endif

串口2通信

串口2用于接收扫码模块传来的二维码信息。

USART2.c
#include "stm32f10x.h"                  
#include <stdio.h>
#include <stdarg.h>

void USART2_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
		
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART2, &USART_InitStructure);
	
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
		
	USART_Cmd(USART2, ENABLE);
}

void USART2_SendByte(uint8_t Byte)
{
	USART_SendData(USART2, Byte);
	while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}

void USART2_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		USART2_SendByte(Array[i]);
	}
}



char USART2_RxData[100];		//全局变量,用于储存USART2获得的文本数据,最多100个字符
uint8_t USART2_RxFlag;		//全局变量,串口接收标志位

/*
下面的中断服务函数只会向USART2_RxData[]数组中储存第1对{}之间的内容
如  fdgh{"lamp": 77,"curtain": 3},"airConditioner": {2}frgtr  发送至USART2后,USART2_RxData[]数组中的内容为  {"lamp": 77,"curtain": 3}
如果只有 { 而没有 } ,USART2_RxData[]数组的'\0'结束符的位置就未被重置,后续处理就会出错
*/

void USART2_IRQHandler(void)
{
	static uint8_t RxState = 0;
	static uint8_t count = 0;	//USART2_RxData[]数组的下标
	
	if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData(USART2);
		
		if (RxState == 0)
		{
			if (RxData == '{' && USART2_RxFlag == 0)
			{
				RxState = 1;
				count = 0;
			}
		}
		if (RxState == 1)
		{
			if (RxData == '}')
			{
				USART2_RxData[count] = '}';
				USART2_RxData[count + 1] = '\0';	//标志数组的结束,方便后续处理
				RxState = 0;
				USART2_RxFlag = 1;
				count = 0;
			}			
			else
			{
				USART2_RxData[count] = RxData;
				count ++;	
			}
		}
		
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);
	}
}
USART2.h
#ifndef __USART2_H
#define __USART2_H

#include <stdio.h>

extern char USART2_RxData[100];		
extern uint8_t USART2_RxFlag;		

void USART2_Init(void);
void USART2_SendByte(uint8_t Byte);
void USART2_SendArray(uint8_t *Array, uint16_t Length);

#endif

串口3通信

串口3用于与指纹模块通信

USART.c
#include "stm32f10x.h"                  
#include <stdio.h>
#include <stdarg.h>

void USART3_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
		
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 57600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART3, &USART_InitStructure);

	USART_Cmd(USART3, ENABLE);
}

void USART3_SendByte(uint8_t Byte)
{
	USART_SendData(USART3, Byte);
	while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
}

void USART3_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		USART3_SendByte(Array[i]);
	}
}
/*
void USART3_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		USART3_SendByte(String[i]);
	}
}

uint32_t USART3_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void USART3_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		USART3_SendByte(Number / USART3_Pow(10, Length - i - 1) % 10 + '0');
	}
}


void USART3_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	USART3_SendString(String);
}



uint8_t USART3_GetRxFlag(void)
{
	if (USART3_RxFlag == 1)
	{
		USART3_RxFlag = 0;
		return 1;
	}
	return 0;
}

void USART3_IRQHandler(void)
{
	static uint8_t RxState = 0;
	static uint8_t pRxPacket = 0;
	if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData(USART3);
		
		if (RxState == 0)
		{
			if (RxData == 0xFF)
			{
				RxState = 1;
				pRxPacket = 0;
			}
		}
		else if (RxState == 1)
		{
			USART3_RxPacket[pRxPacket] = RxData;
			pRxPacket ++;
			if (pRxPacket >= 4)
			{
				RxState = 2;
			}
		}
		else if (RxState == 2)
		{
			if (RxData == 0xFE)
			{
				RxState = 0;
				USART3_RxFlag = 1;
			}
		}
		
		USART_ClearITPendingBit(USART3, USART_IT_RXNE);
	}
}
*/

uint8_t USART3_ReceiveByte(void)
{
	uint8_t Byte;
	while (USART_GetFlagStatus(USART3, USART_FLAG_RXNE) == RESET);	//串口3收到数据会将其置1(SET)
	Byte = USART_ReceiveData(USART3);
	return Byte;
}

uint8_t* USART3_ReceiveArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Array[i] = USART3_ReceiveByte();
	}
	return Array;
}
USART.h
#ifndef __USART3_H
#define __USART3_H

#include <stdio.h>

void USART3_Init(void);
void USART3_SendByte(uint8_t Byte);
void USART3_SendArray(uint8_t *Array, uint16_t Length);
void USART3_SendString(char *String);
void USART3_SendNumber(uint32_t Number, uint8_t Length);
void USART3_Printf(char *format, ...);

uint8_t USART3_ReceiveByte(void);
uint8_t* USART3_ReceiveArray(uint8_t *Array, uint16_t Length);


uint8_t USART3_GetRxFlag(void);

#endif

SPI通信

SPI用于与射频卡识别模块通信

spi_driver.c
#include "stm32f10x.h"
#include "spi_driver.h"

static void SPI_RCC_Configuration(SPI_TypeDef* SPIx)
{
	if(SPIx==SPI1){
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1,ENABLE);
	}else{
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
	}
}
/**
  * @brief  配置指定SPI的引脚
  * @param  SPIx 需要使用的SPI
  * @retval None
  */
static void SPI_GPIO_Configuration(SPI_TypeDef* SPIx)  
{
	GPIO_InitTypeDef GPIO_InitStruct;
   	if(SPIx==SPI1){					 					 
		GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_5 | GPIO_Pin_6|GPIO_Pin_7;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; 
		GPIO_Init(GPIOA, &GPIO_InitStruct);
		//初始化片选输出引脚
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_Init(GPIOA, &GPIO_InitStruct);
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
	}else{
		GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_13 | GPIO_Pin_14|GPIO_Pin_15;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; 
		GPIO_Init(GPIOB, &GPIO_InitStruct);
		//初始化片选输出引脚
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_Init(GPIOB, &GPIO_InitStruct);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);
	} 
}
/**
  * @brief  根据外部SPI设备配置SPI相关参数
  * @param  SPIx 需要使用的SPI
  * @retval None
  */
void SPI_Configuration(SPI_TypeDef* SPIx)
{
	SPI_InitTypeDef SPI_InitStruct;

	SPI_RCC_Configuration(SPIx);

	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_InitStruct.SPI_Direction= SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStruct.SPI_NSS = SPI_NSS_Hard;
	SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_CRCPolynomial = 7;
	SPI_Init(SPIx, &SPI_InitStruct);
	
	SPI_GPIO_Configuration(SPIx);

	SPI_SSOutputCmd(SPIx, ENABLE);
	SPI_Cmd(SPIx, ENABLE);
}
/**
  * @brief  写1字节数据到SPI总线
  * @param  SPIx 需要使用的SPI
  * @param  TxData 写到总线的数据
  * @retval 数据发送状态
  *		@arg 0 数据发送成功
  * 	@arg -1 数据发送失败
  */
int32_t SPI_WriteByte(SPI_TypeDef* SPIx, uint16_t TxData)
{
	uint8_t retry=0;				 
	while((SPIx->SR&SPI_I2S_FLAG_TXE)==0);				//等待发送区空	
	{
		retry++;
		if(retry>200)return -1;
	}			  
	SPIx->DR=TxData;	 	  				//发送一个byte 
	retry=0;
	while((SPIx->SR&SPI_I2S_FLAG_RXNE)==0); 				//等待接收完一个byte  
	{
		retry++;
		if(retry>200)return -1;
	}  
	SPIx->DR;						    
	return 0;          				//返回收到的数据
}
/**
  * @brief  从SPI总线读取1字节数据
  * @param  SPIx 需要使用的SPI
  * @param  p_RxData 数据储存地址
  * @retval 数据读取状态
  *		@arg 0 数据读取成功
  * 	@arg -1 数据读取失败
  */
int32_t SPI_ReadByte(SPI_TypeDef* SPIx, uint16_t *p_RxData)
{
	uint8_t retry=0;				 
	while((SPIx->SR&SPI_I2S_FLAG_TXE)==0);				//等待发送区空	
	{
		retry++;
		if(retry>200)return -1;
	}			  
	SPIx->DR=0xFF;	 	  				//发送一个byte 
	retry=0;
	while((SPIx->SR&SPI_I2S_FLAG_RXNE)==0); 				//等待接收完一个byte  
	{
		retry++;
		if(retry>200)return -1;
	}
	*p_RxData = SPIx->DR;  						    
	return 0;          				//返回收到的数据
}
/**
  * @brief  向SPI总线写多字节数据
  * @param  SPIx 需要使用的SPI
  * @param  p_TxData 发送数据缓冲区首地址
  * @param	sendDataNum 发送数据字节数
  * @retval 数据发送状态
  *		@arg 0 数据发送成功
  * 	@arg -1 数据发送失败
  */
int32_t SPI_WriteNBytes(SPI_TypeDef* SPIx, uint8_t *p_TxData,uint32_t sendDataNum)
{
	uint8_t retry=0;
	while(sendDataNum--){
		while((SPIx->SR&SPI_I2S_FLAG_TXE)==0);				//等待发送区空	
		{
			retry++;
			if(retry>20000)return -1;
		}			  
		SPIx->DR=*p_TxData++;	 	  				//发送一个byte 
		retry=0;
		while((SPIx->SR&SPI_I2S_FLAG_RXNE)==0); 				//等待接收完一个byte  
		{
			SPIx->SR = SPIx->SR;
			retry++;
			if(retry>20000)return -1;
		} 
		SPIx->DR;
	}
	return 0;
}
/**
  * @brief  从SPI总线读取多字节数据
  * @param  SPIx 需要使用的SPI
  * @param  p_RxData 数据储存地址
  * @param	readDataNum 读取数据字节数
  * @retval 数据读取状态
  *		@arg 0 数据读取成功
  * 	@arg -1 数据读取失败
  */
int32_t SPI_ReadNBytes(SPI_TypeDef* SPIx, uint8_t *p_RxData,uint32_t readDataNum)
{
	uint8_t retry=0;
	while(readDataNum--){
		SPIx->DR = 0xFF;
		while(!(SPIx->SR&SPI_I2S_FLAG_TXE)){
			retry++;
			if(retry>20000)return -1;
		}
		retry = 0;
		while(!(SPIx->SR&SPI_I2S_FLAG_RXNE)){
			retry++;
			if(retry>20000)return -1;
		}
		*p_RxData++ = SPIx->DR;
	}	
	return 0;
}

/*********************************END OF FILE**********************************/
spi_driver.h
#ifndef __SPI_DRIVER_H
#define __SPI_DRIVER_H

#include "stm32f10x.h"

void SPI_Configuration(SPI_TypeDef * SPIx);
int32_t SPI_WriteNBytes(SPI_TypeDef* SPIx, uint8_t *p_TxData,uint32_t sendDataNum);
int32_t SPI_ReadNBytes(SPI_TypeDef* SPIx, uint8_t *p_RxData,uint32_t readDataNum);
int32_t SPI_WriteByte(SPI_TypeDef* SPIx, uint16_t TxData);
int32_t SPI_ReadByte(SPI_TypeDef* SPIx, uint16_t *p_RxData);

#endif

错误和注意事项

未完待续

  1. 门锁的高电压会导致系统崩溃(开锁后系统重启),单独用继电器没问题,加上门锁就不行。
  2. 当储存卡号和二维码的数组太大时,系统无法运行,这是因为内存超出了任务堆栈大小。
  3. 当二维码格式不对时,系统会一直等待正确格式的二维码录入,从而卡住,最好是设置一个解码时间,超时退出任务并播报录入失败。
  4. 裸机的延时函数会启动定时器,干扰Freertos节拍,导致系统卡死。
  5. FreeRTOS_demo.c中的第323行“while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)    //如果没有二维码输入,死等”这种判断方法不合理。因为引脚电平只会在扫描到二维码的瞬间为高,延时会使这种判断方法失效。较合理的判断方法是模仿按键中断判断引脚电压是否出现了变化。

参考

未完待续

;