1. 項(xiàng)目簡(jiǎn)介
信息時(shí)代的校園, 離不開信息化的管理, 數(shù)字化"校園一卡通"建設(shè)是校園信息化建設(shè)的重要組成部分, 是為信息化校園提供信息采集的基礎(chǔ)工程也是獲取學(xué)校信息化服務(wù)的主要方式之一。
校園一卡通將只能 IC 卡的強(qiáng)大功能與計(jì)算機(jī)網(wǎng)絡(luò)的數(shù)字化理念融入校園, 將學(xué)校各個(gè)系統(tǒng)連為一體, 動(dòng)態(tài)掌握每一持卡人情況, 極大提高學(xué)校的管理水平和服務(wù)質(zhì)量。
本文介紹通過STM32 微控制器+RFID RC522設(shè)計(jì)的一個(gè)校園一卡通消費(fèi)充值機(jī)的項(xiàng)目,可以模擬實(shí)現(xiàn)充值、消費(fèi)、修改密碼、掛失、登錄、查詢.......等操作。
硬件介紹:
MCU:STM32F103ZE6
刷卡模塊: RFID-RC522
LCD屏: 正點(diǎn)原子的3.5寸LCD屏+觸摸屏
完整項(xiàng)目下載地址: https://download.csdn.net/download/xiaolong1126626497/63983899
視頻演示地址: https://live.csdn.net/v/182606
運(yùn)行效果:
2. 項(xiàng)目實(shí)現(xiàn)
2.1 RFID-RC522模塊
RFID-RC522模塊直接淘寶購買的現(xiàn)成模塊,模塊實(shí)物圖如下:
RC522是NXP公司設(shè)計(jì)的13.56MHz非接觸式讀寫卡芯片,可以讀寫IC卡,具備低電壓、低成本、體積小的特點(diǎn),本身支持SPI接口通信,任何單片機(jī)都與通信,SPI時(shí)序模擬也非常簡(jiǎn)單。
現(xiàn)在地鐵卡、校園卡、公交卡都是屬于M1(S50)卡,M1卡內(nèi)部有16個(gè)扇區(qū),每個(gè)扇區(qū)分為4個(gè)塊,每個(gè)塊的容量是16個(gè)字節(jié),每個(gè)扇區(qū)里的最后一個(gè)塊是存放密碼,每次對(duì)塊里的數(shù)據(jù)讀寫都需要驗(yàn)證IC卡的密碼,只有具備寫權(quán)限才可以對(duì)塊進(jìn)行讀寫,密碼驗(yàn)證通過之后可以直接利用修改密碼、讀寫扇區(qū)等等,讀取卡號(hào)是不需要驗(yàn)證密碼的。
關(guān)于IC卡的詳細(xì)介紹請(qǐng)看這里: https://blog.csdn.net/xiaolong1126626497/article/details/117075834
本項(xiàng)目里STM32與RCC522通信使用的SPI是模擬時(shí)序,可以很方便的移植到其他的單片機(jī)。
SPI模擬時(shí)序代如下:
/*
函數(shù)功能:移植接口--SPI時(shí)序讀寫一個(gè)字節(jié)
函數(shù)參數(shù):data:要寫入的數(shù)據(jù)
返 回 值:讀到的數(shù)據(jù)
*/
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;
}
/*
函數(shù)功能:初始化RC522的IO口
*/
void RC522_IO_Init(void)
{
RCC->APB2ENR |= 0x01 << 0;
AFIO->MAPR |= 0x01 << 26;
RCC->APB2ENR |= 0x01 << 2; //PA時(shí)鐘使能
//#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使用的是正點(diǎn)原子3.5寸屏,驅(qū)動(dòng)芯片是NT35310,支持8080時(shí)序,本身STM32大容量芯片具備FSMC接口的,可以直接使用FSMC接口操作LCD屏完成操作,這里考慮到程序的移植性,因?yàn)樾∪萘浚腥萘康谋热鏢TM32F103C8T6就沒有FSMC接口,為了方便程序可以移植到這些開發(fā)板正常運(yùn)行,當(dāng)前項(xiàng)目采用的是模擬8080時(shí)序方式,直接使用GPIO口模擬時(shí)序操作LCD屏;雖然刷屏效率比FSMC慢不少,但是本項(xiàng)目的界面也不需要很高的刷新率,沒有圖頻繁的切換效果,所以整體效果還是不錯(cuò)的。
模擬時(shí)序代碼如下: 如果要移植到其他單片機(jī)上,只需要修改GPIO口即可。
void lcd_write_cmd(u8 reg)
{
LCD_CS = 0; //拉低片選腳,選中 LCD
LCD_RS = 0; //拉低數(shù)據(jù)/命令控制線,選擇要操作命令
LCD_RD = 1; //禁止讀
LCD_WR = 0; //拉低 WR,準(zhǔn)備寫操作
//數(shù)據(jù)總線輸出命令, 把要發(fā)送的命令放到數(shù)據(jù)總線上
GPIOB->ODR = (u16)reg;
LCD_WR = 1; //拉高 WR 寫使能
LCD_CS = 1; //拉高片選,結(jié)束操作
}
void lcd_write_data(u16 data)
{
LCD_CS = 0; //拉低片選腳,選中 LCD
LCD_RS = 1; //拉高數(shù)據(jù)/命令控制線,選擇要操作數(shù)據(jù)
LCD_RD = 1; //禁止讀
LCD_WR = 0; //拉低 WR,準(zhǔn)備寫操作
//數(shù)據(jù)總線輸出數(shù)據(jù), 把要發(fā)送的數(shù)據(jù)放到數(shù)據(jù)總線上
GPIOB->ODR = data;
LCD_WR = 1; //拉高 WR 寫使能
LCD_CS = 1; //拉高片選,結(jié)束操作
}
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); //設(shè)置光標(biāo)位置
lcd_write_cmd(WRITE_MEMORY_START); //開始寫入GRAM
while( i < size ){
lcd_write_data( *image<<8 | *(image+1) );
image += 2;
++i;
}
}
//畫矩形
//(x1,y1),(x2,y2):矩形的對(duì)角坐標(biāo)
void lcd_draw_rectblock(u16 y1, u16 y2, u16 color)
{
u16 i;
for( ; y1<=y2; ++y1){
lcd_set_cursor(0,y1); //設(shè)置光標(biāo)位置
lcd_write_cmd(WRITE_MEMORY_START); //開始寫入GRAM
for(i=0; i<320; ++i){
lcd_write_data( color ); //寫數(shù)據(jù)
}
}
}
void lcd_show_image(const u8 * image, u16 width, u16 high, u16 x, u16 y)
{
u32 i,j;
for(i=0; i0)incx=1; //設(shè)置單步方向
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; //選取基本增量坐標(biāo)軸
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//畫線輸出
{
lcd_draw_dot(uRow,uCol, WHITE);//畫點(diǎn)
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); //判斷下個(gè)點(diǎn)位置的標(biāo)志
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; //得到總點(diǎn)數(shù)
lcd_set_cursor(0x00,0x00); //設(shè)置光標(biāo)位置
lcd_write_cmd(WRITE_MEMORY_START); //開始寫入GRAM
for(index=0; index;>;>
3.3 觸摸屏
觸摸屏是LCD屏本身自帶的,觸摸芯片是XPT2046,是一個(gè)12位的ADC芯片,通信協(xié)議是SPI時(shí)序。
項(xiàng)目里采用模擬SPI時(shí)序進(jìn)行與觸摸屏芯片通信,因?yàn)閄PT2046本身是ADC芯片,所以在屏幕上按下后讀取出來的數(shù)據(jù)是模擬數(shù)據(jù)值—物理坐標(biāo)值,我們還需要將它轉(zhuǎn)為屏幕坐標(biāo)與LCD屏的像素點(diǎn)對(duì)應(yīng)起來,這樣使用起來才比較方便。
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時(shí)鐘周期轉(zhuǎn)換
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卡
+關(guān)注
關(guān)注
2文章
165瀏覽量
34123 -
STM32
+關(guān)注
關(guān)注
2270文章
10900瀏覽量
355985 -
一卡通
+關(guān)注
關(guān)注
1文章
24瀏覽量
9659
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論