📄 7816.c
字号:
//**********************************************************************************
// Copyright (c), 1994 - 2007
// 文件名:7816.c
// 创建人:
// 日期: 2007.4.28
// 描述: IEC 7816协议在电能表CPU卡片和SAM上的应用
// 接口数据:
// bSamCardFlag: 位变量,1--卡片命令 0--ESAM命令
// bRESPAUTOFlag: 位变量,1--自动取响应 0--不自动取响应
// 接口函数:
// 停活卡片或ESAM
// void Deactivation(void);
// 返回:无
// 参数:无
// 复位应答
// unsigned char resetICC(unsigned char *len, unsigned char *resp);
// 返回:0为成功,其它为不成功。
// 参数:len为返回数据长度指针,resp为返回数据指针
// 发送命令并接收返回的数据
// unsigned char command(unsigned char len, unsigned char *comm, unsigned char *lenr, unsigned char *resp);
// 返回:0为成功,其它为不成功。
// 参数:len为命令数据长度,comm为为命令数据指针
// lenr为返回数据长度指针,resp为返回数据指针
// 修改人:
// 日期:070525
// 描述:command中的两处bug;复位时控制时钟
// 单片机型号:PIC18F65J10
// 时钟: Fosc = 4Mz(外部时钟)
// 指令周期: 0.25us(4Mz)
// 版本: V1.0
//**********************************************************************************
// 硬件接口操作函数
unsigned char GetIO(void); // 读I/O状态
void SetVCC(unsigned char s); // 置VCC状态
void Activation(void); // 激活
// 基本函数
void delayETU(unsigned char i); // 延时,i为 1/10 ETU
void delay3us(void); // 延时3us
unsigned char sendAByte(unsigned char c); // 发送一个8位数据,返回0为成功,其它为不成功
unsigned char receiveAByte(unsigned char *c); // 接收一个8位数据,返回0为成功,其它为不成功
// 接口函数
void Deactivation(void); // 停活
unsigned char resetICC(unsigned char *len, unsigned char *resp);
// 复位应答,返回0为成功,其它为不成功。resp为返回数据,len为返回数据长度
unsigned char command(unsigned char len, unsigned char *comm, unsigned char *lenr, unsigned char *resp);
// 发送命令并接收返回的数据
// 程序体
// 以下注释部分为测试程序
/*unsigned char Buflen;
unsigned char Buff[0x50];
void main()
{
unsigned char ReturnFlag;
unsigned char c;
SetIO_IN;
OSCCON = 0x60;
OSCTUNEbits.PLLEN = 1;
Nop();
Nop();
Nop();
Nop();
SetRST_OUT;
SetEsamCommFlag();
PORTHbits.RH3 = 0;
// for (;;)
// {
// PORTHbits.RH3 = 1;
// delayETU(10);
// PORTHbits.RH3 = 0;
// }
do
{
ReturnFlag = resetICC(&Buflen, Buff);
Nop();
}
while(ReturnFlag);
Nop();
do
{
// Buff[0] = 0x00;
// Buff[1] = 0x84;
// Buff[2] = 0x00;
// Buff[3] = 0x00;
// Buff[4] = 0x08;
Buff[0] = 0x00;
Buff[1] = 0xD6;
Buff[2] = 0x83;
Buff[3] = 0x00;
Buff[4] = 0x02;
Buff[5] = 0x00;
Buff[6] = 0x01;
ReturnFlag = command(7,Buff,&Buflen,Buff);
Buff[0] = 0x00;
Buff[1] = 0xB0;
Buff[2] = 0x83;
Buff[3] = 0x00;
Buff[4] = 0x02;
ReturnFlag = command(5,Buff,&Buflen,Buff);
Nop();
}
while(ReturnFlag);
while(1);
}*/
//*********************************************************************************
// 函数名: unsigned char GetIO(void)
// 返回: 卡片或ESAM的IO状态
// 描述: 根据 bSamCardFlag 值返回IO状态
//*********************************************************************************
unsigned char GetIO(void)
{
if (bSamCardFlag) // 1--卡片命令 0--ESAM命令
return ICCData;
else return EsamData;
}
//*********************************************************************************
// 函数名: void SetVCC(unsigned char s)
// 输入: s 为VCC管脚状态
// 描述: 根据 bSamCardFlag 设置卡片或ESAM的VCC端状态
//*********************************************************************************
void SetVCC(unsigned char s) // 置VCC状态
{
// if (bSamCardFlag)
// ICCVCC = s;
// else EsamVCC = s;
}
//*********************************************************************************
// 函数名: void Activation(void)
// 描述: 激活卡片或ESAM
//*********************************************************************************
void Activation(void)
{
SetRST_OUT;
SetCLKControl_OUT;
SetCLKControl_L;
SetRST_L; // RST置为状态L
SetVCC(VCC_ACTIVE); // VCC被加电
SetIO_IN; // IO置为接收模式
// SetIO_Z;
}
//*********************************************************************************
// 函数名: void Deactivation(void)
// 描述: 停活卡片或ESAM
//*********************************************************************************
void Deactivation(void)
{
SetRST_L; // RST置为L
SetCLKControl_L; // CLK置为L
SetIO_A; // IO置为A
SetVCC(VCC_INACTIVE); // VCC停活
}
//*********************************************************************************
// 函数名: void delay3us(void)
// 描述: 延时 3us
//*********************************************************************************
void delay3us(void)
{
Nop();
Nop();
Nop();
Nop();
Nop();
Nop();
Nop();
}
//*********************************************************************************
// 函数名: void delayETU(unsigned char i)
// 输入: i 为 1/10个ETU
// 描述: 延时i/10个ETU
// 注释: delayETU(10)可以延时一个标准ETU,为94us,delayETU(1)比0.1个ETU稍大
//*********************************************************************************
void delayETU(unsigned char i)
{
unsigned char j;
for (j = 1; j > 0; j--)
Nop();
Nop();
Nop();
Nop();
i--;
for (; i > 0; i--)
{
for (j = 2; j > 0; j--)
{
Nop();
Nop();
}
Nop();
Nop();
Nop();
Nop();
Nop();
Nop();
}
}
//*********************************************************************************
// 函数名: unsigned char sendAByte(unsigned char c)
// 输入: c 为要发送的8位数据
// 返回: 0为正确,1为不正确
// 描述: 向卡或ESAM发送一个8位字节
//*********************************************************************************
unsigned char sendAByte(unsigned char c)
{
unsigned char i, check, even, times;
for (times = 0; times < 5; times++)
{
even = 0;
check = 1;
SetIO_OUT;
DisableInt;
SetIO_A; // 启始位
delayETU(9);
delay3us();
delay3us();
Nop();
Nop();
Nop();
Nop();
for (i = 0; i < 8; i++) // 发送8个数据位
{
if (c & check)
{
SetIO_Z;
even = !even;
}
else
{
SetIO_A;
delay3us();
}
Nop();
Nop();
Nop();
Nop();
check <<= 1;
delayETU(9);
}
if (even) // 偶校验位
{
SetIO_Z;
}
else SetIO_A;
delayETU(10);
SetIO_Z;
delayETU(10); // 第11个ETU
SetIO_IN;
check = GetIO();
delayETU(10); // 第12个ETU
EnableInt;
if (check != STATE_A) // 发送正确
return 0;
delayETU(10);
}
return 1; // 出错返回
}
//*********************************************************************************
// 函数名: unsigned char receiveAByte(unsigned char c)
// 输入: c 为要接收的8位数据
// 返回: 0为正确,1为不正确
// 描述: 从卡或ESAM接收一个8位字节
// 注释: 接收成功后等待1个ETU返回,从检测到启始位到成功返回时长大于11个ETU
//*********************************************************************************
unsigned char receiveAByte(unsigned char *c)
{
unsigned long int wait;
unsigned char flag, i, j, rcv, even, times;
for (times = 0; times < 5; times++)
{
flag = 0;
/*
for (wait = 85000; wait > 0; wait--) // 最大等待9600ETU
if (GetIO() == STATE_A) // 收到启始位
{
flag = 1;
break;
}
if (!flag) // 超时
return 1;
*/
///*
//循环查询起始位
for(i = 0; --i>0; )
{
EnableInt;
ClrWdt();
DisableInt;
for( j = 0; --j>0; )
{
if (GetIO() == STATE_A) // 收到启始位
goto getchar;
}
}
EnableInt;
return 1;
getchar:
//*/
DisableInt;
even = 0; // 偶校验数
delayETU(12); // 延时1.5ETU开始采样
// delayETU(14); // 延时1.5ETU开始采样
// delay3us();
// delay3us();
for (i = 0; i < 8; i++)
{
rcv >>= 1;
if (GetIO() == STATE_Z)
{
rcv |= 0x80;
even = !even;
}
else
{
rcv &= 0x7f;
delay3us();
}
delayETU(9);
delay3us();
Nop();
Nop();
Nop();
Nop();
}
if (GetIO() == even) // 偶校验正确
{
// delayETU(24);
delayETU(15); // 校验正确,延时1.5个ETU,此时已进行超过11个ETU
EnableInt;
*c = rcv;
return 0;
}
else
{
delayETU(9); // 第10.5ETU
delay3us();
SetIO_OUT;
SetIO_A;
delayETU(15); // 拉低1.5ETU,要求重发
SetIO_Z;
SetIO_IN;
EnableInt;
}
}
return 1; // 出错返回
}
//*********************************************************************************
// 函数名: unsigned char resetICC(unsigned char *len, unsigned char *resp)
// 输入: len为返回数据长度的指针
// resp为返回数据的指针
// 输出: *len为返回数据长度,范围0--0xff
// *resp为返回数据
// 返回: 0为正确
// 1为不正确
// 描述: 复位卡或ESAM
//*********************************************************************************
unsigned char resetICC(unsigned char *len, unsigned char *resp)
{
unsigned char rcv, Y, K, T, i, check;
unsigned char *p;
Activation(); // 激活ICC
delayETU(40); // 延时372*4个时钟周期,比标准宽
SetRST_H; // RST为状态H
delayETU(5); // 延时0.5ETU=372/2个时钟周期后开始采是否有应答,比标准宽
if (!receiveAByte(&rcv) && (rcv == 0x3b)) // 收TS,最大等待9600ETU,即9600*372个时钟周期,比标准宽
{
p = resp;
*p++ = rcv;
*len = 1;
check = rcv ^ 0x0;
}
else
{
// Deactivation; // 停活ICC
return 1;
}
if (!receiveAByte(&rcv)) // T0
{
*p++ = rcv;
*len = 2;
check ^= rcv;
Y = rcv & 0xf0;
K = rcv & 0xf;
T = 0; // Default
}
else
{
// Deactivation;
return 1;
}
while (Y)
{
for (i = 0x10; i != 0x80; i <<= 1) // TAi, TBi, TCi
{
if (Y & i)
{
if (receiveAByte(&rcv))
{
// Deactivation;
return 1;
}
*p++ = rcv;
(*len)++;
check ^= rcv;
}
}
if (Y & 0x80) // TDi
{
if (receiveAByte(&rcv))
{
// Deactivation;
return 1;
}
*p++ = rcv;
(*len)++;
check ^= rcv;
Y = rcv & 0xf0;
T = rcv & 0xf;
}
else Y = 0;
}
for (i = 0; i < K; i++) // T1 .. TK
{
if (receiveAByte(&rcv))
{
// Deactivation;
return 1;
}
*p++ = rcv;
(*len)++;
check ^= rcv;
}
if (T == 1)
{
if (!receiveAByte(&rcv) && (check == rcv)) // TCK
{
*p++ = rcv;
(*len)++;
}
else
{
// Deactivation;
return 1;
}
}
return 0;
}
//*********************************************************************************
// 函数名: unsigned char command(unsigned char len, unsigned char *comm, unsigned char *lenr, unsigned char *resp)
// 输入: len为命令长度
// comm为命令数据指针
// lenr为返回数据长度指针
// resp为返回数据指针
// 输出: *lenr为返回数据长度,范围0--255
// *resp为返回数据
// 返回: 0为正确
// 1为不正确
// 描述: 向卡或ESAM发送命令并接收返回信息
//*********************************************************************************
unsigned char command(unsigned char len, unsigned char *comm, unsigned char *lenr, unsigned char *resp)
{
unsigned char INS, buflen, rcv, Lc, Le, i, Buf[5];
unsigned char *p;
if (len < 5)
return 1;
buflen = len;
for (i = 0; i < 5; i++)
Buf[i] = *(comm + i);
*lenr = 0;
command_begin:
INS = Buf[1];
if (buflen > 5)
{
Lc = Buf[4];
if (len > 5 + Lc)
Le = Buf[5 + Lc];
else Le = 0;
}
else
{
Le = Buf[4];
Lc = 0;
}
p = resp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -