目录
接触IC的硬件通讯其实不难理解,上层逻辑层就复杂了,可根据ISO7816-3中规定的标准一一完善即可。这是一个漫长切枯燥的过程,但是一般都是站在巨人的肩膀上完善优化就好,自己动手慢慢敲可不太容易。
逻辑协议是根具ISO7816来的,硬件平台是多变的。更换一个硬件平台做到底层修改即可。
1、复位
接触IC主要就两个操作,复位激活+APDU,
复位有冷复位和热复位两种,复位只负责接收数据,APDU有发有收。
1.1、冷复位
就是准备复位之前VCC是没电的,RST也是保持低电平的,准备冷复位后,给VCC供电,给CLK,tb后在置高RST,控制冷复位就完成了,接下来等待接收ATR。
1.2、热复位
在已经有VCC和CLK的的情况下,将RST拉低一段时间,然后等待ATR自动应答就行了。
2、Smart Card功能
2.1、初始配置
测试使用的是STM32F103,USARTx都带有Smart Card功能。UARTx则没有。
初始化就上面两个地方,这里没选择 Card Clock功能,单独用了一个定时器提供4Mhz的PWM信号。
然后重要的就是波特率怎么计算。
2.2、波特率计算
电路I/O上一个bit的标称持续时间为“基本时间单位”,表示为etu。etu应等于电路CLK上的F/D时钟周期,其中F和D为传输参数:F为时钟速率转换整数,D为波特率调整整数
1etu在串口通讯中就是一个bit的时间,
baud rate = 1/1etu
其中 D初始值为1,F初始为 372. f clk时钟设置为4Mhz,
则 1etu = 93us, baud rate = 10,752.68,
所以就用上图中的Smart Card 波特率设置为10752.
上式中的D和F会更具ATR中的值会相应变化,这个得啃ISO规范去了。
说完波特率,来试试复位获取ATR。
硬件逻辑就是复位之后,卡片就会像串口一样通过IO发送数据给读卡器。那么复位后只要等待接收即可。
Smart Card智能卡功能是采用单线单IO通讯的,不用切换管脚也不用其他配置,只是在发送的过程中可能Smart Card模块会被误判为接收到新数据而置位接收标志,所以发送完后记得清除一下接收标志和数据,间隔一段时间后才是真正的应答数据,否则一般第一个字节都是无效的干扰数据。
2.3、IO收发代码
//读取IO应答数据
bool HSC_ReadByte(uint8_t *data,int timeout)
{
if(HAL_SMARTCARD_Receive(&hsc2,data,1,timeout)==HAL_OK)
return true;
else
return false;
}
//通过IO串行发送数据
void HSC_WriteByte(uint8_t data)
{
HAL_SMARTCARD_Transmit(&hsc2,&data,1,10);
}
//丢弃接收的干扰数据
void HSC_Clear()
{
(void)hsc2.Instance->DR;
}
因为老设备的代码,要移植到STM32上面去,这部分的代码原来用的是IO模拟的。看得我是自闭症都犯了。
2.4、IC通讯测试
下面两个函数已经是数据交互的全部了,单线的双向串口通讯,类似RS485通讯。难点是在于收发的数据解析,此处不做介绍。
int getATR(uint8_t ATR[])
{
int rx_len = 0;
uint8_t data;
//复位之后先立即清除一下DR寄存器
HSC_Clear();
while (1)
{
if (HSC_ReadByte(&data, 100) == false)
{
break;
}
ATR[rx_len++] = data;
}
return rx_len;
}
#define ICCardCLKMainFrien 4 //设定ic卡时钟主频率,单位MHZ
int command_exchange(uint8_t tx_buff[], int tx_len, uint8_t rx_buff[])
{
uint16_t rece_index = 0;
float etu = (terminal->terminal_fi / (float)terminal->terminal_di) / ICCardCLKMainFrien; // us
delay_us((terminal->terminal_cgt + 6) * etu);
//发送数据
for (int i = 0; i < tx_len; i++)
{
HSC_WriteByte(tx_buff[i]);
/*cgt额外保护时间有tc1决定*/
delay_us(cgt * etu);
}
HSC_Clear();
//接收数据
while (1)
{
uint8_t data;
if (HSC_ReadByte(&data, 100) == false)
{
break;
}
rx_buff[rece_index++] = data;
}
return rece_index;
}