下面介紹有關此芯片的中文資料:
TRF7960系列是TI推出的載波為13.56MHz、支持ISO15693、ISO14443A/B和FeliCa協議的射頻接口芯片。許多人更關心它能不能讀寫MF1卡片,就我的理解及實際驗證,由于MF1在卡選擇之前的操作是遵守ISO14443A協議的,之后的卡驗證和卡數據讀寫都是NXP自己的保密協議,所以TRF7960可以對MF1卡執行到卡選擇操作,或者通俗的說可以讀MF1的卡片序列號,但不能對MF1卡讀寫數據,除非開發者自己知道NXP的加密協議并自己編寫代碼實現該協議。
在TI官方公開的TRF7960說明書中,有詳細的參考電路及基于MSP430單片機的參考代碼,參考這些資料做自己的開發板或者產品板基本上難度不大。MCU可以使用并口或SPI串口操作TRF7960,并口相對簡單一些,SPI通信則有一些問題需要特別注意。
首先,TI給出的SPI參考代碼使用的是MSP430的內置SPI接口,我們實際開發中因為單片機內部資源或引腳分配限制往往需要軟件模擬SPI通訊。TRF7960的SPI協議規定:
不通訊的時候,片選NSS保持高電平,時鐘CLOCK保持低電平,通訊的時候NSS保持低電平。
主機向TRF7960寫一位數據時,在CLOCK為低電平期間根據數據的值設置MOSI數據線,然后CLOCK上升沿通知TRF7960可以接收數據,CLOCK下降沿后繼續準備下一位要發送的數據,代碼如下:
for(j=8;j>0;j--)
{
if(((*pbuf)&0x80)==0x80)TRF796X_MOSI_HIGH;
else TRF796X_MOSI_LOW;
TRF796X_SCK_HIGH;
(*pbuf) <<= 1;
TRF796X_SCK_LOW;
}
主機從TRF7960讀一位數據時,在CLOCK為高電平期間TRF7960根據數據的值設置MISO數據線,然后CLOCK下降沿通知MCU可以接收數據,CLOCK上升沿后繼續準備下一位要發送的數據,代碼如下:
for(j=8;j>0;j--)
{
TRF796X_SCK_HIGH;
_NOP();_NOP();
TRF796X_SCK_LOW;
(*pbuf) <<= 1;
if(TRF796X_MISO_LOW)(*pbuf)+=1;
}
其次,MCU可以使用Direct Command直接向TRF7960發送一字節的命令碼,執行復位、進入省電模式、向卡片發送數據、調整接收電路增益等功能。Direct Command的SPI時序有一個特殊的要求,在發送完一字節的命令后,在SS拉高之前,CLOCK要多出一個上升沿,代碼如下:
SLAVE_SELECT_LOW;
for(j=8;j>0;j--)
{
if(((*pbuf)&0x80)==0x80)TRF796X_MOSI_HIGH;
else TRF796X_MOSI_LOW;
TRF796X_SCK_HIGH;
(*pbuf) <<= 1;
TRF796X_SCK_LOW;
}
_NOP(); _NOP();
TRF796X_SCK_HIGH;
_NOP(); _NOP();
SLAVE_SELECT_HIGH;
_NOP(); _NOP();
TRF796X_SCK_LOW;
最后,TRF7960向磁場中的卡片發送數據后,等待卡片回應,是否收到卡片回送的數據及是否反應超時等命令的執行情況都是通過中斷機制來表示的。在NXP的射頻芯片中,可以不使用芯片的中斷引腳IRQ而是直接查詢射頻芯片的中斷標志寄存器來獲得各種事件發生的情況,但在TRF7960中不能使用這種方式,因為讀一次TRF7960的中斷標志寄存器將會把寄存器中的中斷標志清除,所以電路中通常要使用IRQ引腳,可以用IRQ引腳使能MCU中斷或直接查詢IRQ引腳,從而得知TRF7960內部發生了中斷事件,進而用SPI讀取其中斷標志寄存器獲取詳細的中斷事件產生情況。
最后提供51單片機驅動TRF7960的源程序:
代碼的完整版本下載:
#include "include.h"
#include <intrins.h>
#define RXERROR 0x0f
#define NOERROR 0X00
//============= 全局變量定義 ======================================================================
unsigned char RXTXstate; //發送數據計數
unsigned char i_reg; //中斷寄存器
unsigned char idata buf[32];
unsigned char Data[10];
unsigned char flags; //標志
unsigned char RXErrorFlag;
//bit fReadUID;
bit fIntrrupt;
void SendByte(unsigned char i);
void delay(int n);
void delay1(void);
void DelayMs(unsigned char j);
void EnableSlotCounter(void);
void DisableSlotCounter(void);
void STOPcondition(void);
void STOPcont(void);
void STARTcondition(void);
void WriteSingle(unsigned char *pbuf, unsigned char lenght);
void WriteCont(unsigned char *pbuf, unsigned char lenght);
void ReadSingle(unsigned char *pbuf, unsigned char lenght);
void ReadCont(unsigned char *pbuf, unsigned char lenght);
void DirectCommand(unsigned char *pbuf);
void RAWwrite(unsigned char *pbuf, unsigned char lenght);
void Initial7960s(void);
bit InventoryRequest(unsigned char *ReadBuff) ;
void InterruptHandlerReader(unsigned char *Register);
void delay(int n)
{
while(n--);
}
void InitPort(void)
{
DataPort = 0x00; //
clkOFF;
//OOKdirIN; // 設置OOK 端口
}
//*************************************************************************************************
// 功能描述 : 時隙計數使能
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void EnableSlotCounter(void)
{
unsigned char buff[2];
buff[1] = IRQMask;
buff[0] = IRQMask;
ReadSingle(&buff[1], 1);
buff[1] |= 0x01;
WriteSingle(&buff[0], 2);
}
//*************************************************************************************************
// 功能描述 : 禁止時隙計數
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void DisableSlotCounter(void)
{
unsigned char buff[2];
buff[1] = IRQMask;
buff[0] = IRQMask;
ReadSingle(&buff[1], 1);
buff[1] &= 0xfe;
WriteSingle(&buff[0], 2);
}
//*************************************************************************************************
// 功能描述 : 簡單結束并口通信
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void STOPcondition(void)
{
// P0M1=00000000;
// P0M0=11111111;
DataPort |= 0x80;
clkON;
DataPort = 0x00;
clkOFF;
}
//*************************************************************************************************
// 功能描述 : 徹底結束并口通信
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void STOPcont(void)
{
//P0M1=00000000;
//P0M0=11111111;
DataPort = 0x00;
//TRFDirOUT;
DataPort = 0x80;
DataPort = 0x00;
}
//*************************************************************************************************
// 功能描述 : 并口通信起始
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void STARTcondition(void)
{
// P0M1=00000000;
// P0M0=11111111;
DataPort = 0x00;
clkON;
DataPort = 0xff;
clkOFF;
}
//*************************************************************************************************
// 功能描述 : 單個寫
// 輸入參數 : 內容指針,長度
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void WriteSingle(unsigned char *pbuf, unsigned char lenght)
{
unsigned char i;
STARTcondition();
while(lenght > 0)
{
*pbuf = (0x1f &*pbuf); // 設置地址為非連續寫 register address
for(i = 0; i < 2; i++) // 單個地址和數據寫操作
{
DataPort = *pbuf; //發送命令和數據
clkON;
clkOFF;
pbuf++;
lenght--;
}
}
STOPcondition();
}
//*************************************************************************************************
// 功能描述 : 連續寫
// 輸入參數 : 內容指針,長度
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void WriteCont(unsigned char *pbuf, unsigned char lenght)
{
STARTcondition();
*pbuf = (0x20 | *pbuf); // 設置地址寫為連續模式 address, write, continous
*pbuf = (0x3f & *pbuf); // 設置寄存器地址
while(lenght > 0)
{
DataPort = *pbuf; //發送命令
clkON;
clkOFF;
pbuf++;
lenght--;
}
STOPcont();
}
//*************************************************************************************************
// 功能描述 : 單個讀
// 輸入參數 : 地址和長度
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void ReadSingle(unsigned char *pbuf, unsigned char lenght)
{
STARTcondition();
while(lenght > 0)
{
*pbuf = (0x40 | *pbuf); //地址, 讀,單個
*pbuf = (0x5f & *pbuf); //積存器地址
// P0M1=00000000;
// P0M0=11111111;
DataPort = *pbuf; // 發送命令
clkON;
clkOFF;
_nop_();
//TRFDirIN; // 上升沿讀取數據
// P0M1=00000000;
// P0M0=00000000;
P0=0xff;
clkON;
*pbuf = DataPort;
clkOFF;
// P0M1=00000000;
// P0M0=11111111;
DataPort = 0x00;
//TRFDirOUT;
_nop_();
pbuf++;
lenght--;
}
STOPcondition();
}
//*************************************************************************************************
// 功能描述 : 連續讀
// 輸入參數 : 地址和長度
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void ReadCont(unsigned char *pbuf, unsigned char lenght)
{
STARTcondition();
*pbuf = (0x60 | *pbuf); //地址, 讀,連續
*pbuf = (0x7f & *pbuf); //積存器地址
DataPort = *pbuf; //發送命令
clkON;
clkOFF;
//TRFDirIN; //上升沿讀取數據
while(lenght > 0)
{
P0=0xff;
clkON;
*pbuf = DataPort;
clkOFF;
pbuf++;
lenght--;
}
STOPcont();
}
//*************************************************************************************************
// 功能描述 : 發送命令
// 輸入參數 : 命令指針
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void DirectCommand(unsigned char *pbuf)
{
STARTcondition();
*pbuf = (0x80 | *pbuf); //命令
*pbuf = (0x9f & *pbuf); //命令碼
DataPort = *pbuf; //發送命令
clkON;
clkOFF;
STOPcondition();
}
//*************************************************************************************************
// 功能描述 : 直接寫數據
// 輸入參數 : 數據指針,長度
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void RAWwrite(unsigned char *pbuf, unsigned char lenght)
{
STARTcondition();
while(lenght > 0)
{
DataPort = *pbuf; //發送命令
clkON;
clkOFF;
pbuf++;
lenght--;
}
STOPcont();
}
//*************************************************************************************************
// 功能描述 : 初始化7860
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void Initial7960s(void)
{
unsigned char command[4];
TRFDisable; // 復位TRF7960
DelayMs(10);
TRFEnable;
//DelayMs(1);
command[0] = ModulatorControl;
command[1] = 0x01; //在此各種調制方式都行,最好為0x07,即ASK 30%
WriteSingle(command, 2);
//DelayMs(1);
// command[0] = 0x0a;
// command[1] = 0x43; //在此各種調制方式都行,最好為0x07,即ASK 30%
// WriteSingle(command, 2);
command[0] = ModulatorControl;
ReadSingle(command, 1);
//SendByte(command[0]);
// command[0] = ModulatorControl;
// command[1] = 0x01; //調制形式為 OOK
// command[2] = ISOControl;
// command[3] = 0x02; // ISO15693 高速, 單載波, 4出1
// WriteSingle(command, 4);
//
// command[0] = ModulatorControl;
// ReadSingle(command, 1);
// SendByte(command[0]);
//
// command[0] = ISOControl;
// ReadSingle(command, 1);
}
//*************************************************************************************************
// 功能描述 : 清點命令
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
bit InventoryRequest(unsigned char *ReadBuff)
{
unsigned char i = 1,j,command, found = 0;
unsigned int size; // 請求命令數據幀大小
unsigned char NoSlots = 2; // 時隙總數,默認1 時隙(NoSlots = 時隙數 + 1)
unsigned int n = 0;
bit bbit;
if((flags & 0x20) == 0x00) // 判斷時隙類型
{
NoSlots = 17; // 16 時隙
EnableSlotCounter();
}
size = 3; //長度 = flag + command + lenght
buf[0] = 0x8f; // 復位
buf[1] = 0x91; // 傳送帶CRC
buf[2] = 0x3d; // 連續寫,從1D開始
buf[3] = (char) (size >> 8); // 發送的長度
buf[4] = (char) (size << 4); // 按15693命令格式寫入FIFO
buf[5] = flags; // 寫FIFO ISO15693 flags
buf[6] = 0x01; // 15693 之清點命令
buf[7] = 0x00; // 掩碼長度
command = IRQStatus;
ReadSingle(&command, 1);
RAWwrite(&buf[0], 8); // 寫 FIFO
fIntrrupt = 0;
i_reg = 0x01;
// command = IRQStatus;
// ReadSingle(&command, 1);
for(i = 1; i < NoSlots; i ++) // 1 或 16 時隙
{
RXTXstate = 1; // 接收數據保存從buf[1]開始
DelayMs(3);
n = 0;
//DelayMs(1);
while(i_reg == 0x01) // 等待RX完成
{
n++;
if(n == 500)
{
i_reg = 0x00;
RXErrorFlag = NOERROR;
break;
}
}
if(i_reg == 0xFF)
{ //接收到 UID
LED1=0;
found = 1;
}
command = Reset; // FIFO 在下個時隙前必須復位
DirectCommand(&command);
if((NoSlots == 17) && (i < 16)) // 發送EOF(next slot)
{
command = StopDecoders;
DirectCommand(&command);
command = RunDecoders;
DirectCommand(&command);
command = TransmitNextSlot;
DirectCommand(&command);
}
else if((NoSlots == 17) && (i == 16)) // 所以時隙發送完成,停止時隙計數
{
DisableSlotCounter();
}
else if(NoSlots == 2)
break;
}
if(found)
{
LED3=0;
for(j = 0;j < 8;j++) //提取接收緩存中的數據
{
ReadBuff[j] = buf[10 - j];
}
bbit = 1;
}
else
{
LED3=1;
for(j = 0;j < 8;j++)
{
ReadBuff[j] = 0;
}
bbit = 0;
}
return bbit;
}
//*************************************************************************************************
// 功能描述 : 獲取單塊數據,無地址的讀
// 輸入參數 : 起始塊
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void ReadSingleBlock(unsigned char Block)
{
unsigned char i;
unsigned char command, found = 0;
unsigned int size; // 請求命令數據幀大小
//unsigned char buf[20]; // 時隙總數,默認1 時隙(NoSlots = 時隙數 + 1)
size=3;
buf[0] = 0x8f; // Reset FIFO command
buf[1] = 0x91; // send with CRC
buf[2] = 0x3d; // write continuous from register 1D
buf[3] = (char)(size>>8);// Data for register 1D, data length
buf[4] = (char)(size<<4);// Data for register 1E
buf[5] = 0x00;// ISO15693 flag with Option flag set
buf[6] = 0x20; // Read mul Blocks command code
buf[7] = Block; // First Block Number
RAWwrite(&buf[0], 8); // 寫 FIFO
i_reg = 0x01; // 當前狀態為RX狀態
DelayMs(5);
RXTXstate = 1; // 接收數據保存從buf[1]開始
while(i_reg == 0x01)
{
}
i_reg = 0x01;
DelayMs(5);
if(i_reg == 0xFF)
{ //接收到 UID
found = 1;
}
command = Reset; // FIFO 在下個時隙前必須復位
DirectCommand(&command);
if(found)
{
LED3=0;
for( i = 0;i <4;i++) //提取接收緩存中的數據
{
Data[i] = buf[5-i];
}
}
else
{
LED3=1;
for( i = 0;i < 8;i++)
{
Data[i] = 0x01;
}
}
//irqOFF;
}
//*************************************************************************************************
// 功能描述 : IRQ中斷處理
// 輸入參數 : FIFO狀態
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void InterruptHandlerReader(unsigned char *Register)
{
// TX IRQ
if(*Register == 0xA0) //正在發送,并且FIFO只剩下3字節
{
i_reg = 0x00;
}
else if(*Register == 0x80) // 在TX開始時置位,TX完畢后產生中斷,發送完畢
{
i_reg = 0x00;
*Register = Reset; // FIFO 復位
DirectCommand(Register);
}
// RX IRQ
else if(*Register == 0x60) // FIFO 中數據超過 75% (9 bytes)
{
i_reg = 0x01; // 仍然是接收狀態
buf[RXTXstate] = FIFO;
ReadCont(&buf[RXTXstate], 9); // 從 FIFO 讀 9 bytes
RXTXstate = RXTXstate + 9;
if(IRQ==0)
{
*Register = IRQStatus; // IRQ 狀態積存器地址
ReadSingle(Register, 1); //irqCLR;
if(*Register == 0x40) // 接收完成
{
*Register = FIFOStatus;
ReadSingle(Register, 1); //判斷在FIFO中剩余的BYTES
*Register = 0x0F & (*Register + 0x01); //
buf[RXTXstate] = FIFO; //將接收的內容放到正確的緩存中
ReadCont(&buf[RXTXstate], *Register);
RXTXstate = RXTXstate + *Register;
*Register = TXLenghtByte2; //判斷是否有數據損壞
ReadSingle(Register, 1); //判斷損壞的位數
if((*Register & 0x01) == 0x01)
{
*Register = (*Register >> 1) & 0x07; // 隱藏前面無關的5位
*Register = 8 - *Register; //壞掉的位數
buf[RXTXstate - 1] &= 0xFF << *Register;
}
i_reg = 0xFF; //接收結束
*Register = Reset; // FIFO 復位
DirectCommand(Register);
}
else if(*Register == 0x50) //CRC錯誤
{ //接收結束,并且錯誤
i_reg = 0x02;
}
}
else // 接收完畢中斷
{
Register[0] = IRQStatus;
ReadSingle(Register, 2);
if(Register[0] == 0x00)
{
i_reg = 0xFF;
//fReadUID = 1;
}
}
}
else if(*Register ==0x40) // RX結束
{
if(RXErrorFlag == RXERROR) // CRC錯誤 BIT60x0f
{
i_reg = 0x02; // RX 完成
return;
}
*Register = FIFOStatus;
ReadSingle(Register, 1); //確定FIFI中剩余的字節數
*Register = (0x0F & *Register) + 0x01; // data bytes + addr
buf[RXTXstate] = FIFO; // 將接收的數據放入緩存中;
ReadCont(&buf[RXTXstate], *Register);
RXTXstate = RXTXstate + *Register;
*Register = TXLenghtByte2; //判斷是否有數據損壞
ReadSingle(Register, 1); //判斷損壞的位數
if((*Register & 0x01) == 0x01)
{
*Register = (*Register >> 1) & 0x07; // 隱藏前面無關的5位
*Register = 8 -*Register;
buf[RXTXstate - 1] &= 0xFF << *Register;
}
*Register = Reset; //接收完成復位FIFO
DirectCommand(Register);
i_reg = 0xFF; //最后接收的字節
//fReadUID = 1;
}
else if((*Register & 0x10) == 0x10) // CRC錯誤
{
if((*Register & 0x20) == 0x20) // 是否有FIFO益出中斷
{
i_reg = 0x01; // 正在RX
RXErrorFlag = RXERROR;
}
else
{
i_reg = 0x02; // RX結束 無FIFO益出
}
}
else if((*Register & 0x04) == 0x04) // byte framing 錯誤
{
if((*Register & 0x20) == 0x20)
{
i_reg = 0x01; //正在RX
RXErrorFlag = RXERROR;
}
else
{
i_reg = 0x02; //RX結束
}
}
else if(*Register == 0x01)
{ //無應答中斷
i_reg = 0x00;
}
else
{ //其他中斷處理
i_reg = 0x02;
*Register = StopDecoders; //接收完成復位FIFO
DirectCommand(Register);
*Register = Reset;
DirectCommand(Register);
*Register = IRQStatus;
ReadSingle(Register, 1);
//irqCLR;
}
}
//*************************************************************************************************
// 功能描述 : 獲取UID
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void GetUID(unsigned char *ReadBuff)
{
unsigned char command[10];
bit bbit;
command[0] = ChipStateControl; // 打開 RF 發射和接收
command[1] = 0x25; // RFID模塊RF場始能,接收始能
command[2] = ISOControl; // ISO15693
command[3] = 0x02; // ISO15693 高速, 單載波, 4出1
WriteSingle(command, 4);
DelayMs(6);
command[0] = ChipStateControl;
ReadSingle(command, 1); // 清除 IRQs
command[0] = ISOControl;
ReadSingle(command, 1); // 清除 IRQs
flags = 0x06;
bbit = InventoryRequest(ReadBuff); // 發送清點命令
command[0] = ChipStateControl; // 關閉 RF 發射
command[1] = 0x01;
WriteSingle(command, 2);
DelayMs(1);
command[0] = IRQStatus;
ReadSingle(command, 1); // 清除 IRQs
}
void Read(unsigned char Block)
{
unsigned char command[10];
command[0] = ChipStateControl; // 打開 RF 發射和接收
command[1] = 0x25; // RFID模塊RF場始能,接收始能
command[2] = ISOControl; // ISO15693
command[3] = 0x01; // ISO15693 高速, 單載波, 4出1
WriteSingle(command, 4);
DelayMs(6);
command[0] = ChipStateControl;
ReadSingle(command, 1); // 清除 IRQs
command[0] = ISOControl;
ReadSingle(command, 1); // 清除 IRQs
ReadSingleBlock(Block); // 發送清點命令
command[0] = ChipStateControl; // 關閉 RF 發射
command[1] = 0x01;
WriteSingle(command, 2);
DelayMs(1);
command[0] = IRQStatus;
ReadSingle(command, 1); // 清除 IRQs
}
//******延時1ms**********************//
void delay1(void) //誤差 0us
{
unsigned char a,b;
for(b=129;b>0;b--)
for(a=45;a>0;a--);
}
void DelayMs(unsigned char j)
{
unsigned char i;
for(i=0;i<j;i++)
delay1();
}
/*串行口初始化函數*/
void SCI_Init(void)
{ /*在11.0592MHz晶振下,設置串行口9600數據傳輸率,方式3*/
PCON=0x0; /*串口接收字符RI置位,允許串口接收*/
SCON=0x50;
TMOD=0x21;
TL1=0xfd;
TH1=0xfd;
TR1=1;
// EA = 1;
ES = 1;
}
void SendByte(unsigned char i)
{
SBUF=i;
while(!TI);
TI=0;
}
//*************************************************************************************************
// 功能描述 : 系統主循環函數
// 輸入參數 : 無
// 返回參數 : 無
// 說 明 :
//*************************************************************************************************
void main()
{
volatile unsigned int i=0;
//bit bbit;
unsigned char idata UID[8]; //卡號,8字節
//unsigned char aa[20];
unsigned char Block = 2;
//P4SW |= 0X02; /* 通過設置P4SW,將NA/P4.4,NA/P4.5和NA/P4.6腳設置成I/O口 */
P4SW |= 0X40;
P4M1=0x00;
P4M0=0x42;
//P4M1=00000000;
//P4M0=01000010;
SCI_Init();
InitPort(); //端口設置
Initial7960s(); //初始化7960
EX0=1; //(INT0)
IT0=1; //下降沿觸發
EA = 1;
delay(20);
fIntrrupt = 0;
while(1)
{
Read(0x02);
EA = 0;
for(i=0;i<4;i++)
SendByte(Data[i]);
DelayMs(20);
EA = 1;
}
}
// IRQ中斷服務程序
void TRF7960(void) interrupt 0
{
unsigned char Register[2];
fIntrrupt = 1;
do
{
Register[0] = IRQStatus;
ReadSingle(Register, 1); // 讀IRQ狀態寄存器,中斷標志自動清除
if(*Register == 0xA0) // TX active and only 3 bytes left in FIFO
{
goto FINISH;
}
InterruptHandlerReader(&Register[0]);
} while(IRQ == 0);
FINISH:
;
}