1.简介
基于RFID的课堂签到系统设计是一种利用无线射频识别(RFID)技术实现课堂自动签到的系统。这种系统通过RFID标签(通常是学生携带的卡片或手环等)与安装在教室内的RFID读写器之间的无线电信号进行数据交换,从而实现学生的自动识别和签到。
研究背景及意义。
2.研究背景及意义
传统的高校课堂考勤方式多采用手工签到或点名,这种方式不仅耗时,而且容易出错,如漏签、代签等问题频发。由于依赖于学生的自觉性,传统考勤方式的数据真实性和准确性难以保证。对于管理员来说,处理和汇总大量的考勤数据是一项繁重的工作,且容易出错。
随着校园信息化的不断推进,各种智能设备和技术被广泛应用于学校管理中。RFID技术作为一种成熟的无线通信技术,具有识别速度快、准确率高、操作简便等优点,被越来越多地应用于校园服务中。
RFID技术已经在物流、零售、交通等多个领域得到广泛应用,并取得了显著成效。在校园环境中,RFID技术也被用于一卡通系统、图书管理系统等多个方面,为学生和教职工提供了极大的便利。
高校作为人才培养的重要基地,其教育管理水平直接影响到教学质量和学生的综合素质。因此,建立一套高效、准确的课堂签到系统对于提升教育管理水平具有重要意义。
基于RFID的课堂签到系统可以实现自动识别和记录学生的签到信息,大大节省了考勤时间,提高了考勤效率。RFID技术具有唯一性和不可复制性,可以确保每个学生的签到信息都是真实准确的,避免了传统考勤方式中的漏签、代签等问题。自动化的考勤系统可以自动处理和汇总考勤数据,减轻了管理员的工作负担,使其能够更多地关注于教学管理工作。
通过建立高效的课堂签到系统,学校可以更加准确地掌握学生的出勤情况,为教学管理提供有力支持。同时,该系统还可以与学校的其他管理系统进行集成,实现数据的共享和互通,进一步提升教育管理水平。基于RFID的课堂签到系统是校园信息化建设的重要组成部分。通过该系统的建设和应用,可以推动学校其他信息化项目的实施和发展,促进校园信息化的全面提升。
综上所述,基于RFID的课堂签到系统设计具有重要的研究背景和意义。它不仅能够解决传统考勤方式中存在的问题,提高考勤效率和数据准确性,还能够减轻管理员负担、提升教育管理水平并推动校园信息化建设的发展。
3.STM32驱动RFID模块
RFID 是 Radio Frequency Identification 的缩写,即射频识别。RFID 射频识别是一种非接触式的自动识别技术,它通过射频信号自动识别目标对象并获取相关数据,识别工作无需人工干预,可工作于各种恶劣环境。RFID 技术可识别高速运动物体并可同时识别多个标签,操作快捷方便。通常情况下,RFID 读写器发送的频率称为 RFID 系统的工作频率或载波频率。对于所设计的工作于高频 13.56MHz 的 RFID 读写器,其标签采用的是能量来源于读写器电磁场的无源标签。基本的工作原理是采用电磁耦合的方式使得标签从读写器耦合线圈的辐射近场中获得能量,从而达到与读写器进行数据交换的目的。
MFRC522模块与STM32控制器之间采用SPI协议,全双工通讯方式。STM32控制器作为主机方,本模块作为从机,通讯过程由主机发起,从机被动响应。SPI通讯时序如图3-2所示,SCK为时钟信号,由主机产生,在时钟的下降沿改变数据,主机通过MOSI线发送数据给从机,从机通过MISO线发送数据到主机;在时钟的上升沿采样数据,主机通过MISO读数据,从机通过MOSI读数据。按字节方式进行数据传输,一般遵循高位先发原则。
- RFID读取寄存器示例
/
//功 能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返 回:读出的值
/
unsigned char ReadRawRC(unsigned char Address)
{
unsigned char i, ucAddr;
unsigned char ucResult=0;
MF522_SCK = 0;
MF522_NSS = 0;
ucAddr = ((Address<<1)&0x7E)|0x80;
for(i=8;i>0;i--)
{
MF522_SI = ((ucAddr&0x80)==0x80);
MF522_SCK = 1;
ucAddr <<= 1;
MF522_SCK = 0;
}
for(i=8;i>0;i--)
{
MF522_SCK = 1;
ucResult <<= 1;
ucResult|=MF522_SO;
MF522_SCK = 0;
}
MF522_NSS = 1;
MF522_SCK = 1;
return ucResult;
}
- RFID写寄存器示例
/
//功 能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
// value[IN]:写入的值
/
void WriteRawRC(unsigned char Address, unsigned char value)
{
unsigned char i, ucAddr;
MF522_SCK = 0;
MF522_NSS = 0;
ucAddr = ((Address<<1)&0x7E);
for(i=8;i>0;i--)
{
MF522_SI = ((ucAddr&0x80)==0x80);
MF522_SCK = 1;
ucAddr <<= 1;
MF522_SCK = 0;
}
for(i=8;i>0;i--)
{
MF522_SI = ((value&0x80)==0x80);
MF522_SCK = 1;
value <<= 1;
MF522_SCK = 0;
}
MF522_NSS = 1;
MF522_SCK = 1;
}
3.1 操作流程
MFRC522模块与IC卡之间以固定频率的电磁波进行数据传输,IC卡本身没有电源,通过内部串联协振电路,在电磁波激励下,LC协振电路产生共振,由此为IC卡提供工作电压。接下来即可在13.56MHZ工作频段下实现IC卡与MFRC522读卡器之间完成数据交互IC卡常用指令如表3-1所示:
完成一次IC卡读写操作,首先需要进行寻卡,寻卡成功后才能对卡片进行后续操作。卡片的读写操作是以块为单位,每块为16个字节。当寻卡成功,则可以获取到IC卡的唯一序列号和卡容量大小,若想进行卡的读写操作,则需要进行秘钥认证,每张卡有多个扇,不同扇区可以用不同的密钥进行认证,每个扇区又可以使用A密钥或B密钥进行认证。秘钥认证成功即可实现数据读写,操作流程如图3-3所示:
3.2 将信息录入到IC卡
本次课堂签到系统硬件部分主要是实现RFID读写操作,将学生信息写入到IC卡中,代码实现如下:
u8 pKey[]={0xff,0xff,0xff,0xff,0xff,0xff};
/*设置卡信息*/
u8 RC522_SetCardInfo(CARDINFO *card_info)
{
u8 buffer[16]={0};
//1.寻卡
if(PcdRequest(PICC_REQALL,card_info->pTagType))return 1;//寻卡失败
//2.防冲撞
if(PcdAnticoll(card_info->pSnr))return 2;//防冲撞失败
//3.选定卡
if(PcdSelect(card_info->pSnr))return 3;//选定卡失败
//4.验证卡密码
if(PcdAuthState(PICC_AUTHENT1B,BLOCK_ID_0,pKey,card_info->pSnr))return 4;//验证密码失败
//5.写入id号
if(PcdWrite(BLOCK_ID_0,card_info->id))return 5;//写ID失败
//6.写入姓名
if(PcdAuthState(PICC_AUTHENT1B,BLOCK_NAME_0,pKey,card_info->pSnr))return 6;//验证密码失败
int len=strlen((char *)card_info->name);
u8 *p=card_info->name;
int count;
if(len>16)count=16;
else count=len;
//printf("len=%d\n",len);
u8 addr=BLOCK_NAME_0;
// int i=0;
while(1)
{
memcpy((char *)buffer,p,count);
// for(i=0;i<count;i++)
// {
// printf("%#x ",buffer[i]);
// }
// printf("\n");
PcdWrite(addr,buffer);
if(count==len)break;
len-=count;
p+=count;
if(len>16)count=16;
else count=len;
memset((char *)buffer,0,16);//清空缓冲区
addr++;//写入地址++
}
return 0;
}
3.3 从IC卡中读取学生信息
基于RFID射频操作,从IC卡中读取学生信息,代码实现如下:
/*读取卡信息*/
u8 RC522_GetCardInfo(CARDINFO *card_info)
{
u8 buffer[16]={0};
//1.寻卡
if(PcdRequest(PICC_REQALL,card_info->pTagType))return 1;//寻卡失败
//2.防冲撞
if(PcdAnticoll(card_info->pSnr))return 2;//防冲撞失败
//3.选定卡
if(PcdSelect(card_info->pSnr))return 3;//选定卡失败
//4.验证卡密码
if(PcdAuthState(PICC_AUTHENT1B,BLOCK_ID_0,pKey,card_info->pSnr))return 4;//验证密码失败
//5.读取ID
if(PcdRead(BLOCK_ID_0,card_info->id))return 5;//读取id失败
//printf("id=%s\r\n",card_info->id);
//6.读取姓名
if(PcdAuthState(PICC_AUTHENT1B,BLOCK_NAME_0,pKey,card_info->pSnr))return 6;//验证密码失败
int i=0;
int len;
u8 *p=card_info->name;
int cnt=0;
u8 addr=BLOCK_NAME_0;
for(i=0;i<4;i++)
{
memset(buffer,0,sizeof(buffer));
PcdRead(addr,buffer);
len=strlen((char *)buffer);
cnt+=len;
memcpy(p,buffer,len);
p+=len;
if(len<16)break;
addr++;//地址偏移
}
*p='\0';
// for(i=0;i<cnt;i++)
// {
// printf("%#x ",card_info->name[i]);
// }
// printf("\n");
//printf("name=%s,cnt=%d\n",card_info->name,cnt);
return 0;
}
4.系统界面设计
本次系统界面包含三部分:时间显示、信息录入、课堂签到。界面效果如下:
- 上电启动界面
上电启动显示提示信息,3s后开始显示当前系统时间。
- 系统时间显示
系统初始化完成后,显示当前实时时间,以及当前室内环境温度。
- 签到界面
签到界面,签到成功显示签到时间以及学号信息。
- 信息录入
信息录入可通过串口传输录入信息,格式为:“#学号,姓名”,如"#20241001,张三";录入成功显示学号信息。
- 课堂签到软件界面
根据签到时间显示签到状态,具体数据通讯过程请参考:https://blog.csdn.net/weixin_44453694/article/details/140556894
5.主程序核心
主程序核心代码如下,实现外设驱动,模块初始化,界面设计及切换。
#include "stm32f10x.h"
#include "beep.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "usart1.h"
#include "timer.h"
#include "esp8266.h"
#include "oled.h"
#include "rtc.h"
#include "ds18b20.h"
#include "adc.h"
#include "dma.h"
#include "exti.h"
#include "rc522.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
u8 rtc_menu_cnt=0;
u16 ds18b20_temp=0;
int main()
{
Beep_Init();//蜂鸣器器初始化
LED_Init();//LED
Key_Init();
USARTx_Init(USART1,115200);//一个字符的收发时间:1s/(115200/10)=86us
USARTx_Init(USART3,115200);//串口3配置
OLED_Init();//oled初始化
//欢迎使用
OLED_DispalyFont(32,16,16,font_16[7]);
OLED_DispalyFont(32+16,16,16,font_16[8]);
OLED_DispalyFont(32+16*2,16,16,font_16[9]);
OLED_DispalyFont(32+16*3,16,16,font_16[10]);
//课堂签到系统
OLED_DispalyFont(16,32,16,font_16[11]);
OLED_DispalyFont(16+16,32,16,font_16[12]);
OLED_DispalyFont(16+16*2,32,16,font_16[13]);
OLED_DispalyFont(16+16*3,32,16,font_16[14]);
OLED_DispalyFont(16+16*4,32,16,font_16[15]);
OLED_DispalyFont(16+16*5,32,16,font_16[16]);
OLED_Refresh();
Delay_Ms(1000);
Delay_Ms(1000);
Delay_Ms(1000);
OLED_ClearGram();
rtc_menu_cnt=1;//1数显时间
RTC_Init();
DS18B20_Init();//DS18B20初始化
if(DS18B20_CheckAck())
{
printf("DS18B20模块响应失败\n");
}
else printf("模块正常\r\n");
ds18b20_temp=DS18B20_GetTemp();//获取一次温度
RC522_Init();
u16 time=0;
u32 time2=0;
CARDINFO card_info={
"201401",
"阿水",
};
CARDINFO temp={0};
u8 stat=0;
u8 key;
u8 cnt=2;//默认为读卡
int len=0;
u8 x=0;
char buffer[100];
while(1)
{
key=Key_GetValue(0);
if(key==1)//录入
{
rtc_menu_cnt=0;
BEEP=1;
Delay_Ms(50);
BEEP=0;
cnt=1;//录入
OLED_ClearGram();
OLED_DispalyFont(40,0,24,font_24[0]);
OLED_DispalyFont(40+24,0,24,font_24[1]);
OLED_Refresh();
}
else if(key==2)//签到
{
rtc_menu_cnt=0;
BEEP=1;
Delay_Ms(50);
BEEP=0;
cnt=2;
OLED_ClearGram();
OLED_DispalyFont(40,0,24,font_24[2]);
OLED_DispalyFont(40+24,0,24,font_24[3]);
OLED_Refresh();
}
else if(key==3)//时间显示
{
BEEP=1;
Delay_Ms(50);
BEEP=0;
OLED_ClearGram();
cnt=2;
rtc_menu_cnt=1;
}
if(usart1_flag)
{
usart1_buffer[usart1_cnt]='\0';
printf("rx1=%s,%d\n",usart1_buffer,usart1_cnt);
//时间校准
//*20241010161018,15
if(usart1_buffer[0]=='*' && usart1_cnt==15)
{
rtcTime.year=(usart1_buffer[1]-'0')*1000+(usart1_buffer[2]-'0')*100+(usart1_buffer[3]-'0')*10+(usart1_buffer[4]-'0');
rtcTime.mon=(usart1_buffer[5]-'0')*10+(usart1_buffer[6]-'0');
rtcTime.day=(usart1_buffer[7]-'0')*10+(usart1_buffer[8]-'0');
rtcTime.hour=(usart1_buffer[9]-'0')*10+(usart1_buffer[10]-'0');
rtcTime.min=(usart1_buffer[11]-'0')*10+(usart1_buffer[12]-'0');
rtcTime.sec=(usart1_buffer[13]-'0')*10+(usart1_buffer[14]-'0');
printf("时间:%04d/%02d/%02d %02d:%02d:%02d\n",rtcTime.year,
rtcTime.mon,
rtcTime.day,
rtcTime.hour,
rtcTime.min,
rtcTime.sec);
Time_To_Sec(&rtcTime);
}
/*
信息录入
数据格式:"#学号,姓名"
如:"#201411,小王"
*/
if(usart1_buffer[0]=='#')
{
u8 i=0;
//获取学号
char *p=(char *)&usart1_buffer[1];
while(*p!=',' && *p!='\0')
{
card_info.id[i++]=*p++;
}
card_info.id[i]='\0';
//获取姓名
if(*p==',')
{
p++;
i=0;
while(*p!='\0')
{
card_info.name[i++]=*p++;
}
card_info.name[i]='\0';
printf("id:%s 姓名:%s\n",card_info.id,card_info.name);
rtc_menu_cnt=0;//关闭时间显示
cnt=1;//进入录入界面
OLED_ClearGram();
OLED_DispalyFont(40,0,24,font_24[2]);
OLED_DispalyFont(40+24,0,24,font_24[3]);
OLED_Refresh();
}
}
usart1_cnt=0;
usart1_flag=0;
}
time++;
if(rtc_menu_cnt==3)//签到成功标志
{
time2++;
if(time2>=5000)//显示签到信息5s后回到时间界面
{
rtc_menu_cnt=1;
time2=0;
OLED_ClearGram();
}
}
Delay_Ms(1);
if(time>=500)
{
ds18b20_temp=DS18B20_GetTemp();
if(cnt==1)//录入
{
stat=RC522_SetCardInfo(&card_info);
if(stat==0)
{
OLED_ClearGram();
OLED_DispalyFont(40,0,24,font_24[0]);
OLED_DispalyFont(40+24,0,24,font_24[1]);
BEEP=1;
len=strlen((char *)card_info.id);
if(len<10)
{
x=(128-len*12)/2;
OLED_DisplayStr(x,30,24,card_info.id);//字符串显示
}
else
{
x=(128-len*8)/2;
OLED_DisplayStr(x,30,24,card_info.id);//字符串显示
}
OLED_Refresh();
Delay_Ms(500);
BEEP=0;
}
else
{
// printf("写卡失败stat=%d\n",stat);
}
}
else if(cnt==2)//签到
{
stat=RC522_GetCardInfo(&temp);
if(stat==0)
{
rtc_menu_cnt=0;//时间显示标志清空
BEEP=1;
OLED_ClearGram();
OLED_DispalyFont(40,0,24,font_24[2]);
OLED_DispalyFont(40+24,0,24,font_24[3]);
len=strlen((char *)card_info.id);
x=(128-len*8)/2;
OLED_DisplayStr(x,30,16,temp.id);//显示签到id
snprintf(buffer,sizeof(buffer),"Time:%02d:%02d:%02d",rtcTime.hour,rtcTime.min,rtcTime.sec);
len=strlen((char *)buffer);
x=(128-len*8)/2;
OLED_DisplayStr(x,30+16,16,(u8*)buffer);//显示签到id
Delay_Ms(200);
snprintf(buffer,sizeof(buffer),"*xh:%s",temp.id);
USART1_DMASend((u8*)buffer);
BEEP=0;
OLED_Refresh();
rtc_menu_cnt=3;//签到成功,显示5s后回到时间显示界面
time2=0;
}
}
LED1=!LED1;
time=0;
}
}
}