1 I2C 通信协议及S3C2410 芯片介绍
I2C(Inter Integrated Circuit) 总线是1980 年由Philips 公司推出的。 I2C 总线用两条线(SDA 和SCL )在总线和装置之间传递信息,在微控制器和外部设备之间进行串行通信或在主设备和从设备之间进行双向数据传送。两条通信线通过上拉电阻被拉升至+5 V 。在控制系统中的每个集成电路可以通过一个CMOS 缓冲器来读每一条线路,也可以通过一个栅极开路的FET 管将每一条线的电平下拉。因此,对每个芯片来 说,每条线既是输入线,又是输出线。
I2C 总线遵从同步串行传输协议,即各位串行(一位接一位)发送,由时钟(clock )线指示读数据(data )线的时刻。每个数据包前有一个地址,以指示由哪个器件来接收该数据。
S3C2410 是一款基于ARM920T 的16/32 位RISC 微处理器,主要用于手持设备,拥有高性价比,低功耗等特点,也是目前市面上出现较多的嵌入式开发板的处理器之一。芯片拥有16 KB 的指令和数据缓存器,有存储管理单元(MMU )、LCD 控制器、3 个串口、4 路DMA 、4 个时钟定时器、8 路10 位的A/D 转换;支持I2C 、 I2S 、SPI 、主从USB 等接口以及SD/MMC 卡。
S3C2410 微处理器的I2C 总线可以处于下面4 种模式下:主接收模式、主发送模式、从接收模式和从发送模式。处理器对I2C 进行的操作,主要是对下面的几个寄存器进行读/ 写:
◇ IIC 控制寄存器,IICCON (物理地址0X54000000 ,内存映射后的虚拟地址);
◇ IIC 控制/ 状态寄存器,IICSTAT (物理地址0X54000004 );
◇ IIC 数据寄存器,IICDS (物理地址0X54000008 );
◇ IIC 地址寄存器,IICADD (物理地址0X5400000C )。
本设计主要是CPU 工作在主模式下与下面的I2C 接口设备进行通信。
2 Windows CE 系统驱动特点
Windows CE.net 驱动有两种模型:本机设备驱动程序和流接口驱动程序。本机设备驱动适于集成到基于Windows CE.net 平台的设备。这些设备驱动程序是一些硬件所必需的,是由原始设备制造商创建的,用以驱动如键盘、触摸屏、音频设备等,往往在设备售出后就不会再更换,如通用LED 驱动、电源驱动、键盘驱动和显示驱动等都是本机设备驱动。对于本机设备驱动程序,Platform Builder 提供了一些驱动程序样本,目的是为了方便开发人员快速开发出自己的驱动程序。当Win CE 系统启动时, 本地设备驱动程序将被加载到系统的内存中。本地驱动程序的开发分为分层驱动和单片驱动程序。分层驱动要利用微软提供的与应用程序通信的上层,称为模块驱动程序层MDD (Model Device Driver )。MDD 层通过设备驱动程序接口DDI (Device Driver Interface )与应用程序通信,开发驱动程序通常不修改MDD 层,主要关心与具体硬件相关的下层,依赖平台的设备驱动程序层 PDD (Platform Dependent Driver ), PDD 层通过设备驱动服务接口(Device Driver Service Provider Interface )直接管理硬件。流接口设备驱动程序(指可安装的启动程序)可以由第三方生产商提供,以支持添加到系统中的设备。Windows CE 下的设备驱动程序在与应用程序相同的保护级上工作。当系统启动时,大多数驱动程序是由设备管理进程(DEVICE.EXE )加载的,所有这些驱动程序 将共享同一个进程地址空间。
3 I2C 总线底层驱动设计
I2C 总线驱动是放在Windows CE 操作系统的内核下层,位于OEM Adaptation Layer (OAL )层的一个真正的驱动。
3.1 初始化I2C 中断和编写ISR 例程
I2C 的通信是通过操作I2C 的寄存器进行的。在I2C 通信中主要对上面介绍的4 个寄存器进行读写。通过读写这些寄存器中的命令状态字可以检测和控制I2C 总线的行为。在Windows CE.net 下,首先要在文件oalintr.h 添加I2C 的中断号的宏定义:
#defineSYSINTR_I2C(SYSINTR_FIRMWARE+19)
然后在文件cfw.c 的文件中添加I2C 中断的初始化,禁止和复位。具体代码如下:
在OEMInterruptEnable 函数中加入
case SYSINTR_IIC:
s2410INT->rSRCPND=BIT_IIC;
if (s2410INT->rINTPND & BIT_IIC) s2410INT->rINTPND = BIT_IIC;
s2410INT->rINTMSK&= ~BIT_IIC;
break;
在OEMInterruptDisable 函数中加入
case SYSINTR_IIC:
s2410INT->rINTMSK|= BIT_IIC;
break;
在OEMInterruptDone 函数中加入
case SYSINTR_IIC:
s2410INT->rSRCPND=BIT_IIC;
if (s2410INT->rINTPND & BIT_IIC) s2410INT->rINTPND = BIT_IIC;
s2410INT->rINTMSK&= ~BIT_IIC;
break;
在armint.c 文件中添加ISR 程序,处理中断发生后返回定义的中断号。具体代码如下:
在OEMInterruptHandler 函数中添加
else if (IntPendVal == INTSRC_IIC) {
s2410INT->rSRCPND= BIT_IIC; /* 清除中断 */
if (s2410INT->rINTPND & BIT_IIC) s2410INT->rINTPND= BIT_IIC;
s2410INT->rINTMSK|= BIT_IIC; /* I2C 中断禁止 */
return (SYSINTR_RTC_ALARM);
}
3.2 编写流驱动程序
I2C 总线驱动程序采用的是Win CE 流驱动的标准形式。在IIC_Init 的函数中,首先通过函数VirtualAlloc ()和VirtualCopy (),把芯片中针对I2C 的物理 地址和操作系统的虚存空间联系起来,对虚拟地址空间的操作就相当于对芯片的物理地址进行操作。地址映射的代码如下:
reg = (PVOID)VirtualAlloc(0, sz, MEM_RESERVE, PAGE_NOACCESS);
if (reg) {
if (!VirtualCopy(reg, addr, sz, PAGE_READWRITE | PAGE_NOCACHE )) {
RETAILMSG( DEBUGMODE,( TEXT( "Initializing interrupt \\n\\r" ) ) );
VirtualFree(reg, sz, MEM_RELEASE);
reg = NULL;
}
}
其中sz 是申请的长度,addr 是申请虚拟地址空间的实际物理地址在Win CE 中的映射地址。
然后对申请到的虚拟地址进行操作,安装Windows 中的流驱动的模型进行驱动的编写,主要包括下面函数的编写。
IIC_Init ()
在函数中,主要是对I2C 的初始化,主要语句如下:
v_pIICregs = ( volatile IICreg *)IIC_RegAlloc((PVOID)IIC_BASE, sizeof(IICreg));
v_pIOPregs = ( volatile IOPreg *)IOP_RegAlloc((PVOID)IOP_BASE, sizeof(IOPreg));
v_pIOPregs->rGPEUP|= 0xc000;
v_pIOPregs->rGPECON |= 0xa00000;
v_pIICregs->rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);
v_pIICregs->rIICADD= 0x10;
v_pIICregs->rIICSTAT = 0x10;
VirtualFree( ( PVOID )v_pIOPregs,sizeof( IOPreg ),MEM_RELEASE );
v_pIOPregs = NULL;
if ( !StartDispatchThread( pIIcHead) )
{ IIC_Deinit( pIIcHead );return ( NULL );} 在StartDispatchThread() 函数中, 主要是创建线程、关联事件和中断, 主要语句如下:
InterruptInitialize( 36,pIicHead->hIicEvent,NULL,0 );// 关联时间和中断
CreateThread( NULL,0,IicDispatchThread,pIicHead,0,NULL );// 创建线程等待时间
在IicDispatchThread() 函数中, 主要是等待中断的产生, 然后去执行:WaitReturn = WaitForSingleObject( pIicHead->hIicEvent,INFINITE );
IicEventHandler( pIicHead );// 事件处理函数
InterruptDone( 36 );
最后,在函数IIC_Open 、IIC_Read 、IIC_Write 中,对各个寄存器进行操作,进行数据的赋值, 得到I2C 读取的数据和发送数据。
4 I2C 驱动的封装和添加到Windows CE 中
通过上面的工作,能编译一个DLL 函数,但这还不能叫流接口驱动程序。因为它的接口函数还没有导出,还需要告诉链接程序需要输出什么样的函数,为此要建立一个自己的def 文件,可以用记事本建一个,取名mydrive.Def :
LIBRARY MyDriver
EXPORTS
IIC_Close
IIC_Deinit
IIC_Init
IIC_IOControl
IIC_Open
IIC_PowerDown
IIC_PowerUp
IIC_Read
IIC_Seek
IIC_Write
然后同样用记事本编写一个注册表文件,取名为mydrive.reg:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\STRINGS]
"Index"=dword:1
"Prefix"="IIC"
"Dll"="MyDriver.dll"
"Order"=dword:0
最后编写自己的CEC 文件。主要是添加一个Build Method ,任务是复制注册表到Win CE 的系统目录下面。加一个Bib File ,其主要功能是把编译的mydrive.dll 文件添加到系统内核中去。保存写好的CEC 文件。打开Platform Builder ,打开“File” 菜单,添加刚刚编写的CEC 特征到系统选项中去。生成系统的时候,添加自己的CEC 特性,就可以包含刚刚编写的I2C 驱 动了。
以上介绍了Win CE 的驱动结构,并给出了基于Win CE 的 I2C 驱动程序部分源代码。实验证明该设计是可行的。
1.1 初始化IIC 中断和编写ISR 例程
IIC 的通信是通过操作IIC 的寄存器进行的。在IIC 通信中主要对上面介绍的4 个寄存器进行读写。通过读写这些寄存器中的命令状态字可以检测和控制IIC 总线的行为。在Windows CE.NET 下,我们首先要在文件oalintr.h 添加IIC 的中断号的宏定义:
#define SYSINTR_IIC (SYSINTR_FIRMWARE+19)
然后在文件cfw.c 的文件中添加IIC 中断的初始化,禁止和复位。
在OEMInterruptEnable 函数中,通过向中断寄存器rSRCPND 的第27 位写入1 表示IIC 中断源有中断请求产生。向中断中断寄存器rINTMSK 的第27 位写入0 则将IIC 中断标记为可处理状态。这样就实现了IIC 中断的初始化。在OEMInterruptDisable 函数中,通过向中断寄存器rINTMSK 的第27 位写入1 实现了IIC 中断的禁止。
IIC 驱动程序需要建立一个事件,可以使用CreateEvent 函数实现,然后调InterruptInitialize 将该事件与IIC 中断号绑定,则使能该中断,OAL 中的OEMInerrupteEnable 就会被调用,如果该函数不返回true 的话,InterruptInitialize 就会失败。然后驱动程序中的IST 就可以使用WaitForSingleObject 函数来等待中断的发生。
当一个IIC 中断发生之后,操作系统陷入异常,中断向量指示进入CE 的异常处理程序,该异常处理程序然后调用OAL 的OEMInterruptHandler 函数,该函数检测硬件之后,将硬件中断转换为软件的中断号,返回给系统。该中断号就是上面的InterruptInitialize 中使用的那个中断号。系统得到该中断号之后,就会找到该中断号对应的事件,并唤醒等待相应事件的线程(IST) ,然后IST 就可以在用户态进行中断处理。处理完成之后,IST 需要调用InterruptDone 来告诉操作系统中断处理结束,操作系统再次调用OAL 中的OEMInterruptDone 函数,最后完成中断的处理。
1.2 编写流驱动程序
本IIC 驱动程序采用的是Windows CE 流驱动的标准形式编写,在IIC_Init 的函数中,通过函数VirtualAlloc ()和VirtualCopy (),把芯片中针对IIC 的物理地址和操作系统的虚存地址空间联系起来,对虚拟地址空间的操作就相当于对芯片的物理地址进行操作。
然后对申请到的虚拟地址进行操作,安装Windows 中的流驱动的模型进行驱动的编写,主要包括下面的个几个函数的编写:
1 . IIC_Init( ) :对IIC 进行初始化,在设备管理器加载IIC 驱动后首先调用,用于初始化所需的变量,硬件设备等资源。该过程分配代表设备硬件实例的数据结构,并通过硬件抽象接口HWInit 初始化硬件。同时该函数会调用InterruptInitialize 为接收内核中的逻辑中断创建相应事件并初始化临界区。2 .StartDispatchThread( ) :主要是创建线程, 关联事件和中断, 主要语句如下:
InterruptInitialize( 36,pIicHead->hIicEvent,NULL,0 );// 关联事件和中断
CreateThread( NULL,0,IicDispatchThread,pIicHead,0,NULL );// 创建线程等待时间。
如果StartDispatchThread() 执行后返回false, 则执行 IIC_Deinit() 卸载IIC 驱动。
3 . IicDispatchThread( ) :主要是等待中断的产生, 然后去执行
WaitReturn = WaitForSingleObject( pIicHead->hIicEvent,INFINITE );
IicEventHandler( pIicHead ); // 事件处理函数
InterruptDone( 36 );
最后,在函数IIC_Open ,IIC_Read ,IIC_Write 中,对各个寄存器进行操作,进行数据的赋值。得到IIC 读取的数据和发送速据。
2 IIC 驱动的封装和添加到Windows CE 中
通过上面的工作,我们能编译一个DLL 函数,但是这个还不能叫流接口驱动程序,因为它的接口函数还没有导出,我们还需要告诉链接程序需要输出什么样的函数,为此要建立一个自己的def 文件,可以用记事本程序编辑一个文件名为“mydrive.Def” 的文件。该文件做的工作就是在EXPORTS 段后面列出要从DLL 中输出的函数的名称。在LIBRARY 后面必须加上要编译文件的实际名,然后将这个文件添加到流接口驱动程序的工程里面。
然后同样用记事本编写一个注册表文件,取名为“mydrive.reg” ,并保存到流接口驱动程序的目录里,下面是这个注册表文件的内容。
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\STRINGS]
"Index"=dword:1
"Prefix"="IIC"
"Dll"="MyDriver.dll"
"Order"=dword:0
最后编写自己的CEC 文件。主要任务是添加一个Build Method ,任务是copy 注册表到Window CE 的系统目录下面。接着添加一个Bib File ,其主要功能是把编译的mydrive.dll 文件添加到系统内核中去。然后保存写好的CEC 文件。打开Platform Build ,打开“File” 菜单,添加刚刚编写的CEC 特征到系统选项中去。最后生成系统的时候,添加自己的CEC 特性,就可以包含刚刚编写的IIC 驱动了
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cfanlwn/archive/2009/12/04/4939786.aspx
总线的构成及信号类型
I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程中,I2C总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂在同一条总线上,却彼此独立,互不相关。
I2C总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。
目前有很多半导体集成电路上都集成了I2C接口。带有I2C接口的单片机有:CYGNAL的 C8051F0XX系列,PHILIPSP87LPC7XX系列,MICROCHIP的PIC16C6XX系列等。很多外围器件如存储器、监控芯片等也提供I2C接口
I2C 总线是一种用于IC器件之间连接的双向二线制总线,所谓总线它上面可以挂多个器件,并且通过两根线连接,占用空间非常的小,总线的长度可长达25英尺,并且能够以10Kbps的最大传输速率支持4个组件。它的另一优点是多主控,只要能够进行接收和发送的设备都可以成为主控制器,当然多个主控不能同一时间 工作。
I2C总线有两根信号线,一根为SDA(数据线),一根为SCL(时钟线)。任何时候时钟信号都是由主控器件产生。
I2C规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。 总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。
控制字节
在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。
写操作
写操作分为字节写和页面写两种操作,对于页面写根据芯片的一次装载的字节不同有所不同。
读操作
读操作有三种基本操作:当前地址读、随机读和顺序读。图4给出的是顺序读的时序图。应当注意的是:最后一个读操作的第9个时钟周期不是“不关心”。为了结束读操作,主机必须在第9个周期间发出停止条件或者在第9个时钟周期内保持SDA为高电平、然后发出停止条件。
在这里,我们可以用器件的i2c接口链接到i2c总线上,也可以用主控芯片的GPIO端口来模拟i2c接口。
I2C数据格式如下:
无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。
当数据为单字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。
需要注意的是:
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,不发送ACK,有时可以起到减少系统开销的效果。