FM1208 CPU卡操作程序调试小结

FM1208 CPU卡操作程序调试小结

 

FM1702这款芯片的操作可以通过SPI串口,也可以通过并口。在我们的设计中,是通过SPI串口进行操作的。所以在硬件电路搭建好了之后,首先要做的是调试SPI接口。我们所用的控制芯片是STM32F103C8TL,其中集成了SPI接口,对其进行操作就是进行一些设置,然后读写缓冲区,标志位,片选信号线。在程序中设置如下:

SCKMISOMOSI对应芯片上的引脚设为推挽复用,

RSTNSS对应的芯片上的引脚设为推挽输出,

设置SPI端口:方向,为双向全双工;模式,主模式;数据大小,8bitsNSS为高时钟电平为高;在NSS有效后第二个周期数据有效;发送或者接受的第1比特为8bits中的最高位;循环纠正码为7

ST芯片上的SPI端口设定好之后,就是进行SPI端口调试,这个模块的调试,我们是费了一些周折的,先是直接连上FM1702芯片,发送数据并接收数据,结果总是令人沮丧,这是第一步,SPI口没有调通,之后的操作都不能进行。后来改了一下,接上Flash来调试SPI端口,由于开发平台是ST的一个开发板,把以前一个废板子,上面有Flash的,通过飞线将对应端口连接起来,结果受到废板子电路的影响,高低电平都无法稳定得到,然后换了一个有Flash的废板子,就可以了,这个大概是侥幸吧。

让我觉得欣慰的是,在两天内,我就完成了对AT45DB161这个Flash存储器的读写操作的程序,当然是在借鉴别人的一些程序的基础上。让我有点纳闷,并且有点出乎我的意料的是,SPI端口的读操作,并不是我分析出来的那样:

先让NSS有效(低)并保持有效,然后发送一个地址命令(1byte),接着检测接收缓冲器的标识位,有效以后读取缓冲区的值,然后将NSS无效。这个流程是我分析出来的,但是这样是不对的,因为这样无法读出数据,读出来的数据也是不对的。

在我参考、学习了好几个SPI操作程序之后,我发现了正确的读操作流程,1,让NSS有效(低)并保持有效;2,发送一个无效字节(DUMMY),3,检测接收缓冲器的标示位,有效后读取接收缓冲区的数据。4,让NSS无效(高)。而正确的发送一个字节操作流程为1,让NSS有效(低)并保持有效;2,发送一个命令或者数据;3,检测接收缓冲器的标志位,有效后读取数据(1byte);4,让NSS无效(高)。其中读操作的第二步,是为了给SPI端口提供时钟,而写操作的第3步好似多余,因为读出的数据是无效数据。

不管怎样,经过折腾,把SPI接口的操作调试通过了。紧接着做的工作就是调试FM1702的读写,参看了文档之后,发送地址,数据,发送地址,接收数据,主要是对FM1702中的FIFO进行读写,对Command寄存器进行读写,以及对一些寄存器进行设置。

测试这些寄存器通过之后,写好一个初始化FM1702的程序进行设置。设置好了以后,就要通过FM1702对卡片(FM1208)进行操作,1,寻卡;2,防冲突;3,选卡;这三步执行成功就会选定一张卡接下来的操作就是针对这张卡的。在第2步中,会获得卡片的序列号,这个序列号是生产卡片时就确定了的。

接着是RatsPPS命令,这两条命令通过之后就进入了MFMF是默认的一个文件夹,将CPU卡内的64k空间当做一个文件夹便于管理操作。之后的操作就是建立文件夹(应用),然后建立文件,添加记录等的操作。

在发送命令对卡进行操作时,有个问题然我很头痛,直到最后才解决,那就是发送命令等待返回值,有时候是操作失败,没有成功,那么可以继续发送,但是我并不知道怎样去判断该等待多长时间才去读取返回值,在调试的过程中,(1)如果把它设为死循环,只要没有得到相应的相应,就一直发送,结果这样很不好,假如在寻卡时失败,或者发送命令有误,那么程序无法跳出。(2)又试着把延迟设置得比较小,因为这样可以让系统反应快一点,有错就重新来过,但是有些命令,(如创建文件,添加记录)执行正确之后,需要很长时间才能有返回值,还没有到执行结束,由于延迟不够,就判定执行失败了,又重新发命令,结果是一直都操作失败。后来我发现,(3)可以通过检测FM1702的一个寄存器(中断请求寄存器)的值来判断是否完成了接受返回值。不能进行的操作是,等待期间读取FIFO的值,或者FIFO长度。这样还是有(1)中的问题,当一开始没有卡,而后来有卡时就没有办法检测到,所以在(3)的基础上添加一个延迟判别,到达一定延迟时间还没有返回正确值就判定命令执行失败,再重新发送。这个问题在调试的过程中让我吃尽苦头,有时候都不知道是这个地方的问题,最后终于解决,心中也有那么一点的成就感。

还有一些问题,是在复旦微电子那边的技术支持(唐工)的帮助下解决的,主要是后面建立钱包,管理钱包,充值,消费程序调试的时候,有一个问题,困扰了几天,在调试初始化圈存命令时,总是返回69 85,使用条件不满足,唐工帮我看了好久,排除了各种权限问题,还是不行,后来我仔细看文档,发现其中说钱包文件的标识符要设为00 02,改过来之后就执行成功了;还有一个问题也是让我很困扰,在调试圈存命令时,总是返回6901,一开始,唐工告诉我要建立一个应用公共基本数据文件,并且往里面添加记录,当我把这个完成(其实也不简单,这个文件是线路保护的二进制文件,添加记录也是一件麻烦的事情)之后,圈存命令返回值还是6901,后来在我都觉得没有希望的时候,唐工帮我仔细分析了我的操作流程,发现问题就在初始化圈存之后,卡片的状态发生了改变,我仔细检查,发现有一个获取随机数的命令,改过来之后,调试成功了,心中又是一阵欢喜。就这样,后面的消费指令也顺利调试通过了。

 

原文:

 

 

查看评论
 
13楼 wdl11272012-09-25 10:26发表 [回复] [引用][举报]
复旦的FM1208,外部认证时说找不到密钥文件. 是怎么回事。还有,在外部认证没通过的情况下,删除卡和创建文件是不是不行了?我的外部认证没通过,发送删除卡命令,能返回FA0101,但一直都返回FA0101,死循环了。最后在外部认证没通过的情况下,为什么创建定长记录文件还能返回9000?谁能给我讲解下。。
Re: taot20092012-09-29 15:00发表 [回复] [引用][举报]
回复wdl1127:你这个有点奇怪哦,发送获取随机数的命令能正常返回吗?
12楼 wdl11272012-09-14 13:44发表 [回复] [引用][举报]
我的卡,在发送外部认证的时候,因为密钥错误,导致锁死了,怎么办?还有后面的建立钱包这些有参考资料吗?
Re: taot20092012-09-14 16:57发表 [回复] [引用][举报]
回复wdl1127:select MF 后,用这个800E000000擦除命令试一下,不行的话你的卡就报废了。这个据说是卡片生产商的安全保护,多次(次数可设(1--15))认证不通过,卡片就报废,所以不好破解,不好复制
11楼 guozhangxing2012-05-18 16:30发表 [回复] [引用][举报]
您好,我是用THM3060 RF芯片来写操作FM1208卡的。但是我发完RATS 和PPS后,返回都是正确的。但是往下发APDU指令时,卡怎么都没反应该如何发想请教一下。

66 32 36 0a // atqa

66 39 33 32 30 0a // anti
66 39 33 37 30 37 36 44 33 44 41 37 34 30 42 0a 选卡
66 45 30 35 31 0a 返回:10788090022090000000000076D3DA74
66 44 31 31 31 30 30 0a PPS 返回 44 31
在往后发其它指令就没反应了 如取随机数:
66 30 35 30 31 30 30 38 34 30 30 30 30 30 38 0A 这里是 PCB 是05 CID 01 后面是 0084000008随机数指令,卡片没反应
10楼 guozhangxing2012-05-18 16:21发表 [回复] [引用][举报]
发完PPS指令后往下发APDU指令怎么发。我怎么发下去APDU指令卡一点反应都没有
9楼 guozhangxing2012-05-18 16:20发表 [回复] [引用][举报]
TAO2009 我想问一下 我发PPS命令到FM1208卡片返回正确。但是在发取随机数指令或则00A4000000指令看片一点反应没有什么值好像都没返回
8楼 taot20092012-05-08 16:36发表 [回复] [引用][举报]
擦除命令和添加密钥命令耗费时间较长
7楼 taot20092012-02-29 14:49发表 [回复] [引用][举报]
回复yjyxlfy:u8 Rats(void)
{
u8 temp,i;
SPIWrite(SPI1,ChannelRedundancy,0x0f);

RevBuffer[0] = 0xE0;
RevBuffer[1] = 0x51;
temp = Command_Send(2, RevBuffer, Transceive);
if(temp == false)
{
return(FM1702_NOTAGERR);
}
else
{
temp = SPIRead(SPI1,FIFO_Length);
if(temp < 0x03)
{
return FM1702_BYTECOUNTERR;
}
temp=Read_FIFO(RevBuffer); //for debug
for(i = 0;i<temp;i++) //for debug
a = RevBuffer[i]; //for debug
return FM1702_OK; /* 从FIFO中读取应答信息 */
}
}
6楼 yjyxlfy2012-02-29 10:38发表 [回复] [引用][举报]
请问一下 RATs 指令时什么啊,时0X0E51吗,在哪里可以查到的
Re: taot20092012-02-29 14:46发表 [回复] [引用][举报]
回复yjyxlfy:对的,是0E 51
Re: taot20092012-02-29 14:50发表 [回复] [引用][举报]
回复taot2009:不对,是E0 51
Re: yjyxlfy2012-03-09 16:40发表 [回复] [引用][举报]
回复taot2009:temp = Command_Send(2, RevBuffer, Transceive);
这个函数用的,Command_Send 给我看一下行不
5楼 guojunkb2011-12-07 10:40发表 [回复] [引用][举报]
你这个是怎么做的?
4楼 guojunkb2011-12-07 10:40发表 [回复] [引用][举报]
你好!我用STM32F103CBT6,SPI操作FM1702NL也老是读有问题,郁闷中,还有就是删除MF下面文件800E000000时候,也老是报错,可能是发送FA0101有问题
Re: taot20092011-12-14 11:27发表 [回复] [引用][举报]
回复guojunkb:先要确认FM1702的读写是正确的,才能通过FM1702对FM1208进行操作的
Re: guojunkb2011-12-14 13:36发表 [回复] [引用][举报]
回复taot2009:FM1702的读写是正确没有问题了,就是发送800E000000时,我没有收到卡片请求延时的 FA0101 数据,郁闷了,
Re: taot20092011-12-14 21:08发表 [回复] [引用][举报]
回复guojunkb:这个命令要慎用的,如果你进入MF用这个擦除命令之后,没有建立KEY文件,并且写入密钥。卡片就算是废了,下一次再进行操作都不对了,我在调试这个命令时就弄坏了两张卡片。这是我的经历,不知道有没有高人能想办法恢复那弄坏了的卡。我觉得你可能是在某一次擦除掉了,以后再进入就一直不行了。
Re: guojunkb2011-12-16 09:01发表 [回复] [引用][举报]
回复taot2009:调试了两天终于搞定了,我有几个厂家的卡片,有的卡片不用回复 FA0101 ,也可以反回9000,有的必须要回复FA0101,我调试的卡片发送的是却是F2010E。。厂家不一样这个不一样的
还有进入MF后,使用擦除命令,然后就停卡,没有建立KEY和密钥,下次也可以操作,没有出现卡片废掉的情况,呵呵,也可能是卡片厂家不一样的原因,你多试几个厂家的看看
Re: taot20092011-12-20 11:06发表 [回复] [引用][举报]
回复guojunkb:哦,是这样啊,谢谢了!
3楼 zeng78nj163com2011-11-23 23:33发表 [回复] [引用][举报]
nss接到哪个管腿?
2楼 taot20092011-11-18 14:56发表 [回复] [引用][举报]
什么问题?具体一点?
1楼 catzl72011-11-18 11:04发表 [回复] [引用][举报]
我也遇到同样的问题,可否参考下你的代码?
 
 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的使用C语言编写的FM1208 CPU和读的代码示例,其中使用了串口通信和APDU指令: ```c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <termios.h> #include <fcntl.h> #include <unistd.h> #define MAX_BUF_LEN 1024 int serialOpen(char *port, int baudrate) { int fd; struct termios newtio; fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { perror("open"); return -1; } tcgetattr(fd, &newtio); memset(&newtio, 0, sizeof(newtio)); newtio.c_cflag = baudrate | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 1; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &newtio); return fd; } void serialClose(int fd) { close(fd); } int sendAPDU(int fd, unsigned char *apdu, int apduLen, unsigned char *respBuf, int *respLen) { unsigned char buf[MAX_BUF_LEN]; int len; write(fd, apdu, apduLen); len = read(fd, buf, MAX_BUF_LEN); memcpy(respBuf, buf, len); *respLen = len; return 0; } int main(int argc, char **argv) { int fd; unsigned char apdu[256] = {0}; unsigned char respBuf[MAX_BUF_LEN] = {0}; int respLen = 0; // 打开串口 fd = serialOpen("/dev/ttyS0", B9600); if (fd < 0) { printf("open serial port failed\n"); return -1; } // 发送APDU指令 // 发 apdu[0] = 0x00; apdu[1] = 0xA4; apdu[2] = 0x04; apdu[3] = 0x00; apdu[4] = 0x08; apdu[5] = 0xA0; apdu[6] = 0x00; apdu[7] = 0x00; apdu[8] = 0x00; apdu[9] = 0x03; apdu[10] = 0xF0; apdu[11] = 0x01; apdu[12] = 0x01; apdu[13] = 0x05; sendAPDU(fd, apdu, 14, respBuf, &respLen); memset(apdu, 0, sizeof(apdu)); memset(respBuf, 0, sizeof(respBuf)); respLen = 0; // 读 apdu[0] = 0x00; apdu[1] = 0xB0; apdu[2] = 0x00; apdu[3] = 0x00; apdu[4] = 0x08; sendAPDU(fd, apdu, 5, respBuf, &respLen); // 处理片响应 if (respBuf[respLen - 2] == 0x90 && respBuf[respLen - 1] == 0x00) { printf("Read card success\n"); } else { printf("Read card failed\n"); } // 关闭串口 serialClose(fd); return 0; } ``` 需要注意的是,上述代码仅供参考,具体的代码实现需要根据所使用的硬件平台和应用场景进行相应的调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值