一、设计目标
该公交刷卡项目利用GEC6818开发板、读卡器、卡片实现一个功能强大,性能稳定、安全可靠、用户界面友好,便于管理的公交卡管理系统,为用户提供便捷的公交出行服务,同时也为后台管理提供高效的管理工具。
二、功能描述
(1)点击首界面的刷卡缴费系统,然后进行公交刷卡,刷卡成功后,蜂鸣器“滴”一声提示;
(2)首界面左下角是查询公交卡的消费记录与支出记录的按键,可以了解充卡与支出金额的记录;
(3)点击首界面后台管理系统,触屏输入账号与密码登录该系统后,可以进行充值,金额充值到公交卡里;
(4)后台管理系统可以修改乘车缴费金额。
三、设计方案
本设计方案旨在构建一个公交卡设备系统,选用RFID读卡器作为刷卡设备,能够快速准确读取公交卡信息,设计合理的数据库表结构,包括公交卡表(卡号、余额)、消费记录等信息,实现功能完善、安全可靠的公交卡系统。
四、系统框架
五、部分源码
font.c
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include "font.h"
#include "font_libs.h"
static int *tmp;
static int lcd_width;
static int lcd_height;
void Font_init(void *fb_addr,int w,int h)
{
tmp = (int *)fb_addr;
lcd_width = w;
lcd_height = h;
}
//画黑色方框,X Y 起点坐标,width宽度,height高度
int Create_LineEdit(int X,int Y,int width,int height,unsigned long color)
{
int x,y;
for(y=Y;y<Y+height && y<lcd_height;y++)
{
for(x=X;x<X+width && x<lcd_width;x++)
{
tmp[y*lcd_width+x] = color;
}
}
return 0;
}
//区域填充color,X Y 起点坐标,width宽度,height高度
int Clean_Area(int X,int Y,int width,int height,unsigned long color )
{
int x,y;
for(y=Y;y<Y+height && y<lcd_height;y++)
{
for(x=X;x<X+width && x<lcd_width;x++)
{
tmp[y*lcd_width+x] = color;
}
}
return 0;
}
/**画点***/
int Draw_Text16(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[])
{
unsigned short int i,j;
unsigned char mask,buffer;
for(i=0;i<16;i++)
{
mask =0x80; //掩码
buffer =ch[i*2]; //提取一行的第一个字节
for(j=0;j<8;j++)
{
if(buffer &mask)
{
tmp[(y+i)*lcd_width+x+j]= color; //为画笔上色
}
mask =mask >>1;
}
mask =0x80;
buffer =ch[i*2+1];
for(j=0;j<8;j++)
{
if(buffer &mask)
{
tmp[(y+i)*lcd_width+x+j+8]= color;
}
mask =mask>>1;
}
}
return 0;
}
int Draw_TextX(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[],int size)
{
unsigned short int i,j,k,m;
unsigned char mask,buffer;
for(i=0;i<16;i++)
{
mask =0x80; //掩码
buffer =ch[i*2]; //提取一行的第一个字节
for(j=0;j<8;j++)
{
if(buffer &mask)
{
for(k=0;k<size;k++)
{
for(m=0;m<size;m++)
{
tmp[(y+i*size+m)*lcd_width+x+j*size+k]= color;
}
}
}
mask =mask >>1;
}
mask =0x80;
buffer =ch[i*2+1];
for(j=0;j<8;j++)
{
if(buffer &mask)
{
for(k=0;k<size;k++)
{
for(m=0;m<size;m++)
{
tmp[(y+i*size+m)*lcd_width+x+(j+8)*size+k]= color;
}
}
}
mask =mask>>1;
}
}
return 0;
}
int Draw_ASCII(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[])
{
unsigned short int i,j;
unsigned char mask,buffer;
for(i=0;i<16;i++)
{
mask=0x80;
buffer=ch[i];
for(j=0;j<8;j++)
{
if(buffer&mask)
{
tmp[(y+i)*lcd_width+(x+j)]= color;
}
mask=mask>>1;
}
}
return 0;
}
int Draw_ASCIIX(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[],int size)
{
unsigned short int i,j,k,m;
unsigned char mask,buffer;
for(i=0;i<16;i++)
{
mask=0x80;
buffer=ch[i];
for(j=0;j<8;j++)
{
if(buffer&mask)
{
for(k=0;k<size;k++)
{
for(m=0;m<size;m++)
{
tmp[(y+i*size+m)*lcd_width+(x+j*size+k)]= color;
}
}
}
mask=mask>>1;
}
}
return 0;
}
//显示字符,中文的函数
int Display_character(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,unsigned long color)
{
int k, xx;
unsigned char qh,wh;
const unsigned char *mould;
unsigned int length =len;
for(k=0,xx=x;k<length-1;k++)
{
if(string[k]&0x80) //中文字符
{
qh =string[k]-0xa0; //区号
wh =string[k+1]-0xa0; //位号
mould =&__CHS[((qh-1)*94+wh-1)*32];
Draw_Text16(4+xx,y,color,mould);
xx+=16;
k++;
}
else
{
mould =&__ASCII[string[k]*16];
Draw_ASCII(4+xx,y,color,mould);
xx+=8;
}
}
return 0;
}
int Display_characterX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size,unsigned long color)
{
int k, xx;
unsigned char qh,wh;
const unsigned char *mould;
unsigned int length =len;
for(k=0,xx=x;k<length-1;k++)
{
if(string[k]&0x80) //中文字符
{
qh =string[k]-0xa0; //区号
wh =string[k+1]-0xa0; //位号
mould =&__CHS[((qh-1)*94+wh-1)*32];
Draw_TextX(4*size+xx,y,color,mould,size); //加4为了让每个中文之间有一定的间隙
xx+=16*size;//当前的中文字模为32*32,每次显示下一个中文时横向偏移32个bit
k++; //加载下一个中文,两次++操作,偏移2个字节
}
else
{
mould =&__ASCII[string[k]*16];
Draw_ASCIIX(4*size+xx,y,color,mould,size);
xx+=8*size;//当前的ASCII字模显示为8*16,每次显示下一个中文时横向偏移8个bit
}
}
return 0;
}
int Display_characterXX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size,unsigned long color)
{
int k, xx;
unsigned char qh,wh;
const unsigned char *mould;
unsigned int length =len;
for(k=0,xx=x;k<length-1;k++)
{
if(string[k]&0x80) //中文字符
{
qh =string[k]-0xa0; //区号
wh =string[k+1]-0xa0; //位号
mould =&__CHS[((qh-1)*94+wh-1)*32];
Draw_TextX(4*size+xx,y,color,mould,size); //加4为了让每个中文之间有一定的间隙
xx+=16*size;//当前的中文字模为32*32,每次显示下一个中文时横向偏移32个bit
k++; //加载下一个中文,两次++操作,偏移2个字节
}
else
{
mould =&__ASCII[string[k]*16];
Draw_ASCIIX(4*size+xx,y,color,mould,size);
xx+=8*size;//当前的ASCII字模显示为8*16,每次显示下一个中文时横向偏移8个bit
}
}
return 0;
}
/*
int Roll_Dispaly(unsigned char *str)
{
int x;
for(x=600;x>20;x--)
{
Display_characterXX(x,45,strlen(str)+1,str,4,0xFF0000);
usleep(50000);
Display_characterXX(x+1,45,strlen(str)+1,str,4,0xFFFFFFFF);
}
Display_characterXX(x+1,45,strlen(str)+1,str,4,0xFFFFFFFF);
return 0;
}
*/
demo2.c
/*
C语言代码实现了基于SQLite数据库的公交卡管理功能,包括打开数据库连接、创建表、插入、查询和更新公交卡记录。下面是各部分详细解释:
BusCard 结构体定义了公交卡的基本属性:card_id 和 balance 分别代表公交卡的唯一标识和当前余额。
open_database 函数负责打开或创建名为 db_name 的SQLite数据库,返回值指示操作是否成功。如果打开失败,则打印错误信息并返回非零错误码;否则输出提示信息并返回0。
create_table 函数创建名为 BusCards 的表,包含 CardID 和 Balance 两列,其中 CardID 是主键。使用 sqlite3_exec 执行SQL创建表语句。
insert_card 函数负责向数据库中插入一条公交卡记录。它使用预编译的SQL语句,通过 sqlite3_prepare_v2 准备SQL插入语句,并通过 sqlite3_bind_... 函数绑定相应的参数值。执行插入操作后,根据返回状态打印插入结果。
query_card 函数根据给定的 card_id 从数据库中查询公交卡记录,并将查询结果填充到 BusCard 结构体中返回。同样使用预编译SQL查询语句和参数绑定,根据查询结果的不同状态输出信息。
update_card_balance 函数用于更新公交卡在数据库中的余额。同样采用预编译SQL更新语句,根据公交卡ID更新对应的余额值。
main 函数中,按照上述功能顺序组织了一系列操作:打开数据库 -> 创建表 -> 插入公交卡记录 -> 查询公交卡记录 -> 更新公交卡余额 -> 关闭数据库连接。每个操作都按照异常处理的原则进行了适当的错误检查和信息输出。最后,程序正常结束并返回0。
余额不足设置,刷卡信息设置
*/
// // 引入标准输入输出头文件,用于打印错误信息等
// #include <stdio.h>
// // 引入SQLite3数据库操作头文件
// #include "sqlite3.h"
#include "demo2.h"
#include "sqlite3.h"
char *c[100]={"0"};
char *k[100]={"0"};
char *id_i[100]={"0"};
char *id_k[100]={"0"};
int ck = 0;
int ck_1=0;
int ck_id = 0;
// 定义一个函数,用于打开数据库连接并返回状态
int open_database(sqlite3 **db, const char *db_name) {
// 使用sqlite3_open打开或创建指定名称的数据库
int rc = sqlite3_open(db_name, db);
// 如果打开数据库失败,则打印错误信息并返回错误码
if (rc) {
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(*db));
return rc;
}
// 打开成功则输出提示信息,并返回0表示成功
printf("成功打开数据库\n");
return 0;
}
// 定义一个函数,用于创建公交卡表
void create_table(sqlite3 *db) {
// SQL语句:如果不存在则创建名为BusCards的表,包含CardID和Balance两列
const char *sql_create_table =
"CREATE TABLE IF NOT EXISTS BusCards ("
" CardID INTEGER PRIMARY KEY,"
" Balance REAL"
");";
// 执行SQL语句,若出错则打印错误信息
char *errmsg;
if (sqlite3_exec(db, sql_create_table, NULL, NULL, &errmsg)) {
fprintf(stderr, "创建表时出错: %s\n", errmsg);
sqlite3_free(errmsg);
} else {
printf("公交卡表创建成功\n");
}
}
// 定义一个函数,用于向数据库中插入一条公交卡记录
void insert_card(sqlite3 *db, const BusCard *card) {
// 准备插入数据的SQL语句,使用问号作为占位符
const char *sql_insert =
"INSERT INTO BusCards(CardID, Balance) VALUES (?, ?);";
sqlite3_stmt *stmt;
// 准备SQL语句并绑定公交卡结构体中的值
if (sqlite3_prepare_v2(db, sql_insert, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_int(stmt, 1, card->card_id);
sqlite3_bind_double(stmt, 2, card->balance);
// 执行插入操作
if (sqlite3_step(stmt) != SQLITE_DONE) {
//fprintf(stderr, "插入数据时出错: %s\n", sqlite3_errmsg(db));
} else {
printf("成功插入卡号为%X,余额为%.2f的公交卡记录\n", card->card_id, card->balance);
}
// 清理准备好的语句资源
sqlite3_finalize(stmt);
}
}
// 定义一个函数,用于从数据库中查询公交卡记录并返回结构体
BusCard query_card(sqlite3 *db, int card_id) {
// 初始化查询结果结构体
BusCard result = {0};
// 准备查询数据的SQL语句,使用问号作为占位符
const char *sql_query = "SELECT CardID, Balance FROM BusCards WHERE CardID = ?;";
sqlite3_stmt *stmt;
// 准备SQL语句并绑定要查询的卡号
if (sqlite3_prepare_v2(db, sql_query, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_int(stmt, 1, card_id);
// 执行查询操作
if (sqlite3_step(stmt) == SQLITE_ROW) {
// 从查询结果中提取公交卡信息并填充到结果结构体中
result.card_id = sqlite3_column_int(stmt, 0);
result.balance = sqlite3_column_double(stmt, 1);
printf("查询到的公交卡信息:卡号:%X,余额:%.2f\n", result.card_id, result.balance);
} else {
//printf("未找到卡号为%d的公交卡记录\n", card_id);
printf("未找到卡号为%X的公交卡记录\n", card_id);
}
// 清理准备好的语句资源
sqlite3_finalize(stmt);
}
// 返回查询结果
return result;
}
// 定义一个函数,用于更新公交卡在数据库中的余额
void update_card_balance(sqlite3 *db, const BusCard *card) {
// 准备更新数据的SQL语句,使用问号作为占位符
const char *sql_update = "UPDATE BusCards SET Balance = ? WHERE CardID = ?;";
sqlite3_stmt *stmt;
// 准备SQL语句并绑定公交卡ID和新的余额
if (sqlite3_prepare_v2(db, sql_update, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_double(stmt, 1, card->balance);
sqlite3_bind_int(stmt, 2, card->card_id);
// 执行更新操作
if (sqlite3_step(stmt) != SQLITE_DONE) {
fprintf(stderr, "更新数据时出错: %s\n", sqlite3_errmsg(db));
} else {
printf("成功更新卡号为%X的公交卡余额为%.2f\n", card->card_id, card->balance);
}
// 清理准备好的语句资源
sqlite3_finalize(stmt);
}
}
// 示例主函数,演示如何使用上述功能进行数据库操作
int demo_2(int a) {
sqlite3 *db;
// 打开数据库连接
if (open_database(&db, "buscard.db") == 0) {
// 创建公交卡表
create_table(db);
// 创建并插入一个新的公交卡记录
BusCard new_card = {ID, 50.0};
insert_card(db, &new_card);
// 查询并显示刚才插入的公交卡信息
BusCard queried_card = query_card(db, ID);
// 更新公交卡的余额
if(fufei_bz==1)
{
if(queried_card.balance<-a)
{
show_bmp("./car21.bmp");
printf("余额不足\n");
return 0;
}
}
queried_card.balance += a;
beep_start(0);
update_card_balance(db, &queried_card);
// 关闭数据库连接
sqlite3_close(db);
}
// 程序正常结束,返回0
return 0;
}
demo2.h
#ifndef _DEMO2_H_
#define _DEMO2_H_
#include <stdio.h>
#include "sqlite3.h"
#include "rfid_write_read.h"
#include "beep.h"
#include "show.h"
// 定义公交卡结构体,包含卡ID和余额两个字段
typedef struct {
int card_id; // 公交卡ID
double balance; // 公交卡余额
} BusCard;
int open_database(sqlite3 **db, const char *db_name);
void create_table(sqlite3 *db);
void insert_card(sqlite3 *db, const BusCard *card);
BusCard query_card(sqlite3 *db, int card_id);
void update_card_balance(sqlite3 *db, const BusCard *card);
int demo_2(int a);
extern char *c[100];
extern char *k[100];
extern char *id_i[100];
extern char *id_k[100];
extern int ck;
extern int ck_1;
extern int ck_id;
#endif
main.c
//显示第一个界面(首界面),登录界面的键盘设置,登录密码设置
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <linux/input.h>
#include "font.h"
#include "show.h"
#include "beep.h"
#include "Swiping_card.h"
#include "Development_board.h"
#include "manager.h"
#include "rfid_write_read.h"
#include "pthread.h"
#include "record.h"
int Login(void);
int get_xy(int *x,int *y);
int Display_font(int x0,int y0,int len,char *buf,int size,unsigned int color);
int main()
{
//线程
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 以分离属性启动线程
pthread_t tid;
pthread_create(&tid, &attr, (void *)shuaka, NULL);
for(int i=0;i<100;i++)
{
c[i]=malloc(12);
k[i]=malloc(12);
id_i[i]=malloc(12);
id_k[i]=malloc(12);
}
char s[]= "1 ";
strcpy(k[0],s);
//beep_start(0);
// led_off();//led全部关闭
char loginflag=0;
int x=0;
int y=0;
//先加载文件里面的账号和密码
do
{
show_bmp("./car1.bmp"); //显示第一个界面
get_xy(&x,&y);
if(x>283&&y>120&&x<555&&y<168) //直接进入刷卡付费界面----左
{
loginflag=1;
}
if(x>313&&y>205&&x<545&&y<256) //唤出键盘输入密码并且比对----右
{
Login();
loginflag=2;
}
if(x>0&&x<165&&y>372&&y<478) //535 310 625 365
{
loginflag=3;
}
switch (loginflag)
{
case 1:
printf("请刷卡付款\n");
slod();
loginflag=0;
break;
case 2:
//登录
printf("请输入密码\n");
loginflag=0;
break;
case 3:
record();
loginflag=0;
break;
}
}while(1);
}
int Login(void)//登录
{
printf("请输入密码");
show_bmp("./car31.bmp");
show(250,0,"./jianpan.bmp");//键盘图片
//char passwd[12]={'6','6','6','6','6','6'};//设置的密码
//char passwd[12]={'1','2','3'};//设置的密码
char passwd[12]={'6','6','6'};//设置的密码
char spasswd[12]={0};//存输入的密码
int bz3=0; //输入账号的标志位
int sr_bz3=1;//输入账号的标志位
int bz4=0; //输入密码的标志位
int sr_bz4=1;
int bz5 = 0;
int x=0;
int y=0;
while (1)
{
printf("请输入登录密码");
get_xy(&x,&y);
if(x>210&&x<587&&y>188&&y<242)
{
bz4=1;
}
if(x>250&&x<350&&y>270&&y<320) { bz4=2;}
else if(x>350&&x<450&&y>270&&y<320) { bz4=3;}
else if(x>450&&x<550&&y>270&&y<320) { bz4=4;}
else if(x>250&&x<350&&y>320&&y<370) { bz4=5;}
else if(x>350&&x<450&&y>320&&y<370) { bz4=6;}
else if(x>450&&x<550&&y>320&&y<370) { bz4=7;}
else if(x>250&&x<350&&y>370&&y<420) { bz4=8;}
else if(x>350&&x<450&&y>370&&y<420) { bz4=9;}
else if(x>450&&x<550&&y>370&&y<420) { bz4=10;}
else if(x>250&&x<350&&y>430&&y<480) { bz4=11;break;} //退出按键
else if(x>450&&x<550&&y>430&&y<480) { bz4=12;} //删除
switch(bz4)
{
case 2:
printf("%s\n",spasswd);strcat(spasswd,"1");bz4=0;sr_bz4=1;break;
case 3:
printf("%s\n",spasswd);strcat(spasswd,"2");bz4=0;sr_bz4=1;break;
case 4:
printf("%s\n",spasswd);strcat(spasswd,"3");bz4=0;sr_bz4=1;break;
case 5:
printf("%s\n",spasswd);strcat(spasswd,"4");bz4=0;sr_bz4=1;break;
case 6:
printf("%s\n",spasswd);strcat(spasswd,"5");bz4=0;sr_bz4=1;break;
case 7:
printf("%s\n",spasswd);strcat(spasswd,"6");bz4=0;sr_bz4=1;break;
case 8:
printf("%s\n",spasswd);strcat(spasswd,"7");bz4=0;sr_bz4=1;break;
case 9:
printf("%s\n",spasswd);strcat(spasswd,"8");bz4=0;sr_bz4=1;break;
case 10:
printf("%s\n",spasswd);strcat(spasswd,"9");bz4=0;sr_bz4=1;break;
case 11:
printf("%s\n",spasswd);;bz4=0;sr_bz4=0;bz5=1;break;
case 12:
printf("%s\n",spasswd);
spasswd[strlen(spasswd) - 1] = '\0'; // 删除最后一个字符
Display_font(50,165, strlen(spasswd) + 1, spasswd, 2, 0x000000); // 更新显示
break;
}
if(sr_bz4)
{
show_bmp("./car31.bmp");
show(250,0,"./jianpan.bmp");//键盘图片
//Display_font(211,200,strlen(spasswd)+1,spasswd,2,0x000000);
Display_font(60,168,strlen(spasswd)+1,spasswd,2,0x000000);
}
}
//对比
printf("开始对比\n");
if(strcmp(passwd,spasswd) == 0)
{
printf("登录成功\n");
manager();
}
else
{
printf("密码错误\n");
show_bmp("./car311.bmp");
sleep(5);
}
}
六、编译命令
在Ubuntu软件下编译
arm-linux-gcc main.c show.c font.c Swiping_card.c Development_board.c manager.c rfid_write_read.c demo2.c record.c beep.c -o main -lpthread -L ./ -lsqlite3
七、下载到开发板
把编译生成的main文件拖到._cache_MobaXterm_Personal_21.3软件的文件夹里,所涉及的图片.bmp也要拖进去才能实现效果。
执行编译命令:
(1) chmod 777 main
(2) ./main