用DSP的GPIO接腳實現與IC卡通訊
IC卡可以分為接觸式的和非接觸式(射頻卡),本文主要討論儲存卡和智慧卡(CPU卡)這兩種接觸式IC卡的結構特點和讀寫作業,詳細敘述如何使用DSP的GPIO(通用輸入輸出)接腳實現與各種IC卡進行通訊,此外亦同時討論了相關的DSP函數。
常見與IC卡連接的都是基於單片機的系統,但是某些應用要求IC卡讀寫終端具有較強的即時運算和控制能力,這時DSP就是最好的選擇。以TI公司的C5409為例,它的8根HPI接腳(HD0∼HD7)可以配置成GPIO使用,配置方法是在復位時將HPI16接腳置高或者HPIENA接腳置低,這樣就可以經由配置DSP內部GPIOCR和GPIOSR兩個暫存器來控制這8根GPIO接腳的輸出和輸入。GPIOCR暫存器的低8位元用來控制每個GPIO接腳的方向,若接腳為輸出則對應位設為‘1’,若為輸入則設為‘0’(DSP復位後GPIOCR缺省值為‘0’,即GPIO接腳默認為輸入)。GPIOSR暫存器的低8位元用來控制每個GPIO接腳的值,若為輸出,向對應位寫‘1’,則該接腳輸出高,寫‘0’則該接腳輸出低;若為輸入,則對應位的值為接腳上輸入的值,向其寫作業無效。下面給出了控制GPIO的函數(控制低兩位元GPIO接腳,即HD0和HD1):
#define gpio_dir *(short *)0x3c
#define gpio_val *(short *)0x3d //定義兩個暫存器的地址
void gpio_setval(short i,short j) //設置GPIO的輸出
{
if(i==0) /控制HD0接腳
gpio_val=gpio_val&0xfffe; //設為0
else if(i==1)
gpio_val=gpio_val|0x0001; //設為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; //設為輸出
Velse if(i==0)
gpio_dir=gpio_dir&0xfffe; //設為輸入
}
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卡可以分為儲存卡和智慧卡(又叫作CPU卡),下面分別介紹DSP如何與這兩種卡進行連接通訊。
DSP和儲存卡的連接
儲存卡只具有簡單儲存功能,實際上是一片串列EEPROM的IC卡模式,以Atmel公司的AT24C16SC為例,它實質上就是兩線串列EEPROM AT24C16,兩者介面時序基本一樣。儲存卡的接腳如圖1所示。AT24C16SC同時支援3V和5V,存取速度分別可以達到100Kbps(3V)和400Kbps(5V);內部容量為16Kb,分為128頁,每頁16位元組;雙向數據線(SDA)為OD(Open-Drain)驅動,需要加上拉電阻才能正常通訊。
儲存卡的存取時序為I2C標準時序。首先,正常通訊中只有在時脈線(SCL)為低時SDA才可變化,即在SCL為高時,SDA必須保持狀態(數據有效期),而在SCL為高時,SDA的變化表示下面兩種控制狀態:
開始狀態:當SCL為高時,SDA由高變低表示一個開始狀態,通常任何作業前均需要一個開始狀態;
停止狀態:當SCL為高時,SDA由低變高表示一個停止狀態,通常跟在每個作業後,因而將卡置於等待模式。
在讀寫中,地址和數據都是按照8位元的大小進行傳輸,接收的一方需要返回一個ACK訊號表示確認,這個ACK訊號是在第9位元的位置返回一個‘0’來表示。如在讀卡的時候,DSP在收到8位元後,在第9個時脈應向卡發送‘0’表示收到了正確的數據,同時要求卡繼續發送下一個8位元數據,如果沒有這個ACK訊號,則將會中止目前讀作業返回等待模式。寫卡的時候,卡在收到DSP發送的地址和數據後也應該返回ACK訊號以表示收到了正確的命令。開始和停止狀態、確認訊號時序如圖2所示。
一個讀寫作業的開始需要先發送一個元件地址(device address)位元組,該位元組的高4位元是‘1010’,接著3位元是卡的高位地址,如AT24C16SC需要有11位元地址(2K位元組的大小),高3位元地址就是這?來指示,最後1位元是讀寫控制位,若為‘1’,則表示後面進行一個讀作業,若為‘0’,則表示後面進行一個寫作業。
寫卡作業分為字寫和頁寫。字寫時,當發送完元件地址位元組(最後1位元為‘0’指明寫作業)後,接著再發送一個字地址(word address)位元組,即為卡的低8位元地址,然後就可送入一個位元組的數據,最後發送停止狀態。對於頁寫時,可以連續發送16個位元組後再發送停止狀態,需要注意的是,當頁寫時,低4位元地址在卡內部自動遞增,當到達頁末地址時會自動返回頁首地址,所以要正確發送停止狀態,否則繼續寫入的位元組就會覆蓋原來的數據。
讀卡作業分為讀目前地址、讀任意地址和順序讀幾種方式。幾種方式大同小異,下面主要介紹讀任意地址的作業,另兩種方式都較簡單。讀任意地址時,在發送完元件地址位元組(最後1位元為‘1’指明讀作業)後,發送字地址位元組,這一過程是裝載要讀的地址,下面再發送一個元件地址位元組(同樣最後1位元為‘1’指明讀作業),然後便可從卡讀到一個連續8位元數據,然後DSP發送停止狀態(而不是ACK訊號)結束讀作業。
透過以上介紹可以看出,DSP與儲存卡連接的關鍵就是如何做出SCL和SDA的時序,也就是I2C時序。用DSP的兩根GPIO分別連接儲存卡的SCL和SDA,然後同時設置兩者的高低關係並且正確改變連接SDA那根GPIO的輸入和輸出方向,我們就可以解決這個問題。例如,對於圖3這個數據有效期的時序,我們可以將兩根GPIO依次設置為:‘01’、‘11’、‘01’,需要注意的是,改變狀態之間需要插入等待週期,因為DSP的工作時脈很高,其GPIO的改變遠高於I2C時序的要求。
下面給出一個寫卡函數:
short write_ic(short page,short addr,short length,short *buff)
//page-要訪的問高3位元地址,addr-要存取的低8位元地址,length-要寫入的數據長度,通常為16,buff-要寫入的數據
{
short device_address,ack,i,loop=0;
start_ic(); //發送一個開始狀態
device_address=0xa0|(page<<1);
put_ic(device_address); //發送元件地址位元組,0xa0表示寫
gpio_setdir(0); //改變GPIO方向為輸入
ack=1;
do
{
ack=get_ic();
loop++;
if(loop>10000)
return 0;
}while(ack!=0); //等待讀入確認訊號,否則超時退出
device_address=addr; //要存取的低8位元地址
put_ic(device_address); //發送字地址位元組
gpio_setdir(0);
ack=1;
do
{
ack=get_ic();
loop++;
if(loop>10000)
return 0;
}while(ack!=0);
DSP和智慧卡的連接
智慧卡是IC卡中最高級的一種,其內部一般有CPU、ROM、RAM和EEPROM等資源,卡內一般都駐有智慧卡作業系統(COS),該作業系統對卡進行維護和管理並解釋終端的各種命令。由於卡內有CPU和RAM,所以可以根據需要進行一些運算和數據加密,同時卡內的EEPROM也可以存放一些用戶資料(容量也較儲存卡大)。智慧卡的接腳如圖4所示。 智慧卡的作業遵循ISO7816-3規格,通訊時序類似於雙向RS-232通訊協定(RS-232是單向的)。首先,作業前需要對卡進行啟動,啟動過程必須保証智慧卡的觸點接觸良好。啟動的步驟為:VCC供電,RST為低,I/O設為輸入,提供CL,然後對卡進行復位。復位分為冷復位和熱復位,兩者區別在於冷復位時RST由低變高,而熱復位時RST由高變低再變高,在復位後,卡應有復位應答;接受到卡正確的復位應答後,DSP可以向卡發送命令;取卡前需要進行釋放,步驟順序與啟動相反:RST變低,CLK變低,VCC掉電。
卡的復位應答可以告訴終端一些卡的原始資訊,它的組成如圖5所示:
TS:初始化位元組,用來進行位同步和指示後續通訊的編碼方式,例如0x3f表示反碼編碼,0x3b表示正常編碼;
可以看出,1個位元組訊框由13位元組成,1個開始位、8個數據位、1個校驗位和2個保護時間位。在外部提供時脈方式(常用方式)下,上圖中每個位元所佔的時間(ETU,Elementary Time Unit)默認為外部時脈週期的372倍,如當外部時脈為3.57M時,1/ETU約為9600,即工作在9600速率下。保護時間默認為2個ETU,最大可以為254個ETU。
DSP與智慧卡的連接跟儲存卡有些差異。首先,智慧卡比儲存卡多一個RST接腳用來對CPU進行復位,可以使用DSP的一根GPIO來控制;儲存卡的CLK為通訊時脈,速度不高,沒有速率要求,可以隨時停止,只要時序關係正確即可,可以由DSP的GPIO實現;但智慧卡的CLK為卡內CPU的工作時脈,速度很高(通常介於1M∼5M),並且要求穩定,用DSP的GPIO來提供該時脈是不可能的,因為DSP的GPIO的翻轉速度有限制,並且高速翻轉必定佔用大量DSP系統資源。因此可以使用DSP的一個串列埠McBSP的發送時脈CLKX來提供智慧卡時脈。同時,還需要DSP的一根GPIO來連接智慧卡的I/O訊號,跟智慧卡一樣的是,I/O線同樣是雙向的,需要正確改變DSP的GPIO方向,但不同的是智慧卡的I/O有精確的速率要求,即為CLK速率的1/372,可以採用DSP內部的定時器來結合GPIO實現。
選擇DSP內部定時器的週期為提供時脈的串列埠時脈週期的372/8倍,當每個定時器中斷到來時進行一次GPIO作業,這樣等於是8倍頻輸出和採入數據,即作業的最小週期是1/8個ETU,因而確保了精密度。從實現上看,上述過程類似於用定時器和GPIO來實現RS-232協議。需要注意的是,為了保証中斷響應和系統資源,定時器中斷程式中最好不進行GPIO作業,而只作標誌位設置,GPIO作業留給讀寫函數實現。下面為一個讀卡函數的實現。 void readword_sim(unsigned short *buff,unsigned short length)
作者:張彬
for(i=0;i
{
put_ic(*buff++);
dir(0);
ack=1;
do
{
ack=get_ic();
}while(ack!=0);
} //寫入數據
stop_ic();
return 1;
}
void put_ic(short c)
{
short temp,i;
gpio_setdir(1); //改變GPIO為輸出
for(i=7;i>=0;i--)
{
temp=1<
temp=c&temp;
temp=temp<<(-i);
if(temp==0)
{
gpio_setval(0,0); //設置兩根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);
}
}
}
T0:格式位元組,高4位元用來指示是否傳輸TA1、TB1、TC1、TD1,低4位元用來指示有多少個歷史字符;
TAi、TBi、TCi:介面位元組,用來設置作業的一些參數,比如速率,保護時間,編程電壓等;
TDi:介面位元組,高4位元用來指示是否傳輸TAi+1、TBi+1、TCi+1,低4位元用來指示傳輸類型T。T=0,字符半雙工模式;T=1,塊半雙工模式,其他的T值保留。我們常用T=0即字符模式;
T1∼TK:歷史字符,由制卡商提供;
TCK:校驗位,是T0∼TK所有位元組的異或,T=0時不傳此位元組。
存取卡的時序如圖6所示。
//buff-讀入的數據放的buffer,length-讀入的長度
{
short tempbit,tempbyte,i,j,bitcount,parry;
rw=0; //設置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++;
} //組成一個位元組
else if(bitcount==9)
{
if((tempbit&0x18)!=0)
parry=1;
else parry=0;
bitcount++;
} //讀入校驗位
else if(bitcount==10) bitcount++; //保護時間
}
buff[j]=tempbyte;
}
}
shorterrupt void tshort() //定時器中斷
{
if(ready==0)
{
if(rw==0)
simdata=gpio_getval();
ready=1;
}
}
上面的程式實現了智慧卡的讀寫協議,餘下的就是對卡發送命令即可,關於命令的結構和定義本文不作闡述。
研究生
北京郵電大學
Email: bingo@263.net
社區今日頭條 |
---|