这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » IC 接触式IC卡读写问题!求助

共3条 1/1 1 跳转至

IC 接触式IC卡读写问题!求助

院士
2006-09-17 18:14:16     打赏
IC 接触式IC卡读写问题!求助



关键词: 接触式     读写     问题     求助    

院士
2006-12-22 22:43:00     打赏
2楼
问 一、使用LPC2114单片机通过IO口加上拉电阻(4.7K),与SLE4428卡连接读写数据。无法读去数据程序如下希望朋友们看一下:
#include "config.h"

#define IC_rst  1<<2
#define IC_clk  1<<3
#define IC_dat  1<<4
#define IC_IN   1<<11  //IC插入

uint8 IcRead[3]={0x0E,0x20,0x00};   //读卡数据命令
uint8 IcWrite[3]={0x33,0x00,0x00};  //写卡数据命令
uint8 IcCheck1[3]={0xCD,0xFE,0xFF}; //密码校验低位
uint8 IcCheck2[3]={0xCD,0xFF,0xFF}; //密码校验高位
uint8 IcRcume[3]={0xCD,0xFD,0x08};  //错误计数器复位
uint8 EnabWrtIc;//IC卡写使能
uint8 ICAddr;
uint8 IcRcv[10];

void DlayNS(uint32 dly)
{
for(;dly>0;dly--);
}

void Clock(void)
{
uint32 regist;

regist=IO0DIR;
IO0CLR=IC_clk;
DlayNS(10); //时钟高、低至少9us
IO0SET=IC_clk;
DlayNS(10); //10us
IO0DIR=regist;
}
void Uart0Send(uint8 dat)
{
U0THR=dat;
while((U0LSR&0x40)==0);
}

void Uart0Int(uint32 BPS)
{
uint16 Fdiv;
PINSEL0|=0x05;
U0LCR=0x83;
Fdiv=(Fpclk/16)/BPS;
U0DLM=Fdiv/256;
U0DLL=Fdiv%256;
U0LCR=0x03;
}

void SendIC(uint8 *buf,uint8 num)
{
uint32 i;
uint8 j;
uint8 dat;

IO0SET=IC_rst;
for(i=0;i<num;i++)
{
  dat=*buf;
  for(j=0;j<8;j++)
  {
   if((dat&0x01)!=0)
   {
    IO0CLR=IC_clk;
    DlayNS(90);
    IO0SET=IC_dat;//写1
    DlayNS(25000);
    IO0SET=IC_clk;
    DlayNS(90);
    }
   else
   {
    IO0CLR=IC_clk;
    DlayNS(90);
    IO0CLR=IC_dat;//写0
    IO0DIR=IC_dat;
    DlayNS(25000);
    IO0SET=IC_clk;
    DlayNS(90);
   }
   dat>>=1;
  }
  buf++;
}
IO0CLR=IC_rst;
}

void RcvIC(uint8 num)
{
uint8 i;
uint8 j;
uint32 iodat;
uint8 dat=0x00;
//
IO0CLR=IC_rst;
for(i=0;i<num;i++)
{
  for(j=0;j<8;j++)
  {
   IO0CLR=IC_clk;//clk由1变为0时,开始接收一位数据
   DlayNS(25);//延时
   IO0SET=IC_clk;
   iodat=IO0PIN;
   if((iodat&IC_dat)==IC_dat)
   {
     dat|=(1<<j);
     }
   else dat=0<<j;
   DlayNS(25);
   }
  IcRcv[i]=dat;
  }
IO0SET=IC_rst;
IO0DIR|=IC_dat;
}

uint8 CheckIcMA(void)
{
   uint8 err;
   uint8 i;
   //密码校验
   IcRead[0]=0xCE;
   IcRead[1]=0xFD;
   SendIC(&IcRead[0],0x03);//读错误计数器当前值
   RcvIC(1);
   if(IcRcv[0]!=0)         //不等于0开始校验密码
   {
    SendIC(&IcCheck1[0],3);//校验第一字节的密码
    RcvIC(1);
    if(IcRcv[0]==0)
    {
     SendIC(&IcCheck2[0],3);//校验第二字节的密码
     RcvIC(1);
     if(IcRcv[0]==0)
     {
      SendIC(&IcRcume[0],3);//复位错误计数器为8
      for(i=0;i<103;i++)    //写卡完延续103个脉冲
      {
       Clock();
       }
      err=0;
      }
     else err=1;
    }
   }
  return(err);
}

void ICTransact(void)
{
uint8 stand;
uint8 err;
uint8 i;
//密码校验
   err=CheckIcMA();
   if(err==0) EnabWrtIc=1;
   else EnabWrtIc=0;
   
   //写站台代码及到站时间
   if(EnabWrtIc==1)
   {
    //LED显示开始给IC卡写到站数据
    //预写卡地址校验
    IcRead[1]=0x08;
    SendIC(&IcRead[0],0x03);
    RcvIC(0x01);
    ICAddr=IcRcv[0];
    IcRead[1]=0x09;
    SendIC(&IcRead[0],0x03);
    RcvIC(0x01);
    ICAddr=IcRcv[0]<<8;
    
    IcWrite[1]=ICAddr+1;
    IcWrite[0]|=(ICAddr>>8)&0x0C;
    IcWrite[2]=stand;          //获取站台代码
    SendIC(&IcWrite[0],0x03);  //写站台代码
    for(i=0;i<103;i++)
    {
     Clock();
     }
     
    IcWrite[1]=ICAddr+1;
    IcWrite[0]|=(ICAddr>>8)&0x0C;
    IcWrite[2]=stand;          //获取年
    SendIC(&IcWrite[0],0x03);  //写到站时间年
    for(i=0;i<103;i++)
    {
     Clock();            
     }
     
    IcWrite[1]=ICAddr+1;
    IcWrite[0]|=(ICAddr>>8)&0x0C;
    IcWrite[2]=stand;          //获取月
    SendIC(&IcWrite[0],0x03);  //写到站时间月
     for(i=0;i<103;i++)
     {
      Clock();            
     }
    IcWrite[1]=ICAddr+1;
    IcWrite[0]|=(ICAddr>>8)&0x0C;
    IcWrite[2]=stand;          //获取日
    SendIC(&IcWrite[0],0x03);  //写到站时间日
    for(i=0;i<103;i++)
    {
     Clock();            
     }
     
    IcWrite[1]=ICAddr+1;
    IcWrite[0]|=(ICAddr>>8)&0x0C;
    IcWrite[2]=stand;          //获取时
    SendIC(&IcWrite[0],0x03);  //写到站时间时
    for(i=0;i<103;i++)
    {
     Clock();            
     }
   }
}

uint8 ICReast(void)
{  
   uint8 i;
   uint8 err=0;
   //从上电到IC卡复位操作必须大于100us
   IO0SET=IC_dat;
   IO0CLR=IC_rst;
   IO0CLR=IC_clk;
   DlayNS(430);
   IO0SET=IC_rst;
   DlayNS(23);
   IO0SET=IC_clk;
   DlayNS(45);
   IO0CLR=IC_clk;
   DlayNS(23);
   IO0CLR=IC_rst;
   
   RcvIC(0x04); //读卡复位后的4个数据
   for(i=0;i<4;i++)
   {
    Uart0Send(IcRcv[i]);
    }
   
   IO0SET=IC_clk;
   DlayNS(15);
   IO0CLR=IC_clk;
   DlayNS(1);
   IO0SET=IC_dat;
   return(err);
   
}
  
int main(void)
{
uint8 err;
uint8 i;
uint8 sta=0;
PINSEL0=0x00000005;
PINSEL1=0x00000000;
Uart0Int(115200);
IO0DIR=IC_rst|IC_clk|IC_dat;
while(1)
{
    err=ICReast();
    if(err==1) ICReast();
    else sta=1;
    //读IC卡数据
    while(sta==1)
    {
     SendIC(&IcRead[0],0x03);//写命令
     RcvIC(3);
     for(i=0;i<3;i++)
     {
         //读数据
     Uart0Send(IcRcv[i]);
     }
     IcRead[1]+=1;
     if(IcRead[1]>=0xFF)
     {
      IcRead[1]=0x00;
      }
    }
  }
}
sle4428卡资料:http://www.21ic.com/news/n1283c78.aspx

二:使用LPC2114单片机通过I2C接口加上拉电阻(4.7K),与AT24C16卡连接读写数据。无法读去IC卡数据,单可以对AT24C01EEPROM芯片进行读写操作。程序如下希望朋友们看一下:
#include "config.h"
#define power 1<<4;
extern  void  __irq  IRQ_I2C(void);
extern  uint8  IRcvStr(uint8 sla, uint8 suba, uint8 *s, uint8 no);
extern  uint8  ISendStr(uint8 sla, uint8 suba, uint8 *s, uint8 no);
extern  uint8  ISendByte(uint8 sla, uint8 dat);
extern  uint8  IRcvByte(uint8 sla, uint8 *dat);


void I2Cint(uint32 fi2c)
{
//PINSEL0=0x50;

I2SCLH=(Fpclk/fi2c+1)/2;
I2SCLL=(Fpclk/fi2c)/2;
I2CONCLR=0x2C;
I2CONSET=0x40;

VICIntSelect=0x00000000;
VICVectCntl0=0x29;
VICVectAddr0=(uint32)IRQ_I2C;
VICIntEnable=(1<<9);
}

void Uart0Send(uint8 dat)
{
U0THR=dat;
while((U0LSR&0x40)==0);
}

void Uart0Int(uint32 BPS)
{
uint16 Fdiv;
//PINSEL0|=0x05;
U0LCR=0x83;
Fdiv=(Fpclk/16)/BPS;
U0DLM=Fdiv/256;
U0DLL=Fdiv%256;
U0LCR=0x03;
}

void  DelayNS(uint32  dly)
{  uint32  i;

   for(; dly>0; dly--)
      for(i=0; i<50000; i++);
}

int main(void)
{
uint8 i;
uint8 dat=0x11;
uint8 err0,err1;
uint8 dat2[8];
uint8 dat1[8]={0x05,0x06,0x07,0x08,0x05,0x06,0x07,0x08};

PINSEL0=0x00000055;
PINSEL1=0x00000000;
IO0DIR=0x1C;

I2Cint(9600);
Uart0Int(115200);


while(1)
{
  loop:
  IO0SET=power;
  err0=ISendStr(0xA0, 0x60, &dat1[0],0x08);
  IO0CLR=power;
  if(err0==0)
  {
   dat+=0x10;
   goto loop;
   }
  else
  {
   DelayNS(1);
   dat=0x10;
   loop1:
   IO0SET=power;
   err1=IRcvStr(0xA0, 0x60, &dat2[0], 0x08);
   IO0CLR=power;
   if(err1==0)
   {
    dat+=0x10;
    goto loop1;
   }
   else
   {
    for(i=0;i<8;i++)
    {
     Uart0Send(dat2[i]);
     }
    }
  dat=0x01;
  DelayNS(1);
  }
}
}

/****************************************************************************
* 文件名:I2CINT.C
* 功能:硬件I2C软件包,利用中断方式操作。
* 说明:主程序要配置好I2C总线接口(I2C引脚功能和I2C中断,并已使能I2C主模式)
****************************************************************************/
#include  "config.h"

/* 以下为I2C操作时所需要的变量 */
volatile  uint8  I2C_sla;           // 从机地址(即器件地址)
volatile  uint8  I2C_suba;          // 子地址
volatile  uint8  *I2C_buf;          // 数据缓冲区指针 (读操作时会被更改)
volatile  uint8  I2C_num;           // 操作数据个数 (会被更改)
volatile  uint8  I2C_end;           // 操作结束标志,为1时表示操作结束,为0xFF时表示操作失败 (会被设置)
volatile  uint8  I2C_suba_en;       // 子地址使能控制,读操作时请设置为1,写操作时请设置为2 (会被更改)

void DelayMS(uint32 dly)
{
uint8 i;
for(;dly>0;dly--)
  for(i=0;i<100;i++);
}
/****************************************************************************
* 名称:IRQ_I2C()
* 功能:I2C中断,通过判断I2C状态字进行相应的操作。
* 入口参数:无
* 出口参数:无
****************************************************************************/
void  __irq  IRQ_I2C(void)
{  uint8  sta;
   
   sta = I2STAT;                    // 读出I2C状态字
   switch(sta)
   {  case  0x08:                   // 己发送起始条件
            if(1==I2C_suba_en) I2DAT = I2C_sla&0xFE;    // 指定子地址读时,先写入地址
              else I2DAT = I2C_sla;                     // 否则直接发送从机地址
            I2CONCLR = 0x28;        // SI=0
            break;
            
      case  0x10:
            I2DAT = I2C_sla;        // 重启动总线后,发送从地址
            I2CONCLR = 0x28;        // SI=0
            break;
   
      case  0x18:                   // 已发送SLA+W,并已接收应答
            if(0==I2C_suba_en)      // 无子地址,则直接发送数据
            {  if(I2C_num>0)
               {  I2DAT = *I2C_buf++;
                  I2CONCLR = 0x28;
                  I2C_num--;
               }
               else
               {  I2CONSET = 0x10;  // 无数据发送,结束总线
                  I2CONCLR = 0x28;
                  I2C_end = 1;      // 设置总线操作结束标志
               }
               break;
            }
            if(1==I2C_suba_en)      // 发送子地址
            {  I2DAT = I2C_suba;
               I2CONCLR = 0x28;
            }
            if(2==I2C_suba_en)
            {  I2DAT = I2C_suba;
               I2CONCLR = 0x28;
               I2C_suba_en = 0;     // 子地址己处理
            }
            break;
            
      case  0x28:                   // 已发送I2C数据,并接收到应答
            if(0==I2C_suba_en)      // 无子地址,则直接发送数据
            {  if(I2C_num>0)
               {  I2DAT = *I2C_buf++;
                  I2CONCLR = 0x28;
                  I2C_num--;
               }
               else
               {  I2CONSET = 0x10;  // 无数据发送,结束总线
                  I2CONCLR = 0x28;
                  I2C_end = 1;
               }
               break;
            }
            if(1==I2C_suba_en)      // 若是指定地址读,则重新启动总线
            {  I2CONSET = 0x20;
               I2CONCLR = 0x08;
               I2C_suba_en = 0;     // 子地址己处理
            }
            break;
   
   
      case  0x20:
      case  0x30:
      case  0x38:
            I2CONCLR = 0x28;        // 总线进入不可寻址从模式
            I2C_end = 0xFF;         // 总线出错,设置标志
            break;
   
   
      case  0x40:                   // 己发送SLA+R,并已接收到应答
            if(1==I2C_num)          // 最后一字节,接收数据后发送非应答信号
            {  I2CONCLR = 0x2C;     // AA=0,接收到数据后产生非应答
            }
            else                    // 接收数据并发送应答信号
            {  I2CONSET = 0x04;     // AA=1,接收到数据后产生应答
               I2CONCLR = 0x28;
            }
            break;
            
      case  0x50:
            *I2C_buf++ = I2DAT;     // 读取数据
            I2C_num--;
            if(1==I2C_num)
            {  I2CONCLR = 0x2C;     // AA=0,接收到数据后产生非应答
            }
            else
            {  I2CONSET = 0x04;     // AA=1,接收到数据后产生应答
               I2CONCLR = 0x28;
            }
            break;
      
      case  0x58:
            *I2C_buf++ = I2DAT;     // 读取最后一字节数据
            I2CONSET = 0x10;        // 结束总线
            I2CONCLR = 0x28;
            I2C_end = 1;
            break;
      
      case  0x48:
            I2CONCLR = 0x28;        // 总线进入不可寻址从模式
            I2C_end = 0xFF;
            break;
            
      default:
            break;
   }
   VICVectAddr = 0x00;              // 中断处理结束
}



/****************************************************************************
* 名称:ISendByte()
* 功能:向无子地址器件发送一字节数据。
* 入口参数:sla        器件地址
*          dat        要发送的数据
* 出口参数:返回值为0时表示出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8  ISendByte(uint8 sla, uint8 dat)
{  /* 参数设置 */
   I2C_sla = sla;        // 写操作的器件地址
   I2C_buf = &dat;        // 待发送的数据
   I2C_num = 1;            // 发送1字节数据
   I2C_suba_en = 0;        // 无子地址
   I2C_end = 0;
   
   I2CONCLR = 0x2C;
   I2CONSET = 0x60;             // 设置为主机,并启动总线
   
   while(0==I2C_end);
   if(1==I2C_end) return(1);
     else return(0);
}



/****************************************************************************
* 名称:ISendStr()
* 功能:向有子地址器件发送多字节数据。
* 入口参数:sla        器件从机地址
*          suba        器件子地址
*          s        数据发送缓冲区指针
*          no        发送数据个数
* 出口参数:返回值为0时表示出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8  ISendStr(uint8 sla, uint8 suba, uint8 *s, uint8 no)
{  /* 参数设置 */
   I2C_sla = sla;        // 写操作的器件地址
   I2C_suba = suba;        // 子地址
   I2C_buf = s;            
   I2C_num = no;    
   I2C_suba_en = 2;        // 有子地址写
   I2C_end = 0;
   
   I2CONCLR = 0x2C;
   I2CONSET = 0x60;             // 设置为主机,并启动总线
   
   while(0==I2C_end);
   if(1==I2C_end) return(1);
     else return(0);
}



/****************************************************************************
* 名称:IRcvByte()
* 功能:向无子地址器件读取一字节数据。
* 入口参数:sla        器件地址
*          dat        接收数据的变量指针
* 出口参数:返回值为0时表示操作出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8  IRcvByte(uint8 sla, uint8 *dat)
{  /* 参数设置 */
   I2C_sla = sla+1;        // 读操作的器件地址
   I2C_buf = dat;
   I2C_num = 1;
   I2C_suba_en = 0;        // 无子地址
   I2C_end = 0;
   
   I2CONCLR = 0x2C;
   I2CONSET = 0x60;             // 设置为主机,并启动总线
   
   while(0==I2C_end);
   if(1==I2C_end) return(1);
     else return(0);
}



/****************************************************************************
* 名称:IRcvStr()
* 功能:向有子地址器件读取多字节数据。
* 入口参数:sla        器件地址
*          suba        器件子地址
*          s        数据接收缓冲区指针
*              no         读取数据个数
* 出口参数:返回值为0时表示操作出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8  IRcvStr(uint8 sla, uint8 suba, uint8 *s, uint8 no)
{  if(0==no) return(0);
   
   /* 参数设置 */
   I2C_sla = sla+1;        // 读操作的器件地址
   I2C_suba = suba;        
   I2C_buf = s;
   I2C_num = no;
   I2C_suba_en = 1;        // 有子地址读
   I2C_end = 0;
   
   I2CONCLR = 0x2C;
   I2CONSET = 0x60;             // 设置为主机,并启动总线
   
   while(0==I2C_end);
   if(1==I2C_end) return(1);
     else return(0);
}

1: AT24C01A/02/04/08/16/32/64卡资料1.    
  这几个型号的IC卡为ATMEL存储卡,是一种不具备加密功能的EEPROM卡,AT24C为系列号,数字部分为K位容量,分别为1K、2K、4K、8k、16K、32K、64K位。它的使用方法与EEPROM完全相同,存储结构简单,只有读写两种操作功能,主要用于存放一些保密性要求不高的数据。
功能:
●容量别为1k/2k/4k/8k/16k/64k bit;
●循环擦写:1,000,000次;
●数据保存期:100年;
●温度范围:-250C-700C;
●结构单一,无分区,只有读、写操作
AT24C系列的工作频率为lMHz(5V),lMHz(2.7V),400KHz(1.8V);工作电压为5V士l0%,根据要求最低可至1.8V;Icc电流读最大为lmA,写最大为3mA:写/擦除次数为l00万次;数据保持lO0年;工作温度为O-70℃,根据要求可超过指定工作温度;通讯协议符合ISO/IEC 7816-3同步协议,双向串行接口。

    存储结构:
     

    AT24C系列型号的后两位数字为该型号的最大K位数(1K=1024),8位为1字节,最大字节存储容量的算法为K位数×l024÷8。如:AT24C01A的最大存储容量的1×1024÷8=128,其字节地址空间为O—127(16进制为0x00—0x7F)。
2: 用I2C协议给AT24C16卡,读写时总是接收到非ACK应答! 3: 对AT24C16卡读写的程序,可以使用。是那个卡座接触不良的原因! 4: 4428 io 最低 电压?4428 io  h 的最低 电压 和 2114 h 的最到电压 好象不符合。
5: 电压的问题卡的高电平最小值》3.3v
6: IO口上拉接的是5v电源我给数据线、时钟线、复位线IO口接的上拉电阻电源是5v,IC卡电源也是5v。也没调试通过。现在时间比较急,项目做完了再好好调试一下。
谢谢朋友们了! 7: TO:冯广周  AT24C16有分区还是没有分区啊,怎么有人说有而有的说没有呢?怎么回事?郁闷!!

菜鸟
2011-04-18 11:27:04     打赏
3楼
好历史悠久的问题。楼主作何解答呢?

共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]