WINCE下IIC接口FLASH驱动

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

1.4 AT24LC08 读写流驱动开发

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

1.4.1 A T24LC08 工作原理

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

1 AT24LC08 主要引脚描述

引脚名称

功能

A0 A1 A2

地址输入

SDA

串行数据

SCL

串行时钟输入

WP

写保护

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

l         器件寻址

AT24LC08 寻址的地址部分包括: 8bit 的器件地址和 8bit 的字地址。器件地址的各 bit 位分布如表 2 所示。前四位是由芯片厂商确定的,为固定值。 A2 必须和硬件的输入管脚一致。 P1 P0 为页选地址。 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 位字地址。器件地址最末位为 WRITE 0 )。从设备验证完地址并发回 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)       应用程序须使用该设备,首先调用 CreateFile TEXT (“ 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, DWORD dwCode,

                            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;                       //IIC bus data output enable(Rx/Tx)

s2440IIC->rIICADD  = 0x10;                       //2440 slave 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 ()函数

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

BOOL IIC_IOControl(DWORD hOpenContext, DWORD dwCode,

EEPROM_INFO *pBufIn, DWORD dwLenIn,

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 has been 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;

}

这两个函数都是完成和 S3C2440 IIC 寄存器相关的操作,按照 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 long time...

                           //The pending bit will not be set after issuing stop condition.

                break;   

            }     

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

//The last data has to be read with no ack.

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

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

            else

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

            break;

        case WRDATA:

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

            {

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

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

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

                            //The pending bit will not be set after issuing stop condition.

                break;    

            }

         

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

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

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

        break;

        case SETRDADDR:

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

            {                 

                            break;                //IIC operation is stopped because of IICCON[4]

            }

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

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

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

            break;

        default:

            break;     

    }

}

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

1 SETRDADDR

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

2 RDDATA

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

2 WRDATA

当执行写操作时进入 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值