從上高中開始,我們學校用的餐卡就為接觸式的IC卡,在校園里還分布著很多的IC卡電話,上大學以后學校使用的校園一卡通,為非接觸式是射頻式IC卡,因此對IC卡有了興趣,在學習單片機的過程中,了解到單片機可以實現(xiàn)IC卡的讀寫控制,在參考有關資料的基礎上,學習的利用單片機實現(xiàn)接觸式的IC卡讀寫控制。
主要器件:
1、 AT89S8252單片機芯片,此芯片具有SPI接口,可以用來讀寫IC卡芯片。
2、 使用與SPI接口兼容的串行數(shù)據(jù)接口的IC卡芯片AT45D041A,支持在系統(tǒng)重編程,可用于數(shù)字語音、圖像和數(shù)據(jù)的存儲。
試驗流程圖:
試驗電路圖:
試驗程序代碼:
//ICRdWr.h程序
#ifndef _ICRDWR_H // 防止ICRdWr.h被重復引用
#define _ICRDWR_H
#include // 重要的頭文件引用
#define uchar unsigned char
#define uint unsigned int
/* 指令宏定義 */
#define BUFFER_1_WRITE 0x84 // buffer1寫指令代碼
#define B1_TO_MM_PAGE_NO_ERA 0x88 // 無在線擦除的buffer1寫主內(nèi)存頁指令代碼
#define MM_PAGE_READ 0xD2 // 主內(nèi)存頁讀指令代碼
#define STAT_REG_READ 0xD7 // 狀態(tài)寄存器讀指令代碼
#define DATA_IN_MAX_LEN 8
#define DATA_OUT_MAX_LEN 8
uint page_start_addr; // 頁中起始字節(jié)地址
uint page_addr; // 頁地址,16位中低9位為有效位
uint buf_start_addr; // buffer中起始字節(jié)地址,16位中低11位為有效位
uchar data_in[DATA_IN_MAX_LEN]; // 要寫入IC卡的數(shù)據(jù)
uchar data_out[DATA_OUT_MAX_LEN]; // 要從IC卡中讀出的數(shù)據(jù)
#endif
//ICRdWr.c程序
#include "ICRdWr.h"
/* 延時t毫秒 */
void delay(uint t)
{
uint i;
while(t--)
{
/* 對于11.0592M時鐘,約延時1ms */
for (i=0;i<125;i++)
{}
}
}
/* 獲取需要存入IC卡數(shù)據(jù)的函數(shù)*/
void getdata()
{
// 此函數(shù)簡化如下:
uchar i;
for (i=0;i<8;i++)
data_in[i]=i+1;
}
/* 寫單片機AT89S8252的SPDR寄存器,數(shù)據(jù)通過SPI口串行輸出給IC卡芯片 */
void write_spi(uchar dat)
{
SPDR = dat;
while (!(SPSR & 0x80)) ; // 等待一次傳輸完成
}
/* 獲取IC卡芯片狀態(tài)函數(shù) */
uchar IC_stat(void)
{
P1_1 = 0; // 使能IC卡芯片;/cs=0
write_spi(STAT_REG_READ); // 寫入讀IC卡芯片狀態(tài)指令
write_spi(0x00); // 寫無關比特
P1_1 = 1; // 禁用IC卡芯片;/cs=1
return SPDR; // 返回IC卡芯片狀態(tài)字節(jié)
}
/* 寫IC卡芯片函數(shù):將數(shù)據(jù)寫入buffer,如果buffer滿,
則將buffer中數(shù)據(jù)寫入主內(nèi)存頁 */
void write_to_IC(uchar dat)
{
uchar stat;
/* 檢查IC卡芯片是否忙 */
stat = IC_stat();
while ((stat&0x80)==0x00);
/* 數(shù)據(jù)寫入buffer */
P1_1 = 0; // 使能IC卡芯片;/cs=0
write_spi(BUFFER_1_WRITE); // buffer1寫指令代碼
write_spi(0x00); // 寫入8位無關位
write_spi((uchar)(buf_start_addr>>8)); // 寫入7位無關位加上9位buffer起始字節(jié)地址的第1位
write_spi((uchar)buf_start_addr); // 寫入9位buffer起始字節(jié)地址的后8位
write_spi(dat); // 寫入數(shù)據(jù)
P1_1 = 1; // 禁用IC卡芯片;結(jié)束buffer write指令
buf_start_addr++; // 下一buffer起始字節(jié)地址
/* 如果buffer寫滿,則將buffer中數(shù)據(jù)寫入主內(nèi)存頁 */
if (buf_start_addr > 263)
{
buf_start_addr = 0; // buffer起始字節(jié)地址重置0
if (page_addr < 2047) // 如果主內(nèi)存頁不滿
{
/* buffer數(shù)據(jù)寫入主內(nèi)存頁 */
P1_1 = 0; // 使能IC卡芯片;/cs=0
write_spi(B1_TO_MM_PAGE_NO_ERA); // 寫入無在線擦除的buffer1寫主內(nèi)存頁指令代碼
write_spi((uchar)(page_addr>>7)); // 寫入4位保留位加上11位頁地址的高4位
write_spi((uchar)(page_addr<<1)); // 寫入11位頁地址的低7位和1位無關位
write_spi(0x00); // 再寫入8位無關位
P1_1 = 1; // 禁用IC卡芯片;結(jié)束無在線擦除的buffer寫主內(nèi)存頁指令
page_addr++; // 下一頁地址
}
}
}
/* 讀IC卡芯片函數(shù),如果一頁讀完,則讀取下一頁 */
uchar read_from_IC()
{
uchar stat;
uchar tmp;
/* 檢查IC卡芯片是否忙 */
stat = IC_stat();
while ((stat&0x80)==0x00);
/* 從主內(nèi)存頁中讀出數(shù)據(jù) */
P1_1 = 0; // 使能IC卡芯片;/cs=0
write_spi(MM_PAGE_READ) ; // 寫入主內(nèi)存頁讀指令代碼
tmp = (uchar)(page_addr>>7);
write_spi(tmp); // 寫入4位保留位加上11位頁地址的高4位
tmp = (uchar)(page_addr<<1)|((uchar)(page_start_addr>>8)&0x01);
write_spi(tmp); // 寫入11位頁地址的低7位和9位頁起始字節(jié)地址的最高位
tmp = (uchar)(page_start_addr);
write_spi(tmp); // 寫入9位頁起始字節(jié)地址的低8位
write_spi(0x00) ; // 寫入8位無關位
write_spi(0x00) ; // 寫入8位無關位
write_spi(0x00) ; // 寫入8位無關位
write_spi(0x00) ; // 再寫入8位無關位,共寫入32位無關位
write_spi(0xff) ; // 寫入8位無意義值以確保完成一字節(jié)數(shù)據(jù)的讀出
P1_1 = 1; // 禁用IC卡芯片;結(jié)束主內(nèi)存頁讀指令
page_start_addr++; // 下一頁中起始字節(jié)地址
/* 如果讀完一頁,則讀取下一頁 */
if (page_start_addr > 263)
{
page_start_addr = 0; // 頁起始字節(jié)地址重置0
if (page_addr < 2047) // 如果主內(nèi)存頁沒有讀完
page_addr++; // 下一頁地址
}
return SPDR; // 返回讀出數(shù)據(jù)
}
接上篇程序:
/* 主函數(shù) */
void main()
{
uchar i;
P1_0 = 1; // /RST引腳置高
/* SPIE=0,SPE=1,DORD=0,MSTR=1,CPOL=CPHA=1,SPR1=0,SPR0=1*/
SPCR=0x5d;
buf_start_addr = 0;
page_start_addr = 0;
page_addr = 0;
/* 獲取需要寫入IC卡的數(shù)據(jù),存放在data_in[]中 */
getdata();
/* 將data_in[]中存放數(shù)據(jù)寫入IC卡 */
for (i=0;i
{
write_to_IC(data_in[i]);
delay(2); // 延時2ms
}
delay(10); // 延時10ms
buf_start_addr = 0;
page_start_addr = 0;
page_addr = 0;
/* 數(shù)據(jù)讀出IC卡,存放在data_out[]中 */
for (i=0;i
{
data_out[i] = read_from_IC();
delay(2); // 延時2ms
}
while(1);
}