IIC(Inter-Integrated Circuit,I2C)总线是一种由PHILIPS公司开发的两线式串行总线,用于衔接微处理器及其外围设备,它的最首要长处是简略和有用。它只需求数据线SDA和时钟线SCL,就能够完结CPU与被控IC之间、IC与IC之间进行双向传送。
s3c2440内部有一个IIC总线接口,因而为咱们衔接带有IIC通讯模块的外围设备供给了便当。它具有四种操作形式:主设备发送形式、主设备接纳形式、从设备发送形式和从设备接纳形式。在这里咱们只把s3c2440作为IIC总线的主设备来运用,因而只介绍前两种操作形式。在主设备发送形式下,它的作业流程为:首要装备IIC形式,然后把从设备地址写入接纳发送数据移位寄存器IICDS中,再把0xF0写入操控状况寄存器IICSTAT中,这时等候从设备发送应对信号,假如想要持续发送数据,那么在接纳到应对信号后,再把待发送的数据写入寄存器IICDS中,铲除中止标志后,再次等候应对信号;假如不想再发送数据了,那么把0x90写入寄存器IICSTAT中,铲除中止标志并等候中止条件后,即完结了一次主设备的发送。在主设备接纳形式下,它的作业流程为:首要装备IIC形式,然后把从设备地址写入接纳发送数据移位寄存器IICDS中,再把0xB0写入操控状况寄存器IICSTAT中,这时等候从设备发送应对信号,假如想要接纳数据,那么在应对信号后,读取寄存器IICDS,铲除中止标志;假如不想接纳数据了,那么就向寄存器IICSTAT写入0x90,铲除中止标志并等候中止条件后,即完结了一次主设备的接纳。在完结上述两个形式时,首要用到了操控寄存器IICCON、操控状况寄存器IICSTAT和发送接纳数据移位寄存器IICDS。咱们咱们只把s3c2440作为主设备来用,并且体系的IIC总线上只要这么一个主设备,因而用来设置从设备地址的地址寄存器IICADD,和用于裁定总线的多主设备线路操控寄存器IICLC都无需装备。寄存器IICCON的第6位和低4位用于设置IIC的时钟频率,咱们IIC的时钟线SCL都是由主设备供给的。s3c2440的IIC时钟源为PCLK,当体系的PCLK为50MHz,而从设备最高需求100kHz时,能够将IICCON的第6方位1,IICCON的低4位全为0即可。寄存器IICCON的第7位用于设置是否宣布应对信号,第5位用于是否使能发送和接纳中止,第4位用于中止的标志,当接纳或发送数据后必定要对该位进行清零,以铲除中止标志。寄存器IICSTAT的高2位用于设置是哪种操作形式,当向第5位写0或写1时,则一共完毕IIC或开端IIC通讯,第4位用于是否使能接纳/发送数据。
咱们通讯是两边的工作,在了解了主设备的操作形式后,还要清楚从设备的运行机制,两者要到达完美地结合,才干完结互相的通讯。在这里,从设备是EEPROM——AT24C02A,要想让s3c2440能够正确地对AT24C02A读写,就必须让s3c2440的时序彻底依照AT24C02A的时序。AT24C02A的写操作有两种形式:字节写和页写。字节写是先接纳带有写指令的设备地址信息,假如契合就应对,再接纳设备内存地址信息,宣布应对后,再接纳要写入的数据,这样就完结了字节写进程。页写与字节写的差异便是,页写能够一次写多个数据,而字节写只能一次写一个数据。但咱们AT24C02A的一页才8个字节,所以页写也最多写8个数据,并且只能在该页内写,不会产生一次页写一起写两页的状况。AT24C02A的读操作有三种形式:当时地址读、随机读和序列读。当时地址读是只能读取当时地址内的数据,它的时序是先接纳带有读指令的设备地址信息,假如契合就应对,然后发送当时地址内的数据,在没有接纳从主设备发来的应对信号的状况下停止该次操作。随机读的时序是,接连接纳带有写指令的设备地址信息和设备内存地址信息,然后主设备从头舱位IIC通讯,AT24C02A再次接纳到带有读指令的设备地址信息,在宣布应对信号今后,发送该内存地址的数据,在没有接纳到任何应对信号的状况下完毕该次通讯。当时地址读和随机读一次都只能读取一个数据,而序列读一次能够读取若干个数据,它的时序便是在当时地址读或随机读宣布数据后,接纳到了应对信号,那么AT24C02A会把下一个内存地址中的数据送出,除非AT24C02A接纳不到任何应对信号,不然它会一直把下一个内存地址中的数据送出。序列读没有一页8个字节的约束。
在介绍完了s3c2440和AT24C02A的IIC通讯方法后,咱们就能够写程序了。在这里,咱们用UART来完结PC机对AT24C02A的读写。UART的通讯协议是,PC机先发送指令字节:0xC0一共要向AT24C02A写数据,0xC1一共要读取AT24C02A的数据,在指令字节后,紧跟着的是设备内存地址和写入或读取的字节数。假如是要写EEPROM数据,则在这三个字节后是要写入的数据内容。在UART通讯完毕后,s3c2440会依据指令的不同,写入或读取AT24C02A,假如是读取EEPROM,则s3c2440还会运用UART把读取到的数据上传到PC机。在这个程序中,咱们只舱位了UART的接纳中止,而没有舱位发送中止,即让s3c2440主动去完结发送使命。并且在与AT24C02A操作中,咱们运用的是页写和序列读的形式,这样能够最大程度的完结一次读或写操作,并且咱们所编写的页写和序列读子程度也相同能够完结字节写和随机读的形式。在这里咱们约束一次读或写的数据量最多为8个字节。
unsigned char iic_buffer[8];//IIC数据通讯缓存数组
unsigned char address,length;//EEPROM内存地址和数据通讯的长度
unsigned char flag;//应对标志
unsigned char comm;//指令
unsigned char devAddr=0xa0;//从设备AT24C02A的地址
…………
//IIC通讯中止
void __irq IicISR(void)
{
rSRCPND |= 0x1<<27;
rINTPND |= 0x1<<27;
flag = 0;//清标志
}
//UART通讯中止,只舱位了接纳中止,因而无需判别是接纳仍是发送
void __irq uartISR(void)
{
char ch;
static char command;
static char count;
rSUBSRCPND |= 0x1;
rSRCPND |= 0x1<<28;
rINTPND |= 0x1<<28;
ch = rURXH0;//接纳字节数据
if(command==0)//判别指令
{
switch(ch)
{
case 0xc0://写EEPROM
command = 0xc0;
count=0;
comm = 0;
break;
case 0xc1://读EEPROM
command = 0xc1;
count=0;
comm = 0;
break;
default:
command = 0;
count =0;
rUTXH0=ch;
break;
}
}
else
{
if(command == 0xc0)//写指令
{
count++;
if (count == 1)
{
address = ch;//接纳设备内存地址信息
}
else if(count == 2)
{
length = ch;//接纳写入数据个数信息
}
else//接纳详细要写入EEPROM的数据
{
iic_buffer[count-3] = ch;
if(count==length+2)//接纳完本次一切数据
{
rUTXH0=0xc0;
count=0;
command=0;
comm=1;//标志写指令,用于主程序
}
}
}
else if(command ==0xc1)//读指令
{
count++;
if(count==1)
{
address = ch;//接纳设备内存地址信息
}
else
{
length = ch;//接纳读取数据个数信息
rUTXH0=0xc1;
count=0;
command=0;
comm = 2;//标志读指令,用于主程序
}
}
}
}
//AT24C02A页写,当sizeofdate为1时,是字节写
//输入参数依次为设备内存地址、IIC数据缓存数组和要写入的数据个数
void wr24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate )
{
int i;
flag =1;//应对标志
rIICDS = devAddr;
rIICCON &= ~0x10;//清中止标志
rIICSTAT = 0xf0;//主设备发送形式
while(flag == 1)//等候从设备应对,
delay(100);//一旦进入IIC中止,即可跳出该死循环
flag = 1;
rIICDS = wordAddr;//写入从设备内存地址
rIICCON &= ~0x10;
while(flag)
delay(100);
//接连写入数据
for(i=0;i
flag = 1;
rIICDS = *(buffer+i);
rIICCON &= ~0x10;
while(flag)
delay(100);
}
rIICSTAT = 0xd0;//宣布stop指令,完毕该次通讯
rIICCON = 0xe0;//为下次IIC通讯做准备
delay(100);//等候
}
//AT24C02A的序列读,当sizeofdate为1时,是随机读
//输入参数依次为设备内存地址、IIC数据缓存数组和要读取的数据个数
void rd24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate )
{
int i;
unsigned char temp;
flag =1;
rIICDS = devAddr;//
rIICCON &= ~0x10;//清中止标志
rIICSTAT = 0xf0;//主设备发送形式
while(flag)
delay(100);
flag = 1;
rIICDS = wordAddr;
rIICCON &= ~0x10;
while(flag)
delay(100);
flag = 1;
rIICDS =devAddr;//
rIICCON &= ~0x10;
rIICSTAT = 0xb0;//主设备接纳形式
while (flag)
delay(100);
flag = 1;
temp = rIICDS;//读取从设备地址
rIICCON &= ~0x10;
while(flag)
delay(100);
//接连读
for(i=0;i
flag = 1;
if(i==sizeofdate-1)//假如是最终一个数据
rIICCON &= ~0x80;//不再呼应
*(buffer+i) = rIICDS;
rIICCON &= ~0x10;
while(flag)
delay(100);
}
rIICSTAT = 0x90;//完毕该次通讯
rIICCON = 0xe0;//
delay(100);
}
void Main(void)
{
…………
//初始化IIC
rIICCON = 0xe0;//设置IIC时钟频率,使能应对信号,并舱位中止
rIICSTAT = 0x10;
pISR_UART0 = (U32)uartISR;
pISR_IIC = (U32)IicISR;
flag=1;
comm=0;
while(1)
{
switch(comm)//判别指令
{
case 1://写EEPROM指令
wr24c02a(address,iic_buffer,length);
comm=0;
flag=1;
address=0;
length=0;
break;
case 2://读EEPROM指令
rd24c02a(address,iic_buffer,length);
comm=0;
flag=1;
address=0;
for(i=0;i
delay(500);
rUTXH0 = iic_buffer[i];
}
length=0;
break;
}
}
}
尽管这段程序不是很难,但也花费了我不少的时刻,总是呈现这样或那样的问题。现在我就把编写上述程序时需求留意的事项总结几点,防止咱们少走弯路:
⑴清IIC中止标志查办rIICCON &= ~0x10;必定要在读写寄存器IICDS的后边,不能放到它的前面;
⑵在等候应对的死循环while内,必定要加上延时的程序;
⑶在读取AT24C02A数据时,当读到最终一个数据时,必定不能让s3c2440发送应对信号,不然今后会无法再读取AT24C02A数据,除非关电重启;
⑷在真正对AT24C02A进行读取数据时,在发送带有读指令的从设备地址后,AT24C02A会再回来一个从设备地址信息或从设备内存地址信息作为应对,所以必定要把该字节读取后扔掉,咱们它不是咱们所要读取的信息;
⑸依照AT24C02A的时序,在发送从设备地址字节时,它的最低位是0一共写,1一共读。但关于s3c2440来说,不必人为设置这一位,便是0是1都无所谓,咱们这一位是由s3c2440依据是主设备发送形式仍是主设备接纳形式来主动设置。