1. 項目簡介
信息時代的校園, 離不開信息化的管理, 數字化"校園一卡通"建設是校園信息化建設的重要組成部分, 是為信息化校園提供信息采集的基礎工程也是獲取學校信息化服務的主要方式之一。
校園一卡通將只能 IC 卡的強大功能與計算機網絡的數字化理念融入校園, 將學校各個系統連為一體, 動態掌握每一持卡人情況, 極大提高學校的管理水平和服務質量。
本文介紹通過STM32 微控制器+RFID RC522設計的一個校園一卡通消費充值機的項目,可以模擬實現充值、消費、修改密碼、掛失、登錄、查詢.......等操作。
硬件介紹:
MCU:STM32F103ZE6
刷卡模塊: RFID-RC522
LCD屏: 正點原子的3.5寸LCD屏+觸摸屏
完整項目下載地址: https://download.csdn.net/download/xiaolong1126626497/63983899
視頻演示地址: https://live.csdn.net/v/182606
運行效果:
2. 項目實現
2.1 RFID-RC522模塊
RFID-RC522模塊直接淘寶購買的現成模塊,模塊實物圖如下:
RC522是NXP公司設計的13.56MHz非接觸式讀寫卡芯片,可以讀寫IC卡,具備低電壓、低成本、體積小的特點,本身支持SPI接口通信,任何單片機都與通信,SPI時序模擬也非常簡單。
現在地鐵卡、校園卡、公交卡都是屬于M1(S50)卡,M1卡內部有16個扇區,每個扇區分為4個塊,每個塊的容量是16個字節,每個扇區里的最后一個塊是存放密碼,每次對塊里的數據讀寫都需要驗證IC卡的密碼,只有具備寫權限才可以對塊進行讀寫,密碼驗證通過之后可以直接利用修改密碼、讀寫扇區等等,讀取卡號是不需要驗證密碼的。
關于IC卡的詳細介紹請看這里: https://blog.csdn.net/xiaolong1126626497/article/details/117075834
本項目里STM32與RCC522通信使用的SPI是模擬時序,可以很方便的移植到其他的單片機。
SPI模擬時序代如下:
/*
函數功能:移植接口--SPI時序讀寫一個字節
函數參數:data:要寫入的數據
返 回 值:讀到的數據
*/
u8 RC522_SPI_ReadWriteOneByte(u8 tx_data)
{
u8 rx_data=0;
u8 i;
for(i=0;i<8;i++)
{
RC522_SCLK=0;
if(tx_data&0x80){RC522_OUTPUT=1;}
else {RC522_OUTPUT=0;}
tx_data<<=1;
RC522_SCLK=1;
rx_data<<=1;
if(RC522_INPUT)rx_data|=0x01;
}
return rx_data;
}
/*
函數功能:初始化RC522的IO口
*/
void RC522_IO_Init(void)
{
RCC->APB2ENR |= 0x01 << 0;
AFIO->MAPR |= 0x01 << 26;
RCC->APB2ENR |= 0x01 << 2; //PA時鐘使能
//#define RC522_CS PAout(10)
//#define RC522_SCLK PAout(13)
//#define RC522_OUTPUT PAout(14)
//#define RC522_INPUT PAin(15)
//#define RC522_RST PAout(0)
GPIOA->CRL &= 0xFFFFFFF0;
GPIOA->CRL |= 0x00000003;
GPIOA->CRH &= 0x000FF0FF;
GPIOA->CRH |= 0x43330300;
RC522_CS = 1;
RC522_SCLK = 1;
}
2.2 LCD屏
LCD使用的是正點原子3.5寸屏,驅動芯片是NT35310,支持8080時序,本身STM32大容量芯片具備FSMC接口的,可以直接使用FSMC接口操作LCD屏完成操作,這里考慮到程序的移植性,因為小容量,中容量的比如STM32F103C8T6就沒有FSMC接口,為了方便程序可以移植到這些開發板正常運行,當前項目采用的是模擬8080時序方式,直接使用GPIO口模擬時序操作LCD屏;雖然刷屏效率比FSMC慢不少,但是本項目的界面也不需要很高的刷新率,沒有圖頻繁的切換效果,所以整體效果還是不錯的。
模擬時序代碼如下: 如果要移植到其他單片機上,只需要修改GPIO口即可。
void lcd_write_cmd(u8 reg)
{
LCD_CS = 0; //拉低片選腳,選中 LCD
LCD_RS = 0; //拉低數據/命令控制線,選擇要操作命令
LCD_RD = 1; //禁止讀
LCD_WR = 0; //拉低 WR,準備寫操作
//數據總線輸出命令, 把要發送的命令放到數據總線上
GPIOB->ODR = (u16)reg;
LCD_WR = 1; //拉高 WR 寫使能
LCD_CS = 1; //拉高片選,結束操作
}
void lcd_write_data(u16 data)
{
LCD_CS = 0; //拉低片選腳,選中 LCD
LCD_RS = 1; //拉高數據/命令控制線,選擇要操作數據
LCD_RD = 1; //禁止讀
LCD_WR = 0; //拉低 WR,準備寫操作
//數據總線輸出數據, 把要發送的數據放到數據總線上
GPIOB->ODR = data;
LCD_WR = 1; //拉高 WR 寫使能
LCD_CS = 1; //拉高片選,結束操作
}
void lcd_set_cursor(u16 x, u16 y)
{
lcd_write_cmd(SET_X_ADDR);
lcd_write_data(x>>8);
lcd_write_data(x&0xff);
lcd_write_cmd(SET_Y_ADDR);
lcd_write_data(y>>8);
lcd_write_data(y&0xff);
}
void lcd_write_reg(u16 cmd, u16 parameter)
{
lcd_write_cmd(cmd);
lcd_write_data(parameter);
}
void lcd_draw_dot(u16 x, u16 y, u16 color)
{
lcd_set_cursor(x, y);
lcd_write_cmd(WRITE_MEMORY_START);
lcd_write_data(color); // [15:0] --> [R4-R0:G5-G0:B4-B0]
}
void lcd_show_screen(const u8 * image, u32 size, u16 x, u16 y)
{
u32 i = 0;
lcd_set_cursor(x, y); //設置光標位置
lcd_write_cmd(WRITE_MEMORY_START); //開始寫入GRAM
while( i < size ){
lcd_write_data( *image<<8 | *(image+1) );
image += 2;
++i;
}
}
//畫矩形
//(x1,y1),(x2,y2):矩形的對角坐標
void lcd_draw_rectblock(u16 y1, u16 y2, u16 color)
{
u16 i;
for( ; y1<=y2; ++y1){
lcd_set_cursor(0,y1); //設置光標位置
lcd_write_cmd(WRITE_MEMORY_START); //開始寫入GRAM
for(i=0; i<320; ++i){
lcd_write_data( color ); //寫數據
}
}
}
void lcd_show_image(const u8 * image, u16 width, u16 high, u16 x, u16 y)
{
u32 i,j;
for(i=0; i0)incx=1; //設置單步方向
else if(delta_x==0)incx=0;//垂直線
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平線
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x; //選取基本增量坐標軸
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//畫線輸出
{
lcd_draw_dot(uRow,uCol, WHITE);//畫點
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
void lcd_draw_circle(u16 x,u16 y,u8 r, u16 color)
{
int a, b, di;
a = 0;
b = r;
di = 3 - (r << 1); //判斷下個點位置的標志
while(a<=b){
lcd_draw_dot(x+a,y-b, color); //5
lcd_draw_dot(x+b,y-a, color); //0
lcd_draw_dot(x+b,y+a, color); //4
lcd_draw_dot(x+a,y+b, color); //6
lcd_draw_dot(x-a,y+b, color); //1
lcd_draw_dot(x-b,y+a, color);
lcd_draw_dot(x-a,y-b, color); //2
lcd_draw_dot(x-b,y-a, color); //7
++a;
//使用Bresenham算法畫圓
if(di < 0)
di += 4*a + 6;
else{
di+=10+4*(a-b);
--b;
}
}
}
void lcd_clear(u16 color)
{
u32 index;
u32 point;
point = 480*320; //得到總點數
lcd_set_cursor(0x00,0x00); //設置光標位置
lcd_write_cmd(WRITE_MEMORY_START); //開始寫入GRAM
for(index=0; index;>;>
3.3 觸摸屏
觸摸屏是LCD屏本身自帶的,觸摸芯片是XPT2046,是一個12位的ADC芯片,通信協議是SPI時序。
項目里采用模擬SPI時序進行與觸摸屏芯片通信,因為XPT2046本身是ADC芯片,所以在屏幕上按下后讀取出來的數據是模擬數據值—物理坐標值,我們還需要將它轉為屏幕坐標與LCD屏的像素點對應起來,這樣使用起來才比較方便。
XPT2046核心代碼如下:
#include "stm32f10x.h"
#include "xpt2046.h"
#include "delay.h"
/*
#define T_SCK PAout(12)
#define T_MI PAin(6)
#define T_MO PAout(11)
#define T_PEN PAin(7)
#define T_CS PAout(8)
*/
void xpt2046_init(void)
{
RCC->APB2ENR |= 0x01 << 2; // ENABLE port a clock
GPIOA->CRL &= 0x00FFFFFF; // 浮空輸入
GPIOA->CRL |= 0x44000000; // 推挽輸出
GPIOA->CRH &= 0xFFF00FF0;
GPIOA->CRH |= 0x00033003;
T_CS = 1;
T_SCK = 0;
}
/*
cmd format 1: 10010000 0x90 Y-POSITION Measure
cmd format 2: 11010000 0xd0 X-POSITION Measure
*/
u16 xpt2046_read(u8 cmd)
{
T_SCK = 0;
T_MO = 0;
T_CS = 0;
for(u8 i=0; i<8; ++i){
T_SCK = 0;
if( cmd & 0x80 )
T_MO = 1;
else
T_MO = 0;
cmd <<= 1;
T_SCK = 1;
}
// 15時鐘周期轉換
T_SCK = 0;
T_MO = 0;
T_SCK = 1;
u16 data = 0;
for(u8 i=0; i<12; ++i){
T_SCK = 0;
data <<= 1;
T_SCK = 1;
if( T_MI )
data |= 0x01;
}
T_CS = 1;
return (data);
}
u8 xpt2046_position(TOUCH * xpt2046_pos)
{
if( !T_PEN ){
u8 i, j;
u16 tmp;
u16 x[16], y[16];
for(i=0; i<16; ++i){
x[i] = xpt2046_read(XPOS);
y[i] = xpt2046_read(YPOS);
}
for(i=0; i<16; ++i){
for(j=0; j<16-i; ++j){
if(x[j]>x[j+1]){
tmp = x[j];
x[j] = x[j+1];
x[j+1] = tmp;
}
if(y[j]>y[j+1]){
tmp = y[j];
y[j] = y[j+1];
y[j+1] = tmp;
}
}
}
u32 sum_x, sum_y;
sum_x = sum_y =0;
for(i=3; i<13; i++){
sum_x += x[i];
sum_y += y[i];
}
xpt2046_pos->x = sum_x / 10;
xpt2046_pos->y = sum_y / 10;
return 0;
}
else
return 1;
}
u8 touch_position(TOUCH * touch_pos)
{
TOUCH xpt2046_pos;
if( !xpt2046_position(&xpt2046_pos) ){
touch_pos->x = 320 - (xpt2046_pos.x - 300) / 11.25;
touch_pos->y = 480 - (xpt2046_pos.y - 200) / 7.7;
return 0;
}
else{
touch_pos->x = 0xffff;
touch_pos->y = 0xffff;
return 1;
}
}
審核編輯:湯梓紅
-
IC卡
+關注
關注
2文章
165瀏覽量
34114 -
STM32
+關注
關注
2270文章
10895瀏覽量
355729 -
一卡通
+關注
關注
1文章
24瀏覽量
9658
發布評論請先 登錄
相關推薦
評論