13、U盘、IC卡、SD卡读写、时钟芯片应用
【实例94】读写U盘
1(变量定义和基本操作函数
(1)变量的定义
#include
#define uchar unsigned char
uchar volatile xdata CH372_CMD_PORT _at_ 0x7DFF; // CH372命令端口的I/O地址 uchar volatile xdata CH372_DAT_PORT _at_ 0x7CFE; //CH372数据端口的I/O地址 uchar Usb_Length; //USB数据缓冲区中数据的长度
uchar Usb_Buffer[ CH372_MAX_DATA_LEN ]; // USB数据缓冲区
#define CH372_MAX_DATA_LEN 0x40 //最大数据包的长度,内部缓冲区的长度 //命令代码
#define CMD_RESET_ALL 0x05 //执行硬件复位
#define CMD_CHECK_EXIST 0x06 //测试工作状态
#define CMD_SET_USB_ID 0x12 // 设置USB厂商VID和产品PID #define CMD_SET_USB_ADDR 0x13 //设置USB地址
#define CMD_SET_USB_MODE 0x15 //设置USB工作模式 #define CMD_SET_ENDP2 0x18 //设置USB端点0的接收器 #define CMD_SET_ENDP3 0x19 //设置USB端点0的发送器 #define CMD_SET_ENDP4 0x1A //设置USB端点1的接收器 #define CMD_SET_ENDP5 0x1B //设置USB端点1的发送器 #define CMD_SET_ENDP6 0x1C //设置USB端点2/主机端点的接收器 #define CMD_SET_ENDP7 0x1D //设置USB端点2/主机端点的发送器 //命令CMD_SET_ENDP2~CMD_SET_ENDP7输入为:工作方式,位7为1则位6为同步触发位,
否则同步触发位不变,位3~位0为事务响应方:
0000~1000-就绪ACK,1101-忽略,1110-正忙NAK,1111-错误STALL #define CMD_GET_TOGGLE 0x0A //获取OUT事务的同步状态,输入:数据1AH, 输出:同步状态
#define CMD_GET_STATUS 0x22 // 获取中断状态并取消中断请求 #define CMD_UNLOCK_USB 0x23 //释放当前USB缓冲区 #define CMD_RD_USB_DATA 0x28 //从当前USB中断的端点缓冲区读取数据块,
并
释放缓冲区
#define CMD_WR_USB_DATA3 0x29 //向USB端点0的发送缓冲区写入数据块 #define CMD_WR_USB_DATA5 0x2A //向USB端点1的发送缓冲区写入数据块 #define CMD_WR_USB_DATA7 0x2B // 向USB端点2的发送缓冲区写入数据块 //操作状态
#define CMD_RET_SUCCESS 0x51 //命令操作成功
#define CMD_RET_ABORT 0x5F //命令操作失败
(2)基本操作函数
?函数DelayMs:在CH372读写过程中需要用到毫秒延时,该函数可以满足要求,程
序代码如下。
void DelayMs(uchar n)
{
uchar i;
unsigned int j;
for(i=0; i分析中断状态
{
case USB_INT_EP2_OUT: //批量端点2下传成功,接收到数据
{
WR_CH372_CMD_PORT( CMD_RD_USB_DATA );//从当前USB中断的端点缓冲区
读取数据块,并释放缓冲区
Usb_Length =RD_CH372_DAT_PORT( ); //首先读取后续数据长度
length= Usb_Length;
if (length)
{ //接收数据放到缓冲区中
buf = Usb_Buffer; //指向缓冲区
do {
*buf = RD_CH372_DAT_PORT( );
buf ++;
} while ( -- length );
}
else break; //长度为0,没有数据
//下面是回传数据
WR_CH372_ CMD_PORT( CMD_WR_USB_DATA7 ); //向USB端点2的发送缓冲区写
入数据块
length = Usb_Length;
WR_CH372_DAT_PORT( length ); //首先写入后续数据长度
if (length)
{ //将缓冲区中的数据发出
buf = Usb_Buffer; // 指向缓冲区
do{
WR_CH372_DAT_PORT( *buf ); // 写入数据到CH372
buf ++;
} while ( -- length );
}
break;
case USB_INT_EP2_IN: //批量端点上传成功,数据已发送成功
{
WR_CH372_ CMD_PORT( CMD_UNLOCK_USB ); //释放当前USB缓冲区,收到
上
传成功中断后,必须解锁USB缓冲区,以便继续收发
break;
}
case USB_INT_EP1_IN:// 中断端点上传成功,中断数据发送成功
{
WR_CH372_CMD_PORT ( CMD_UNLOCK_USB ); //释放当前USB缓冲区
break;
}
case USB_INT_EP1_OUT: //辅助端点下传成功,接收到辅助数据辅助端点可以用于计算
机
端向单片机端发送包
{
WR_CH372_ CMD_PORT( CMD_UNLOCK_USB ); //释放当前USB缓冲区
break;
}
}
}
【实例95】非接触IC卡读写
MF RC500的命令集的程序定义代码如下。
#define M500Pcd_IDLE 0x00 //取消当前命令
#define M500Pcd _WRITEE2 0x01 //写EEPROM
#define M500Pcd _READE2 0x03 //读EEPROM
#define M500Pcd _LOADCONFIG 0x07 //调EEPROM中保存的RC500设置 #define M500Pcd _LOADKEYE2 0x0B //将EEPROM中保存的密钥调入缓存 #define M500Pcd _AUTHENT1 0x0C //验证密钥第一步
#define M500Pcd _AUTHENT2 0x14 //验证密钥第二步
#define M500Pcd _RECEIVE 0x16 //接收数据
#define M500Pcd _LOADKEY 0x19 //传送密钥
#define M500Pcd _TRANSMIT 0x1A //发送数据
#define M500Pcd _TRANSCEIVE 0x1E //发送并接收数据
#define M500Pcd _Startup 0x3F //复位
#define M500Pcd _CALCCRC 0x12 //CRC计算
MF RC500的64个寄存器的程序定义代码如下。
// PAGE 0
#define RegPage 0x00 #define RegCommand 0x01 #define RegFIFOData 0x02
#define RegPrimaryStatus 0x03 #define RegFIFOLength 0x04 #define RegSecondaryStatus 0x05 #define RegInterruptEn 0x06 #define RegInterruptRq 0x07 // PAGE 1
#define RegPage 0x08 #define RegControl 0x09 #define RegErrorFlag 0x0A #define RegCollPos 0x0B #define RegTimerValue 0x0C #define RegCRCResultLSB 0x0D #define RegCRCResultMSB 0x0E #define RegBitFraming 0x0F // PAGE 2
#define RegPage 0x10 #define RegTxControl 0x11 #define RegCwConductance 0x12 #define RFU13 0x13 #define RegCoderControl 0x14 #define RegModWidth 0x15 #define RFU16 0x16 #define RFU17 0x17 // PAGE 3
#define RegPage 0x18 #define RegRxControl1 0x19 #define RegDecoderControl 0x1A #define RegBitPhase 0x1B #define RegRxThreshold 0x1C #define RFU1D 0x1D #define RegRxControl2 0x1E #define RegClockQControl 0x1F // PAGE 4
#define RegPage 0x20 #define RegRxWait 0x21 #define RegChannelRedundancy 0x22 #define RegCRCPresetLSB 0x23 #define RegCRCPresetMSB 0x24 #define RFU25 0x25 #define RegMfOutSelect 0x26 #define RFU27 0x27 // PAGE 5
#define RegPage 0x28 #define RegFIFOLevel 0x29
#define RegTimerClock 0x2A #define RegTimerControl 0x2B #define RegTimerReload 0x2C #define RegIRqPinConfig 0x2D #define RFU2E 0x2E #define RFU2F 0x2F // PAGE 6
#define RegPage 0x30 #define RFU31 0x31 #define RFU32 0x32 #define RFU33 0x33 #define RFU34 0x34 #define RFU35 0x35 #define RFU36 0x36 #define RFU37 0x37 // PAGE 7
#define RegPage 0x38 #define RFU39 0x39 #define RegTestAnaSelect 0x3A #define RFU3B 0x3B #define RFU3C 0x3C #define RegTestDigiSelect 0x3D #define RFU3E 0x3E #define RegTestDigiAccess 0x3F 主程序的代码如下:
#define MI_OK 0
#define uchar unsigned char
#define uint unsigned int
//操作子函数
extern char M500PcdReset();//复位并初始化RC500
extern char M500PcdRequest(uchar req_code); //寻卡
extern char M500PcdAnticoll(uchar *snr); //防冲撞
extern char M500PcdSelect(uchar *snr); //选定一张卡
extern char M500ChangeCodeKey(uchar *uncoded,uchar *coded); //转换密钥格式 extern char M500PcdAuthKey(uchar *coded); //传送密钥
extern char M500PcdAuth(uchar auth_mode,uchar block,uchar *snr); //验证密钥 extern char M500PcdRead(uchar addr,uchar *readdata); //读块
extern char M500PcdWrite(uchar addr,uchar *writedata); //写块
extern char M500PcdHalt(void); //卡休眠
extern char M500PcdReadE2(uint startaddr,uchar length,uchar *readdata); //读RC500-EEPROM数据 extern char M500PcdWriteE2(uint startaddr,uchar length,uchar *writedata); //写数据到
RC500-EEPROM
extern char M500PcdConfigRestore();//恢复RC500出厂设置
//Mifarel卡命令字
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态的卡 #define PICC_REQALL 0x52 //寻天线区内全部卡 #define PICC_ANTICOLL1 0x93 //防冲撞
#define PICC_AUTHENT1A 0x60 //验证A密钥
#define PICC_AUTHENT1B 0x61 //验证B密钥
#define PICC_READ 0x30 //读块
#define PICC_WRITE 0xA0 //写块
#define PICC_DECREMENT 0xC0 //减值
#define PICC_INCREMENT 0xC1 //加值
#define PICC_RESTORE 0xC2 //存储
#define PICC_TRANSFER 0xB0 //传送
#define PICC_HALT 0x50 //休眠
void main (void)
{ int count;
idata struct TranSciveBuffer{uchar MFCommand; uchar MFLength; uchar MFData[16];
}MFComData;
M500PcdReset() ; //初始化RC500
M500PcdReadE2(startaddr, length, readdata); //读MF RC500的系列号并存贮它
For (count = 0 ;count<100 ;count + + )
{
status = M500PcdRequest(req_code); //发送请求代码给卡,并等待应答
if (status= =MI_OK)
status= M500PcdAnticoll(serialno); //防冲撞
if (status= =MI_OK)
status= M500PcdSelect(serialno); //选择一个指定的卡
if (status= =MI_OK)
status = M500ChangeCodeKey(uncoded, coded); //转换密钥格式
if (status= =MI_OK)
status =M500PcdAuthKey(coded); //传送密钥
if (status= =MI_OK)
status = M500PcdAuth(auth_mode, block, serialno); //验证密钥,鉴定卡
if (status= =MI_OK)
status = M500PcdRead(addr, blockdata); //读卡
for ( i=0;i<16;i + + )
*(blockdata+i) = MFComData.MFData[i]; ;
if (status==MI_OK)
status= M500PcdWrite(addr, blockdata); //写卡
}
}
【实例96】SD卡读写
1(SD卡读写的操作子函数
(1)延时函数delay
void delay(uint n) {
while(--n)
{
}
}
(2)函数SD_read_byte uchar SD_read_byte (void)
{
uchar Byte = 0;
uchar i = 0;
DI=1;
for (i=0; i<8; i++)
{
CLK=0;
delay(4);
Byte=Byte<<1; //先接收最高位。
if (DO==1)
{
Byte |= 0x01;
}
CLK=1;
delay(4);
}
return (Byte); }
(3)函数SD_write_byte void SD_write_byte(uchar Byte)
{
uchar i ;
CLK=1;
for (i =0; i<8; i++)
{
if (Byte&0x80) //先写高位的。
{
DI=1;
}
else
{
DI=0;
}
CLK=0;
delay(4);
Byte=Byte<<1;
CLK=1;
delay(4);
}
DI=1;
}
(4)函数SD_write_command uchar SD_write_command (uchar *cmd)
{
uchar tmp = 0xff;
uint Timeout = 0;
uchar a;
SD_Disable();
SD_write_byte(0xFF); //发送8个时钟
SD_Enable();
for(a = 0;a<0x06;a++) //发送6字节命令
{
SD_write_byte(cmd[a]);
}
while (tmp == 0xff)
{
tmp = SD_read_byte(); //等待回复
if (Timeout++ > 500)
{
break; //超时返回
}
}
return(tmp); //返回响应信息 }
2(SD卡的上电初始化
SD卡的上电初始化程序代码如下。
//预定义变量
sbit CS=P2^0;
sbit CLK= P2^3;
sbit DI=P2^1;
sbit DO=P2^2;
#define SD_Disable() CS=1 //片选关 #define SD_Enable() CS=0 //片选开 uchar SD_read_byte (void) //读出一字节 void SD_write_byte(uchar Byte) //写入一个字节 uchar SD_write_command (uchar *cmd)//发送SD命令
uchar SD_write_sector(ulong addr,uchar *Buffer)//写单块,512字节 void SD_read_block(uchar *cmd,uchar *Buffer,uint Bytes) //读N个字节数据放在缓冲区内
uchar SD_read_sector (ulong addr,uchar *Buffer)//读单块,512字节 uchar SD_init () //sd 初始化
void delay(uint n) //延时微秒
//SD初始化
uchar SD_init ( ) {
uchar Timeout = 0;
uchar b;
uchar idata CMD[ ] = {0x40,0x00,0x00,0x00,0x00,0x95}; //CMD0
for (i=0;i<0x0f;i++)
{
SD_write_byte(0xff); //延迟74个以上的时钟
}
SD_Enable( ); //开片选
//发送CMD0
while(SD_write_command (CMD) !=0x01) //等于1
示复位成功
{
if (Timeout++ > 5)
{
return(1);
}
}
//发送 CMD1
Timeout = 0;
CMD[0] = 0x41;
CMD[5] = 0xFF; //CMD1
while( SD_write_command (CMD) !=0)
{
if (Timeout++ > 100)
{
return(2);
}
}
SD_Disable();
return(0); //成功
}
2(对SD卡的读写操作
写SD卡的程序代码如下。
//写单块,512字节
uchar SD_write_sector (ulong addr,uchar *Buffer)//参数:写扇区的地址,数据的指针
{
uchar tmp;
uint a ;
uchar idata cmd[] = {0x58,0x00,0x00,0x00,0x00,0xFF}; //CMD24
addr = addr << 9; //addr = addr * 512
cmd[1] = ((addr & 0xFF000000) >>24 );
cmd[2] = ((addr & 0x00FF0000) >>16 );
cmd[3] = ((addr & 0x0000FF00) >>8 );
tmp = SD_write_command (cmd); //发送命令cmd24,写单块512字节
if (tmp != 0)
{
return(tmp);
}
for (a=0;a<100;a++)
{
SD_read_byte();
}
SD_write_byte(0xFE); //发送读、写命令后都要发送起始令牌FEH
for ( a=0;a<512;a++)
{
SD_write_byte(*Buffer++);
}
//写入CRC字节
SD_write_byte(0xFF);
SD_write_byte(0xFF);
while (SD_read_byte() != 0xff)
{
};
SD_Disable();
return(0);
}
读SD卡的程序代码如下。
//读N个字节数据放在缓冲区内
void SD_read_block(uchar *cmd,uchar *Buffer,uint N_Byte)//参数:命令、缓冲区指针、长度 {
uint a;
if (SD_write_command (cmd) != 0)
{
return;
}
while (SD_read_byte( )!= 0xfe)
{
};
for (a=0;a< N_Byte;a++)
{
*Buffer++ = SD_read_byte();
}
//取走CRC字节
SD_read_byte();
SD_read_byte();
SD_Disable();
return;
}
//读一个扇区数据
uchar SD_read_sector (ulong addr,uchar *Buffer)//参数:扇区地址,缓冲区指针 {
uchar idata cmd[] = {0x51,0x00,0x00,0x00,0x00,0xFF}; //读单块
addr = addr << 9; //addr = addr * 512
cmd[1] = ((addr & 0xFF000000) >>24 );
cmd[2] = ((addr & 0x00FF0000) >>16 );
cmd[3] = ((addr & 0x0000FF00) >>8 );
SD_read_block(cmd,Buffer,512);
return(0);
}
【实例97】高精度实时时钟芯片的应用
1(变量及SD2300A的相关定义
//变量定义
#define true 1
#define false 0
//SD2300A相关定义
sbit SDA=P2^0;
sbit SCL=P2^1;
uchar data1,data2,data3,data4; uchar date[7]; //日期数组
//date[6]=year,date[5]=month,date[4]=day,date[3]=week,
// date[2]=hour,date[1]=minute,date[0]=second
//SD2300A函数名
void I2C_delay(void);
bit I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_NoAck(void);
bit I2C_Read_ACK(void);
void I2C_Send_Byte(uchar demand); uchar I2C_Receive_Byte(void); void I2C_Read_Date(void);
void Delay(uint nn);
void I2C_Write_Date(void);
2(SD2300A的相关子程序
2(1)函数_delay的程序代码如下。 IC
2void Delay(void) IC
{
nop();nop();nop();nop();
}
2(2)函数_Start IC
bit I2C_Start(void)
{
SDA=1;
SCL=1;
I2C_delay();
if(!SDA)
return false;//SDA线为低电平则总线忙,退出
SDA=0;
I2C_delay();
while(SDA)
return false;//SDA线为高电平则总线出错,退出
SCL=0;
I2C_delay();
2 return true;//开启总线 IC
}
2(3)函数_Stop IC
void I2C_Stop(void)
{
SDA=0;
SCL=0;
I2C_delay();
SCL=1;
I2C_delay();
SDA=1;
}
2(4)函数_Ack IC
void I2C_Ack(void)
{
SDA=0;
SCL=0;
I2C_delay();
SCL=1;
I2C_delay();
SCL=0;
}
2(5)函数_NoAck IC
void I2CNoAck(void)
{
SDA=1;
SCL=0;
I2C_delay();
SCL=1;
I2C_delay();
SCL=0;
}
2_Read_Ack (6)函数IC
bit I2C_Read_ACK(void) //返回true=1时有ACK, true=0时无ACK
{
uchar errtime=255;
SCL=0;
SDA=1;
I2C_delay();
SCL=1;
I2C_delay();
while(SDA)
{
errtime--;
if(!errtime)
SCL=0;
return false;
}
SCL=0;
return true; }
22(7)函数_Send_Byte和_Receive_Byte ICIC//向SD2300A发送一个字节 void I2C_Send_Byte(uchar demand) //数据从高位到低位发送
{
uchar i=8;
while(i--)
{
SCL=0;
nop();
SDA=(bit)(demand&0x80);
demand<<=1;
I2C_delay();
SCL=1;
I2C_delay();
}
SCL=0;
}
//从SD2300A读入一字节
uchar I2C_Receive_Byte(void) //数据从高位到低位 {
uchar i=8;
uchar ddata=0;
SDA=1;
while(i--)
{
ddata<<=1; //数据从高位开始读取
SCL=0;
I2C_delay();
SCL=1;
I2C_delay(); //从高位开始 ddata|=SDA;ddata<<=1
if(SDA)
{
ddata|=0x01;
}
}
SCL=0;
return ddata;
}
2(8)函数_Read_Date IC
void I2C_Read_Date(void) {
uchar n;
I2C_Start();
I2C_Stop();
I2C_Start();
I2C_Send_Byte(0x64); //从年开始读取数据
I2C_Read_ACK();
I2C_Send_Byte(0x00);
I2C_Read_ACK();
I2C_Start();
I2C_Send_Byte(0x65);
I2C_Read_ACK();
for(n=0;n<7;n++)
{
date[n]= I2C_Receive_Byte();
if (n!=6) //最后一个数据不应答
{
I2C_Ack();
}
}
I2CNoAck();
I2C_Stop();
}
2(9)函数_Write_Date IC
void I2C_Write_Date(void)
{
I2C_Start();
I2C_Stop();
I2C_Start();
I2C_Send_Byte(0x64);
I2C_Read_ACK();
I2C_Send_Byte(0xf0);//设置写起始地址
I2C_Read_ACK();
I2C_Send_Byte(0x20);//二十四小时制
I2C_Read_ACK();
I2C_Send_Byte(0x01);//秒
I2C_Read_ACK();
I2C_Send_Byte(0x01);//分
I2C_Read_ACK();
I2C_Send_Byte(0x08);//时
I2C_Read_ACK();
I2C_Send_Byte(0x01);//日
I2C_Read_ACK();
I2C_Send_Byte(0x01);//周
I2C_Read_ACK();
I2C_Send_Byte(0x07);//月
I2C_Read_ACK();
I2C_Send_Byte(0x04);//年
I2C_Read_ACK();
I2C_Send_Byte(0x00);//清零数字调整寄存器
I2C_Read_ACK();
I2C_Stop(); }
(10)函数Delay
void Delay(uint nn) {
while(nn--);
}
3(主程序
main()
{
P2=0xFF;
I2C_Write_Date();
while(1)
{
I2C_Read_Date();
Delay(250);
Delay(250);
}
}