IIC
s3c2440内部有一个IIC总线接口。它具有四种操作模式:主设备发送模式、主设备接收模式、从设备发送模式和从设备接收模式。
IIC的具体时序:
由此可知SCL为高位时SDA从高位跃迁到低位时表示开始、SCL为高位时SDA从低位跃迁到高位时表示结束。我们可以设置IICSTAT寄存器来发送开始或结束信号。在第九个时钟时主设备或从设备拉低SDA,表示应答ACK。应答后产出中断,此时可以将数据写入IICDS或读出,然后清除中断标志位IICCON[4]恢复操作
主设备发送模式流程:首先配置IIC模式,然后把从设备地址写入接收发送数据移位寄存器IICDS中,再把0xF0写入控制状态寄存器IICSTAT中,这时等待从设备发送应答信号,如果想要继续发送数据,那么在接收到应答信号后,再把待发送的数据写入寄存器IICDS中,清除中断标志后,再次等待应答信号;如果不想再发送数据了,那么把0x90写入寄存器IICSTAT中,清除中断标志并等待停止条件后,即完成了一次主设备的发送。
主设备接收模式流程:首先配置IIC模式,然后把从设备地址写入接收发送数据移位寄存器IICDS中,再把0xF0写入控制状态寄存器IICSTAT中,这时等待从设备发送应答信号,如果想要接收数据,那么在应答信号后,读取寄存器IICDS,清除中断标志;如果不想接收数据了,那么就向寄存器IICSTAT写入0x90,清除中断标志并等待停止条件后,即完成了一次主设备的接收。
IICCON:[7]设置是否发出应答信号
[6]设置IIC的时钟频率
[5]用于是否使能发送和接收中断
[4]用于中断的标志,当接收或发送数据后一定要对该位进行清零,以清除中断标志。
IICSTAT:[7:6]用于设置是哪种操作模式
[5]写0或写1时,则表示结束IIC或开始IIC通讯
[4]用于是否使能接收/发送数据。
寄存器IICCON的第6位和低4位用于设置IIC的时钟频率,因为IIC的时钟线SCL都是由主设备提供的。s3c2440的IIC时钟源为PCLK,当系统的PCLK为50MHz,而从设备最高需要100kHz时,可以将IICCON的第6位置1,IICCON的低4位全为0即可。
在这里,从设备是EEPROM——AT24C08A,要想让s3c2440能够正确地对AT24C08A读写,就必须让s3c2440的时序完全按照AT24C08A的时序。
AT24C08A的写操作有两种模式:字节写和页写。字节写是先接收带有写命令的设备地址信息,如果符合就应答,再接收设备内存地址信息,发出应答后,再接收要写入的数据,这样就完成了字节写过程。页写与字节写的区别就是,页写可以一次写多个数据,而字节写只能一次写一个数据。但由于AT24C08A的一页才8个字节,所以页写也最多写8个数据,而且只能在该页内写,不会发生一次页写同时写两页的情况。
AT24C08A的读操作有三种模式:当前地址读、随机读和序列读。当前地址读是只能读取当前地址内的数据,它的时序是先接收带有读命令的设备地址信息,如果符合就应答,然后发送当前地址内的数据,在没有接收从主设备发来的应答信号的情况下终止该次操作。随机读的时序是,连续接收带有写命令的设备地址信息和设备内存地址信息,然后主设备重新开启IIC通信,AT24C08A再次接收到带有读命令的设备地址信息,在发出应答信号以后,发送该内存地址的数据,在没有接收到任何应答信号的情况下结束该次通信。当前地址读和随机读一次都只能读取一个数据,而序列读一次可以读取若干个数据,它的时序就是在当前地址读或随机读发出数据后,接收到了应答信号,那么AT24C08A会把下一个内存地址中的数据送出,除非AT24C08A接收不到任何应答信号,否则它会一直把下一个内存地址中的数据送出。序列读没有一页8个字节的限制。
I2C设备并不只有一个设备地址。这一点往往被忽略,一般情况下认为在I2C启动信号之后的字节为I2C从机地址(7位)。对于AT24C04而言,内部具有4Kb存储位,合计512字节。若需要访问512字节内容,总共需要9根地址线(8位宽度),那么上图中的存储地址(8位长度)显然还差了一位,那么就需要从设备地址中“借”1位,这就使得AT24C04具有两个I2C地址,例如0x50和0x51
⑴清IIC中断标志语句rIICCON &= ~0x10;一定要在读写寄存器IICDS的后面,不能放到它的前面;
⑵在等待应答的死循环while内,一定要加上延时的程序;
⑶在读取AT24C02A数据时,当读到最后一个数据时,一定不能让s3c2440发送应答信号,否则以后会无法再读取AT24C02A数据,除非关电重启;
⑷在真正对AT24C02A进行读取数据时,在发送带有读命令的从设备地址后,AT24C02A会再返回一个从设备地址信息或从设备内存地址信息作为应答,所以一定要把该字节读取后抛弃,因为它不是我们所要读取的信息;
⑸按照AT24C02A的时序,在发送从设备地址字节时,它的最低位是0表示写,1表示读。但对于s3c2440来说,不用人为设置这一位,即是0是1都无所谓,因为这一位是由s3c2440根据是主设备发送模式还是主设备接收模式来自动设
读写是站在主机的立场上定义的.
"读"是主机接收从机数据,"写"是主机发送数据给从机
7位I2C总线可以挂接127个不同地址的I2C设备,0号"设备"作为群呼地址.
10位I2C总线可以挂接更多的10位I2C设备
常用IIC接口通用器件的器件地址是由种类型号,及寻址码组成的,共7位。
如格式如下:
D7 D6 D5 D4 D3 D2 D1 D0
1-器件类型由:D7-D4 共4位决定的。这是由半导公司生产时就已固定此类型的了,也就是说这4位已是固定的。
2-用户自定义地址码:D3-D1共3位。这是由用户自己设置的,通常的作法如EEPROM这些器件是由外部IC的3个引脚所组合电平决定的(用常用的名字如A0,A1,A2)。这也就是寻址码。
所以为什么同一IIC总线上同一型号的IC只能最多共挂8片同种类芯片的原因了。
3-最低一位就是R/W位。
mini2440-256M上面用的eeprom的型号为AT24C08B,这个eeprom的大小为1024*8(8k),这是什么意思呢?
存储器芯片容量=存储单元数*数据位数
用AT24C08B来具体解释就是,AT24C08B共有1024个存储单元,每个存储单元的大小事8位(bits),而我们知道8位为一个字节,所以mini2440开发板的eeprom的大小为1024个字节,即1K大小,那么括弧里面的8K是什么意思的,8K的意思是总共有8*1024个位,不要混淆了哦。所以mini2440用户手册上说eeprom的大小是256字节是错误的,256字节那是AT24C0A的大小。
查数据手册可以其地址为0xa0或0xa1。最低位为读写标志。在s3c2440中根据IIC模式自动选择,所以地址我们只需填写0xa0即可。
值得注意的是EEPROM读写速度不是很快,所以每次读写一个字节都要加一定延时,这点十分关键。这往往是程序读写失败的原因。当然我们也需要设置好SCL的频率。在IICCON里设置这里不多说。
在接收模式下最后一个字节数据不发送ACK这点也需要注意。
测试代码中我们采用将数据写入EEPROM中然后读取出来输出到串口来检验,程序可以采用中断或轮询,代码如下:
mian.c部分
- #define GLOBAL_CLK 1
- #include "def.h"
- #include "option.h"
- #include "2440addr.h"
- #include "profile.h"
- #include "mmu.h"
- typedef unsigned char uchar;
- typedef unsigned int uint;
- extern void AT24C08_wirte(uchar waddr, uchar *dat, int num);
- extern void AT24C08_read(uchar waddr, uchar *rev, int num);
- extern void IIC_init(void);
- extern void delay(int time);
- uchar dat[]={"0123456789abcdef"};
- uchar rev[50]={0};
- void Main(void)
- {
- rGPBCON=(1<<0);//关闭蜂鸣器
- rGPBDAT=0x00;
- IIC_init();
- AT24C08_wirte(0x00, dat, 16);
- Uart_Printf("\n\n");
- AT24C08_read(0x00, rev, 10);
- Uart_Printf("\n%s\n",rev);
- }
IIC.c部分
- #include "def.h"
- #include "option.h"
- #include "2440addr.h"
- #include "profile.h"
- #include "mmu.h"
- typedef unsigned char uchar;
- typedef unsigned int uint;
- void __irq IIC_INT(void);
- int flag=1;//中断标志
- void delay(int time)
- {
- int i,j,k;
- for(i=0;i<100;++i)
- for(k=0;k<100;++k)
- for(j=0;j<time;j++);
- }
- //IIC初始化
- void IIC_init(void)
- {
- //设置GPE15、GPE14为SDA、SCL
- rGPECON |= (0x0a<<28);
- //允许ACK、允许中断、发送频率Khz
- rINTMSK &= ~(1<<27);
- rIICCON |= (1<<7 | 0<<6 | 1<<5 | 0xf);
- rIICSTAT |= 1<<4;//IICDS可写
- MMU_Init();//映射地址
- pISR_IIC = (unsigned)IIC_INT;//中断入口
- }
- //AT24C08页写
- void AT24C08_wirte(uchar waddr, uchar *dat, int num)
- {
- int i=0;
- rIICDS = 0xa0;//AT24C08地址
- rIICSTAT =0xf0;//发送模式、发送开始信号
- while(flag == 1)
- delay(100);//等待中断 (等待ACK应答发出的中断信号)
- flag = 1;
- rIICDS = waddr;//起始地址
- rIICCON = 0xaf;//清除中断标志位
- while(flag == 1)
- delay(100);//等待中断
- flag = 1;
- for(i=0;i<num;++i){
- rIICDS = *(dat + i);
- rIICCON = 0xaf;//清除中断标志位
- while(flag == 1)
- delay(100);//等待中断
- flag = 1;
- }
- rIICSTAT =0xd0;//发送停止位
- rIICCON = 0xaf;//清除中断标志位
- delay(100);
- }
- //AT24C08连续读
- void AT24C08_read(uchar waddr, uchar *rev, int num)
- {
- int i=0;
- rIICDS = 0xa0;
- rIICSTAT =0xf0;//发送从机地址
- while(flag == 1)
- delay(100);//等待中断
- flag = 1;
- rIICDS = waddr;//初始地址
- rIICCON = 0xaf;//清除中断标志位
- while(flag == 1)
- delay(100);//等待中断
- flag = 1;
- rIICDS = 0xa0;
- rIICSTAT =0xb0;//接收模式
- rIICCON = 0xaf;//清除中断标志位
- while(flag == 1)
- delay(100);//等待中断
- flag = 1;
- for(i=0;i<num;++i){
- if(i == (num-1)) rIICCON = 0x2f;
- else rIICCON = 0xaf;
- while(flag == 1)
- delay(100);//等待中断
- flag = 1;
- *(rev + i) = rIICDS;
- delay(100);
- }
- rIICSTAT =0x90;//发送停止位
- rIICCON = 0xaf;//清除中断标志位
- delay(100);
- }
- //中断服务函数
- void __irq IIC_INT(void)
- {
- flag = 0;
- rSRCPND |= 1<<27;//先清除SRCPND
- rINTPND |= 1<<27;
- }
- /*
- #include "2440addr.h"
- typedef unsigned char uchar;
- typedef unsigned int uint;
- void delay(int time)
- {
- int i,j,k;
- for(i=0;i<100;++i)
- for(k=0;k<100;++k)
- for(j=0;j<time;j++);
- }
- //IIC初始化
- void IIC_init(void)
- {
- //设置GPE15、GPE14为SDA、SCL
- rGPECON |= (0x0a<<28);
- //允许ACK、允许中断、发送频率Khz
- rIICCON |= (1<<7 | 0<<6 | 1<<5 | 0xf);
- rIICSTAT |= (1<<4);//IICDS可写
- }
- //AT24C08页写
- void AT24C08_wirte(uchar waddr, uchar *dat, int num)
- {
- int i=0;
- rIICDS = 0xa0;//AT24C08地址
- rIICSTAT =0xf0;//发送模式、发送开始信号
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- rIICDS = waddr;//起始地址
- rIICCON &= ~(1<<4);//清除中断标志位
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- for(i=0;i<num;++i){
- rIICDS = *(dat + i);
- rIICCON &= ~(1<<4);//清除中断标志位
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- }
- rIICSTAT =0xd0;//发送停止位
- rIICCON &= ~(1<<4);//清除中断标志位
- delay(100);
- }
- //AT24C08连续读
- void AT24C08_read(uchar waddr, uchar *rev, int num)
- {
- int i=0;
- rIICDS = 0xa0;
- rIICSTAT =0xf0;//发送EEPROM地址
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- rIICDS = waddr;//初始地址
- rIICCON &= ~(1<<4);//清除中断标志位
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- rIICDS = 0xa0;
- rIICSTAT =0xb0;//接收模式
- rIICCON &= ~(1<<4);//清除中断标志位
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- for(i=0;i<num;++i){
- if(i == (num-1)) rIICCON &= ~(1<<7 | 1<<4);//不发送ACK
- else rIICCON &= ~(1<<4);//清除中断标志位
- while(!(rIICCON & 0x10))
- delay(100);//等待中断
- *(rev+i) = rIICDS;
- delay(100);
- }
- rIICSTAT =0x90;//发送停止位
- rIICCON &= ~(1<<4);//清除中断标志位
- delay(100);
- }
- */