用DSP的GPIO管腳實(shí)現(xiàn)與IC卡通信
文章出處:http://botanicstilllife.com 作者:北京郵電大學(xué) 張彬 人氣: 發(fā)表時(shí)間:2011年11月03日
IC卡可以分為接觸式的和非接觸式(射頻卡),本文主要討論存儲(chǔ)卡和智能卡(CPU卡)這兩種接觸式IC卡的結(jié)構(gòu)特點(diǎn)和讀寫操作,詳細(xì)敘述如何使用DSP的GPIO(通用輸入輸出)管腳實(shí)現(xiàn)與各種IC卡進(jìn)行通信,并給出了DSP函數(shù)實(shí)現(xiàn)。
常見與IC卡連接的都是基于單片機(jī)的系統(tǒng),但是某些應(yīng)用要求IC卡讀寫終端具有較強(qiáng)的實(shí)時(shí)運(yùn)算和控制能力,這時(shí)DSP就是最好的選擇。以TI公司的C5409為例,它的8根HPI管腳(HD0~HD7)可以配置成GPIO使用,配置方法是在復(fù)位時(shí)將HPI16管腳置高或者HPIENA管腳置低,這樣就可以通過配置DSP內(nèi)部GPIOCR和GPIOSR兩個(gè)寄存器來控制這8根GPIO管腳的輸出和輸入。GPIOCR寄存器的低8位用來控制每個(gè)GPIO管腳的方向,若管腳為輸出則對(duì)應(yīng)位設(shè)為“1”,若為輸入則設(shè)為“0”(DSP復(fù)位后GPIOCR缺省值為“0”,即GPIO管腳默認(rèn)為輸入)。GPIOSR寄存器的低8位用來控制每個(gè)GPIO管腳的值,若為輸出,向?qū)?yīng)位寫“1”,則該管腳輸出高,寫“0”則該管腳輸出低;若為輸入,則對(duì)應(yīng)位的值為管腳上輸入的值,向其寫操作無效。
下面給出了控制GPIO的函數(shù)(控制低兩位GPIO管腳,即HD0和HD1):
#define gpio_dir *(short *)0x3c
#define gpio_val *(short *)0x3d //定義兩個(gè)寄存器的地址
void gpio_setval(short i,short j) //設(shè)置GPIO的輸出
{
if(i==0) //控制HD0管腳
gpio_val=gpio_val&0xfffe; //設(shè)為0
else if(i==1)
gpio_val=gpio_val|0x0001; //設(shè)為1
if(j==0) //控制HD1管腳
gpio_val=gpio_val&0xfffd;
else if(j==1)
gpio_val=gpio_val|0x0002;
wait();
}
void gpio_setdir(short i) //控制GPIO的方向
{
if(i==1)
gpio_dir=gpio_dir|0x0001; //設(shè)為輸出
else if(i==0)
gpio_dir=gpio_dir&0xfffe; //設(shè)為輸入
}
short gpio_getval(short i) //讀入GPIO的值
{
short j;
if(i==0) j=gpio_val&0x0001;
else if(i==1) j=(gpio_val&0x0002)>>1;
return j;
}
常見的接觸式IC卡可以分為存儲(chǔ)卡和智能卡(又叫作CPU卡),下面分別介紹DSP如何與這兩種卡進(jìn)行連接通信。
DSP和存儲(chǔ)卡的連接
存儲(chǔ)卡只具有簡(jiǎn)單存儲(chǔ)功能,實(shí)際上是一片串行EEPROM的IC卡模式,以Atmel公司的AT24C16SC為例,它實(shí)質(zhì)上就是兩線串行EEPROM AT24C16,兩者接口時(shí)序基本一樣。存儲(chǔ)卡的管腳如圖1所示。AT24C16SC同時(shí)支持3V和5V,訪問速度分別可以達(dá)到100Kbps(3V)和400Kbps(5V);內(nèi)部容量為16Kb,分為128頁,每頁16字節(jié);雙向數(shù)據(jù)線(SDA)為OD(Open-Drain)驅(qū)動(dòng),需要加上拉電阻才能正常通信。
存儲(chǔ)卡的訪問時(shí)序?yàn)镮2C標(biāo)準(zhǔn)時(shí)序。首先,正常通信中只有在時(shí)鐘線(SCL)為低時(shí)SDA才可變化,即在SCL為高時(shí),SDA必須保持狀態(tài)(數(shù)據(jù)有效期),而在SCL為高時(shí),SDA的變化表示下面兩種控制狀態(tài):開始狀態(tài):當(dāng)SCL為高時(shí),SDA由高變低表示一個(gè)開始狀態(tài),通常任何操作前均需要一個(gè)開始狀態(tài);停止?fàn)顟B(tài):當(dāng)SCL為高時(shí),SDA由低變高表示一個(gè)停止?fàn)顟B(tài),通常跟在每個(gè)操作后,從而將卡置于等待模式。
在讀寫中,地址和數(shù)據(jù)都是按照8位的大小進(jìn)行傳輸,接收的一方需要返回一個(gè)ACK信號(hào)表示確認(rèn),這個(gè)ACK信號(hào)是在第9位的位置返回一個(gè)“0”來表示。如在讀卡的時(shí)候,DSP在收到8位后,在第9個(gè)時(shí)鐘應(yīng)向卡發(fā)送“0”表示收到了正確的數(shù)據(jù),同時(shí)要求卡繼續(xù)發(fā)送下一個(gè)8位數(shù)據(jù),如果沒有這個(gè)ACK信號(hào),則將會(huì)中止當(dāng)前讀操作返回等待模式。寫卡的時(shí)候,卡在收到DSP發(fā)送的地址和數(shù)據(jù)后也應(yīng)該返回ACK信號(hào)以表示收到了正確的命令。開始和停止?fàn)顟B(tài)、確認(rèn)信號(hào)時(shí)序如圖2所示。
一個(gè)讀寫操作的開始需要先發(fā)送一個(gè)器件地址(device address)字節(jié),該字節(jié)的高4位是“1010”,接著3位是卡的高位地址,如AT24C16SC需要有11位地址(2K字節(jié)的大小),高3位地址就是這里來指示,最后1位是讀寫控制位,若為“1”,則表示后面進(jìn)行一個(gè)讀操作,若為“0”,則表示后面進(jìn)行一個(gè)寫操作。
寫卡操作分為字寫和頁寫。字寫時(shí),當(dāng)發(fā)送完器件地址字節(jié)(最后1位為“0”指明寫操作)后,接著再發(fā)送一個(gè)字地址(word address)字節(jié),即為卡的低8位地址,然后就可送入一個(gè)字節(jié)的數(shù)據(jù),最后發(fā)送停止?fàn)顟B(tài)。對(duì)于頁寫時(shí),可以連續(xù)發(fā)送16個(gè)字節(jié)后再發(fā)送停止?fàn)顟B(tài),需要注意的是,當(dāng)頁寫時(shí),低4位地址在卡內(nèi)部自動(dòng)遞增,當(dāng)?shù)竭_(dá)頁末地址時(shí)會(huì)自動(dòng)返回頁首地址,所以要正確發(fā)送停止?fàn)顟B(tài),否則繼續(xù)寫入的字節(jié)就會(huì)覆蓋原來的數(shù)據(jù)。
讀卡操作分為讀當(dāng)前地址、讀任意地址和順序讀幾種方式。幾種方式大同小異,下面主要介紹讀任意地址的操作,另兩種方式都較簡(jiǎn)單。讀任意地址時(shí),在發(fā)送完器件地址字節(jié)(最后1位為“1”指明讀操作)后,發(fā)送字地址字節(jié),這一過程是裝載要讀的地址,下面再發(fā)送一個(gè)器件地址字節(jié)(同樣最后1位為“1”指明讀操作),然后便可從卡讀到一個(gè)連續(xù)8位數(shù)據(jù),然后DSP發(fā)送停止?fàn)顟B(tài)(而不是ACK信號(hào))結(jié)束讀操作。
通過以上介紹可以看出,DSP與存儲(chǔ)卡連接的關(guān)鍵就是如何做出SCL和SDA的時(shí)序,也就是I2C時(shí)序。用DSP的兩根GPIO分別連接存儲(chǔ)卡的SCL和SDA,然后同時(shí)設(shè)置兩者的高低關(guān)系并且正確改變連接SDA那根GPIO的輸入和輸出方向,我們就可以解決這個(gè)問題。例如,對(duì)于圖3這個(gè)數(shù)據(jù)有效期的時(shí)序,我們可以將兩根GPIO依次設(shè)置為:“01”、“11”、“01”,需要注意的是,改變狀態(tài)之間需要插入等待周期,因?yàn)镈SP的工作時(shí)鐘很高,其GPIO的改變遠(yuǎn)高于I2C時(shí)序的要求。
下面給出一個(gè)寫卡函數(shù):
short write_ic(short page,short addr,short length,short *buff)
//page-要訪的問高3位地址,addr-要訪問的低8位地址,length-要寫入的數(shù)據(jù)長(zhǎng)度,通常為16,buff-要寫入的數(shù)據(jù)
{
short device_address,ack,i,loop=0;
start_ic(); //發(fā)送一個(gè)開始狀態(tài)
device_address=0xa0|(page<<1);
put_ic(device_address); //發(fā)送器件地址字節(jié),0xa0表示寫
gpio_setdir(0); //改變GPIO方向?yàn)檩斎?
ack=1;
do
{
ack=get_ic();
loop++;
if(loop>10000)
return 0;
}while(ack!=0); //等待讀入確認(rèn)信號(hào),否則超時(shí)退出
device_address=addr; //要訪問的低8位地址
put_ic(device_address); //發(fā)送字地址字節(jié)
gpio_setdir(0);
ack=1;
do
{
ack=get_ic();
loop++;
if(loop>10000)
return 0;
}while(ack!=0);
for(i=0;i{
put_ic(*buff++);
dir(0);
ack=1;
do
{
ack=get_ic();
}while(ack!=0);
} //寫入數(shù)據(jù)
stop_ic();
return 1;
}
void put_ic(short c)
{
short temp,i;
gpio_setdir(1); //改變GPIO為輸出
for(i=7;i>=0;i--)
{
temp=1temp=temp<<(-i);
if(temp==0)
{
gpio_setval(0,0); //設(shè)置兩根GPIO的輸出
gpio_setval(1,0);
gpio_setval(0,0);
}
else if(temp==1)
{
gpio_setval(0,1);
gpio_setval(1,1);
gpio_setval(0,1);
}
}
}
DSP和智能卡的連接
智能卡是IC卡中最高級(jí)的一種,其內(nèi)部一般有CPU、ROM、RAM和EEPROM等資源,卡內(nèi)一般都駐有智能卡操作系統(tǒng)(COS),該操作系統(tǒng)對(duì)卡進(jìn)行維護(hù)和管理并解釋終端的各種命令。由于卡內(nèi)有CPU和RAM,所以可以根據(jù)需要進(jìn)行一些運(yùn)算和數(shù)據(jù)加密,同時(shí)卡內(nèi)的EEPROM也可以存放一些用戶資料(容量也較存儲(chǔ)卡大)。智能卡的管腳如圖4所示。
智能卡的操作遵循ISO7816-3規(guī)范,通信時(shí)序類似于雙向RS-232通信協(xié)議(RS-232是單向的)。首先,操作前需要對(duì)卡進(jìn)行激活,激活過程必須保證智能卡的觸點(diǎn)接觸良好。激活的步驟為:VCC供電,RST為低,I/O設(shè)為輸入,提供CL,然后對(duì)卡進(jìn)行復(fù)位。復(fù)位分為冷復(fù)位和熱復(fù)位,兩者區(qū)別在于冷復(fù)位時(shí)RST由低變高,而熱復(fù)位時(shí)RST由高變低再變高,在復(fù)位后,卡應(yīng)有復(fù)位應(yīng)答;接受到卡正確的復(fù)位應(yīng)答后,DSP可以向卡發(fā)送命令;取卡前需要進(jìn)行釋放,步驟順序與激活相反:RST變低,CLK變低,VCC掉電。
卡的復(fù)位應(yīng)答可以告訴終端一些卡的原始信息,它的組成如圖5所示:
TS:初始化字節(jié),用來進(jìn)行位同步和指示后續(xù)通信的編碼方式,例如0x3f表示反碼編碼,0x3b表示正常編碼;
T0:格式字節(jié),高4位用來指示是否傳輸TA1、TB1、TC1、TD1,低4位用來指示有多少個(gè)歷史字符;
TAi、TBi、TCi:接口字節(jié),用來設(shè)置操作的一些參數(shù),比如速率,保護(hù)時(shí)間,編程電壓等;
TDi:接口字節(jié),高4位用來指示是否傳輸TAi+1、TBi+1、TCi+1,低4位用來指示傳輸類型T。T=0,字符半雙工模式;T=1,塊半雙工模式,其他的T值保留。我們常用T=0即字符模式;
T1~TK:歷史字符,由制卡商提供;
TCK:校驗(yàn)位,是T0~TK所有字節(jié)的異或,T=0時(shí)不傳此字節(jié)。
訪問卡的時(shí)序如圖6所示。
可以看出,1個(gè)字節(jié)幀由13位組成,1個(gè)開始位、8個(gè)數(shù)據(jù)位、1個(gè)校驗(yàn)位和2個(gè)保護(hù)時(shí)間位。在外部提供時(shí)鐘方式(常用方式)下,上圖中每個(gè)位所占的時(shí)間(ETU,Elementary Time Unit)默認(rèn)為外部時(shí)鐘周期的372倍,如當(dāng)外部時(shí)鐘為3.57M時(shí),1/ETU約為9600,即工作在9600速率下。保護(hù)時(shí)間默認(rèn)為2個(gè)ETU,最大可以為254個(gè)ETU。
DSP與智能卡的連接跟存儲(chǔ)卡有些差異。首先,智能卡比存儲(chǔ)卡多一個(gè)RST管腳用來對(duì)CPU進(jìn)行復(fù)位,可以使用DSP的一根GPIO來控制;存儲(chǔ)卡的CLK為通信時(shí)鐘,速度不高,沒有速率要求,可以隨時(shí)停止,只要時(shí)序關(guān)系正確即可,可以由DSP的GPIO實(shí)現(xiàn);但智能卡的CLK為卡內(nèi)CPU的工作時(shí)鐘,速度很高(通常介于1M~5M),并且要求穩(wěn)定,用DSP的GPIO來提供該時(shí)鐘是不可能的,因?yàn)镈SP的GPIO的翻轉(zhuǎn)速度有限制,并且高速翻轉(zhuǎn)必定占用大量DSP系統(tǒng)資源。因此可以使用DSP的一個(gè)串口McBSP的發(fā)送時(shí)鐘CLKX來提供智能卡時(shí)鐘。同時(shí),還需要DSP的一根GPIO來連接智能卡的I/O信號(hào),跟智能卡一樣的是,I/O線同樣是雙向的,需要正確改變DSP的GPIO方向,但不同的是智能卡的I/O有精確的速率要求,即為CLK速率的1/372,可以采用DSP內(nèi)部的定時(shí)器來結(jié)合GPIO實(shí)現(xiàn)。
選擇DSP內(nèi)部定時(shí)器的周期為提供時(shí)鐘的串口時(shí)鐘周期的372/8倍,當(dāng)每個(gè)定時(shí)器中斷到來時(shí)進(jìn)行一次GPIO操作,這樣等于是8倍頻輸出和采入數(shù)據(jù),即操作的最小周期是1/8個(gè)ETU,從而確保了精度。從實(shí)現(xiàn)上看,上述過程類似于用定時(shí)器和GPIO來實(shí)現(xiàn)RS-232協(xié)議。需要注意的是,為了保證中斷響應(yīng)和系統(tǒng)資源,定時(shí)器中斷程序中最好不進(jìn)行GPIO操作,而只作標(biāo)志位設(shè)置,GPIO操作留給讀寫函數(shù)實(shí)現(xiàn)。下面為一個(gè)讀卡函數(shù)的實(shí)現(xiàn)。
void readword_sim(unsigned short *buff,unsigned short length)
//buff-讀入的數(shù)據(jù)放的buffer,length-讀入的長(zhǎng)度
{
short tempbit,tempbyte,i,j,bitcount,parry;
rw=0; //設(shè)置GPIO為讀
for(j=0;j{
bitcount=0;
while(!bitcount)
{
ready=0;
while(!ready);
if(simdata==0) bitcount=1;
} //讀入開始位
tempbyte=0;
bitcount=0;
for(;bitcount<11;)
{
tempbit=0;
for(i=0;i<8;i++)
{
ready=0;
while(!ready);
if(simdata==1)
tempbit=tempbit+(1<} //讀入一位的8倍采樣
if((tempbit&0x18)==0 && bitcount==0)
bitcount++;
else if(bitcount>0 && bitcount<9)
{
if((tempbit&0x18)!=0)
tempbyte=tempbyte+(1<<(bitcount-1));
bitcount++;
} //組成一個(gè)字節(jié)
else if(bitcount==9)
{
if((tempbit&0x18)!=0)
parry=1;
else parry=0;
bitcount++;
} //讀入校驗(yàn)位
else if(bitcount==10) bitcount++; //保護(hù)時(shí)間
}
buff[j]=tempbyte;
}
}
shorterrupt void tshort() //定時(shí)器中斷
{
if(ready==0)
{
if(rw==0)
simdata=gpio_getval();
ready=1;
}
}
上面的程序?qū)崿F(xiàn)了智能卡的讀寫協(xié)議,余下的就是對(duì)卡發(fā)送命令即可,關(guān)于命令的結(jié)構(gòu)和定義本文不作闡述。