WINCE下IIC接口FLASH驱动

来源:互联网 发布:淘宝分析数据软件知乎 编辑:程序博客网 时间:2024/04/29 16:05

转自:http://blog.sina.com.cn/s/blog_61ebc5f30100fpdy.html

1.4 AT24LC08读写流驱动开发

AT24LC08是一款I2C接口的EEPROM芯片,容量为8Kbit,内部分4Page,每个Page256B。访问AT24LC08上的地址空间需要10位地址线。编写AT24LC08芯片的读写驱动需要了解它的读写方式和I2C总线的访问时序,这里以Micro2440平台为例介绍AT24LC08工作过程。该驱动编写的总体流程与1.3类似。

1.4.1 AT24LC08工作原理

AT24LC08的主要管脚定义如表1所示。其中A2是和硬件上的连接一致的;A1A0是用于选择Page页;SDA用于双向的串行数据传输;SCL是串行时钟线,时钟上升沿时,数据从总线进入EEPROM,时钟下降沿时,数据从EEPROM传到总线;WP信号接地,即采用普通的读写方式。

1 AT24LC08主要引脚描述

引脚名称

功能

A0 A1 A2

地址输入

SDA

串行数据

SCL

串行时钟输入

WP

写保护

按芯片的工作流程顺序,分别分析器件寻址、写操作、读操作三个过程。

l        器件寻址

AT24LC08寻址的地址部分包括:8bit的器件地址和8bit的字地址。器件地址的各bit位分布如表2所示。前四位是由芯片厂商确定的,为固定值。A2必须和硬件的输入管脚一致。P1P0为页选地址。R/W是读写控制位,0表示写操作,1表示读操作。

2 AT24LC08器件地址

1

0

1

0

A2

P1

P0

R/W

8位字地址用于寻址当前页中的某个地址空间,8位对应256个字节。

在芯片读写操作开始时,首先要依次往总线上发送器件地址和字地址,寻址正确才能进行后续的读写操作。

l        写操作

Micro2440开发板上,我们定义S3C2440为主设备,AT24LC08芯片为从设备。主设备会发送START信号,STOP信号;从设备会回复ACK信号。

如图3所示,当SCL为高电平时,SDA信号被拉低,即出现START信号;当SCL为高电平时,SDA信号由低变高,即为STOP信号。

WINCE下IIC接口FLASH驱动(一)

                                     3 启动、停止条件

当主设备发送完一个字节的数据,从设备会在下一个时钟周期回复一个ACK,即一个低电平信号,如图4所示。

WINCE下IIC接口FLASH驱动(一)

4 输出ACK信号

AT24LC08的写操作过程中(以页写模式为例),其操作流程如图5所示。首先主设备发出START信号,紧接着发送8位器件地址和8位字地址。器件地址最末位为WRITE0)。从设备验证完地址并发回ACK信号后,主机就开始发送数据。从设备每收到一个字节的数据就返回一个ACK。发送结束后,主机发出STOP信号,至此,一个完整的写操作结束。

需要注意的是:页写模式最多支持16个字节的写操作。按字地址低四位逐次累加,当累加到1111时,下一个时钟周期地址翻转到0000,新写入的数据将覆盖之前的值。

WINCE下IIC接口FLASH驱动(一)

WINCE下IIC接口FLASH驱动(一)

5 页写数据顺序

l        读操作

以页读模式为例分析读操作的过程:首先主机发出START信号,依次发送器件地址和字地址,此时的读写控制位为WRITE。从机收到地址返回ACK。然后,主机再发送START信号,再传器件地址,此时的读写控制位为READ。主机等到从机返回的ACK,就开始读数据。读到最后一个字节时,从机返回一个NACK信号,主机发出STOP,标志读过程结束。

同样,页读模式也支持最多16个字节的操作。按字地址的低四位累加,如果溢出就翻转到0000地址开始读。

WINCE下IIC接口FLASH驱动(一)

WINCE下IIC接口FLASH驱动(一)

6 页读数据顺序

 

 

1.4.2 驱动程序架构和访问流程

l        体系结构和访问流程

在流式接口驱动程序中,驱动程序负责把外设抽象成一个文件,而应用程序则使用操作系统提供的文件API对外设进行访问。文件API被操作系统转发到FileSys.exe进程中;然后FileSys.exe发现是对设备的操作,就会把执行交给Device.exe处理;接着Device.exe根据具体的请求,调用流驱动接口函数;最终,驱动程序负责与硬件交互。

WINCE下IIC接口FLASH驱动(二)

7 流接口驱动体系结构

      具体过程分析如下:

(1)      应用程序须使用该设备,首先调用CreateFileTEXT(“IIC1”)…)打开设备。CreateFile函数是在FileSys.exe中实现的。但是FileSys.exe只作简单判断:如果发现打开的是设备驱动而不是一个文件,那么就重新把主动权交还给设备管理器。

(2)      设备管理器调用驱动程序中的IIC_Open()函数打开设备。在IIC_Open中,驱动程序可以对硬件进行一些额外的初始化工作,使硬件进入工作状态。

(3)      IIC_Open()函数把打开设备的结果返回给设备管理器。

(4)      设备管理器把IIC_Open()返回的结果再传给应用程序中的CreateFile()函数调用。

(5)      设备已被成功打开,接下来可对设备进行读写和控制操作。以控制操作为例,CreateFile函数返回的句柄作为DeviceIoControl()的第1个参数,向设备发送控制请求。同样,DeviceIoControl()要FileSys.exe转发给设备管理器。

(6)      设备管理器调用驱动程序中的IIC_IOControl()函数,与硬件完成交互,读写设备的数据信息,然后返回给设备管理器,再返回给应用程序。

(7)      当应用程序不再使用该设备时,它可调用CloseHandle()将设备关闭。此时调用的是驱动程序中的IIC_Close()函数。

l        驱动目录组织

AT24LC08读写驱动中,根据访问和交互的对象不同,将驱动程序目录组织如下:

Ø        IICBus.c              用于和用户态程序的交互,提供流驱动接口函数;

Ø        IIC2440.c             S3C2440的硬件交互,完成寄存器的配置;

Ø        24LC08.c                    AT24LC08芯片操作,按照芯片的读写模式配置。

1.4.3 流驱动接口函数的实现

      在本驱动中,定义前缀为“IIC”,IICBus.c文件中的流接口驱动函数都以该前缀命名,本驱动中流接口重点实现以下3个函数:

BOOL IIC_Init(DWORD dwcontext)

BOOL IIC_Deinit(DWORD hDeviceContext)

BOOL IIC_IOControl(DWORD hOpenContext, DWORDdwCode,

                          EEPROM_INFO *pBufIn, DWORD dwLenIn,

                           U8 *pBufOut, DWORD dwLenOut, PDWORD pdwActualOut)

        IIC_Init()函数

该函数在设备加载时被调用,具体代码实现:

BOOL IIC_Init(DWORD dwcontext)

{

RETAILMSG(DBG_OUT,(TEXT("IIC_Init---/r/n")));

Virtual_Alloc();

Setup_IIC();

return TRUE;

}

Setup_IIC()函数完成一些端口的初始化,以及IIC总线寄存器的初始化配置。

Setup_IIC()

{

RETAILMSG(DBG_OUT,(TEXT("IIC_INTR_Gpio_setting ---/r/n")));

s2440IOP->rGPEUP  |=  0xc000;                         //Pull-up disable

s2440IOP->rGPECON=(s2440IOP->rGPECON&~(3<<30))|(2<< 30);  //GPE15=IICSDA.

s2440IOP->rGPECON=(s2440IOP->rGPECON&~(3<< 28))|(2<< 28);  //GPE14=IICSCL.

s2440IIC->rIICCON =(1<<7) |(0<<6) |(1<<5) | (0xf);          //Tx clock = 0.195MHz

      //[Bit7] = Enable ACK, [Bit6] = Prescaler IICCLK=PCLK/16, [Bit5] =Enable interrupt;

      //[Bit3:0] = Transmit clock value Tx clock=IICCLK/16

s2440IIC->rIICSTAT =0x10;                     //IICbus data output enable(Rx/Tx)

s2440IIC->rIICADD  = 0x10;                    //2440slave address = [7:1]

s2440IIC->rIICLC =(1<<2)|(3);              //Filter enable, 15 clocks SDA output delay

return TRUE;

}

       IIC_Deinit()函数

该函数在设备卸载时被调用,释放加载时申请的虚拟地址空间,具体代码实现:

      BOOL IIC_Deinit(DWORD hDeviceContext)

{

            RETAILMSG(DBG_OUT, (TEXT("IIC_INTR_Deinit --- /r/n")));

            VirtualFree((void*)s2440IOP, sizeof(IOPreg),MEM_RELEASE);

            VirtualFree((void*)s2440IIC, sizeof(PWMreg),MEM_RELEASE);

            return TRUE;

}

        IIC_IOControl()函数

该函数用于处理应用程序发送过来的控制命令,控制指令dwCodeIOCTL_IIC_READIOCTL_IIC_WRITE两种。当收到读指令IOCTL_IIC_READ时,保存读操作的起始地址和读取字节数,然后调用E2P_Read()函数读取AT24LC08上的数据,将实际读取的字节数保存到*pdwActualOut。同样,当收到写指令IOCTL_IIC_WRITE时,保存写操作的起始地址和写字节数,然后调用E2P_PageWrite()函数往AT24LC08上写数据,将实际写入的字节数保存到*pdwActualOut

BOOLIIC_IOControl(DWORD hOpenContext, DWORD dwCode,

EEPROM_INFO *pBufIn, DWORDdwLenIn,

U8 *pBufOut, DWORD dwLenOut,PDWORD pdwActualOut)

{

      RETAILMSG(DBG_OUT,(TEXT("IIC: +IIC_IOControl (%d) /r/n"),dwCode));

      switch(dwCode){

             case IOCTL_IIC_READ:

             {

                    U16 ReadAddress = pBufIn->start_address;                   

                    U8 ReadNum = (U8)dwLenOut;                        //ReadNum为要读取的字节数

                    *pdwActualOut = E2P_Read(ReadAddress, ReadNum, pBufOut);

                    return TRUE;

             }

             case IOCTL_IIC_WRITE:

             {

                    U16 WriteAddress =pBufIn->start_address;              

                    U8 WriteNum = (U8)dwLenIn;

                    *pdwActualOut = E2P_Write(WriteAddress, WriteNum,pBufIn->dwData);

                    return TRUE; 

             }

      }

      return TRUE;

}

 

E2P_Read()函数和E2P_Write()函数都是对AT24LC08操作的,在24LC08.c中实现,具体代码如下:

U8 E2P_Read(U16 Address, U8 Num, U8*DataRead)

{

      U8 DevAddr_W, DevAddr_R;

      U8 PageAddr, WordAddr;

      U8 ActualNum;

      int i;

 

      if(Address > END_ADDRESS)

      {

             RETAILMSG(1,(TEXT("IIC: START ADDRESS ERROR!!!")));

             return 0;

      }

      if((Address + Num) > END_ADDRESS)                         //超出存储器容量

      {

             ActualNum =(U8)(END_ADDRESS - Address + 1);

      }

      else

             ActualNum = Num;

             

      memset(&E2pInfo, 0, sizeof(IIC_INFO));

 

      WordAddr = (U8)(Address & 0x00ff);                             //获取字地址

      PageAddr = (Address >> 8)& 0x03;                               //获取A1, A0页地址

      DevAddr_W = 0xa0 | (PageAddr <<1);         //DevAddr = 0x 1 0 1 0 A2 A1 A0 R/W

 

      E2pInfo.iicMode     = SETRDADDR;

      E2pInfo.iicPt       = 0;

      E2pInfo.iicData[0]   =WordAddr;

      E2pInfo.iicDataCount = 1;

      E2pInfo.DeviceAddress = DevAddr_W;

 

      IIC_TX(&E2pInfo);

      

      memset(&E2pInfo, 0, sizeof(IIC_INFO));

 

      DevAddr_R = 0xa1 | (PageAddr <<1);

 

      E2pInfo.iicMode     = RDDATA;

      E2pInfo.iicPt       = 0;

      E2pInfo.iicDataCount = ActualNum;                              //读取的字节数

      E2pInfo.DeviceAddress = DevAddr_R;

 

      IIC_RX(&E2pInfo);

 

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

             *DataRead++ = *data++;

      return ActualNum;

}

      E2P_Read()函数功能如下:

(1)      判断读入的地址参数是否有效,若无效,则退出;

(2)      判断所读的数据长度是否会超出存储器的地址空间,截取实际能读取的数据长度保存;

(3)      将输入的16位地址转换成所需的8位器件地址和8位字地址;

(4)      把读写模式指令、器件地址、字地址、可读取的实际字节数保存到结构体E2pInfo中,传递给IIC_TX()函数和IIC_RX()函数实现IIC总线上的数据收发。

(5)      返回读取的实际字节数;

U8 E2P_Write(U16 Address, U8 Num, U8*DataToWrite)

{

      U8 *DataTmp, ActualNum;

      U16 Address_End;

      U8 FirstSector, SectorNum, EndSector;

      int i;

 

      if(Address > END_ADDRESS)

      {

             RETAILMSG(1,(TEXT("IIC: START ADDRESS ERROR!!!")));

             return 0;

      }

 

      if((Address + Num) > END_ADDRESS)                         //超出存储器容量

      {    

             ActualNum = (U8)(END_ADDRESS - Address + 1);//获取实际能写入的字符数

      }

      else

             ActualNum = Num;

 

      FirstSector = MAX_BUF - (U8)(Address & 0x000f) ;//计算第一页能存的字节数

      if(ActualNum > FirstSector)                    //判断是否能在第一页存完数据

      {

             SectorNum = (ActualNum - FirstSector) / MAX_BUF;

             DataTmp = DataToWrite + FirstSector;      

             E2P_PageWrite(Address, FirstSector, DataToWrite);         //写第一页数据

             for(i = 0; i < SectorNum; i++)                          //按每页16B 写数据

E2P_PageWrite(Address + FirstSector + i* MAX_BUF,MAX_BUF, DataTmp + i*MAX_BUF);

             Address_End = Address + FirstSector + SectorNum*MAX_BUF;//获取末页地址

             EndSector = (ActualNum - FirstSector) %MAX_BUF;//获取末页待写字节数

             E2P_PageWrite(Address_End, EndSector, DataTmp +SectorNum*MAX_BUF);

      }

      else

             E2P_PageWrite(Address, ActualNum, DataToWrite);         //写第一页数据

 

      return ActualNum;

}

      E2P_Write()函数功能如下:

(1)      判断写操作起始地址参数是否有效,若无效,则退出;

(2)      判断写数据长度是否会超出存储器的地址空间,截取实际能写的数据长度保存;

(3)      计算起始地址所在页能写的字节数;

(4)      判断实际要写的数据数是否超出第一页,如没有,则直接调用E2P_PageWrite()函数完成页写操作;

(5)      若超出,则判断需分几页写,分别调用E2P_PageWrite()函数按页写数据;

(6)      返回实际写入的数据字节数。

 

void E2P_PageWrite(U16 Address, U8 Num, U8*SectorData)

{

      U8 DevAddr_W;

      U8 PageAddr, WordAddr;

      int i;

 

      memset(&E2pInfo, 0, sizeof(IIC_INFO));

      

      WordAddr = (U8)(Address & 0x00ff);                             //获取字地址

      PageAddr = (Address >> 8)  & 0x03;                            //获取A1, A0页地址

      DevAddr_W = 0xa0 | (PageAddr <<1);             //DevAddr = 0x 1 0 1 0 A2 A1 A0 R/W

      

      E2pInfo.iicMode     = WRDATA;

      E2pInfo.iicPt       = 0;

      E2pInfo.iicData[0]   =WordAddr;

      E2pInfo.DeviceAddress = DevAddr_W;

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

      {

             E2pInfo.iicData[i+1]  =*(SectorData + i);

      }

      E2pInfo.iicDataCount = 1+Num;

 

      IIC_TX(&E2pInfo);

}

E2P_PageWrite()函数主要完成地址转换和传递结构体E2pInfo的过程,然后调用IIC_TX()函数实现IIC总线上的数据发送。

IIC_TX()函数和IIC_RX()函数在IIC2440.c中实现,代码如下:

void IIC_TX(IIC_INFO *piicinfo)

{

      g_iicinfo = piicinfo;

 

      s2440IIC->rIICDS   =g_iicinfo->DeviceAddress;

      s2440IIC->rIICSTAT = 0xf0;                   //MasTx,Start 

      s2440IIC->rIICCON    = 0xAf;

      //Clearing the pending bit isn't needed because the pending bit hasbeen cleared.

      while(g_iicinfo->iicDataCount!=-1)

             Run_IICPoll();

      return;

}

 

void IIC_RX(IIC_INFO *piicinfo)

{

      g_iicinfo = piicinfo;

 

      s2440IIC->rIICDS =g_iicinfo->DeviceAddress;

      s2440IIC->rIICSTAT = 0xb0;                                  //MasRx,Start

      s2440IIC->rIICCON = 0xaf;                            //Resumes IIC operation.  

      while(g_iicinfo->iicDataCount!=-1)

        Run_IICPoll();

      

      data = g_iicinfo->iicData + 1 ; 

      return;

}

这两个函数都是完成和S3C2440IIC寄存器相关的操作,按照IIC协议的时序依次往寄存器中器件地址和相关值,然后调用Run_IICPoll()函数逐个字节传递数据。

void Run_IICPoll(void)

{

      // When using polling mode

   if(s2440IIC->rIICCON &0x10)                 //Tx/Rx Interrupt Enable

        IICPoll();

} 

 

void IICPoll(void)

{

   switch(g_iicinfo->iicMode)

   {

       case RDDATA:

           if((g_iicinfo->iicDataCount--)==0)

           {

               g_iicinfo->iicData[g_iicinfo->iicPt++]= s2440IIC->rIICDS;

               s2440IIC->rIICSTAT = 0x90;     //Stop MasRx condition

               s2440IIC->rIICCON  = 0xAf;     //Resumes IIC operation.

               Delay(10);      //Wait until stop condtion is in effect., Too longtime...

                         //The pending bit will notbe set after issuing stop condition.

               break;   

           }     

           g_iicinfo->iicData[g_iicinfo->iicPt++]= s2440IIC->rIICDS;    

//The last data has to be read with noack.

           if((g_iicinfo->iicDataCount)==0)

               s2440IIC->rIICCON = 0x2f;   //Resumes IIC operation with NOACK

           else

               s2440IIC->rIICCON = 0xAf;   //Resumes IIC operation withACK

           break;

       case WRDATA:

           if((g_iicinfo->iicDataCount--)==0)

           {

               s2440IIC->rIICSTAT = 0xd0;           //stop MasTx condition

               s2440IIC->rIICCON  = 0xAf;          //resumesIIC operation.

               Delay(10);                             // we should adjust this time.

                         //Thepending bit will not be set after issuing stopcondition.

               break;    

           }

         

             s2440IIC->rIICDS=g_iicinfo->iicData[g_iicinfo->iicPt++];

             Delay(1);                       //forsetup time until rising edge of IICSCL

             s2440IIC->rIICCON = 0xAf;                          //resumes IICoperation.

       break;

       case SETRDADDR:

           if((g_iicinfo->iicDataCount--)==0)

           {                 

                           break;              //IIC operation is stoppedbecause of IICCON[4]

           }

           s2440IIC->rIICDS =g_iicinfo->iicData[g_iicinfo->iicPt++];

                    Delay(1);                   //for setup time until rising edge ofIICSCL

                    s2440IIC->rIICCON = 0xAf;                  //resumesIIC operation.

           break;

       default:

           break;     

   }

}

S3C2440rIICCON寄存器中断位使能,Run_IICPoll()函数调用IICPoll()函数。IICPoll()函数实现三种模式的数据传输:

1SETRDADDR

当执行读操作时,主机往从机写入第一个地址时使用该模式,只写两个字节数据(器件地址和字地址),然后退出。

2RDDATA

当执行读操作,主机发完起始地址后,进入RDDATA模式,地址依次累加读取AT24LC08上的数据,最完最后一个字节时,回复NACK,主机收到后发STOP信号退出。

2WRDATA

当执行写操作时进入WRDATA模式,主机往从机发送器件地址和字地址,然后地址依次累加往AT24LC08写入数据,最后主机发STOP信号退出。

 

 

 

1.4.4 添加makefile文件和Source文件

(一)makefile文件

             !INCLUDE $(_MAKEENVROOT)/makefile.def

(二)Source文件

      RELEASETYPE=PLATFORM

TARGETNAME=IICBus

TARGETTYPE=DYNLINK

DLLENTRY=DllEntry

TARGETLIBS= /

 $(_COMMONSDKROOT)/lib/$(_CPUINDPATH)/coredll.lib /

 

MSC_WARNING_LEVEL=$(MSC_WARNING_LEVEL) /W3/WX

INCLUDES= /

 $(_TARGETPLATROOT)/inc; /

 $(_COMMONOAKROOT)/inc; /

 $(_PUBLICROOT)/common/oak/inc;$(_PUBLICROOT)/common/sdk/inc;$(_PUBLICROOT)/common/ddk/inc;/

  ../../inc/

 

SOURCES= /

  24LC08.c/

  IIC2440.c/

  IICBus.c/

 

FILE_VIEW_INCLUDES_FOLDER= /

  IIC.h/

  IIC2440.h/

  24LC08.h/

1.4.5编写DLL的导出函数定义文件

.DEF文件定义了DLL的导出函数列表。在IIC中添加一个文本文件,命名为IICBus.def,然后在该文件中输入如下内容:

LIBRARY IICBus

 

EXPORTS

      IIC_Init

      IIC_Deinit

      IIC_Open

      IIC_Close

      IIC_IOControl

      IIC_PowerUp

      IIC_PowerDown

      IIC_Read

      IIC_Write

      IIC_Seek

1.4.6配置注册表

platform.reg中添加如下内容:

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/IICBus]

      "Dll" = "IICBus.dll"

      "Prefix" = "IIC"

      "Index" = dword:1

      "Order" = dword:0

另外,还要在platform.bib中添加如下内容:

      IICBus.dll                           $(_FLATRELEASEDIR)/IICBus.dll                   NK  SH

原创粉丝点击