1. 需求
需求: 一个寝室一个标签,设备端扫描标签,通过显示屏键入维修成功与否之类的的信息提交到平台
系统构架: 一个设备端 一个上位机
硬件选型: (1)STM32F103RCT6作为设备端的主控MCU (2)RC522作为设备端的射频刷卡设备,读写IC卡 (3)多张IC卡,模拟代表每个宿舍
实现思路: 设计一个上位机,用来管理查看维修检修信息,当维修寝室的设备或者检修完成时,通过STM32上的RC522刷一下这个寝室的IC卡,识别这是哪个寝室,识别成功后在软件上弹出一个对话框,填写本次维修或者检修的事件,填写完毕点击提交即可。
软件分为两个功能: (1)注册功能:每个寝室都有一张IC卡,这张卡第一次使用需要在维检系统里进行注册,填写这个IC卡对应的这个寝室的信息。 (2)维修、检修报告提交:当完成检修、检修之后,填写报告。 (3)查看历史记录,可以查看维修,检修的所有详细报告信息,可以导出execl表格,方便公布出去公众查看。 (4)注册、维修、检修记录都存放在数据库里,方便管理。
硬件的具体功能: STM32上有两个按键,一个LCD屏,一个RC522模块,当终端刷卡后,LCD显示屏会弹出一个询问提示? “当前是维修还是检修”,按下按键1或者按键2之后,就会将卡号上传到上位机。 上位机与下位机通过串口进行通信。
2. 演示效果
3. 上位机软件设计
3.1 通信说明
上位机与设备之间通过串口进行通信,上位机里使用SQLITE数据库存放所有关键信息,数据库里创建了3张表,一张表存放维检信息,一张表是账号信息,一张表是意见反馈记录。
代码语言:javascript复制A,F39A471B, 注册
B,F39A471B, 检修
C,F39A471B, 维修
3.2 搭建开发环境
上位机软件采用Qt框架设计,Qt是一个跨平台的C 图形用户界面应用程序框架。Qt是一个1991年由Qt Company开发的跨平台C 图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。简单来说,QT可以很轻松的帮你做带界面的软件,甚至不需要你投入很大精力。
QT官网: https://www.qt.io/
QT学习入门实战专栏文章: https://blog.csdn.net/xiaolong1126626497/category_11400392.html
QT5.12.6的下载地址: https://download.qt.io/archive/qt/5.12/5.12.6/
打开下载链接后选择下面的版本进行下载:
qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details
软件安装时断网安装,否则会提示输入账户。
安装的时候,第一个复选框里勾选一个mingw 32编译器即可,其他的不管默认就行,直接点击下一步继续安装。
选择MinGW 32-bit 编译器:
选择MinGW 32-bit 编译器:
安装好之后,将源码工程复制到英文路径下,双击工程文件打开。
工程打开之后,点击左下角的绿色三角形按钮即可编译运行。
运行效果如下:
3.3 效果
3.4 数据库的插入代码示例
代码语言:javascript复制//在数据库创建维检信息表
void Widget::CreateStudentSurface()
{
//数据库:建表,如果存在就不创建,不存在就创建
QSqlQuery sql_query(database);
//下面语句查询指定的表是否存在.
sql_query.exec(QString("select count(*) from sqlite_master where type='table' and name='%1'").arg("student"));
if(sql_query.next())
{
if(sql_query.value(0).toInt()==0)
{
qDebug("维检记录数据库表是不存在的.准备创建.n");
//创建表格 创建表格语句:create table <table_name> (f1 type1, f2 type2,…);
/* CREATE TABLE 是告诉数据库系统创建一个新表的关键字。
* CREATE TABLE 语句后跟着表的唯一的名称
* 或标识*/
/*下面的语句: 创建一个名称为student的表,字段分别是ID、编号、地点名称、类型、事件*/
QString create_sql = "create table student(id int primary key, number varchar(100),name varchar(100),type varchar(100),enevt varchar(1024))";
sql_query.prepare(create_sql);
if(!sql_query.exec())
{
Log_Text_Display("维检记录数据库表创建失败.n");
}
else
{
Log_Text_Display("维检记录数据库表创建成功.n");
}
}
else
{
Log_Text_Display("维检记录数据库表是存在的.不需要创建.n");
}
}
}
//添加信息
void Widget::on_pushButton_add_student_clicked()
{
QString number=ui->lineEdit_student_number->text();
QString name=ui->lineEdit_student_name->text();
if(number.isEmpty()||name.isEmpty())
{
QMessageBox::information(this,"提示","请认真填写数据后再添加.",
QMessageBox::Ok,QMessageBox::Ok);
return;
}
//添加维检地点信息
LuRu_data(number,name);
}
//录入数据 传入设备编号和地点名称
void Widget::LuRu_data(QString number,QString name)
{
//没有表就先创建
CreateStudentSurface();
//添加数据到表
//保存数据到数据库
QSqlQuery sql_query(database);
//查询原数据库表里有没有重复数据
//查询全部数据
sql_query.prepare("select * from student");
if(!sql_query.exec())
{
Log_Text_Display("维检记录数据库查询错误.n");
}
else
{
while(sql_query.next())
{
//ID、编号、地点名称、类型、事件
// int id = sql_query.value(0).toInt(); //ID
QString find_number = sql_query.value(1).toString(); //编号
QString find_name = sql_query.value(2).toString(); //地点名称
QString find_type = sql_query.value(3).toString(); //类型
QString find_event = sql_query.value(4).toString(); //事件
//判断编号有没有冲突的
if(number==find_number)
{
QMessageBox::information(this,"提示","你输入的编号在数据库已经存在!n请认真填写.",
QMessageBox::Ok,QMessageBox::Ok);
return;
}
}
}
//准备插入数据
//查询最大ID
QString select_max_sql = "select max(id) from student";
int max_id = 0;
sql_query.prepare(select_max_sql);
if(!sql_query.exec())
{
Log_Text_Display("维检信息表最大ID查找失败.n");
}
else
{
while(sql_query.next())
{
max_id = sql_query.value(0).toInt();
}
Log_Text_Display(QString("data base max id:%1n").arg(max_id));
//添加数据
//插入数据 插入语句:insert into <table_name> values (value1, value2,…);
QString insert_sql = tr("insert into student values(?,?,?,?,?)");
sql_query.prepare(insert_sql);
//ID、编号、地点名称、类型、事件
sql_query.addBindValue(max_id 1); //id
sql_query.addBindValue(number);
sql_query.addBindValue(name);
sql_query.addBindValue("----");
sql_query.addBindValue("----");
if(!sql_query.exec())
{
Log_Text_Display("维检信息表数据插入失败.n");
return;
}
else //插入成功
{
//插入成功就清除页面
ui->lineEdit_student_name->clear();
// ui->lineEdit_student_type->clear();
ui->lineEdit_student_number->clear();
}
}
//更新维检信息表格数据显示
on_pushButton_update_student_clicked();
}
4. STM32设备端设计
如果需要全部工程源码、上位机的源码可以在这里下载即可: https://download.csdn.net/download/xiaolong1126626497/85682742
如果需要看项目视频演示,可以看这里:
基于STM32 RFID设计的宿舍维检管理系统
4.1 汉字取模
4.2 keil工程
4.3 硬件连线
代码语言:javascript复制RC522射频模块外部的接口:
*1--SDA <----->PB5--片选脚
*2--SCK <----->PB4--时钟线
*3--MOSI<----->PA12--输出
*4--MISO<----->PA11--输入
*5--悬空
*6--GND <----->GND
*7--RST <----->PA8--复位脚
*8--VCC <----->VCC
4.4 main.c代码
代码语言:javascript复制#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "RFID_RC522.h"
#include "key.h"
#include "beep.h"
unsigned char SN[4]={88,88,88,88}; //默认卡号
u8 SendBuff[50];
/*
函数功能: 打印卡号
*/
void print_info(unsigned char *p,int cnt)
{
int i;
for(i=0;i<cnt;i )
{
printf("0x%X ",p[i]);
}
printf("rn");
}
/*
函数功能: 读卡号--电子标签的卡号
返回值: 1成功 0失败
*/
int ReadCardNumber(void)
{
unsigned char CT[2];//卡类型
u8 status=1;
status=RC522_PcdRequest(PICC_REQIDL ,CT);//(寻卡模式,卡类型),成功返回0
if(status==MI_OK)//寻卡成功
{
status=MI_ERR;
status=RC522_PcdAnticoll(SN); //防冲撞,成功返回0,SN是读到卡号的地址
printf("卡类型:");
print_info(CT,2);//打印类型
printf("卡号:");
print_info(SN,4);//打印卡号
return 1;
}
return 0;
}
int main(void)
{
u32 cnt=0;
u8 key=0;
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init();
KEY_Init();
BEEP_Init();
POINT_COLOR=RED;
RC522_Init(); //RC522
//LCD_DisplayData(24*1,0,24,24,font_data[0]);
//LCD_DisplayData(24*2,0,24,24,1);
//LCD_DisplayData(24*3,0,24,24,2);
DisplayData(24*0,0,24,24,(u8*)font_data[0],WHITE,BLACK);
DisplayData(24*1,0,24,24,(u8*)font_data[1],WHITE,BLACK);
DisplayData(24*2,0,24,24,(u8*)font_data[2],WHITE,BLACK);
DisplayData(24*3,0,24,24,(u8*)font_data[3],WHITE,BLACK);
DisplayData(24*4,0,24,24,(u8*)font_data[4],WHITE,BLACK);
DisplayData(24*5,0,24,24,(u8*)font_data[5],WHITE,BLACK);
DisplayData(24*6,0,24,24,(u8*)font_data[6],WHITE,BLACK);
DisplayData(24*7,0,24,24,(u8*)font_data[7],WHITE,BLACK);
DisplayData(24*8,0,24,24,(u8*)font_data[8],WHITE,BLACK);
DisplayData(24*9,0,24,24,(u8*)font_data[9],WHITE,BLACK);
delay_ms(100);
while(1)
{
//读取卡号
if(ReadCardNumber())
{
DisplayData(0,100,24,24,(u8*)font_data[16],WHITE,BLACK);
DisplayData(24,100,24,24,(u8*)font_data[17],WHITE,BLACK);
sprintf((char*)SendBuff,"%X%X%X%X",SN[0],SN[1],SN[2],SN[3]);
LCD_ShowString(24*3,105,300,300,16," ");
LCD_ShowString(24*3,105,300,300,16,SendBuff);
BEEP=1;
delay_ms(100);
BEEP=0;
//注册
DisplayData(0,200,24,24,(u8*)font_data[10],WHITE,BLACK);
DisplayData(24*1,200,24,24,(u8*)font_data[11],WHITE,BLACK);
//检修
DisplayData(24*2 24*3,200,24,24,(u8*)font_data[12],WHITE,BLACK);
DisplayData(24*3 24*3,200,24,24,(u8*)font_data[13],WHITE,BLACK);
//维修
DisplayData(24*4 24*6,200,24,24,(u8*)font_data[14],WHITE,BLACK);
DisplayData(24*5 24*6,200,24,24,(u8*)font_data[15],WHITE,BLACK);
while(1)
{
key=KEY_Scan(0);
if(key)
{
printf("key=%drn",key);
//注册
if(key==3)
{
sprintf((char*)SendBuff,"A,%X%X%X%X,",SN[0],SN[1],SN[2],SN[3]);
printf("%srn",SendBuff);
}
//检修
else if(key==2)
{
sprintf((char*)SendBuff,"B,%X%X%X%X,",SN[0],SN[1],SN[2],SN[3]);
printf("%srn",SendBuff);
}
//维修
else if(key==1)
{
sprintf((char*)SendBuff,"C,%X%X%X%X,",SN[0],SN[1],SN[2],SN[3]);
printf("%srn",SendBuff);
}
break;
}
delay_ms(50);
LED0=!LED0;
}
LCD_Clear(WHITE);
DisplayData(24*0,0,24,24,(u8*)font_data[0],WHITE,BLACK);
DisplayData(24*1,0,24,24,(u8*)font_data[1],WHITE,BLACK);
DisplayData(24*2,0,24,24,(u8*)font_data[2],WHITE,BLACK);
DisplayData(24*3,0,24,24,(u8*)font_data[3],WHITE,BLACK);
DisplayData(24*4,0,24,24,(u8*)font_data[4],WHITE,BLACK);
DisplayData(24*5,0,24,24,(u8*)font_data[5],WHITE,BLACK);
DisplayData(24*6,0,24,24,(u8*)font_data[6],WHITE,BLACK);
DisplayData(24*7,0,24,24,(u8*)font_data[7],WHITE,BLACK);
DisplayData(24*8,0,24,24,(u8*)font_data[8],WHITE,BLACK);
DisplayData(24*9,0,24,24,(u8*)font_data[9],WHITE,BLACK);
}
cnt ;
delay_ms(10);
if(cnt>=20)
{
cnt=0;
LED1=!LED1;
LED0=!LED0;
}
}
}