CH32串口智能卡模式

在 CH32 系列微控制器中,串口的智能卡模式(ISO 7816 - 3 协议)允许其与智能卡进行通信。下面将从功能、配置步骤到具体程序,详细分析该模式。

功能概述

智能卡模式是基于串口(USART)的一种特殊工作模式,遵循 ISO 7816 - 3 协议。该协议规定了智能卡与读卡器之间的电气接口和传输协议,主要用于在微控制器和智能卡之间进行数据传输。智能卡模式具备以下特点:

 1)半双工通信:数据在同一时刻只能在一个方向上传输。

 2)异步通信:通信双方不需要共同的时钟信号,通过起始位和停止位来同步数据传输。

 3)特定的帧格式:数据帧包含起始位、数据位、奇偶校验位和停止位,并且有特定的传输速率和时钟频率要求

配置步骤

1. 使能时钟

需要使能 USART 和 GPIO 的时钟,因为 USART 用于通信,GPIO 用于连接智能卡的引脚。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

2. 配置 GPIO 引脚

配置 USART 的 Tx 和 Rx 引脚为复用功能,同时配置智能卡时钟引脚(SCK)。
GPIO_InitTypeDef GPIO_InitStructure; // 配置USART1 Tx (PA9)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 Rx (PA10)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 SCK (PA8)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

3. 配置 USART 为智能卡模式

设置 USART 的工作模式、波特率、数据位、停止位、奇偶校验位等参数。
USART_InitTypeDef USART_InitStructure;

USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_Clock = USART_Clock_Enable;
USART_InitStructure.USART_CPOL = USART_CPOL_Low;
USART_InitStructure.USART_CPHA = USART_CPHA_1Edge;
USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
USART_Init(USART1, &USART_InitStructure);

// 使能智能卡模式
USART_SmartCardCmd(USART1, ENABLE);
// 使能智能卡NACK发送
USART_SmartCardNACKCmd(USART1, ENABLE);

4. 使能 USART

USART_Cmd(USART1, ENABLE);

通信协议概述

ISO 7816 - 3 协议定义了智能卡与读卡器之间的电气接口和传输协议,采用半双工异步通信方式。通信过程主要包括 ATR(Answer To Reset)阶段和 APDU(Application Protocol Data Unit)交互阶段。

1. ATR 阶段

1.1 含义

ATR 是智能卡对复位信号的响应,用于建立通信参数,如波特率、字符格式等。

1.2 命令与数据

    • 命令:在该阶段,读卡器向智能卡发送复位信号(RST),这并非严格意义上的命令字节,但它触发了智能卡的响应流程。
    • 数据(ATR):智能卡返回的 ATR 是一个可变长度的字节序列,一般格式如下:
      | 字段 | 含义 |
      | --- | --- |
      | TS | 初始字符,指示后续字符的传输方向,通常为0x3B0x3F。 |
      | T0 | 格式字节,包含历史字节数、后续字符格式信息。 |
      | TAi、TBi、TCi、TDi | 可选的协议参数字节,用于定义波特率调整因子、时钟停止模式等。 |
      | HIST_BYTES | 历史字节,包含卡的制造商信息、应用类型等。 |
      | TCK | 校验字节(可选),用于验证 ATR 数据的完整性。 |
 

1.3 返回值含义

ATR 数据中的各个字段包含了智能卡的重要信息,例如:

2. APDU 交互阶段

2.1 含义

APDU 是应用层协议数据单元,用于在智能卡和读卡器之间交换应用数据和命令。分为命令 APDU 和响应 APDU。

2.2 命令 APDU

命令 APDU 有两种格式:短 APDU 和扩展 APDU,这里主要介绍短 APDU,其格式如下:
 
    • TS 字段:0x3B表示后续字符按 ISO/IEC 7816 - 3 标准传输;0x3F表示后续字符按其他标准传输。
    • T0 字段的低 4 位表示历史字节的数量,通过解析这些字节可以获取智能卡的相关特性。

2.3 响应 APDU

响应 APDU 的格式为:

 

常见状态字含义

 

示例代码中与协议的结合

 

#include "ch32f10x.h"

#define BUFFER_SIZE 256

// 之前的配置函数保持不变
void USART1_SmartCard_Config(void);
void USART1_SendByte(uint8_t byte);
uint8_t USART1_ReceiveByte(void);

// 接收ATR
void ReceiveATR(uint8_t *atrBuffer, uint16_t *atrLength)
{
    uint16_t index = 0;
    while (1)
    {
        atrBuffer[index] = USART1_ReceiveByte();
        if (index > 0 && atrBuffer[index - 1] == 0x0D && atrBuffer[index] == 0x0A)
        {
            break;
        }
        index++;
        if (index >= BUFFER_SIZE)
        {
            break;
        }
    }
    *atrLength = index;
}

// 发送APDU命令并接收响应
void SendAPDUCommand(uint8_t *command, uint16_t commandLength, uint8_t *response, uint16_t *responseLength)
{
    for (uint16_t i = 0; i < commandLength; i++)
    {
        USART1_SendByte(command[i]);
    }

    uint16_t index = 0;
    while (1)
    {
        response[index] = USART1_ReceiveByte();
        if (index > 0 && index >= commandLength + 1 && response[index - 1] == 0x90 && response[index] == 0x00)
        {
            break;
        }
        index++;
        if (index >= BUFFER_SIZE)
        {
            break;
        }
    }
    *responseLength = index;
}

int main(void)
{
    USART1_SmartCard_Config();

    uint8_t atrBuffer[BUFFER_SIZE];
    uint16_t atrLength;
    ReceiveATR(atrBuffer, &atrLength);

    uint8_t selectCommand[] = {0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00};
    uint8_t response[BUFFER_SIZE];
    uint16_t responseLength;
    SendAPDUCommand(selectCommand, sizeof(selectCommand), response, &responseLength);

    while (1)
    {
        // 主循环
    }
}

代码解释

ReceiveATR函数:用于接收智能卡的 ATR 数据,将其存储在atrBuffer中,并记录数据长度。

SendAPDUCommand函数:发送 APDU 命令,并接收智能卡的响应,将响应存储在response中,记录响应长度。

main函数:先接收 ATR 数据,然后发送选择文件的 APDU 命令并接收响应。

注意事项

    • 错误处理:实际应用中,需要对接收的数据进行完整性检查和错误处理,例如检查 ATR 的校验字节、判断 APDU 响应的状态字是否表示成功。
    • 超时机制:在接收数据时,应添加超时机制,避免程序因等待数据而陷入死循环。
posted @ 2025-04-01 21:29  WCH_CH32  阅读(209)  评论(0)    收藏  举报