20165114 个人课程设计实验报告
简介
-
本小组课程设计题目:基于Z32的虚拟机制作与测试
-
本小组成员:
- 20165104孟凡斌
- 20165111朱思腾
- 20165114戴乔宇(组长)
此次实验中我负责的是
- 第三章 12864液晶屏串行显示实验
- 第七章 SLE4428逻辑加密卡实验
- 第八章 SM1加密实验
第三章 12864液晶屏串行显示实验
一、实验目的
- 学习12864液晶屏原理
- 掌握Z32的GPIO工作原理
- 掌握12864液晶屏的使用方法
二、实验内容
学习GPIO原理,阅读《ARM cortex-m0权威手册》,参考Z32HUA_DEMO工程函数库(详见软件资料),通过设置通过GPIO1,GPIO2,GPIO3串行通信控制液晶屏,实现字符显示功能。
三、预备知识
- 有C语言基础知识
- 掌握Keil uVision4的开发环境
- 掌握Z32应用程序的框架结构
四、实验设备及工具
- 硬件:信息安全系统设计与应用试验平台、USB转9针串口线、标准双头USB连接线、配有Win7及以上系统的PC机,硬盘10G以上。
- 软件:Keil uVision4开发环境
五、实验原理
(一)概述
12864是一种图形点阵液晶显示器。它主要采用动态驱动原理由行驱动—控制器和列驱动器两部分组成了128(列)×64(行)的全点阵液晶显示。此显示器采用了COB的软封装方式,通过导电橡胶和压框连接LCD,使其寿命长,连接可靠。
(二)特性
- 工作电压为+5V±10% ,可自带驱动LCD所需的负电压。
- 全屏幕点阵,点阵数为128(列)×64(行),可显示8(/行)×4(行)个(16×16点阵)汉字,也可完成图形,字符的显示。
- 与CPU接口采用5条位控制总线和8位并行数据总线输入输出,适配M6800系列时序。
- 内部有显示数据锁存器。
- 简单的操作指令,显示开关设置,显示起始行设置,地址指针设置和数据读/写等指令。
六、实验程序分析
打开12864液晶屏串行显示实验工程文件,如图所示:
- 然后在user组下分别双击Main.c和LCD.c可以看到主函数代码和12864液晶屏的函数代码。
12864
的代码LCD.c
分析:
LCD12864的代码包括其初始化、设定显示位置,以及写数据的函数:
- void lcd_sendbyte(UINT8 byte)函数的作用是串行方式发送一个字节;
- void lcd_wcmd(UINT8 cmd) 函数的作用是写指令操作到LCD12864,包括写命令,状态等指令;
- void lcd_wdat(UINT8 dat)函数是写数据操作,以并行方式从D0~D7向12864写入显示数据,先送高4位,再送低4位;
- void lcd_string(UINT8 *str)是写字符串操作;
- void lcd_init(void)是12864的初始化操作,首先对串行引脚进行初始化,设置Z32的GPIO-1,2,3为输出,分别作为片选、读写口和时钟端口并初始输出低电平,然后进行LCD的初始化,包括选择指令集,显示设置以及屏幕和光标的基本设定;
- void lcd_pos(UINT8 X,UINT8 Y)是设定12864开始显示的位置。
== LCD.c
源代码如下:==
/*串行发送一个字节 */
/* */
/*******************************************************************/
void lcd_sendbyte(UINT8 byte)
{
UINT8 i;
for(i=0;i<8;i++)
{
GPIO_SetVal(CLK,0);
GPIO_SetVal(SID,byte&0x80);
GPIO_SetVal(CLK,1);
byte=(byte<<1);
}
GPIO_SetVal(CLK,0);
}
/*******************************************************************/
/* */
/*写指令数据到LCD */
/* */
/*******************************************************************/
void lcd_wcmd(UINT8 cmd)
{
GPIO_SetVal(CS,1);
delay(5); //串行不支持读操作,不可检测忙操作,这里用延时替代
lcd_sendbyte(0xf8); //送入5个连续的“1“,启动一个周期,11111,RW(0),RS(0),0
lcd_sendbyte(0xf0&cmd); //取高四位,数据分两次传送,
//每个字节的内容被送入两个字节,高四位放在第一个字节的高四位
lcd_sendbyte(0xf0&(cmd<<4)); //低四位放在第二个字节的高四位
GPIO_SetVal(CS,0);
}
/*******************************************************************/
/* */
/*写显示数据到LCD */
/*RS=H,RW=L,E=高脉冲,D0-D7=数据。 */
/* */
/*******************************************************************/
void lcd_wdat(UINT8 dat)
{
GPIO_SetVal(CS,1);
delay(5);
lcd_sendbyte(0xfa);
lcd_sendbyte(0xf0&dat);
lcd_sendbyte(0xf0&(dat<<4));
GPIO_SetVal(CS,0);
}
/*******************************************************************/
/* */
/*写字符串到LCD */
/* */
/*******************************************************************/
void lcd_string(UINT8 *str)
{
UINT8 *p ;
p=str;
while(*p!=0)
{
lcd_wdat(*p++);
}
}
/*******************************************************************/
/* */
/* LCD初始化设定 */
/* */
/*******************************************************************/
void lcd_init(void)
{
/********串行引脚初始化*******/
GPIO_InOutSet(CS,0); //设置GPIO 1,2,3为输出
GPIO_InOutSet(SID,0);
GPIO_InOutSet(CLK,0);
/********lcd初始化*******/
delay(50); //等待时间要大于40ms
lcd_wcmd(0x30); //选择基本指令集
delay(3);
lcd_wcmd(0x0c); //开显示,无游标,不反白
delay(3);
lcd_wcmd(0x01); //清除显示屏幕,把DDRAM位址计数器调整为00H
delay(3);
lcd_wcmd(0x06); //设定光标右移,整体显示不移动
delay(3);
}
/*********************************************************/
/* */
/* 设定显示位置 */
/* */
/*********************************************************/
void lcd_pos(UINT8 X,UINT8 Y)
{
UINT8 pos;
if (X==0)
{X=0x80;}
else if (X==1)
{X=0x90;}
else if (X==2)
{X=0x88;}
else if (X==3)
{X=0x98;}
pos = X+Y ;
lcd_wcmd(pos); //显示地址
}
主函数代码的执行过程为:
- 系统初始化,中断设置,使能所有中断;
- 判断按键,返回boot条件,确认是否进行程序下载;
- LCD12864初始化;
- 进入执行循环程序:定位至第一行显示“欢迎使用Z32HUA!”,延时约1800ms。
- 定位至第二行显示“LCD12864液晶测试”,延时约1800ms。
- 定位至第三行显示“1.2.3.4.5.6.7.8.”,延时约1800ms。
- 定位至第四行,显示“A.B.C.D.E.F.G.H.”。
- 延时约1800ms后清屏幕,重新进入下一次循环显示。
==主函数main.c源代码如下:==
int main(void)
{
/*********************此段代码勿动***********************/
//系统中断向量设置,使能所有中断
SystemInit ();
// 返回boot条件
if(0 == GPIO_GetVal(0))
{
BtApiBack(0x55555555, 0xAAAAAAAA);
}
/*********************此段代码勿动***********************/
lcd_init();
while(1)
{
lcd_pos(0,0);//定位第一行
lcd_string("欢迎使用Z32HUA!");
delay(1800);
lcd_pos(1,0);//定位第二行
lcd_string("LCD12864液晶测试");
delay(1800);
lcd_pos(2,0);//定位第三行
lcd_string("1.2.3.4.5.6.7.8.");
delay(1800);
lcd_pos(3,0);//定位第四行
lcd_string("A.B.C.D.E.F.G..H.");
delay(1800);
lcd_wcmd(0x01);//清屏
}
}
//延时函数,当系统时钟为内部OSC时钟时,延时1ms
void delay(int ms)
{
int i;
while(ms--)
{
for(i=0;i<950;i++) ;
}
}
七、实验操作
1、打开“Z32开发指南\实验3-12864液晶屏串行显示”目录的工程文件。编译工程,产生后缀名为.bin的可执行代码。下图为编译成功的截图:
2、将实验箱接入电源,用USB公对公线将实验箱的USB接口连接到电脑的USB接口上,在电脑上找到“Z32开发指南\2.软件资料\Z32下载调试工具”目录打开Z32下载调试工具NZDownloadTool.exe。如下图所示:
3、打开Z32的电源开关前,按住Reboot按键不放,两次打开电源开关,Z32即可被电脑识别,进行下载调试。下图中设备已连接:
4、当左边框出现“1设备已连接”,设备选择中显示芯片型号,此时就可以下载程序了。点击“下载”,左边状态提示框更新显示“程序下载成功!”实验3的程序就下载进Z32的实验板上了。
5、实验3的内容是LCD12864液晶屏的显示操作。 关闭Z32电源开关,再打开,程序自动运行,此时可以看到实验现象:屏幕上依次显示以下字符: “欢迎使用Z32HUA! LCD12864液晶测试 1.2.3.4.5.6.7.8. A.B.C.D.E.F.G.H.” 其中行与行之间显示的间隔时间约为1.8s,四行字符全部显示完成后清屏幕,重新循环显示。如下图所示:
第七章 SLE4428逻辑加密卡实验
一、实验目的
- 学习GPIO中断原理
- 掌握Z32安全模块SLE4428逻辑加密卡的工作原理
二、实验内容
阅读SLE4428 IC数据手册,学习SLE4428逻辑加密卡操作原理,阅读《ARM cortex-m0权威手册》(详见目录Z32开发指南\3.参考资料),参考Z32HUA_DEMO工程函数库(详见Z32开发指南\2.软件资料),通过Z32安全模块对SLE4428 IC卡进行数据读写实验。
三、预备知识
- 有C语言基础知识
- 掌握Keil uVision4的开发环境
- 掌握Z32应用程序的框架结构
四、实验设备及工具
- 硬件:信息安全系统设计与应用试验平台、USB转9针串口线、标准双头USB连接线、配有Win7及以上系统的PC机,硬盘10G以上。
- 软件:Keil uVision4开发环境、串口调试助手SSCOM4.2。
五、实验原理
-
逻辑加密存储器卡:在非加密存储器卡的基础上增加了加密逻辑电路,加密逻辑电路通过校验密码方式来保护卡内的数据对于外部访问是否开放,但只是低层次的安全保护,无法防范恶意性的攻击。
-
本实验所用IC卡:
-
SLE4428是SIMENS公司设计的逻辑加密IC卡,容量为1K*8Bit,设有两个字节的密码。只有通过了密码验证,才可以对IC卡内的没有设置写/擦除保护的内容进行写/擦除。内部有错误计数器(EC),错误计数器总是可以被写的,如果连续8次教研密码不成功,IC卡将自动被锁死,数据只能独处,不可再对数据进行写/擦除操作,也不可以再校验密码。每个字节都可以单独的设置写/擦除保护,一单设置了写/擦除保护功能,这个字节的数据就不能在写/擦除了,而且写保护功能只能设置一次。除了密码区,其他所有字节在任何时候都可以读出来。
六、实验程序分析
本此实验例程的详细程序请打开“Z32开发指南\实验7-SLE4428逻辑加密卡”目录文件夹下的工程文件,下面我们对程序的主要部分做一下简单的分析。 打开SLE4428逻辑加密卡实验工程文件,如图所示:
- 在user组下分别双击Main.c和SLE4428.c,就可以看到主函数代码和SLE4428程序的源代码。 ==打开SLE4428.c,首先介绍SLE4428相关函数,代码如下:==
/**
* @函数: SLE4428初始化和复位
* @voltage:电压值 - 0: 1.8V (默认)
* - 1: 3V
* - 2: 3.3V
* - 3: 5V
* @返回: none
*/
UINT32 SLE4428_InitAndRST(UINT8 voltage)
{
UINT32 ATR=0;
IOM->CRB &= ~(1<<4); //SCC0接口作为GPIO口
GPIO_InOutSet(8,0); //CLK输出使能
GPIO_InOutSet(9,0); //RST输出使能
GPIO_InOutSet(10,0); //IO输出使能
GPIO_PuPdSel(8,0); //CLK上拉使能
GPIO_PuPdSel(9,0); //RST上拉使能
GPIO_PuPdSel(10,0); //IO上拉使能
GPIO_SetVal(8,0) ; //CLK输出0
GPIO_SetVal(9,0) ; //RST输出0
GPIO_SetVal(10,0) ; //IO输出0
SCU->SCFGVRCARD |= (voltage<<2); //VCARD0配置为voltage
SCU->SCFGVCCACTRL |= (1<<1) ; //使能vCard0电源
/******开始复位******/
GPIO_SetVal(9,1) ; //RST输出1
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
GPIO_SetVal(9,0) ; //RST输出0
GPIO_InOutSet(10,1); //IO输入使能
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
for(UINT8 i=0;i<32;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
ATR = ATR|(GPIO_GetVal(10)<<i); //获取应答
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
return ATR;
}
/**
* @函数:发送命令控制字
* @S: 控制字
* @addr:10位地址
* @Data: 数据
* @返回:none
*/
void SLE4428_SendCmd(UINT8 S,UINT16 addr,UINT8 Data)
{
UINT32 CTL=0; //命令控制字(三个字节24位)
CTL |= S; //首6位控制字(S5~S0)
CTL |= (addr&0x0300)>>2; //地址的A9,A8
CTL |= (addr&0x00FF)<<8; //地址的A7~A0
CTL |= Data<<16; //数据的D7~D0
GPIO_SetVal(8,0) ; //CLK输出0
GPIO_SetVal(9,1) ; //RST输出1
GPIO_InOutSet(10,0); //IO输出使能
for(UINT8 i = 0 ; i < 0x20 ; i++) ; //等待大约40us
for(UINT8 i=0;i<24;i++) //输出三个字节的控制字
{
GPIO_SetVal(10,CTL&0x01) ; //IO输出数据
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
CTL = CTL>>1;
}
GPIO_SetVal(9,0) ; //RST输出0
GPIO_InOutSet(10,1); //IO输入使能
}
/**
* @函数:读取一个字节
* @addr:10位地址
* @返回:读取的数据
*/
UINT8 SLE4428_ReadByte(UINT16 addr) //输入10位地址
{
UINT8 Data=0;
/*****命令001110(读8位,无保护位)*****/
SLE4428_SendCmd(0x0E,addr,0);
/*****过滤接收前的脉冲*****/
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ; //等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ; //等待大约40us
/*****正式接收一个字节*****/
for(UINT8 i=0;i<8;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
Data = Data|(GPIO_GetVal(10)<<i); //读取数据
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
return Data;
}
/**
* @函数:读取数据
* @addr:10位地址
* @str:1数组首地址
* @n: 读取长度
* @返回:None
*/
void SLE4428_ReadData(UINT16 addr,UINT8* str,UINT8 n)
{
/*****命令001110(读8位,无保护位)*****/
SLE4428_SendCmd(0x0E,addr,0);
/*****过滤接收前的脉冲*****/
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
/*****正式接收一个字节*****/
for(UINT8 j=0;j<n;j++)
{
*(str+j) = 0;
for(UINT8 i=0;i<8;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
*(str+j) = *(str+j)|(GPIO_GetVal(10)<<i); //读取数据
for(UINT8 i = 0 ; i < 0x20 ; i++) ; //等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ; //等待大约40us
}
}
}
/**
* @函数:验证密码(地址为0x03FE,0x03FF)
* @PSC1: 第一个字节
* @PSC2: 第二个字节
* @返回:0->验证失败,1->验证成功
*/
BOOL SLE4428_PassWord(UINT8 PSC1,UINT8 PSC2)
{
/*****第一步,写错误寄存器*****/
//命令:110010,地址:0x03FD,数据:读取该地址的数据右移1位
SLE4428_SendCmd(0x32,0x03FD,(SLE4428_ReadByte(0x03FD)>>1));
//给103个脉冲完成写操作
for(UINT8 i=0;i<103;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
/********第二步:验证PSC1*******/
//命令:001101,地址:0x03FE,数据:PSC1
SLE4428_SendCmd(0x0D,0x03FE,PSC1);
//给2个脉冲完成操作
for(UINT8 i=0;i<2;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
/********第三步:验证PSC2*******/
//命令:001101,地址:0x03FF,数据:PSC2
SLE4428_SendCmd(0x0D,0x03FF,PSC2);
//给2个脉冲完成操作
for(UINT8 i=0;i<2;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
/********第四步:擦除错误计数器*******/
SLE4428_SendCmd(0x33,0x03FD,0xFF);
//命令:110011,地址:0x03FD,数据:0xFF
//给103个脉冲完成写操作
for(UINT8 i=0;i<103;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
if(SLE4428_ReadByte(0x03FD)==0xFF)
return 1;
return 0;
}
/**
* @函数:写一个字节
* @addr:10位地址
* @Byte: 要写的数据
* @返回:none
*/
void SLE4428_Write_Byte(UINT16 addr,UINT8 Byte)
{
SLE4428_SendCmd(0x33,addr,Byte);
//给103个脉冲完成写操作
for(UINT8 i=0;i<103;i++)
{
GPIO_SetVal(8,1) ; //CLK输出1
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
GPIO_SetVal(8,0) ; //CLK输出0
for(UINT8 i = 0 ; i < 0x20 ; i++) ;//等待大约40us
}
}
/**
* @函数:接口释放,电源下电
* @返回:none
*/
void SLE4428_Deactivation(void)
{
GPIO_InOutSet(10,0); //IO输出使能
GPIO_SetVal(8,0) ; //CLK输出0
GPIO_SetVal(9,0) ; //RST输出0
GPIO_SetVal(10,0) ; //IO输出0
SCU->SCFGVCCACTRL &= ~(1<<1) ;//失能vCard0电源
SCU->CARD0VCTRL &= ~(1<<0);
}
SLE4428相关函数包括初始化和复位++、发送命令控制字、读/写字节、读取数据、验证密码、接口释放及断电函数。
-
- UINT32 SLE4428_InitAndRST(UINT8 voltage)是SLE4428初始化和复位函数,实现对控制SLE4428引脚的配置以及使能。
-
- void SLE4428_SendCmd(UINT8 S,UINT16 addr,UINT8 Data)是发送命令控制字函数,实现向SLE4428 IC卡发送24位的命令控制字、地址以及数据。
-
- UINT8 SLE4428_ReadByte(UINT16 addr)是读取单字节函数,实现读取SLE4428卡地址的单字节数据。
-
- void SLE4428_Write_Byte(UINT16 addr,UINT8 Byte)是写入单字节函数,实现向SLE4428 IC卡地址写入单字节数据。
-
- void SLE4428_ReadData(UINT16 addr,UINT8* str,UINT8 n)是读取数据函数,实现读取SLE4428卡某一地址起始的多字节数据。
-
- BOOL SLE4428_PassWord(UINT8 PSC1,UINT8 PSC2)是验证密码函数,实现SLE4428 IC卡双字节密码验证。
-
- void SLE4428_Deactivation(void)是释放接口并断电函数,实现SLE4428读卡器控制引脚的失能以及断电操作。
==下面介绍主函数,代码如下:==
int main(void)
{
/*********************此段代码勿动***********************/
//系统中断向量设置,使能所有中断
SystemInit ();
// 返回boot条件
if(0 == GPIO_GetVal(0))
{
BtApiBack(0x55555555, 0xAAAAAAAA);
}
/*********************此段代码勿动***********************/
/*初始化IC卡插入检测IO口GPIO6*/
GPIO_Config(6);
GPIO_PuPdSel(6,0); //上拉
GPIO_InOutSet(6,1); //输入
UART_Init();
lcd_init();
KEY_Init();
lcd_pos(0,0);//定位第一行
lcd_string("SLE4428 实验!");
A: while(1)
{
lcd_pos(1,0);//定位第二行
lcd_string("请插入IC卡. ");
delay(1000);
if(GPIO_GetVal(6)==0) break;
lcd_pos(1,0);//定位第二行
lcd_string("请插入IC卡.. ");
delay(1000);
if(GPIO_GetVal(6)==0) break;
lcd_pos(1,0);//定位第二行
lcd_string("请插入IC卡...");
delay(1000);
if(GPIO_GetVal(6)==0) break;
}
if(SLE4428_InitAndRST(2)!=0xFFFFFFFF) //收到ATR
{
lcd_pos(1,0);//定位第二行
lcd_string("已插入SLE4428");
}
else
{
lcd_pos(1,0);//定位第二行
lcd_string("卡不正确 ");
SLE4428_Deactivation(); //下电,去激活
delay(1000);
goto A;
}
lcd_pos(2,0);//定位第三行
lcd_string("用户代码为:");
SLE4428_ReadData(0x15,UserCode,6); //读取用户代码
lcd_pos(3,0);//定位第四行
for(UINT8 i=0;i<6;i++)
lcd_Hex(UserCode[i]) ;
while(KEY_ReadValue()!='A'); //等待A键按下
lcd_wcmd(0x01);//清屏
lcd_pos(0,0);//定位第一行
lcd_string("按-A键校验密码");
lcd_pos(1,0);//定位第二行
lcd_string("校验0xFF,0xFF");
while(KEY_ReadValue()!='A'); //等待A键按下
lcd_pos(2,0);//定位第三行
if(SLE4428_PassWord(0xFF,0xFF)==1)
lcd_string("校验成功");
else
lcd_string("校验失败");
lcd_pos(3,0);//定位第四行
switch(SLE4428_ReadByte(0x03fd)) //查看剩余密码验证机会
{
case 0xff: lcd_string("剩余机会: 8次");break;
case 0x7f: lcd_string("剩余机会: 7次");break;
case 0x3f: lcd_string("剩余机会: 6次");break;
case 0x1f: lcd_string("剩余机会: 5次");break;
case 0x0f: lcd_string("剩余机会: 4次");break;
case 0x07: lcd_string("剩余机会: 3次");break;
case 0x03: lcd_string("剩余机会: 2次");break;
case 0x01: lcd_string("剩余机会: 1次");break;
case 0x00: lcd_string("剩余机会: 0次");break;
default: break;
}
while(KEY_ReadValue()!='A'); //等待A键按下
lcd_wcmd(0x01);//清屏
B:
lcd_pos(0,0);//定位第一行
lcd_string("金额:");
Money=SLE4428_ReadByte(0x10);
lcd_wdat(0x30+Money/100);
lcd_wdat(0x30+Money%100/10);
lcd_wdat(0x30+Money%10);
lcd_pos(1,0);//定位第一行
lcd_string("输入金额,A键修改");
lcd_pos(2,0);//定位第三行
for(UINT8 j=0;j<3;j++)
{
do
{
delay(5);
C = KEY_ReadValue();
}
while(KeyFlag != 2); //直到按下数字键,KeyFlag为2是代表数字。
lcd_wdat(C);
money[j] = C-0x30;
}
while(KEY_ReadValue()!='A'); //等待A键按下
Money = money[0]*100+money[1]*10+money[2];
if(Money>=255) Money = 255;
SLE4428_Write_Byte(0x10,Money);
lcd_wcmd(0x01);//清屏
goto B;
SLE4428_Deactivation(); //下电,去激活,实验结束
while(1)
{
}
}
//延时函数,当系统时钟为内部OSC时钟时,延时1ms
void delay(int ms)
{
int i;
while(ms--)
{
for(i=0;i<950;i++) ;
}
}
主函数代码的执行过程为:
-
- 系统初始化,中断设置,使能所有中断;
-
- 判断按键,返回boot条件,确认是否进行程序下载;
-
- 初始化IC卡插入检测端口GPIO6;
-
- 串口初始化;
-
- LCD12864初始化;
-
- 矩阵键盘初始化;
-
- 液晶屏第一行显示字符串“SLE4428 实验!”。
A段程序:
-
- 第二行显示“请插入IC卡”,等待卡片插入;
-
- SLE4428 IC卡正确插入,第二行显示“已插入SLE4428”,卡片插入错误则第二行显示“卡不正确 ”;
-
- IC卡正确插入,则显示“用户代码为:XXXXXXXXXX”(XXXXXXXXXX代表用户的代码),等待按下键盘的“A”键;
-
- 按下“A”键,显示屏第一行显示“按-A键校验密码”,第二行显示“校验0xFF,0xFF”,等待“A”键按下。
-
- 按下“A”键,若校验密码正确,显示屏第三行显示“校验成功”,否则显示“校验失败”,第四行显示剩余密码验证机会次数“剩余机会: X次”(X初始最大为8,最小0,当校验密码错误验证一次后,X减1),等待“A”键按下;
B段程序:
-
- A键按下,显示屏第一行显示卡内保存数据“金额:XXX”(XXX最大255,最小为000),第二行显示“输入金额,A键修改”,光标定位第三行,等待键盘输入,输入后则显示在第三行。
-
- 输入更改的金额数字(输入完整三位数字),按下“A”键,程序跳转至B段程序起始,显示“金额:YYY”(YYY表示更改之后的金额数字,最大255,最小为000)。
-
- 断电,去除IC卡激活,实验结束。
七、实验步骤
1、打开“Z32开发指南\实验7-SLE4428逻辑加密卡”目录的工程文件。编译工程,产生后缀名为.bin的可执行代码。 2、将实验箱接入电源,用USB公对公线将实验箱的USB接口连接到电脑的USB接口上,在电脑上找到“Z32开发指南\2.软件资料\Z32下载调试工具”目录打开Z32下载调试工具NZDownloadTool.exe。 3、打开Z32的电源开关前,按住Reboot按键不放,两次打开电源开关,Z32即可被电脑识别,进行下载调试。 4、当左边框出现“1设备已连接”,设备选择中显示芯片型号,此时就可以下载程序了。
5、点击“下载”,左边状态提示框更新显示“程序下载成功!”实验7的程序就下载进Z32的实验板上了。
八、实验现象
-
实验7的内容是SLE4428逻辑加密卡实验,按照程序提示,插入SLE4428逻辑加密卡,A段程序显示用户代码并进行密码校验,B段程序读取卡片存储的金额信息并修改金额。
-
关闭Z32电源开关,再打开,程序自动运行,此时可以看到实验现象:显示屏第一行显示“SLE4428实验!”,第二行显示“请插入IC卡...”。
-
插入SLE4428 IC卡,显示屏第三行显示:“用户代码为:”,第四行显示用户代码“D27600000400”。
-
若插入了正确的卡片,显示出用户代码,再按下矩阵键盘的“A”键,屏幕第一行显示提示“按-A键校验密码”并在第二行显示两个字节的校验密码“校验0xFF,0xFF”。
-
按下矩阵键盘的“A”键,屏幕第三行显示“校验成功”,第四行显示校验剩余机会“剩余机会:8次”。
-
再按下矩阵键盘的“A”键,则屏幕第一行显示卡内金额数“金额:100”,第二行提示“输入金额,A键修改”。
-
在矩阵键盘上输入三位修改的金额数560,按下矩阵键盘的“A”键确认。发现修改变为了255,这是因为程序中写不允许大于255,如果大于只显示255.
-
但是可以修改为小于255的值,比如输入200试试,确认后,金额更新,屏幕上显示更改后的卡内金额“金额200”,第四行继续提示“输入金额,A键修改”,可以再次进行更改。
-
修改完金额后,就可以拔出IC卡,实验结束。
第八章 SM1加密实验
一、实验目的
- 学习SM1加解密算法原理,掌握SM1加、解密算法用法
二、实验内容
学习SM1加密原理,阅读《ARM cortex-m0权威手册》和《Z32HUA国密算法库用户手册》(详见目录Z32开发指南\3.参考资料),参考Z32HUA_DEMO工程函数库(详见Z32开发指南\2.软件资料),调用SM1加解密函数,实现对SLE4428 IC的数据加解密操作。
三、预备知识
- 有C语言基础知识
- 掌握Keil uVision4的开发环境
- 掌握Z32应用程序的框架结构
- 掌握国密SM1加解密算法函数的用法
四、实验设备及工具
- 硬件:信息安全系统设计与应用试验平台、USB转9针串口线、标准双头USB连接线、配有Win7及以上系统的PC机,硬盘10G以上。
- 软件:Keil uVision4开发环境、串口调试助手SSCOM4.2。
五、实验原理
国密SM1(SM1 cryptographic algorithm),国密SM1算法是由国家密码管理局编制的一种商用密码分组标准对称算法。该算法是国家密码管理部门审批的SM1分组密码算法,分组长度和密钥长度都为128 比特,算法安全保密强度及相关软硬件实现性能与AES相当,该算法不公开,仅以IP核的形式存在于芯片中。采用该算法已经研制了系列芯片、智能IC卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。
六、程序分析
-
本此实验例程的详细程序请打开“Z32开发指南\实验8-SM1”目录文件夹下的工程文件,下面我们对程序的主要部分做一下简单的分析。
-
打开SM1加解密实验工程文件,如图所示:
-
在user组下分别双击Main.c和SLE4428.c,就可以看到主函数代码和SLE4428程序的源代码。其中工程文件目录的algorithm文件夹包含了Z32HUA_ALG.ALG函数库,其中包含了国密的加解密算法相关函数库,SM1加解密函数的调用需要这个库的支持。 ==打开Main.c,介绍一下主函数,代码如下:==
UINT8 jiamiqian[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
UINT8 jiamimiyue[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
UINT8 jiamihou[16];
UINT8 jiemiqian[16],jiemimiyue[16],jiemihou[16];
UINT8 cuowumiyue[16]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
UINT8 UserCode[5];
UINT8 C;
int main(void)
{
/*********************此段代码勿动***********************/
//系统中断向量设置,使能所有中断
SystemInit ();
// 返回boot条件
if(0 == GPIO_GetVal(0))
{
BtApiBack(0x55555555, 0xAAAAAAAA);
}
/*********************此段代码勿动***********************/
/*初始化IC卡插入检测IO口GPIO6*/
GPIO_Config(6);
GPIO_PuPdSel(6,0); //上拉
GPIO_InOutSet(6,1); //输入
UART_Init();
lcd_init();
KEY_Init();
lcd_pos(0,0);//定位第一行
lcd_string("SLE4428 实验!");
A: while(1)
{
lcd_pos(1,0);//定位第二行
lcd_string("请插入IC卡. ");
delay(1000);
if(GPIO_GetVal(6)==0) break;
lcd_pos(1,0);//定位第二行
lcd_string("请插入IC卡.. ");
delay(1000);
if(GPIO_GetVal(6)==0) break;
lcd_pos(1,0);//定位第二行
lcd_string("请插入IC卡...");
delay(1000);
if(GPIO_GetVal(6)==0) break;
}
if(SLE4428_InitAndRST(2)!=0xFFFFFFFF) //收到ATR
{
lcd_pos(1,0);//定位第二行
lcd_string("已插入SLE4428");
}
else
{
lcd_pos(1,0);//定位第二行
lcd_string("卡不正确 ");
SLE4428_Deactivation(); //下电,去激活
delay(1000);
goto A;
}
lcd_pos(2,0);//定位第三行
lcd_string("用户代码为:");
SLE4428_ReadData(0x15,UserCode,6); //读取用户代码
lcd_pos(3,0);//定位第四行
for(UINT8 i=0;i<6;i++)
lcd_Hex(UserCode[i]) ;
while(KEY_ReadValue()!='A'); //等待A键按下
lcd_wcmd(0x01);//清屏
lcd_pos(0,0);//定位第一行
lcd_string("按-A键校验密码");
lcd_pos(1,0);//定位第二行
lcd_string("校验0xFF,0xFF");
while(KEY_ReadValue()!='A'); //等待A键按下
lcd_pos(2,0);//定位第三行
if(SLE4428_PassWord(0xFF,0xFF)==1)
lcd_string("校验成功");
else
{lcd_string("校验失败"); return 0;}
lcd_pos(3,0);//定位第四行
switch(SLE4428_ReadByte(0x03fd)) //查看剩余密码验证机会
{
case 0xff: lcd_string("剩余机会: 8次");break;
case 0x7f: lcd_string("剩余机会: 7次");break;
case 0x3f: lcd_string("剩余机会: 6次");break;
case 0x1f: lcd_string("剩余机会: 5次");break;
case 0x0f: lcd_string("剩余机会: 4次");break;
case 0x07: lcd_string("剩余机会: 3次");break;
case 0x03: lcd_string("剩余机会: 2次");break;
case 0x01: lcd_string("剩余机会: 1次");break;
case 0x00: lcd_string("剩余机会: 0次");break;
default: break;
}
while(KEY_ReadValue()!='A'); //等待A键按下
B: lcd_wcmd(0x01);//清屏
lcd_pos(0,0);//定位第一行
lcd_string("加密解密实验");
lcd_pos(1,0);//定位第二行
lcd_string("1.加密");
lcd_pos(2,0);//定位第三行
lcd_string("2.解密");
do
{
C=KEY_ReadValue();
}
while(C!='1'&&C!='2'); //等待1或2键按下
lcd_wcmd(0x01);//清屏
if(C=='1') goto jiami;
else if(C=='2') goto jiemi;
else ;
jiami:
lcd_pos(0,0);//定位第一行
lcd_string("观看串口调试助手");
lcd_pos(1,0);//定位第二行
lcd_string("A 键确认加密");
UART_SendString("将加密以下数据:\r\n");
for(UINT8 i=0;i<16;i++)
{
UART_SendHex(jiamiqian[i]);
}
UART_SendString("\r\n");
UART_SendString("加密密钥:\r\n");
for(UINT8 i=0;i<16;i++)
{
UART_SendHex(jiamimiyue[i]);
}
UART_SendString("\r\n");
while(KEY_ReadValue()!='A'); //等待A键按下
SM1_Init(jiamimiyue); //SM1初始化
SM1_Crypto(jiamiqian, 16, 0, 0, 0,jiamihou); //进行加密
SM1_Close(); //关闭安全模块
UART_SendString("加密后的数据:\r\n");
for(UINT8 i=0;i<16;i++)
{
UART_SendHex(jiamihou[i]);
}
UART_SendString("\r\n");
lcd_pos(2,0);//定位第三行
lcd_string("加密完成");
lcd_pos(3,0);//定位第四行
lcd_string("A 键存入IC卡");
while(KEY_ReadValue()!='A'); //等待A键按下
for(UINT8 i=0;i<16;i++)
{
SLE4428_Write_Byte(0x20+i,jiamihou[i]); //设置IC卡 0x20地址为存储加密数据的地址
}
UART_SendString("已将数据写入IC卡。\r\n");
UART_SendString("\r\n");
goto B;
jiemi:
lcd_pos(0,0);//定位第一行
lcd_string("观看串口调试助手");
lcd_pos(1,0);//定位第二行
lcd_string(" A键读取IC卡数据");
while(KEY_ReadValue()!='A'); //等待A键按下
SLE4428_ReadData(0x20,jiemiqian,16);
UART_SendString("读取的数据为:\r\n");
for(UINT8 i=0;i<16;i++)
{
UART_SendHex(jiemiqian[i]);
}
UART_SendString("\r\n");
lcd_wcmd(0x01);//清屏
lcd_pos(0,0);//定位第一行
lcd_string("读取成功");
lcd_pos(1,0);//定位第二行
lcd_string("选择密钥解密:");
lcd_pos(2,0);//定位第三行
lcd_string("1.正确密钥");
lcd_pos(3,0);//定位第四行
lcd_string("2.错误密钥");
do
{
C=KEY_ReadValue();
}
while(C!='1'&&C!='2'); //等待1或2键按下
lcd_wcmd(0x01);//清屏
if(C=='1')
{
for(UINT8 i=0;i<16;i++)
jiemimiyue[i] = jiamimiyue[i];
}
else if(C=='2')
{
for(UINT8 i=0;i<16;i++)
jiemimiyue[i] = cuowumiyue[i];
}
else ;
UART_SendString("将使用以下密钥进行解密:\r\n");
for(UINT8 i=0;i<16;i++)
{
UART_SendHex(jiemimiyue[i]);
}
UART_SendString("\r\n");
lcd_pos(0,0);//定位第一行
lcd_string("A 键确认解密");
while(KEY_ReadValue()!='A'); //等待A键按下
SM1_Init(jiemimiyue); //SM1初始化
SM1_Crypto(jiemiqian, 16, 1, 0, 0,jiemihou); //进行解密
SM1_Close(); //关闭安全模块
lcd_pos(1,0);//定位第二行
lcd_string("解密完成");
lcd_pos(2,0);//定位第三行
lcd_string("A 键返回");
UART_SendString("解密后的数据为:\r\n");
for(UINT8 i=0;i<16;i++)
{
UART_SendHex(jiemihou[i]);
}
UART_SendString("\r\n");
UART_SendString("\r\n");
while(KEY_ReadValue()!='A'); //等待A键按下
goto B;
SLE4428_Deactivation(); //下电,去激活,实验结束
while(1)
{
}
}
//延时函数,当系统时钟为内部OSC时钟时,延时1ms
void delay(int ms)
{
int i;
while(ms--)
{
for(i=0;i<950;i++) ;
}
}
主函数代码的执行过程为:
-
- 系统初始化,中断设置,使能所有中断;
-
- 判断按键,返回boot条件,确认是否进行程序下载;
-
- 初始化IC卡插入检测端口GPIO6;
-
- 串口初始化;
-
- LCD12864初始化;
-
- 矩阵键盘初始化;
-
- 液晶屏第一行显示字符串“SLE4428 实验!”。
A段程序:
-
- 第二行显示“请插入IC卡”,等待卡片插入;
-
- SLE4428 IC卡正确插入,第二行显示“已插入SLE4428”,卡片插入错误则第二行显示“卡不正确 ”;
-
- IC卡正确插入,则显示“用户代码为:XXXXXXXXXX”(XXXXXXXXXX代表用户的代码),等待按下键盘的“A”键;
-
- 按下“A”键,显示屏第一行显示“按-A键校验密码”,第二行显示“校验0xFF,0xFF”,等待“A”键按下。
-
- 按下“A”键,若校验密码正确,显示屏第三行显示“校验成功”,否则显示“校验失败”,第四行显示剩余密码验证机会次数“剩余机会: X次”(X初始最大为8,最小0,当校验密码错误验证一次后,X减1),等待“A”键按下;
B段程序:
-
- 按下“A”键,显示屏第一行显示“加密解密试验”,第二、三行分别显示“1.加密”、“2.解密”两个选项。等待按键按下:如果“1”按下,跳转至加密程序段,如果“2”按下,跳转至解密程序段;
加密程序段:
-
- 第一行显示“观看串口调试助手”,第二行显示“A键确认加密”,通过串口发送字符串“将加密以下数据:”并将加密前的数据发送至PC机,发送换行,串口继续发送“加密密钥:”并将加密密钥数组发送至PC机,发送完毕等待“A”键按下;
-
- 按下“A”键后,SM1初始化;
-
- 进行SM1加密;
-
- 关闭SM1加密安全模块;
-
- 通过串口发送字符串“加密后的数据:”并将加密后的数据发送至PC机,换行,在液晶屏第三行显示“加密完成”,第四行显示“A键存入IC卡”,等待“A”键按下。当“A”键按下后,向SLE4428 IC卡加密后的数据,通过串口向PC发送“已将数据写入IC卡。”跳转至B段程序。
解密程序段:
-
- 屏幕第一行显示“观看串口调试助手”,第二行显示“A键读取IC卡数据”,当“A”键按下,读取SLE4428 IC卡解密前数据,通过串口发送“读取的数据为:”至PC机并发送解密前的数据至PC机。在显示屏的四行分别显示“读取成功”,“选择密钥解密”,“1.正确密钥”,“错误密钥”,等待按键“1”或“2”按下。如果“1”按下,解密密钥为正确的密钥,“2”按下,解密密钥为错误的密钥,然后通过串口发送“将使用以下密钥进行解密:”并将相应的解密密钥数据发送至PC机。发送完毕,第一行显示“A键确认解密”,等待“A”键按下。
-
- 按下“A”键后,SM1初始化;
-
- 进行SM1解密;
-
- 关闭SM1解密安全模块;
-
- 显示屏第二行显示“解密完成”,第三行显示“A键返回”,通过串口将“解密后的数据为:”和解密后的数据发送至PC机,发送完毕等待“A”键按下,若“A”键按下,跳转至B段程序。
-
- 断电,去除IC卡激活,实验结束。
七、实验步骤
1、打开“Z32开发指南\实验8-SM1”目录的工程文件。编译工程,产生后缀名为.bin的可执行代码。 2、下载程序。 3、实验8的内容是SM1加解密实验,待加密的原始数据以及加密秘钥已经内置到程序中,修改UINT8 jiamiqian[16]数组即可更换待加密数据,修改jiamimiyue[16]数组和cuowumiyue[16]即可修改正确和错误的加密密钥。 4、本实验同样使用SLE4428 IC卡作为加解密介质,按照程序提示,插入SLE4428逻辑加密卡,A段程序显示用户代码并进行密码校验,B段程序进行加密/解密选择,加密程序段进行SM1加密并将加密前后数据通过串口发送至PC机,解密程序段进行SM1解密并将解密前后数据通过串口发送至PC机。 5、我们用9针串口线将Z32模块的串口与电脑USB接口连接。 6、如果发现连接9针串口线后虚拟机仍然没有反应,可以在虚拟机中进行连接设置,确保虚拟机与Z32芯片正常连接。如下图即为连接成功。
7、首先在电脑上打开串口助手,选择对应的串口号,设置波特率为115200,偶校验(Even),然后打开串口。 8、如果不清楚串口号选择,可在设备查看器中查看。如下图:
9、关闭Z32电源开关,再打开,程序自动运行,此时可以看到实验现象:显示屏显示“SLE4428实验!请插入IC卡...”。 10、插入SLE4428 IC卡,显示屏第三行显示:“用户代码为:”,第四行显示用户代码“D27600000400”。 11、如果插入错误的卡片,则显示屏第二行显示:“卡不正确”。 12、若插入了正确的卡片,显示出用户代码,再按下矩阵键盘的“A”键,屏幕第一行显示提示“按-A键校验密码”并在第二行显示两个字节的校验密码“校验0xFF,0xFF”。 13、按下矩阵键盘的“A”键,屏幕第三行显示“校验成功”,第四行显示校验剩余机会“剩余机会:8次”。 14、再按下矩阵键盘的“A”键,则屏幕第一行显示“加密解密实验”,第二、三行分别显示选项“1.加密”,“2.解密”。
15、首先进行加密实验。按“1”键选择加密,屏幕第一行显示“观看串口调试助手”,第二行显示“A键确认加密”。此时,串口调试助手显示原始数据和加密密钥。
16、按下“A”键确认加密后,屏幕第三行显示“加密完成”,第四行显示提示“A键存入IC卡”。串口调试助手显示加密后的数据。
17、按“A”键,将加密数据存入IC卡,此时串口显示“已将数据写入IC卡”。屏幕回到加密解密实验选择菜单。 18、下面进行解密实验。按“2”键选择解密实验后屏幕显示“观看串口调试助手 A键读取IC卡数据”。
19、按“A”键后,此时屏幕显示“读取成功 选择密钥解密:1.正确密钥 2.错误密钥”。串口显示“读取的数据:为:0x7E 0xDC 0xA3 0x7B 0xBA 0x53 0x84 0xAC 0x0B 0x75 0x50 0x45 0x2E 0xEC 0x4F 0x4F”。
20、按“1”键选择正确的密钥后,屏幕提示“A键确认解密”,此时串口显示“将使用以下密钥进行解密:0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F”。
21、按“A” 键确认解密后,屏幕提示“解密完成 A键返回”,此时串口显示“解密后的数据为:0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F”。
22、按“A”键返回加/解密选择菜单。
实验中遇到的问题及其解决方案
1、破解软件MDK4.74过程中遇到了错误提示: **错误原因:**由于没有用管理员权限运行该软件,导致了无法成功破解。 **解决方案:**右键,以管理员身份运行该软件,再试一次即可成功。
成功截图:
2、实验二程序代码编译时遇到错误,发现了11个error,无法运行。 **错误原因:**没有更新c++ redistributable packet 运行库,导致找不到头文件stdint.h。 **解决方案:**安装DirectX修复工具,并检测且修复。
成功截图:
3、将Z32模块连进虚拟机中,无法连进虚拟机时,可以根据提示选择虚拟机
->可移动设备
。选择Z32HUA-BOOT
来,连接到Z32。
4、串口选择不能选择错误,如果选择COM1
的话,打开串口
之后,仍然无法成功。串口无法显示内容。
- 需要根据设备,来查看端口号,可看到是
COM5
,串口选择相应的COM5
,再打开时,可以成功。
来源:oschina
链接:https://my.oschina.net/u/4396877/blog/3523610