s3c2410IIC驱动

来源:互联网 发布:孤岛惊魂3低配优化 编辑:程序博客网 时间:2024/03/29 14:38

转自:http://blog.csdn.net/spfyy/article/details/6831904

编写一程序,用S3C2410的I2C接口对串行EEPROM 24LC04(I2C接口)进行读/写操作,写入一组数据,然后读出并显示出来,检验是否正确。

 

分析:S3C2410的I2C为主设备,EEPROM的I2C为从设备,进行的操作为主设备写、和主设备读。

(1)设置I2C控制寄存器

1)收发传输:IICCON=0b 1 0 1 0 1111 = 0xAF

含义:应答使能、时钟分频为IICCLK = PCLK /16 、中断使能、清除中断标志、预分频值取15。

2)接收结束传输:IICCON=0b 0 0 1 0 1111 = 0x2F

含义:禁止应答(非应答)、时钟分频为IICCLK = PCLK /16 、中断使能、清除中断标志、预分频值取15。

(2)I2C控制状态寄存器

1)主模式发送、启动传输

IICSTAT=0b 11 1 1 0 0 0 0 = 0xF0

2)主模式发送、结束传输

IICSTAT=0b 11 0 1 0 0 0 0 = 0xD0

3)主模式接收、启动传输

IICSTAT=0b 10 1 1 0 0 0 0 = 0xB0

4)主模式接收、结束传输

IICSTAT=0b 10 0 1 0 0 0 0 = 0x90

 

一、  写单字节

(1)单字节写操作

字节写操作以来自于主器件的起始位开始, 4 位控制码紧随其后。接下来的3 位是存储块寻址位(不带地址输入引脚的器件)或片选位(带地址输入引脚的器件)。然后主发送器将R/W 位(该位为逻辑低电平)发送到总线。从器件在第九个时钟周期产生一个确认位。主器件发送的第二个字节是地址字节。24XX 器件会对每一个地址字节作出确认,并把地址位锁存进器件内部的地址计数器。对于24XX00 器件,只使用地址字节的低4 位。高4 位可为任意值。送出最后一个地址字节后, 24XX 器件发出确认信号ACK。主器件在接收到该确认信号后即发送数据字,该数据字将被写入已寻址的存储器位置。24XX 器件再次发出确认信号,之后主器件产生停止条件,启动内部写周期。写(内部写)周期期间, 24XX 不会对命令进行确认。因此需要确认查询。

(2)字节读操作

随机读操作允许主器件以随机方式访问任意存储器。执行该指令前必须先设置地址字节。作为写操作的一部分,通过发送字节地址给24XX 来完成地址字节的设置(R/W 设置为‘0’)。字节地址发送完后,主器件一接收到确认信号即产生起始条件。内部地址计数器设置完之后写操作即被终止。主器件再次发送控制字节,而该字节中R/W 位设置为‘1’。之后24XX 会发出确认信号, 并发送8 位数据字节。主器件不会对数据传输作出确认,但会产生停止条件,24XX 即停止数据发送。在随机读取命令之后,内部地址计数器加1 指向下一条地址。

(3)读写操作

主机发送模式(写):数据被发送后,I2C 总线接口会等待IICDS(I2C 数据移位寄存器)被写入新的数据。此时,SCL线保持低电平。

写中断TX: S3C2410X 可以利用中断来判断当前数据字节是否已经被完全移位送出。在CPU 接收到中断请求后,需在中断处理中,再次将下一个新的数据写入IICDS,如此循环。

主机接收模式:数据被接收到后,I2C 总线接口将等待直到IICDS 寄存器被程序读出。在数据被读出之前,SCL 线保持低电平。

读中断RX: S3C2410X 也利用中断来判别是否接收到了新的数据。CPU 收到中断请求之后,中断处理程序将从IICDS 读取数据。

(4)控制字节值:

所寻从设备地址(A0)+操作控制命令(R/ W):

1)主设备发送: 0xA0;主设备接收: 0xA1

2)24LC04有512字节,分成低256字节和高256, 字节地址分别是0xA0和0xA2

(5)确认查询流程

下图是字节写的整个流程,其中的循环是确认查询流程。

在写周期期间器件不会对命令作出确认,这可用来确定写周期何时完成。如果主器件已经发出写命令的停止条件,器件将启动内部定时写周期。可以随时进行确认查询。这包括在主器件发出起始条件后,再发送用于写命令(R/W = 0)的控制字节。如果器件仍处在写周期内,则不返回确认信号。一旦没有返回确认信号,起始位和控制字节必须重新发送。如果写周期结束,器件返回确认信号,主器件就可以执行下一个读或写命令。参见流程图。图中的循环用于检测内部写结束的确认信号,实质是不断查询rIICSTAT[0]的状态。

可见,内写结束后,对确认的查询是关键。

 

 

实验源程序

#include"def.h"

#include"2410addr.h"

#include"2410lib.h"

#include<string.h>

#include"..\INC\config.h"

void Wr24C04(U32 slvAddr,U32 addr,U8 data);

void Rd24C04(U32 slvAddr,U32 addr,U8 *data);

void Main(void)

{

      unsignedint i , j;

       static U8 data[256];

       Target_Init( );

       Uart_Printf("[I2c test using at24c04]\n");

       rGPEUP  |=  0xc000;                //1100禁止上拉(悬浮),有外部上拉

    rGPECON  &=  ~0xf0000000;

    rGPECON  |= 0xa0000000;                          //GPE15:IICSDA   GPE14:IICSCL

    rIICCON = (1<<7) | (0<<6) |(1<<5) | (0xf);  // 0xaf, 允许应答和中断,清已有中断 //IICCLK=PCLK/16

     rIICSTAT= 0x10;                              // I2C总线数据传送允许Rx/Tx

       Uart_Printf("Write test data into AT24C04 \n");

       for(i=0;  i<256;  i++)

              Wr24C04(0xa0, (U8)i , i ); // 0xa0: 24LC04地址,(U8)i:待写入数据到芯片的地址,                                                            // i:要写入的数据

       for(i=0;  i<256;  i++)

              data[i] = 0;

       Uart_Printf("\nRead test data fromAT24C04\n");

       for(i=0;  i<256;  i++)

              Rd24C04(0xa1, (U8)i, &data[i]);

       for(i=0;  i<16; i++)

       {

              for(j=0;  j<16; j++)

              Uart_Printf("%2x",data[i*16+j]);

              Uart_Printf("\n");

      }

    Uart_Printf("OK! Write data is same toRead data!\n");

}

// 写一字节函数定义

void Wr24C04(U32 slvAddr,U32 addr,U8 data)

{

       rIICDS = slvAddr;                      //设置控制字节

       rIICSTAT = 0xf0;                    //启动发送

       while((rIICCON & 0x10)==0);      //查询中断状态

       while(rIICSTAT & 1);               //检查应答ACK

       rIICDS = addr;                           //设置字地址字节

       rIICCON = 0xaf;                        //清除中断状态

       while((rIICCON & 0x10)==0);      //查询中断状态

       while(rIICSTAT & 1);             //检查应答ACK

       rIICDS = data;                           //发送数据

       rIICCON = 0xaf;                  //清除中断状态.

       while((rIICCON & 0x10)==0);  //查询中断状态

       while(rIICSTAT & 1);            //检查应答ACK

       rIICSTAT = 0xd0;              //停止写

       rIICCON = 0xaf;             //

       Delay(1);                            //等待结束生效

       while(1)

       {

              rIICDS   =   slvAddr;

              rIICSTAT   =  0xf0;

             rIICCON   =  0xaf;

             while((rIICCON & 0x10)==0);

             if(!(rIICSTAT & 0x1))         //检查应答ACK

                     break;

       }

       rIICSTAT  =  0xd0;          //Stop 写

       rIICCON =  0xaf;

       Delay(1);                            //等待结束生效

}

// 读一字节函数定义

void Rd24C04(U32 slvAddr,U32 addr,U8 *data)

{

       rIICDS = slvAddr; //设置从设备地址 control byte

       rIICSTAT = 0xf0; //启动发送  S

       while((rIICCON & 0x10)==0); //查询Tx中断状态

       while(rIICSTAT & 1);

       rIICDS = addr;           //置单元偏移地址,发送的   

       rIICCON = 0xaf;            //操作

       while((rIICCON & 0x10)==0); //查询Tx中断状态   有中断时,IICSCL被拉低,IIC停//止传输。若要恢复操作,将rIICCON[4]清零。

       while(rIICSTAT & 1);

       rIICDS = slvAddr;      

       rIICSTAT = 0xb0;   //启动读

       rIICCON = 0xaf;    //清除中断条件,恢复IIC操作

       while((rIICCON & 0x10)==0); //查询中断状态

       while(rIICSTAT & 1);

       rIICCON = 0x2f;        // 清除中断标志,禁止应答,因仅读1字节

       while((rIICCON & 0x10)==0); //查询中断状态

       *data =rIICDS;          //读取数据after the cpu receives the interrupt request,it should                            //read the datafrom the iicds register.

       rIICSTAT = 0x90;                //停止读

       rIICCON = 0xaf;  //清除中断

       Delay(1);

}

 

二、 写多字节

(1)页写入操作

 

 

(2)写多字节的流程

BEGIN

Pages = 0

整页写页数= 待写字节数 / pagesize(16)

While pages < 整页写页数

{

       整页写入数据

       Pages ++

}

剩余不足一页的字节数 = 待写入字节数 %pagesize(16)

写入剩余字节

END

 

(3)连续写时的跨页问题

写控制字节、字地址字节和首个数据字节以和写单字节相同的方式发送给 24XX 器件。不同的是,主器件发送的是多至一整页的数据字节,而不是停止条件,这些数据字节临时存储在片内页缓冲器中。在主器件发送停止条件之后,这些数据

将被写入存储器。每接收一个字,内部地址计数器加一。如果在停止条件产生前,主器件有超出一页的数据要发送,地址计数器将会翻转,先前写入的数据将被覆

盖。对于字节写操作,一旦接收到停止条件,内部写周期开始。

 

(4)跨页问题的解决办法

引入了两个参数pages和i 。pages代表已经占用的页数,i代表写入的总字节数。

以连续写51个字节为例:前48字节以整页方式写入,后3个字节再另起一页连续写入。地址计数器每次翻转,代表addr 又被赋值为该页的首地址。因此每次外写完一页时,就发送停止信号开始内部写,并且 将 addr += 16; 这就解决了跨页。再重新启动,写下一页。如此循环。

 

实验源程序

#include "def.h"

#include "2410addr.h"

#include "2410lib.h"

#include <string.h>

#include "..\INC\config.h"

void Wr24C04(U32 slvAddr,U32 addr,U8 data);

void Rd24C04(U32 slvAddr,U32 addr,U8 *data);

void start(U32 slvAddr);

void internal_write(U32 slvAddr);

void Main(void)

{  

           unsigned int i,j;

              staticU8 data[256];

              Target_Init();

              Uart_Printf("[I2c test using at24c04]\n");

              rGPEUP|= 0xc000;                    

           rGPECON &= ~0xf0000000;

           rGPECON |= 0xa0000000;                            

           rIICCON = (1<<7) | (0<<6) |(1<<5) | (0xf); 

            rIICSTAT= 0x10;                             

              Uart_Printf("Writetest data into AT24C04\n");

                     Wr24C04(0xa0,0,1);

              for(i=0; i<256;  i++)

                     data[i]= 0;

              Uart_Printf("\nReadtest data from AT24C04\n");

              for(i=0; i<256;  i++)

                     Rd24C04(0xa0, (U8)i, &data[i]);

              for(i=0;  i<16; i++)

              {

                     for(j=0;  j<16; j++)

                     Uart_Printf("%2x",data[i*16+j]);

                     Uart_Printf("\n");  

              }

           Uart_Printf("OK! Write data is same toRead data!\n");     

}

// 写函数定义 (写多字节)

void Wr24C04(U32 slvAddr,U32 addr,U8 data)

{

              intsize =51;  //待写入的总字节数

              intm, n, i= 0;  //  i 为已经写入的字节数

              intpage=0;

              n= size % 16;

              m= size - n; //以整页写方式写入的总字节数

              while(page< size/16)

              {    

                     start(0xa0);

                     rIICDS  = addr ;         //置单元偏移地址,发送

                     rIICCON= 0xaf ;                       //清除中断状态

                     while((rIICCON& 0x10)==0);      //查询中断状态

                     while(rIICSTAT& 1);          //检查应答ACK

                     while(m--)

                     {

                           if ((i% 16 == 0)  && (i !=0))//防止第一页被漏写

                         {

                                 internal_write(0xa0);

                             //至此,则将单独一页字节写入eeprom阵列完毕(内写)

                             addr += 0x10;

                             page++;

                             //重新启动

                                   start(0xa0);

                                   rIICDS  = addr;                        //置单元偏移地址,发送

                                   rIICCON= 0xaf;                        //清除中断状态

                                   while((rIICCON& 0x10)==0);      //查询中断状态

                                   while(rIICSTAT& 1);          //检查应答ACK

                         }

                         rIICDS = data;                         //发送数据

                            rIICCON= 0xaf;                 //清除中断状态.

                            while((rIICCON& 0x10)==0); //查询中断状态

                            while(rIICSTAT& 1);          //检查应答ACK

                         i++; //写入的字节数

                     }

                    internal_write(0xa0);

                     addr+= 0x10 ;

                     page++; 

              }

                 // 以下程序为连续写不足一页的字节数据,或者如果size<16就从这执行连续写入

              start(0xa0);

              rIICDS  = addr;                        //置单元偏移地址,发送

              rIICCON= 0xaf;                        //清除中断状态

              while((rIICCON& 0x10)==0);      //查询中断状态

              while(rIICSTAT& 1);          //检查应答ACK

              while(n--)

              {

                     rIICDS  = data;                         //发送数据

                     rIICCON= 0xaf;                 //清除中断状态.

                     while((rIICCON& 0x10)==0); //查询中断状态

                     while(rIICSTAT& 1);          //检查应答ACK

              }

              internal_write(0xa0);

              rIICSTAT= 0xd0;         //Stop 写

              rIICCON  = 0xaf;

              Delay(1);                           //等待结束生效

             

}

// 读一字节函数定义

void Rd24C04(U32 slvAddr,U32 addr,U8 *data)

{

              rIICDS= slvAddr; //设置从设备地址  control byte

              rIICSTAT= 0xf0; //启动发送  S

              while((rIICCON& 0x10)==0); //查询Tx中断状态

              while(rIICSTAT& 1);

              rIICDS= addr;           //置单元偏移地址,发送的   

              rIICCON= 0xaf;            //操作

              while((rIICCON& 0x10)==0); //查询Tx中断状态   有中断时,IICSCL被拉低,IIC停止//传输。若要恢复操作,将rIICCON[4]清零。

              while(rIICSTAT& 1);

              rIICDS= slvAddr; 

              rIICSTAT= 0xb0;   //启动读

              rIICCON= 0xaf;    //清除中断条件,恢复IIC操作

              while((rIICCON& 0x10)==0); //查询中断状态

              while(rIICSTAT& 1);

              rIICCON= 0x2f;        // 清除中断标志,禁止应答,因仅读1字节

              while((rIICCON& 0x10)==0); //查询中断状态

              *data=rIICDS;          //读取数据 after the cpu receives theinterrupt request,it should //read the data from the iicds register.

              rIICSTAT= 0x90;                //停止读

              rIICCON= 0xaf;  //清除中断

              Delay(1);

}

//***************************************       

void start(U32 slvAddr)

{

              rIICDS= slvAddr;                      //设置从设备地址

              rIICSTAT= 0xf0;                //启动发送

              while((rIICCON& 0x10)==0);      //查询Tx中断状态

              while(rIICSTAT& 1);          //检查应答ACK

}    

//****************************************

void internal_write(U32 slvAddr)

{

              rIICSTAT= 0xd0;         //Stop 写

              rIICCON  = 0xaf; 

              Delay(1);     

           //启动一页的内写

           while(1)

              {

                     rIICDS   = slvAddr;

                     rIICSTAT= 0xf0;  //启动发送 S

                    while((rIICCON& 0x10)==0);

                    rIICCON  = 0xaf; //清中断   

                    if(!(rIICSTAT& 0x1))         //检查应答ACK

                     break;

              }   

}

//***************************************

连续写51个1:

 

 

原创粉丝点击