应用笔记 4095

DS5250与磁卡读卡器的连接


摘要 : 本应用笔记阐述了在DS5250安全微控制器上轻松实现磁卡读卡和解码操作的设计。文章还演示了开发同时具有磁卡读卡功能和安全微控制器高级加密功能的实际应用。这一实例采用DS5250评估(EV)板连接磁卡读卡器。本应用笔记提供源代码,很容易装载到Maxim的其它8051微控制器中。

简介

背面带有编码磁条的卡通常称为磁卡,广泛用于银行、储值等金融领域。信用卡、ATM和借记卡都是典型的磁卡,有的卡片还嵌入了智能卡芯片。其它应用包括:游戏、影印和公交等领域的礼品卡、安全卡以及房间钥匙卡等。采用标准ISO格式时,这些磁卡的最大容量只有160字节,一般不能存储大量数据。与智能卡或便携式闪存相比,磁卡的存储容量足以支持金融和银行应用,被广泛应用于该领域。而且,磁卡价格低,比较可靠,不需要内部供电,读取相对方便。

本应用笔记介绍怎样使用DS5250安全微控制器实现和磁卡读卡器的接口。之所以选择DS5250,是因为它具有开发常用读卡设备(例如销售终端POS等设备)所需要的安全和加密功能。

磁卡读卡器电路可以执行底层任务:把磁条磁通量解码成比特流。DS5250管理顶层任务:将这些比特转换成字符,读取并验证数据。

硬件和软件要求

硬件

本实例采用了DS5250评估(EV)板(B版),进行了以下改动:
  • 去掉了端口0的上拉电阻(RN2)
实际应用还需要以下硬件:
  • 读卡器—MagTek®三磁道读卡器,含有3V解码器ASIC;序列号为21030001,G版或更新版本(www.magtek.com/)
  • 两个上拉电阻(大约10kΩ)
  • 线性稳压器(能够从5V电源产生3V至3.6V供电)
  • 用于测试的磁卡(ATM卡、信用卡等)
关于这些磁卡的详细信息,请参考下面的链接:

软件

本实例应用程序以C语言编写,采用了Keil的µVision® IDE 2.40a版进行编译(www.keil.com/)。利用Maxim的微控制器工具包(MTK)将编译后的应用程序装载到DS5250中。

应用程序的详细信息

下面几节介绍实例应用程序的实现。可以从Maxim的FTP网站下载完整的C语言代码

连接读卡器

MagTek读卡器有5个接口引脚(包括电源和地):
  • 引脚1—STROBE
    该信号仅用于读卡器输入。由主机微控制器驱动,逐位输出卡的数据位,触发复位过程。
  • 引脚2—DATA
    该信号通常是读卡器的输出,包含磁道A/B/C的卡数据位,由STROBE信号同步输出。在某些情况下(例如,触发复位时),可以由主机微控制器驱动该信号。
  • 引脚3—VDD
    读卡器的电源(2.7V至3.6V)。
  • 引脚4—GND
  • 引脚5—GND
由于STROBE和DATA引脚的工作电平为3V,DS5250无法直接通过标准端口引脚驱动。因此,以端口0 (该端口工作在开漏模式)替代,当DS5250将引脚置为三态时,通过电阻将信号电平上拉至3.6V。由于DS5250端口的VIH电平能够兼容3V I/O电平,因此,DS5250可以直接从DATA线读取DATA信号(请参考DS5250数据资料,了解详细信息)。

读卡器工作电平为3V;它不能直接采用DS5250的5V电源供电。由于DS5250评估板不提供3V供电,因此,采用了单独的线性稳压器(例如, MAX1658)对读卡器供电。请参见图1

图1. 实际应用的连接
图1. 实际应用的连接

复位

首次上电后,主机微控制器在进行读卡之前必须复位读卡器。每次进行卡扫描之后,也必须按顺序进行复位,以清除内部读卡器ASIC的内存,使读卡器能够准备就绪。接收新的读卡操作。

复位读卡器时,主机微控制器(例如,本应用笔记的DS5250)必须按照以下顺序驱动STROBE和DATA (请参考MagTek文档,了解确切的电压电平和时序参数)。
  1. DATA和STROBE都以高电平(空闲)状态开始。
  2. 将DATA置为低电平。
  3. 保持DATA为低电平,将STROBE驱动为低电平,然后再次驱动为高电平。
  4. 再次驱动STROBE为低电平,随后释放DATA,使其能够浮空至高电平。
  5. 当DATA浮空为高电平时,将STROBE驱动为低电平,然后再次驱动为高电平。在这一点,复位读卡器,处于低功耗等待状态。
  6. 将STROBE驱动为低电平,然后再次驱动为高电平。对读卡器进行“配置”,使其能够进行读卡操作。
// Generate a long delay for card reset and read intervals.

void longDelay()
{
   int i, j;

   for (i = 1; i < 5; i++) {
      for (j = 1; j < 5000; j++) {
         ;
      }
   }
}

// Generate a shorter delay (used between STROBE/DATA transitions).

void delay()
{
   int i;

   for (i = 1; i < 1000; i++) {
      ;
   }
}

// Release the DATA line (P0.0) and allow it to float high.

void dataHigh()
{
   P0 |= 0x01;
   delay();
}

// Drive the DATA line (P0.0) low.

void dataLow()
{
   P0 &= 0xFE;
   delay();
}



// Release the STROBE line (P0.1) and allow it to float high.

void strobeHigh()
{
   P0 |= 0x02;
   delay();
}

// Drive the STROBE line (P0.1) low.

void strobeLow()
{
   P0 &= 0xFD;
   delay();
}

void resetCardReader()
{
   dataHigh();
   strobeHigh();
   longDelay();

   dataLow();                // Force DATA low.
   longDelay();   
   strobeLow();              // Drive STROBE low, then high again.
   strobeHigh();
   strobeLow();              // Drive STROBE low, then release DATA.
   dataHigh();
   longDelay();

   strobeHigh();             // Drive STROBE low and high again two more times
   strobeLow();              //    to complete the reset and leave the card reader
   strobeHigh();             //    in the ready state, prepared to scan a card.
   strobeLow();
}

检测读卡操作

读卡器一旦复位并进行配置后,就可以进行读卡操作。读卡时,MagTek读卡器执行一次完整的读周期;不需要主控制器干预。卡的磁条中所有三个磁道A、B、C的全部内容都被存储到读卡器IC中。读卡周期完成后,可以由主控制器将数据逐位同步输出。

读卡器通过握手周期告诉主机什么时候开始读卡,什么时候完成读卡。
  1. 该周期以STROBE低电平和DATA浮空高电平(空闲状态)开始。
  2. 当读卡器探测到磁条移过读卡器探头时,开始对卡进行扫描。它将DATA线驱动为低电平,告诉主机开始读卡操作。
  3. 主机驱动STROBE为高电平,然后再变为低电平,做出响应。
  4. 读卡器将DATA再次驱动为高电平。
  5. 一旦读卡周期完成后,读卡器再次将DATA驱动为低电平。这一操作告诉主机已经完成了读卡过程,可以同步输出卡数据。
// Wait for the DATA line to be driven low by the card reader.

void waitForDataLow()
{
   int i = 0xFF;

   dataHigh();               // Make sure that DATA is floating high.

   while ((i & 1) == 1) {
      i = P0;
   }
} 

....

   resetCardReader();
   printf("\r\n");
   printf("Waiting for card swipe...\r\n");
   printf("\r\n");

   waitForDataLow();      // DATA low indicates that card swipe has begun.
   strobeHigh();
   longDelay();
   strobeLow();
   longDelay();
   waitForDataLow();      // DATA low indicates that card swipe is complete.

读取并解码卡数据

完成读卡,并且所有卡数据经过解码并存储在读卡器ASIC后,读卡器将DATA线驱动为低电平。如上所述,这一操作提示DS5250可以同步输出卡数据。在这一点,DS5250在同步输出每一位数据时,将STROBE驱动为高电平,然后再驱动低电平,以顺序读出每一位卡数据。在STROBE驱动为高电平,然后再驱动为低电平后,读卡器ASIC将下一位数据在DATA线上同步输出。ASIC在第0位将DATA驱动为高电平,第1位时将DATA驱动为低电平。
// Clock a single bit value out of the card reader by driving STROBE high,
// then low, and reading the DATA line.  

int readBit()
{
   int i;

   strobeHigh();             // Drive STROBE high.
   strobeLow();              // Drive STROBE low (DATA line now contains bit).

   i = P0;
   if ((i & 1) == 0) {   
      return 1;              // Low on DATA line indicates a 1 bit.
   } else {
      return 0;              // High on DATA line indicates a 0 bit.
   }
}
读卡器同步输出的前16位是“前导”位,指出读卡器ASIC的版本。这些为并非卡的数据,应用程序可以忽略它们。

磁道A译码

16位前导码之后,DATA线上同步输出704位数据,其中包括从磁卡磁道A读取的数据。当采用标准ISO格式进行编码时,磁道A含有76个字符,使用的7位字符集含有字母、数字等符号。

每7位字符在同步输出时最低有效位(LSB)在前,最高位(即,第7位)是奇偶校验位,可用于验证卡数据的完整性。除去奇偶校验位,其余6位定义了64个字符中的某一个,可以编码到磁道A上。例如,000000b代表空格符,000001b代表感叹号。以下代码为char7bit[64]字符阵列。
//          0123456789012345678901234567890123456789012345678901234567890 123
char char7bit[64] = 
           " !'#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";


// Clock out and decode a 7-bit character from the track memory, returning the
// character value.  7-bit (alphanumeric) characters are found on Track A only.

char read7BitChar()
{
   int i, c;

   // Each character is composed of 7 bits, which we clock out of the track memory
   // beginning with the least significant bit.  Bit 7 is parity, which is ignored.

   c = 0;
   for (i = 1; i < 128; i *= 2) {
      c |= (readBit() * i);
   }
   c &= 0x3F;

   return char7bit[c];       // Decode/return the character using the 7-bit table.
}

....

   // Track A - 76 characters, 7 bits per alphanumberic character including parity.

   printf("Track A > ");   
             
   for (i = 0; i < 76; i++) {
      putchar(read7BitChar());
   }
   printf("\r\n\r\n");

   // At this point, we have read 532 bits of the 704-bit Track A memory on the 
   // card reader IC.  Flush the remaining 172 bits.

   for (i = 0; i < 172; i++) {
      readBit();
   }
不同类型的卡在磁道A上有不同的数据。磁道A还可以含有字母字符。因此,磁道A常用于存储持卡人的姓名、地址和账号等数字信息。正如上面代码所示,在同步输出磁道B的数据之前,必须读出磁道A的所有704位数据(即使并非所有位都含有编码数据)。

磁道B译码

磁道B的编码和磁道A相似,只是采用了5位(4位数据和1位奇偶校验位)编码,而不是7位编码。磁道B的字符集只含有数字字符和符号,如下面的char5bit[16]字符阵列所示。
//                   0123456789012345
char char5bit[16] = "0123456789:;<=>?";

// Clock out and decode a 5-bit character from the track memory, returning the
// character value.  5-bit (numeric+symbol) characters are found on Tracks B and C.

char read5BitChar()
{
   int i, c;

   // Each character is composed of 5 bits, which we clock out of the track memory
   // beginning with the least significant bit.  Bit 5 is parity, which is ignored.

   c = 0;
   for (i = 1; i < 32; i *= 2) {
      c |= (readBit() * i);
   }
   c &= 0x0F;

   return char5bit[c];       // Decode/return the character using the 5-bit table.
}

....

   // Track B - 40 characters, 5 bits per numeric/symbol character including parity.

   printf("Track B > ");   
             
   for (i = 0; i < 40; i++) {
      putchar(read5BitChar());
   }
   printf("\r\n\r\n");
在磁道A的最后,读取磁道C之前,必须读取磁道B的所有剩余位(如果需要用到磁道C)。由于已经从磁道B (40个字符 x 5位)读取了200位,在访问磁道C之前,必须同步输出其余504位。

磁道C译码

磁道C的编码方式和磁道B一样,并采用了同一字符集,最多可编码107个7位字符。磁道C最初的目的是作为可写数据区,以支持离线金融交易,但不常用。大部分磁卡在磁道C上不带有编码数据。

结论

磁卡广泛应用于金融、接入控制、政府以及储值等领域。通过加入一个简单的MagTek卡扫描读卡器,为DS5250评估板提供少量的支持硬件,开发系统即可支持磁卡读取功能以及安全微控制器的高级安全加密功能。利用Keil的µVision C编译器,很容易在DS5250安全微控制器上演示磁卡读取和解码功能。

注意:由于其内在的安全特性,DS5250安全微控制器受美国出口法的控制。如需获得包括数据资料、用户指南在内的所有DS5250文档,需要签署保密协议(NDA)。而本应用笔记及其源代码都可以免费提供,直接装载到Maxim的任一8051微控制器中。