|
我现在用STC的单片机(基本上兼容51)在做一个直接操作SIM卡的项目。用的是KEIL编程环境。
如果哪位高手有单片机直接读写SIM卡的程序,希望能提供一下,非常感谢。
现在进展的情况:
SIM卡上电复位后,似乎是能读到返回值了,返回值的第一个字节是“3B”,这个应该差不多是正确的,但后面的一些字节就有问题了,有的字节都通不过奇偶校验。(后面会附上测试程序及SIM卡的返回值)
但不管怎么说,SIM卡的复位应该是成功了吧,虽然接收到的复位响应有点问题。那就继续操作吧,我又试了一下select命令。即向SIM卡发送“A0 A4 00 00 02”,这次接收到的是“6E 00 FF”。看了一下ISO7816的说明,好像是说我的CLA(就是A0那个字节)发的不对。我现在怀疑是不是我的发送字节的函数写的不好,延时有问题,以至于时序错误,SIM卡识别不出来呢?
下面是我的完整测试程序,用KEIL2编写的,单片机是用的STC的,这个单片机的速度比51快,所以延时程序部分也相应做了调整。
/*************************************************************
* sim_test.c
* 程序功能:
* 测试单片机与SIM卡的读写操作
*************************************************************/
#include
#include
typedef unsigned char uint8;
sbit SIM_CLK = P1^0; //接SIM卡的CLK引脚,这里用单片机的P1.0为SIM卡提供时钟(2.7648MHz)
sbit SIM_IO = P2^5; //接SIM卡的I/O引脚
sbit SIM_RST = P0^6; //接SIM卡的RST引脚
sbit SIM_VCC_ENABLE = P0^4; //SIM卡的电源使能
//为P1.0管脚输出时钟信号而定义(这部分是STC单片机特有的吧,目的是为了从P1.0输出时钟)
sfr WAKE_CLK0 = 0x8F;
sfr AUXR = 0X8E;
sfr BRT = 0X9C;
uint8 buf[32]={0x55}; //从SIM卡接收到的数据,先存放在buf中,然后再发至串口显示
uint8 i=0; //指向buf的起始
uint8 j=sizeof(buf); //指向buf的结束
/************************************************************
* 初始化单片机相关寄存器
***********************************************************/
void UartInit()
{
SCON = 0x50;
TMOD |= 0x21;
PCON |= 0x80;
TH1 = 0xE8; // Baud:2400 fosc=11.0592MHz
TL1 = 0xE8;
IE |= 0x90;
TR1 = 1;
}
/**************************************************
* 初始化CLK时钟,该时钟用来向SIM卡提供时钟信号
***************************************************/
void SIM_CLK_Init()
{
AUXR = (AUXR | 0x04); //独立波特率发生器工作在1T模式
BRT = 254; //输出频率CLKOUT2=(Fosc/2)/(256-BRT)=(11.0592MHz/2)/(256-254)=2.7648MHz
WAKE_CLK0 = WAKE_CLK0 | 0x04; //允许独立波特率发生器输出时钟
}
/**************************************************
* 延时半个ETU(134.55us/2=67.275)
***************************************************/
void delay_half_ETU()
{
#pragma asm
MOV R2,#9
ETUhalf_DELAY2: MOV R3,#19
ETUhalf_DELAY1: DJNZ R3,ETUhalf_DELAY1
DJNZ R2,ETUhalf_DELAY2
RET
#pragma endasm
}
/**************************************************
* 延时四分之一个ETU(134.55us/4=33.6375us)
***************************************************/
void delay_quarter_ETU()
{
#pragma asm
MOV R2,#11
ETUquar_DELAY2: MOV R3,#7
ETUquar_DELAY1: DJNZ R3,ETUquar_DELAY1
DJNZ R2,ETUquar_DELAY2
RET
#pragma endasm
}
/**************************************************
* 延时五分之一个ETU(134.55us/5=26.91us)
***************************************************/
void delay_one_fifth_ETU()
{
#pragma asm
MOV R2,#6
ETUfif_DELAY2: MOV R3,#11
ETUfif_DELAY1: DJNZ R3,ETUfif_DELAY1
DJNZ R2,ETUfif_DELAY2
RET
#pragma endasm
}
/**************************************************
* 延时1个ETU
*(ETU:基本时间单位,如SIM卡时钟频率为2.7648MHz,则ETU为372/2.7648M=134.55us)
***************************************************/
void delay_1_ETU()
{
#pragma asm
MOV R2,#19
ETU1_DELAY2:MOV R3,#18
ETU1_DELAY1:DJNZ R3,ETU1_DELAY1
DJNZ R2,ETU1_DELAY2
RET
#pragma endasm
}
/**************************************************
*400个ETU为 400*134.55us=53.820ms
*(10*(91*(3+2*162)+3)+3)*(12/11.0592)/6=53819.99us
***************************************************/
void delay_400_ETU()
{
#pragma asm
MOV R1,#10
ETU400_DELAY3: MOV R2,#91
ETU400_DELAY2: MOV R3,#162
ETU400_DELAY1: DJNZ R3,ETU400_DELAY1
DJNZ R2,ETU400_DELAY2
DJNZ R1,ETU400_DELAY3
RET
#pragma endasm
}
/**********************************************
* 向COM1发送一个字符
**********************************************/
void SendChar(uint8 byteToSend)
{
SBUF=byteToSend;
while(!TI);
TI=0;
}
/************************************************************
* SIM卡初始化
***********************************************************/
void SIM_Init()
{
SIM_RST = 0; //初始化时为低电平
SIM_CLK = 0; //初始化时为低电平
SIM_IO = 0; //初始化时为低电平
SIM_VCC_ENABLE = 0; //初始化时为低电平
delay_400_ETU(); //待电压稳定
}
/************************************************************
* SIM卡冷复位
***********************************************************/
void SIM_Cold_Reset()
{
SIM_VCC_ENABLE = 1; //初始时,电源电压先上电
delay_400_ETU(); //待电压稳定
SIM_IO = 1; //将I/O端口置为接收方式
AUXR = (AUXR | 0x10); //启动独立波特率发生器开始计数工作,对系统时钟进行分频输出
delay_400_ETU(); //RST复位信号需在提供CLK信号后400个时钟周期内保持低电平
SIM_RST = 1; //之后才可置为高电平
//提供复位信号后400~40000个时钟周期内,I/O口有复位应答
}
/************************************************************
* 从SIM卡读取一个字节
***********************************************************/
uint8 read_SIM_byte()
{
uint8 loop;
uint8 recvdata = 0;
bit Parity = 0; //奇偶校验位
while(SIM_IO); //等待起始位
delay_1_ETU(); //延时一个etu
// delay_half_ETU(); //延时半个etu
// delay_quarter_ETU(); //延时四分之一个etu
delay_one_fifth_ETU(); //延时五分之一个etu
for(loop=0;loop<8;loop++)
{
recvdata >>= 1;
if (SIM_IO)
{
recvdata |= 0x80;
}
Parity ^= SIM_IO;
delay_1_ETU(); //延时一个etu
}
Parity ^= SIM_IO;
// if (!Parity) //此字节如通过奇偶校验则原样显示,通不过则显示0x99
// {
buf[i++] = recvdata;
// }
// else
// {
// buf[i++] = '\x99';
// }
return recvdata;
}
/************************************************************
* 向SIM卡发送一个字节
***********************************************************/
void write_SIM_byte(uint8 dataToSend)
{
uint8 loop;
bit Parity = 0; //奇偶校验位
SIM_IO = 0; //发送起始位
delay_1_ETU(); //延时一个etu
for(loop=0;loop<8;loop++)
{
SIM_IO = (bit)dataToSend;
Parity ^= (bit)dataToSend;
dataToSend >>= 1;
delay_1_ETU(); //延时一个etu
}
SIM_IO = Parity; //如果前8位异或的结果是1,则校验位发1,如果前8位异或的结果是0,则校验位发0
delay_1_ETU(); //延时一个etu,等待校验位发送出去
SIM_IO = 1; //将IO拉高
delay_1_ETU();
if(SIM_IO)//测试,如果一个etu后SIM_IO仍为高电平,则将'\xab'补充到buf的最末尾,如低电平则添加'\xac'
{
buf[--j] = '\xab';
}
else
{
buf[--j] = '\xac';
}
}
/**************************************************
* 主 程 序
***************************************************/
int main()
{
UartInit(); //串口初始化
SIM_CLK_Init(); //单片机向SIM卡提供的工作时钟初始化
SIM_Init(); //SIM卡初始化
SIM_Cold_Reset(); //SIM卡复位
//读取复位返回数据(因为之前测试时,冷复位返回数据共15个字节,所以这里写了15个read_SIM_byte()用来接收)
read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();
read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();
read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();
//对SIM卡写入select命令(A0 A4 00 00 02),但没加后面的文件ID
write_SIM_byte('\xA0');delay_1_ETU();delay_1_ETU();
write_SIM_byte('\xa4');delay_1_ETU();delay_1_ETU();
write_SIM_byte('\x00');delay_1_ETU();delay_1_ETU();
write_SIM_byte('\x00');delay_1_ETU();delay_1_ETU();
write_SIM_byte('\x02');delay_1_ETU();delay_1_ETU();
//从SIM卡端接收select命令的返回值,故意多写了一些read_SIM_byte()
read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();
read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();read_SIM_byte();
}
//串口中断处理,当用串口调试工具随便发送一个字节给单片机时,buf中存放的数据就在串口调试工具的接收端显示出来
void chuankou() interrupt 4
{
if (RI)
for (i=0;i
{
SendChar(buf);
}
RI = 0;
}
************************************************************
这就是我上电之后,发送select,然后收到的全部内容。
其中红色字体的部分为SIM卡的上电复位返回信息(貌似也不太正确,我现在没有加奇偶校验部分,如果加上的话,第四个字节(07)就会奇偶校验不正确),那个6E 00 FF应该就是对select的响应了
[接收]3B 98 11 07 38 E0 01 06 67 3C D0 03 87 39 80 6E 00 FF 00 00 00 00 00 00 00 00 00 AB AB AB AB AB
6E应该是说我的CLA(即select命令的第一个字节A0)发送不正确,大家帮忙看一下,是因为发送字节程序和接收字节程序有错误吗,或者程序的其他地方有错误呢?因为是第一次做单片机直接和SIM卡通信的部分,很多内容都还很糊涂,望大家多多指教。
|
|