一、定义
Inter-Integrated Circuit ,串行通信
多个I2C模块连接,实现设备间的双向传输;每个设备都有都有唯一的地址,均可发送和接受数据
主设备:在总线上发起数据传输并产生时钟信号以允许传输的设备。
从设备:在这个传输过程中,由这个主设备寻址的任何设备
SDA和SCL都必须上拉,使其空闲时都为高电平
1.1 特点
•符合NXP Semiconductors I2C总线规范(版本2.1):
-支持8位格式传输- 7位和10位寻址模式
-广播功能(地址0000)
- START字节模式
-支持多个主发射机和从接收机
-支持多个从发射机和主接收机
-组合主发送/接收和接收/发送模式
-数据传输速率从10kbps到400kbps(快速模式)
•接收FIFO和发送FIFO (16字节byte)
•支持两个ePIE中断:
- I2Cx中断
-以下任何事件都可以配置为生成I2Cx中断:
•发送数据准备就绪•接收数据准备就绪
•寄存器访问准备就绪
•接受无应答
•仲裁丢失
•检测到停止条件
•被定义为从机
•发送FIFO中断(先进先出)
•接收FIFO中断
•模块启用/禁用能力
•自由数据格式模式
I2C模块不支持:•高速模式(Hs-mode)•cbus兼容模式
有两种主要的转移技术:。
1.2 功能
•标准模式:准确发送n个数据值,其中n是您在I2C模块寄存器中编程的值。有关更多信息,请参见第11.6节。
•重复模式:保持发送数据值,直到使用软件启动停止条件或新的开始条件。RM位信息请参见寄存器。
I2C模块由以下主要模块组成:
数据寄存器和fifo暂时保存SDA引脚和CPU之间的接收数据和传输数据
•控制和状态寄存器
•外设总线接口,使CPU能够访问I2C模块寄存器和fifo。
•一个时钟的同步器同步I2C输入时钟(从设备时钟发生器)和sci销上的时钟,和同步数据传输的主人不同的时钟速度
•预定标器分下来的输入时钟驱动的I2C模块
•噪声滤波器的两个别针,SDA和SCL
•仲裁员处理仲裁I2C模块之间时(主),另一个主人
•中断生成逻辑,
•FIFO中断生成逻辑,使FIFO访问可以同步到I2C模块的数据接收和数据传输
I2CDXR:发送寄存器 I2CXSR:移位寄存器(串行)
I2CRSR:接受移位寄存器 I2CDRR:接受寄存器
可以看出发送和接受在一根线完成的(不能同时,半双工)
当I2C模块配置为发送器时,写入I2CDXR的数据被复制到I2CXSR,并在SDA引脚上每次移出一位。
当I2C模块配置为接收端时,接收到的数据先被转移到I2CRSR,然后再被复制到I2CDRR。
1.3 时钟生成
在IRS=0初始化分频器,IRS=1生效 ,在IRS = 1时改变IPSC值没有效果。
1.4 引脚配置
必须配置GPIO mux寄存器以将该外设连接到设备引脚。
一些IO功能是由独立于外设的GPIO寄存器设置定义的。对于输入信号,应该通过将适当的GPxQSELn寄存器位设置为11b来将GPIO输入限定设置为异步模式。内部上拉可以在GPyPUD寄存器中配置。
1.5 I2C原理
1.5.1 数据有效性
1.5.2 模式
主机发送(一个或者多个从设备),先发送地址信息,再发送数据信息,完毕SCL=0(无效) |
主机接收,先发地址给从机,匹配,从机发送数据,主机接受,SCL=0 |
从机发送,主模块识别地址,发送响应信号,完毕,SCL=0 |
从机接受主机信息反馈,完毕SCL=0 |
Note: 从模式不产生SCL信号,发送或者接受完毕SCL=0
从接受:所有从机接受主机数据
从发送:主机的地址许可
重要信号:起始START 、停止STOP、地址信息ADDR(地址帧)、数据信息(数据帧)、反馈信息ACK
- Slave-receiver模式
I2C模块是从属模块,从主模块接收数据。
所有的从属都以这种模式开始。 SDA上接收到的串行数据位与主控产生的时钟脉冲一起移位。 从机I2C模块不产生时钟信号,但在接收到一个字节后需要设备的干预(I2CSTR中的RSFULL = 1)时,它可以保持SCL低。(RSFULL只读寄存器,超限状态,读不过来了) -
Slave-transmitter模式
I2C模块作为从属模块,负责向主站传输数据。
该模式只能从从端-接收端模式进入;I2C模块必须首先从主机接收命令。当您使用任何7位/10位寻址格式时,如果从地址字节与自己的地址相同(在I2COAR中)并且主设备已传输R/W = 1,则I2C模块进入其从发射机模式。作为一个从发射机,I2C模块然后用主控产生的时钟脉冲将串行数据转移到SDA上。作为从机,I2C模块不产生时钟信号,但在传输一个字节后,当需要设备的干预(I2CSTR中的XSMT = 0)时,它可以保持SCL低。发送移位寄存器空位。 -
Master-receiver模式
I2C模块是主模块,从模块接收数据。
此模式只能从主发送器模式进入;I2C模块必须首先向从机发送命令。当您使用任何7位/10位寻址格式时,I2C模块在发送从地址字节和R/W = 1后进入其主接收模式。SDA上的串行数据位与SCL上I2C模块产生的时钟脉冲一起移位到I2C模块。当接收到一个字节后,需要设备的干预(在I2CSTR中RSFULL = 1)时,时钟脉冲被抑制,SCL保持在低位。 -
Master-transmitter模式
I2C模块作为主控模块,将控制信息和数据传输到从站。
所有大师都以这种模式开始。在这种模式下,以任何7位/10位寻址格式组装的数据在SDA上被移出。位移与SCL上I2C模块产生的时钟脉冲同步。当一个字节被传输后,需要设备的干预(在I2CSTR中XSMT = 0)时,时钟脉冲被抑制,SCL保持在低电平
总而言之,SCL将在以下情况下保持低:•当检测到溢出条件(RSFULL = 1)时,在从端接收模式下。
•当检测到底流条件(XSMT = 0)时,处于从发射机模式。
当I2C主节点请求数据时,I2C从节点必须接受并提供数据。
•从接收模式下释放SCL,从I2CDRR读取数据。
•在从发射机模式下释放SCL,将数据写入I2CDXR。
•要在不处理数据的情况下强制释放,请使用I2CMDR.IRS重置模块。
1.5.3 I2C启停条件
当模块被配置为I2C总线上的master时,I2C模块可以产生START和STOP条件。
•START条件定义为SDA线上从高到低的转换,而SCL是高的。主机驱动这个条件来指示数据传输的开始。(启动信号产生后,总线忙)
•停止条件定义为SDA线上从低到高的转变,而SCL是高的。主机驱动此条件以指示数据传输的结束(停止条件产生后,总线闲)
在START条件之后和随后的STOP条件之前,I2C总线被认为是忙的,并且I2CSTR的总线忙(BB)位为1。
在STOP条件和下一个START条件之间,总线被认为是空闲的,并且BB为0。
为了使I2C模块在start条件下启动数据传输,I2CMDR中的主模式位(MST)和start条件位(STT)必须都为1。
为了使I2C模块以停止条件结束数据传输,停止条件位(STP)必须设置为1。当BB位设为1,STT位设为1时,会产生一个重复的START条件。
当I2C外设复位(IRS = 0)时,I2C外设无法检测到START或STOP条件,当I2C外设复位(IRS = 0)时,BB位将保持在清除状态(BB = 0),当I2C外设从复位状态(IRS设为1)中取出时,BB位将无法正确反映I2C总线状态,直到检测到START或STOP条件
在开始使用I2C进行第一次数据传输之前,请遵循以下步骤:
1.通过将IRS位设置为1,将I2C外设从复位状态中取出后,等待的时间比应用程序中最长数据传输的总时间要长。通过等待I2C复位后的一段时间,用户可以确保I2C总线上至少发生了一个START或STOP条件,并被BB位捕获。在这段时间后,BB位将正确反映I2C总线的状态。
2. 检查BB位,确认BB = 0(总线不忙)后再继续。
3. 开始数据传输。
使用I2C总线传输数据的示例如图11-7所示。I2C模块支持1 ~ 8位数据值。图11-7中传输的是8位数据。SDA线上的每个位相当于SCL线上的1个脉冲,并且这些值总是首先传输最高有效位(MSB)。可以传输或接收的数据值的数量不受限制。图11-7中使用的串口数据格式为7位寻址格式。I2C模块支持的格式如图11-8 ~图11-10所示,图中各段落将对此进行说明。
1.5.3.1 7位地址形式(总线的寻址方式)
7位寻址格式是复位后的默认格式。在一个字节
禁用扩展地址(I2CMDR)。XA = 0)
自由数据格式(I2CMDR)。FDF = 0)启用7位寻址格式。
在这种格式中(如图11-8所示),START条件(S)后的第一个字节由一个7位的从地址和一个读写位组成。R/W决定数据的方向:
•R/W = 0: I2C主站写(发送)数据到指定的从站。这可以通过设置I2CMDR来实现。TRX = 1(发送模式)
•R/W = 1: I2C主站从从站读取(接收)数据。这可以通过设置I2CMDR来实现。TRX = 0(接收模式)
在每个字节之后插入一个专门用于确认(ACK)的额外时钟周期。如果ACK位是由从机插入到主机的第一个字节之后,那么它后面是来自发送器的n位数据(主或从机,取决于R/W位)。n为1 ~ 8之间的数字,由I2CMDR的BC字段决定。数据位传输完成后,接收方插入一个ACK位。
1.5.3.2 10位地址形式
需要两个字节
可以通过设置扩展地址(I2CMDR)来启用10位寻址格式。XA = 1)和禁用自由数据格式(I2CMDR)。FDF = 0)。
10位的寻址格式(如图11-9所示)类似于7位的寻址格式,但是主站在两个单独的字节传输中发送从站地址。第一个字节由11110b、10位从地址的两个msb和R/W组成。第二个字节是10位从地址的剩余8位。
从服务器必须在每两个字节传输之后发送确认。一旦主服务器将第二个字节写入从服务器,主服务器既可以写入数据,也可以使用重复的START条件来更改数据方向。有关使用10位寻址的更多详细信息,请参阅NXP Semiconductors I2C总线规范。
1.5.3.3 自由数据形式
无地址,需要通信双方都支持,且传输方向恒定
可以通过设置I2CMDR启用免费数据格式。FDF = 1
在这种格式中(如图11-10所示),START条件(S)后面的第一个字节是一个数据字节。每个数据字节之后插入一个ACK位,根据I2CMDR的BC字段,ACK位可以是1到8位。不发送地址或数据方向位。因此,发送端和接收端都必须支持自由数据格式,并且在整个传输过程中数据的方向必须是恒定的。
数字环回模式(I2CMDR)不支持自由数据格式。DLB = 1)。
1.5.3.4 重复开始-传输模式
中间无STOP,连续的STart标志,区别于重复模式
I2C主机可以与多个从地址通信,而不必通过驱动STOP条件放弃对I2C总线的控制。这可以通过在每个数据类型的末尾驱动另一个START条件来实现。重复START条件可用于7位寻址、10位寻址和自由数据格式。图11-11显示了7位寻址格式的重复START条件。
n = I2CMDR中BC字段指定的数据位数(1 ~ 8)。
1.5.3.5 I2C数据传输
接收器收到一个完整数据字节后,有可能需要处理其他事情,无法立刻接收下一个字节,这时接收器可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器准备好接收下一个字节时,再释放SCL线为高电平,从而使数据传送可以继续进行。
1.6 应答响应
当I2C模块是接收器(主或从)时,它可以确认或忽略发送器发送的比特。为了忽略任何新位,I2C模块必须在总线上的确认周期中发送一个非应答(NACK)位。表11-5总结了告诉I2C模块发送NACK位的各种方法。
- Slave-receiver模式 •允许超限条件(在I2CSTR中RSFULL = 1) •复位模块(在I2CMDR中IRS = 0) •设置I2CMDR的NACKMOD位在您打算接收的最后一个数据位的上升沿之前
- 主接收模式和重复模式(I2CMDR中RM = 1) • 生成一个STOP条件(在I2CMDR中STP = 1) •复位模块(在I2CMDR中IRS = 0) •将I2CMDR的NACKMOD位设置在您打算接收的最后一个数据位的上升沿之前
- 主接收模式和非重复模式(在I2CMDR中RM = 0) •如果I2CMDR中STP = 1,允许内部数据计数器计数到0,从而强制停止条件 •如果STP = 0,使STP = 1产生停止条件 •复位模块(I2CMDR中IRS = 0)。 •将I2CMDR的NACKMOD位设置在您打算接收的最后一个数据位的上升沿之前
每传输一个字节数据后必须跟一个校验位。
校验位:应答(ACK)(低电平脉冲:继续发送)和非应答位(NACK)(高电平脉冲:结束):提醒发送端数据接受完成
1.7 时钟同步
如果一个设备拉下时钟线较长时间,结果是所有时钟生成器必须进入等待状态。通过这种方式,从设备使快速的主设备变慢,而慢速的设备创造足够的时间来存储接收到的字节或准备要传输的字节
1.8 仲裁
释放SDA线路高电平的第一个主发射机被另一个驱动SDA低电平的主发射机推翻。仲裁程序优先考虑传输二进制值最低的串行数据流的设备。如果两个或更多的设备发送相同的第一个字节,仲裁继续在随后的字节。
如果I2C模块是丢失的主端,则切换到从端-接收端模式,设置仲裁丢失(ARBL)标志,并生成仲裁丢失中断请求。
如果在串行传输期间仲裁程序仍在进行中,当重复的开始条件或停止条件被传输到SDA时,所涉及的主发送器必须在格式帧的相同位置发送重复的开始条件或停止条件。
不允许仲裁:
•重复的START条件和数据位
•停止条件和数据位
•重复的START条件和停止条件
1.9 数字环回模式
I2C模块支持称为数字环回的自测模式,通过在I2CMDR寄存器中设置DLB位来启用。在这种模式下,从I2CDXR寄存器发送的数据在I2CDRR寄存器中接收。数据遵循内部路径,经过n个周期到达I2CDRR,
其中:n = 8 * (SYSCLK) / (I2C模块时钟(Fmod))
发送时钟和接收时钟相同。在外部SDA引脚上看到的地址是I2COAR寄存器中的地址。数字环回模式下信号路由如图11-14所示。
自由数据格式(I2CMDR)。数字环回模式不支持FDF = 1)
1.10 I2C模块中断
1.10.1 基本的I2C模块中断
所有请求都通过仲裁器复用到单个I2C中断请求。每个中断请求在状态寄存器(i2str)中有一个标志位,在中断启用寄存器(I2CIER)中有一个启用位。当指定的事件之一发生时,将设置其标志位。如果相应的使能位为0,则中断请求被阻塞。如果使能位为1,则请求作为I2C中断转发给CPU。
I2C中断是CPU的可屏蔽中断之一。与任何可屏蔽中断请求一样,如果它在CPU中被正确启用,CPU将执行相应的中断服务例程(I2CINT1A_ISR)。I2C中断的I2CINT1A_ISR可以通过读取中断源寄存器I2CISRC来确定中断源。然后,I2CINT1A_ISR可以分支到适当的子例程。
CPU读到I2CISRC后,会发生如下事件。
1.源中断标志在I2CSTR中被清除。例外:读取I2CISRC时,不清除I2CSTR中的ARDY、RRDY和XRDY位。要清除其中一个位,给它写一个1。
2. 仲裁器确定剩余中断请求中哪一个具有最高优先级,将该中断的代码写入I2CISRC,并将中断请求转发给CPU。
- XRDYINT
传输就绪状态:数据传输寄存器(I2CDXR)已经准备好接受新数据,因为以前的数据已经从I2CDXR复制到传输移位寄存器(I2CXSR)。
作为使用XRDYINT的替代方案,CPU可以轮询状态寄存器I2CSTR的XRDY位。
在FIFO模式下不应该使用XRDYINT。使用FIFO中断代替。 -
RRDYINT
接收就绪状态:数据接收寄存器(I2CDRR)已经准备好被读取,因为数据已经从接收移位寄存器(I2CRSR)复制到I2CDRR。
作为使用RRDYINT的替代方案,CPU可以轮询I2CSTR的RRDY位。在FIFO模式下不应该使用RRDYINT。使用FIFO中断代替。 -
ARDYINT
寄存器访问就绪条件:I2C模块寄存器已经准备好被访问,因为先前编程的地址、数据和命令值已经被使用。
生成ARDYINT的特定事件与设置I2CSTR的ARDY位的事件相同。
作为使用ARDYINT的替代方案,CPU可以轮询ARDY位。 -
NACKINT
无确认条件:I2C模块被配置为主发送器,没有收到从接收器的确认。
作为使用NACKINT的替代方案,CPU可以轮询I2CSTR的NACK位。 -
ARBLINT
仲裁失败条件:I2C模块在与另一个主发射机的仲裁竞赛中失败。
作为使用ARBLINT的替代方案,CPU可以轮询I2CSTR的ARBL位。 -
SCDINT
停止状态检测到:在I2C总线上检测到一个停止状态。
作为使用SCDINT的替代方案,CPU可以轮询状态寄存器I2CSTR的SCD位。 -
AASINT
寻址为从属状态:I2C已经被I2C总线上的另一个主设备寻址为从属设备。
作为使用AASINT的替代方案,CPU可以轮询状态寄存器I2CSTR的AAS位。
I2C模块在I2CEMDR寄存器中有一个向后兼容位(BC)。图11-16中的时序图显示了当配置为从发射机时,向后兼容位对I2C模块寄存器和中断的影响。
1.10.2 I2C FIFO 中断
除了7个基本的I2C中断之外,发送和接收fifo每个都包含生成中断(I2CINT2A)的能力。发送FIFO可以配置为在发送定义的字节数(最多16个字节)后产生中断。接收FIFO可以配置为在接收到定义的字节数(最多16个字节)后产生中断。这两个中断源被合并成一个可屏蔽的CPU中断。I2C FIFO中断的结构如图11-17所示。
然后,中断服务程序可以读取FIFO中断状态标志,以确定中断来自哪个源。参见I2C发送FIFO寄存器(I2CFFTX)和I2C接收FIFO寄存器(I2CFFRX)的描述
1.11 模块复位/禁用
有两种复位或禁用I2C模块的方法:•将0写入I2C模式寄存器(I2CMDR)的I2C复位位(IRS)。所有状态位(在I2CSTR中)都被强制为默认值,并且I2C模块保持禁用状态,直到IRS更改为1。SDA和SCL引脚处于高阻抗状态。
•通过驱动XRS引脚低启动设备复位。整个设备复位,并保持在复位状态,直到你驱动引脚高。当XRS引脚被释放时,所有I2C模块寄存器被重置为其默认值。IRS位被强制为0,复位I2C模块。I2C模块一直处于复位状态,直到您将1写入IRS。
配置或重新配置I2C模块时,IRS必须为0。强制IRS为0可用于节省电力和清除错误条件。
二、寄存器
START到STOP结束记作一帧
I2CMDR,I2CCNT(FIFO)设置了每个字节包含的数据位数和一帧字节数
关键RM,STT,STP对传输模式的控制作用
I2CSAR配置从机地址;I2COAR,配置自身地址
I2CPSC,I2CCLKL,I2CCLKH,波特率配置寄存器
2.1 I2COAR
I2C自有地址寄存器(I2COAR)是一个16位寄存器。I2C模块使用这个寄存器来指定它自己的从地址,这将它与连接到I2C总线的其他从地址区分开来。如果选择7位寻址模式(I2CMDR中XA = 0),只有6-0位写入0到9-7位。
2.2 I2CIER
2.3 I2CSTR
2.4 I2CCLKL
2.5 l2CCLKH
2.6 I2CCNT
I2CCNT是一个16位寄存器,用于指示当I2C模块配置为发送器时要传输多少数据字节,或者配置为主接收器时要接收多少数据字节。在重复模式下(RM = 1),不使用I2CCNT。
写入I2CCNT的值被复制到内部数据计数器。内部数据计数器每传输一个字节减1 (I2CCNT保持不变)。如果在主模式下请求STOP条件(在I2CMDR中STP = 1),则I2C模块在倒计时完成时(即在传输最后一个字节时)使用STOP条件终止传输。
2.7 I2CDRR
I2CDRR是CPU用来读取接收数据的16位寄存器。I2C模块可以接收1 ~ 8位的数据字节。位的数量是用I2CMDR中的位计数(BC)位来选择的。每次一位从SDA引脚移到接收移位寄存器(I2CRSR)。当接收到一个完整的数据字节时,I2C模块将数据字节从I2CRSR复制到I2CDRR。CPU无法直接访问I2CRSR。
如果在I2CDRR中存在小于8位的数据字节,则该数据值为右对齐,I2CDRR(7-0)的其他位未定义。例如,BC = 011(3位数据大小),则接收数据在I2CDRR(20)中,I2CDRR(7-3)的内容未定义。
当处于接收FIFO模式时,I2CDRR寄存器充当接收FIFO缓冲区。
2.8 I2CSAR
I2C从地址寄存器(I2CSAR)是一个16位寄存器,用于存储下一个从地址,当I2C模块是主模块时,该从地址将由I2C模块传输。I2CSAR的SAR字段包含一个7位或10位的从地址。当I2C模块不使用自由数据格式(在I2CMDR中FDF = 0)时,它使用该地址与一个或多个从机发起数据传输。当地址非零时,该地址是一个特定的从属地址。当地址为0时,该地址是对所有slave的通用调用。如果选择7位寻址模式(I2CMDR中XA = 0),则只使用I2CSAR的6-0位将0写入9-7位
2.8 I2CDXR
CPU写传输数据到I2CDXR。这个16位的寄存器接受1到8位的数据字节。在写入I2CDXR之前,通过将适当的值加载到I2CMDR的位计数(BC)位中来指定一个数据字节中有多少位。当写入少于8位的数据字节时,请确保该值在I2CDXR中是右对齐的。
数据字节写入I2CDXR后,I2C模块将数据字节复制到传输移位寄存器(I2CXSR)。CPU无法直接访问I2CXSR。从I2CXSR, I2C模块将数据字节移出SDA引脚,一次一个位。
当处于发送FIFO模式时,I2CDXR寄存器充当发送FIFO缓冲区。
2.9 I2CMDR




2.10 I2CISRC

2.11 I2CEMDR
2.12 I2CPSC
2.13 I2CFFTX
2.14 I2CFFRX

三、AT24C02
3.1 硬件
设备/页地址(A2, A1, A0): A2, A1和A0引脚是硬连线的AT24C01A和at24co2的设备地址输入。在单个总线系统上可以寻址多达8个1K/2K设备(设备寻址将在设备寻址一节中详细讨论)。
AT24C04使用A2和A1输入进行硬线寻址,在单个总线系统上总共可以寻址四个4K设备。A0引脚为无连接。
AT24C08仅使用A2输入进行硬线寻址,在单个总线系统上总共可以寻址两个8K设备。A0和A1引脚没有连接。
AT24C16不使用设备地址引脚,这将单个总线上的设备数量限制为一个。A0、A1和A2引脚没有连接。
写保护(WP): AT24C01A/02/04/16有一个写保护引脚,提供硬件数据保护。写保护引脚在连接到地(GND)时允许正常的读写操作。当写保护引脚连接到VCC时,写保护功能被启用,其操作如表2所示。
AT24C01A, 1K串行EEPROM:内部组织为16页,每个页8字节,1K需要一个7位数据字地址进行随机字寻址。
at24co2, 2K串行EEPROM:内部组织为32页,每页8字节,2K需要一个8位数据字地址进行随机字寻址。
AT24C04, 4K串行EEPROM:内部组织有32页,每个16字节,4K需要一个9位的数据字地址进行随机字寻址。
AT24C08, 8K串行EEPROM:内部组织64页,每个16字节,8K需要一个10位的数据字地址进行随机字寻址。
AT24C16, 16K串行EEPROM:内部组织有128页,每个16字节,16K需要一个11位的数据字地址进行随机字寻址。
3.2 器件操作
时钟和数据转换:SDA引脚通常用外部设备拉高。SDA引脚上的数据可能仅在SCL低时间段内发生变化(参见第7页的图4)。SCL高时间段内的数据变化将指示启动或停止条件,定义如下。
启动条件:SDA的高到低转换与SCL高是一个启动条件,它必须先于任何其他命令(参见第7页的图5)。
停止条件:SDA低到高的转换与SCL高是一个停止条件。
在读取序列之后,stop命令将EEPROM置于备用电源模式(参见第7页上的图5)。
确认:所有地址和数据字都以8位字的形式串行地从EEPROM传输到EEPROM。EEPROM发送一个0来确认它已经接收到每个字。这发生在第九个时钟周期。
待机模式:AT24C01A/02/04/08/16具有低功耗待机模式,(a)在上电时启用,(b)在收到停止位并完成任何内部操作后启用。
内存复位:在协议中断,电源丢失或系统复位后,任何2线部分都可以通过以下步骤复位:
1. 时钟最多9个周期。
2. 在每个周期中寻找SDA高而SCL高的地方。
3. 创建启动条件。
3.2.1 总线时序
1K、2K、4K、8K和16K EEPROM设备都需要一个8位设备地址字,后跟一个启动条件,以使芯片能够进行读或写操作(参见第9页的图7)。
设备地址字由一个强制组成,如图所示,前四位最高有效位为零序列。这对所有EEPROM设备都是通用的。
接下来的3位是1K/2K EEPROM的A2、A1和A0设备地址位。
这3位必须与它们对应的硬连线输入引脚进行比较。
4K EEPROM只使用A2和A1设备地址位,第三位是内存页地址位。两个设备地址位必须与它们对应的硬连线输入引脚进行比较。A0引脚未连接。
8K EEPROM仅使用A2设备地址位,接下来的2位用于内存页寻址。A2位必须与其对应的硬连线输入引脚进行比较。A1和A0引脚未连接。
16K不使用任何设备地址位,而是使用3位用于内存页寻址。在4K、8K和16K设备上,这些页寻址位应该被认为是数据字地址中最重要的位。
A0、A1和A2引脚未连接。
设备地址的第八位是读写操作选择位。如果该位高,则进行读操作;如果该位低,则进行写操作。
在比较设备地址时,EEPROM将输出一个零。如果不进行比较,芯片将返回到待机状态。
3.2.2 写时序
写周期时间tWR是从一个写序列的有效停止条件到内部清除/写周期结束的时间。
字节写:写操作需要一个8位的数据字地址跟在设备地址字和确认字后面。在收到此地址后,EEPROM将再次以零响应,然后在第一个8位数据字中进行时钟。在接收到8位数据字后,EEPROM将输出一个零,寻址设备(如微控制器)必须以停止条件终止写序列。此时EEPROM进入到非易失性存储器的内部定时写入周期(tWR)。在这个写入周期中,所有输入都被禁用,并且EEPROM在写入完成之前不会响应(参见第10页上的图8)。
页写:1K/2K EEPROM能够进行8字节的页写,4K、8K和16K设备能够进行16字节的页写。
页写入的初始化与字节写入的初始化相同,但是微控制器在第一个数据字被写入后不发送停止条件。相反,在EEPROM确认接收到第一个数据字后,微控制器可以传输最多7个(1K/2K)或15个(4K, 8K, 16K)数据字。EEPROM将在接收到每个数据字后以零响应。微控制器必须用停止条件终止页写序列(参见第10页上的图9)。
接收到每个数据字后,数据字地址较低的三个(1K/2K)或四个(4K、8K、16K)位在内部递增。较高的数据字地址位不递增,保留内存页行位置。当内部生成的字地址到达页边界时,下一个字节被放置在同一页的开头。如果超过8个(1K/2K)或16个(4K, 8K, 16K)数据字被传输到EEPROM,数据字地址将“翻转”,以前的数据将被覆盖
3.2.3 数据有效性
3.2.4 启动和停止
3.2.5 输出应答
一旦内部定时写周期已经开始,EEPROM输入被禁用,应答循环可以启动。输入一个启动条件,后跟设备地址字。读/写位代表所需的操作。只有当内部写周期完成时,EEPROM才会以零响应,允许读或写序列继续进行
3.2.6 读操作
读操作的初始化方式与写操作相同,不同之处在于设备地址字中的读写选择位被设置为1。有三种读操作:当前地址读、随机地址读和顺序读。
读当前地址:内部数据字地址计数器维护上一次读或写操作中访问的最后一个地址,加1。只要保持芯片电源,此地址在操作之间保持有效。在读取期间的地址“滚转”是从最后一个内存页的最后一个字节到第一页的第一个字节。写期间的地址“滚转”是从当前页的最后一个字节到同一页的第一个字节。
一旦读/写选择位设置为1的设备地址被写入并被EEPROM确认,当前地址数据字被串行地读出。
微控制器不响应输入零,但确实产生以下停止条件(参见第10页的图10)。
随机读:随机读需要一个“虚拟”字节写序列来加载数据字地址。一旦设备地址字和数据字地址被写入并被EEPROM确认,微控制器必须产生另一个启动条件。微控制器现在通过发送一个读/写选择位高的设备地址来启动当前地址读取。EEPROM确认设备地址并串行地输出数据字。微控制器不响应零,但确实生成以下停止条件(参见第11页的图11)。
顺序读取:顺序读取由当前地址读取或随机地址读取发起。当微控制器接收到一个数据字后,它用一个确认来响应。只要EEPROM接收到一个确认,它就会继续增加数据字地址并串行地时钟出顺序数据字。当达到内存地址限制时,数据字地址将“滚过”,顺序读取将继续。当微控制器没有响应零,但确实产生以下停止条件时,顺序读取操作终止(参见第11页的图12)。
3.3 软件程序
框架
1.时钟和GPIO初始化
2.软件模拟I2C通信时序:起始、停止和应答
3.AT24C02读写函数
4.编写主函数
3.3.1 初始化GPIO
时钟:异步
SCL:输出 SDA:读写方向不一致
inline:内联函数
inline的含义_inline void是什么意思-优快云博客
inline用法详解_inline void函数-优快云博客
内联函数 —— C 中关键字 inline 用法解析_c inline 函数 样例-优快云博客
C++之inline的使用_inline void-优快云博客
void Eerom_Gpio_Init(void)
{
EALLOW;
GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0; //上拉
GpioCtrlRegs.GPBDIR.bit.GPIO32 = 1; // 输出端口
GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 0; // IO口
GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3; // 不同步
GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0; //上拉
GpioCtrlRegs.GPBDIR.bit.GPIO33 = 1; // 输出端口
GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 0; // IO口
GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3; // 不同步
EDIS;
}
__inline void SDA_READ(void)
{
EALLOW;
GpioCtrlRegs.GPBDIR.bit.GPIO32=0; //Input, SDA
EDIS;
}
__inline void SDA_WRITE(void)
{
EALLOW;
GpioCtrlRegs.GPBDIR.bit.GPIO32=1; //Output. SDA
EDIS;
}
3.3.2 启动信号
void delay(Uint16 time) //延时函数
{
for(; time>0 ; time--)
{
asm(" nop");
asm(" nop");
asm(" nop");
asm(" nop");
asm(" nop");
asm(" nop");
asm(" nop");
asm(" nop");
}
}
void begintrans(void) //发送START 信号
{
SDA_WRITE(); //SDA=1
delay(DELAY_UNIT * 10); //延时
/*初始SDA和SCL均为高电平*/
SDA_W1; //SDA 方向为输出到EEPROM
delay(DELAY_UNIT * 10); //延时
SCL_1; //SCL=1
delay(DELAY_UNIT * 10); //延时
/*SDA由高变低*/
SDA_W0; //SDA=0
delay(DELAY_UNIT * 10); //延时
}
void stoptrans(void) //发送STOP 信号
{
SDA_WRITE(); //SDA方向为输出到EEPROM
delay(DELAY_UNIT * 10); //延时
/*初始SDA为低。SCL为高*/
SDA_W0; //SDA=0
delay(DELAY_UNIT * 10); //延时
SCL_1; //SCL=1
delay(DELAY_UNIT * 10); //延时
/*SDA由低变高*/
SDA_W1; //SDA=1
delay(DELAY_UNIT * 10);
}
3.3.3 应答信号
/*等待响应*/
unsigned char IIC_Wait_Ack(void)
{
unsigned char tempTime=0;
/*数据传输结束结束SDA SCL为高电平*/
IIC_SDA_SETH;
DELAY_US(1);
SDA_IN(); //SDA设置为输入
IIC_SCL_SETH;
DELAY_US(1);
while(READ_SDA)/*SDA为高电平就死循环*/
{
tempTime++;
if(tempTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL_SETL;//时钟输出0 /*不是就继续往下执行,跳出程序,代表应答,SCL需要拉低*/
return 0;/*跳出程序*/
}
void IIC_Ack(void)
{
/*初始SCL为低电平*/
IIC_SCL_SETL;
/*SDA输出一个低电平应答*/
SDA_OUT();
IIC_SDA_SETL;
DELAY_US(2);
/*SCL一个高低电平变换*/
IIC_SCL_SETH;
DELAY_US(5);
IIC_SCL_SETL;
}
void IIC_NAck(void)
{
IIC_SCL_SETL;
/*SDA输出一个高电平应答*/
SDA_OUT();
IIC_SDA_SETH;
DELAY_US(2);
IIC_SCL_SETH;
DELAY_US(5);
IIC_SCL_SETL;
}
3.3.4 发送字节
void IIC_Send_Byte(unsigned char txd)
{
unsigned char t;
/*发送字节,SDA为输出*/
SDA_OUT();
/*传输SCL需要拉低*/
IIC_SCL_SETL;//拉低时钟开始数据传输
for(t=0;t<8;t++)/*一个字节为8位*/
{
if((txd&0x80)>0) //0x80 1000 0000
IIC_SDA_SETH;/*字节位1*/
else
IIC_SDA_SETL;/*字节为0*/
txd<<=1;/*左移一位*/
DELAY_US(2); //对TEA5767这三个延时都是必须的
IIC_SCL_SETH;
DELAY_US(2);
IIC_SCL_SETL;
DELAY_US(2);
}
}
3.3.5 读字节
unsigned char IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL_SETL;
DELAY_US(2);
IIC_SCL_SETH;
receive<<=1;
if(READ_SDA)receive++; //一位一位移出去
DELAY_US(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
3.3.6 AT24读/写取一个字节
/*读的时候器件地址0xA1 写的时候0xA0*/
/*如果容量大于2048,是10位地址需要先发高位在发低位,其他是7位*/
unsigned char AT24CXX_ReadOneByte(Uint16 ReadAddr)
{
unsigned char temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令AT24地址
IIC_Wait_Ack(); //等待响应,否则一直死循环
IIC_Send_Byte(ReadAddr>>8);//发送高地址
}
else
{
IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据,ReadAddr小于256,所以还是位0
}
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址存放地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0); //ack=0 响应
IIC_Stop();//产生一个停止条件
return temp;
}
void AT24CXX_WriteOneByte(Uint16 WriteAddr,unsigned char DataToWrite)
{
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
}
else
{
IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
DELAY_US(10*1000);
}
3.3.7 写/读多个字节
/*******************************************************************************
* 函 数 名 : AT24CXX_WriteLenByte
* 函数功能 : 在AT24CXX里面的指定地址开始写入长度为Len的数据
用于写入16bit或者32bit的数据
* 输 入 : WriteAddr :写入数据的目的地址
DataToWrite:要写入的数据
Len :要写入数据的长度2,4
* 输 出 : 无
*******************************************************************************/
void AT24CXX_WriteLenByte(Uint16 WriteAddr,Uint32 DataToWrite,unsigned char Len)
{
unsigned char t;
for(t=0;t<Len;t++)
{
AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);//一次移动8位,编数据类型?
}
}
Uint32 AT24CXX_ReadLenByte(Uint16 ReadAddr,unsigned char Len)
{
unsigned char t;
Uint32 temp=0;
for(t=0;t<Len;t++)
{
temp<<=8;//先移动8位
temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
}
return temp;
}
/*******************************************************************************
* 函 数 名 : AT24CXX_Read
* 函数功能 : 在AT24CXX里面的指定地址开始读出指定个数的数据
* 输 入 : ReadAddr :开始读出的地址 对24c02为0~255
pBuffer :数据数组首地址
NumToRead:要读出数据的个数
* 输 出 : 无
*******************************************************************************/
void AT24CXX_Read(Uint16 ReadAddr,unsigned char *pBuffer,Uint16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
/*******************************************************************************
* 函 数 名 : AT24CXX_Write
* 函数功能 : 在AT24CXX里面的指定地址开始写入指定个数的数据
* 输 入 : WriteAddr :开始写入的地址 对24c02为0~255
pBuffer :数据数组首地址
NumToRead:要读出数据的个数
* 输 出 : 无
*******************************************************************************/
void AT24CXX_Write(Uint16 WriteAddr,unsigned char *pBuffer,Uint16 NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
3.3.8 检查AT24是否正常
/*******************************************************************************
* 函 数 名 : AT24CXX_Check
* 函数功能 : 检查AT24CXX是否正常
* 输 入 : 无
* 输 出 : 1:检测失败,0:检测成功,写的和读的是否一致
*******************************************************************************/
unsigned char AT24CXX_Check(void)
{
unsigned char temp;
temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
if(temp==0x36)return 0;
else//排除第一次初始化的情况
{
AT24CXX_WriteOneByte(255,0X36);
temp=AT24CXX_ReadOneByte(255);
if(temp==0X36)return 0;
}
return 1;
}
四、DSP例程
4.1 玻尔电子PPT
I2CSAR=0x50
4.2 变量定义
struct I2CMSG
{
Uint16 MsgStatus; // 消息状态
// I2C_MSGCMD_INACTIVE = 没有传输数据
// I2C_MSGCMD_BUSY = 数据已经发送,等待停止信号
// I2C_MSGCMD_SEND_WITHSTOP = 发送命令 主消息传输完成带停止位
// I2C_MSGCMD_SEND_NOSTOP = 发送命令 主消息传输完成无停止位
// I2C_MSGCMD_RESTART = 发送重启命令 作为带有停止位的主接收器
Uint16 SlaveAddress; // I2C address of slave msg is intended for
Uint16 NumOfBytes; // 要放入MsgBuffer有效字节数
// EEPROM 内部寻址高位
//
Uint16 MemoryHighAddr;
//
// EEPROM 内部寻址低位
//
Uint16 MemoryLowAddr;
//
// Array holding msg data - max that MAX_BUFFER_SIZE can be is 16 due to
// the FIFO's
Uint16 MsgBuffer[I2C_MAX_BUFFER_SIZE];
};
4.3 初始化
void I2CA_Init(void)
{
//
// Initialize I2C 0000 0000 0101 0000
//
I2caRegs.I2CSAR = 0x0050; // 从地址 7位寻址
#if (CPU_FRQ_150MHZ) // Default - For 150MHz SYSCLKOUT
//
// Prescaler - need 7-12 Mhz on module clk (150/15 = 10MHz) (输入时钟频率/(IPSC+1)
//
I2caRegs.I2CPSC.all = 14;
#endif
I2caRegs.I2CCLKL = 10; // NOTE: must be non zero 较低的时钟持续时间模块时钟*(ICCL+d)
I2caRegs.I2CCLKH = 5; // NOTE: must be non zero 较高的时钟持续时间模块时钟*(ICCH+d)
I2caRegs.I2CIER.all = 0x24; // Enable SCD & ARDY interrupts
0010 0100 ADRY访问就绪 SCD:停止检测
//
// Take I2C out of reset
// Stop I2C when suspended
// 0000 0000 0010 0000
I2caRegs.I2CMDR.all = 0x0020; //一个字节有两位 注:如果比特数小于8,则接收数据在I2CDRR(7-0)中为右对齐,其他I2CDRR(7-0)位未定义。此外,写入I2CDXR的传输数据必须右对齐
I2caRegs.I2CFFTX.all = 0x6000; // Enable FIFO mode and TXFIFO 0110 I2CFFEN TXFFRST
I2caRegs.I2CFFRX.all = 0x2040; // Enable RXFIFO, clear RXFFINT,
0010 0000 0100 0000 RXFFRST RXSSINTCLR
return;
}
4.4 写数据
Uint16 I2CA_WriteData(struct I2CMSG *msg)
{
Uint16 i;
//
//等待,直到STP位从任何先前的主通信中清除
//模块延迟清除该位,直到SCD位被清除
/ /设置。如果在初始化新消息之前未检查此位,则
// I2C可能会混淆。
//
if (I2caRegs.I2CMDR.bit.STP == 1) //停止条件位 当I2C模块内部数据计数器计数到0时产生STOP条件
{
return I2C_STP_NOT_READY_ERROR;//当STOP状态为0,总线不忙,可以继续,为1则返回一个错误状态,不继续执行下面的程序
}
//
// Setup slave address
//
I2caRegs.I2CSAR = msg->SlaveAddress;//从设备地址
//
// Check if bus busy
//
if (I2caRegs.I2CSTR.bit.BB == 1)//总线忙:I2C模块在总线上接收或发送了一个START位。
{
return I2C_BUS_BUSY_ERROR;
}
//
// Setup number of bytes to send MsgBuffer + Address
//
I2caRegs.I2CCNT = msg->NumOfBytes+2; //传输多少字节,发送或者接受数据计数器,每发送一个数据计数器减一,到0发送完毕,2代表的是高低地址字节
//
// Setup data to send
//
I2caRegs.I2CDXR = msg->MemoryHighAddr;
I2caRegs.I2CDXR = msg->MemoryLowAddr;
for (i=0; i<msg->NumOfBytes; i++)
{
I2caRegs.I2CDXR = *(msg->MsgBuffer+i);//在写入I2CDXR之前,通过将适当的值加载到I2CMDR的位计数(BC)位中来指定一个数据字节中有多少位。当写入少于8位的数据字节时,请确保该值在I2CDXR中是右对齐的。
}
//
// Send start as master transmitter
//0110 1110 0010 0000
I2caRegs.I2CMDR.all = 0x6E20; //发送指令
// FREE 1:I2C模块自由运行,也就是说,当出现断点时,它继续运行。
// STT 1:=在主模式下,设置STT为1会导致I2C模块在I2C总线上产生START条件
//STP :设备已经设置了STP,当I2C模块的内部数据计数器计数到0时,生成一个STOP条件。
//MST: 主模式。I2C模块是主控模块,在SCL引脚上产生串行时钟。
//TRX: 发射机模式。I2C模块是一个发射器,通过SDA引脚传输数据
//XA: 0 7位寻址
//IRS: 1 模块使能
return I2C_SUCCESS;
}
//
// Write data to EEPROM section
//
//
// Check the outgoing message to see if it should be sent.
// In this example it is initialized to send with a stop bit.
//
if(I2cMsgOut1.MsgStatus == I2C_MSGSTAT_SEND_WITHSTOP)
{
Error = I2CA_WriteData(&I2cMsgOut1);
//
// If communication is correctly initiated, set msg status to busy
// and update CurrentMsgPtr for the interrupt service routine.
// Otherwise, do nothing and try again next loop. Once message is
// initiated, the I2C interrupts will handle the rest. Search for
// ICINTR1A_ISR in the i2c_eeprom_isr.c file.
//
if (Error == I2C_SUCCESS)
{
CurrentMsgPtr = &I2cMsgOut1;
I2cMsgOut1.MsgStatus = I2C_MSGSTAT_WRITE_BUSY;
}
} // end of write section
4.5 读数据
Uint16
I2CA_ReadData(struct I2CMSG *msg)
{
//
// Wait until the STP bit is cleared from any previous master communication.
// Clearing of this bit by the module is delayed until after the SCD bit is
// set. If this bit is not checked prior to initiating a new message, the
// I2C could get confused.
//
if (I2caRegs.I2CMDR.bit.STP == 1)
{
return I2C_STP_NOT_READY_ERROR;
}
I2caRegs.I2CSAR = msg->SlaveAddress;
if(msg->MsgStatus == I2C_MSGSTAT_SEND_NOSTOP)//发送地址信息
{
//
// Check if bus busy
//
if (I2caRegs.I2CSTR.bit.BB == 1)
{
return I2C_BUS_BUSY_ERROR;
}
I2caRegs.I2CCNT = 2;//传输或者接受两个字节
I2caRegs.I2CDXR = msg->MemoryHighAddr;
I2caRegs.I2CDXR = msg->MemoryLowAddr;
I2caRegs.I2CMDR.all = 0x2620; // Send data to setup EEPROM address
0010 0110 0010 0000
//STT=1 在主模式下,设置STT为1会导致I2C模块在I2C总线上产生START条件
//MST=1 主模式。I2C模块是主控模块,在SCL引脚上产生串行时钟
//TRX=1 发射机模式。I2C模块是一个发射器,通过SDA引脚传输数据。
//IRS=1 I2C模块使能。这有释放I2C总线的效果,如果I2C外设持有它。
}
else if(msg->MsgStatus == I2C_MSGSTAT_RESTART)//发送数据信息
{
I2caRegs.I2CCNT = msg->NumOfBytes; // Setup how many bytes to expect
I2caRegs.I2CMDR.all = 0x2C20; // Send restart as master receiver
}
// 0010 1100 0010 0000
//STP=1 =设备设置了STP,当I2C模块内部数据计数器计数到0时产生STOP条件。
//TRX=0 =接收模式。I2C模块是一个接收器,接收SDA引脚上的数据
return I2C_SUCCESS;
//RM=0 STT=1 STP=1 S A D*n P模式
}
// Read data from EEPROM section
//
//从机读取数据,一定要主机把地址发到I2C总线上
//
// Check outgoing message status. Bypass read section if status is
// not inactive.
//
if (I2cMsgOut1.MsgStatus == I2C_MSGSTAT_INACTIVE)
{
//
// Check incoming message status.
//
if(I2cMsgIn1.MsgStatus == I2C_MSGSTAT_SEND_NOSTOP)
{
//
// EEPROM address setup portion
//
while(I2CA_ReadData(&I2cMsgIn1) != I2C_SUCCESS)
{
//
// Maybe setup an attempt counter to break an infinite
// while loop. The EEPROM will send back a NACK while it is
// performing a write operation. Even though the write
// communique is complete at this point, the EEPROM could
// still be busy programming the data. Therefore, multiple
// attempts are necessary.
//
}
//
// Update current message pointer and message status
//
CurrentMsgPtr = &I2cMsgIn1;
I2cMsgIn1.MsgStatus = I2C_MSGSTAT_SEND_NOSTOP_BUSY;
}
//
// Once message has progressed past setting up the internal address
// of the EEPROM, send a restart to read the data bytes from the
// EEPROM. Complete the communique with a stop bit. MsgStatus is
// updated in the interrupt service routine.
//从机向主机写入数据
else if(I2cMsgIn1.MsgStatus == I2C_MSGSTAT_RESTART)
{
//
// Read data portion
//
while(I2CA_ReadData(&I2cMsgIn1) != I2C_SUCCESS)
{
//
// Maybe setup an attempt counter to break an infinite
// while loop.
//
}
//
// Update current message pointer and message status
//
CurrentMsgPtr = &I2cMsgIn1;
I2cMsgIn1.MsgStatus = I2C_MSGSTAT_READ_BUSY;
}
} // end of read section
4.6 中断
/*中断*/
DINT;
//
// Initialize PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2833x_PieCtrl.c file.
//
InitPieCtrl();
//
// Disable CPU interrupts and clear all CPU interrupt flags
//
IER = 0x0000;
IFR = 0x0000;
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example. This is useful for debug purposes.
// The shell ISR routines are found in DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
//
InitPieVectTable();
//
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
//
EALLOW; // This is needed to write to EALLOW protected registers
PieVectTable.I2CINT1A = &i2c_int1a_isr;
EDIS; // This is needed to disable write to EALLOW protected registers
PieCtrlRegs.PIEIER8.bit.INTx1 = 1;
//
// Enable CPU INT8 which is connected to PIE group 8
//
IER |= M_INT8;
EINT;
__interrupt void
i2c_int1a_isr(void)
{
Uint16 IntSource, i;
//
// 读取中断源
//
IntSource = I2caRegs.I2CISRC.all;
//
// Interrupt source = 停止条件中断
//
if(IntSource == I2C_SCD_ISRC)
{
//
// If completed message was writing data, reset msg to inactive state
//
if (CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_WRITE_BUSY)
{
CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_INACTIVE;
}
else
{
//
//如果消息在地址设置部分接收到NACK
//的EEPROM读取,下面的代码进一步包含在
//寄存器访问就绪中断源代码将生成一个停止
/ /条件。收到停止条件后(此处),设置
//消息状态再次尝试。用户可能想要限制数量
//在生成错误之前重试。
//
if(CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_SEND_NOSTOP_BUSY)
{
CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_SEND_NOSTOP;
}
//
//如果完成消息正在读取EEPROM数据,则复位msg
//进入非活动状态,从FIFO读取数据。
//
else if (CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_READ_BUSY)
{
CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_INACTIVE;
for(i=0; i < I2C_NUMBYTES; i++)
{
CurrentMsgPtr->MsgBuffer[i] = I2caRegs.I2CDRR;
}
{
//
// Check received data
//
for(i=0; i < I2C_NUMBYTES; i++)
{
if(I2cMsgIn1.MsgBuffer[i] == I2cMsgOut1.MsgBuffer[i])
{
PassCount++;
}
else
{
FailCount++;
}
}
if(PassCount == I2C_NUMBYTES)
{
pass();
}
else
{
fail();
}
}
}
}
} // end of stop condition detected
//
//中断源=寄存器访问就绪
//此中断用于确定EEPROM地址何时设置
//部分读数据通信完成。因为没有停止位
//命令,这个标志告诉我们消息何时被发送,而不是
// SCD标志。如果收到NACK,则清除NACK位,执行命令a
/ /停止。否则,继续进行通信的读取数据部分。
//
else if(IntSource == I2C_ARDY_ISRC)
{
if(I2caRegs.I2CSTR.bit.NACK == 1)
{
I2caRegs.I2CMDR.bit.STP = 1;
I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;//I2C状态寄存器(I2CSTR)是一个16位寄存器,用于确定发生了哪个中断并读取状态信息。
}
else if(CurrentMsgPtr->MsgStatus == I2C_MSGSTAT_SEND_NOSTOP_BUSY)
{
CurrentMsgPtr->MsgStatus = I2C_MSGSTAT_RESTART;
}
}
else
{
//
// Generate some error due to invalid interrupt source
//
__asm(" ESTOP0");
}
//
// Enable future I2C (PIE Group 8) interrupts
//
PieCtrlRegs.PIEACK.all = PIEACK_GROUP8;
}
void pass()
{
__asm(" ESTOP0");//ESTOP0:当用仿真器连接时如果ESTOP0置位(ESTOP0=1),那么整个DSP停止运行;
for(;;);
}
//
// fail -
//
void fail()
{
__asm(" ESTOP0");
for(;;);
}
NTM:全局中断屏蔽位
EINT: 给INTM置0,使能全局中断
DINT:给INTM置1,禁止全局中断
DBGM: 调试启用屏蔽位
ERTM:使能调试事件,允许仿真器调试和实时访问内存
DRTM:禁止调试事件
EALLOW:允许操作被保护的寄存器
EDIS::禁止操作被保护的寄存器
ESTOP0:当用仿真器连接时如果ESTOP0置位(ESTOP0=1),那么整个DSP停止运行;
4.6 IIC-LED
/*
* User_Iic_Led.c
*
* Created on: 2014年3月23日
* Author: Administrator
*/
//###########################################################################
//
// FILE: DSP2833x_I2C.c
//
// TITLE: DSP2833x SCI Initialization & Support Functions.
//
//###########################################################################
// $TI Release: 2833x/2823x Header Files and Peripheral Examples V133 $
// $Release Date: June 8, 2012 $
//###########################################################################
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP2833x Examples Include File
//---------------------------------------------------------------------------
// InitI2C:
//---------------------------------------------------------------------------
// This function initializes the I2C to a known state.
//
void I2CA_Init(void);
void InitI2CGpio();
static Uint16 I2cWriteRegister(Uint16 i2cAddr,Uint16 reg,Uint16 data );
void Led_Test();
#if 0
#define I2C_SLAVE_ADDR 0x50
#define I2C_NUMBYTES 1
#define I2C_EEPROM_HIGH_ADDR 0x00
#define I2C_EEPROM_LOW_ADDR 0x30
/********I2C LEDS*********/
#define I2C_INPUT_PORT 0x00
#define I2C_OUTPUT_PORT 0x01
#define I2C_POLARITY_REG 0x02
#define I2C_CONFIG_REG 0x03
#define I2C_COMMAND_ADDR I2C_OUTPUT_PORT
#define I2C_INPUT_ALL 0xFF
#define I2C_OUTPUT_ALL 0x00
#define I2C_LED 0x00
#define I2C_EEPR 0x01
#define I2C_DEVS I2C_LED
/************Duan_led********************/
const unsigned char Duan_led[6]=
{
0xf7, //'0' JM4
0xfb, //'1' JM3
0xfd, //'2' JM2
0xdf, //'3' JM1
0xEF, //'4' JM5
0x10, // JM 1,2,3,4
};
/*************Wei_led *******************/
const unsigned char Wei_led[2*7]=
{ //addr:0x1a: addr:0x19
0xff,0x10, //'0'
0x22,0x00, //'1'
0x2c,0x70, //'2'
0x26,0x70, //'3'
0x32,0x60, //'4'
0x16,0x70, //'5'
0x1e,0x70, //'6'
};
// Global variables
// Two bytes will be used for the outgoing address,
// thus only setup 14 bytes maximum
struct I2CMSG I2cMsgConfigOut1={I2C_MSGSTAT_SEND_WITHSTOP,
I2C_SLAVE_ADDR,
I2C_NUMBYTES,
I2C_CONFIG_REG,
I2C_EEPROM_LOW_ADDR,
I2C_OUTPUT_ALL};
struct I2CMSG I2cMsgIn1={ I2C_MSGSTAT_SEND_NOSTOP,
I2C_SLAVE_ADDR,
I2C_NUMBYTES,
I2C_EEPROM_HIGH_ADDR,
I2C_EEPROM_LOW_ADDR};
#endif
#define I2C_OUTPUT_ALL 0x00
#define I2C_CONFIG_REG 0x03
//---------------------------------------------------------------------------
// Example: InitI2CGpio:
//---------------------------------------------------------------------------
// This function initializes GPIO pins to function as I2C pins
//
// Each GPIO pin can be configured as a GPIO pin or up to 3 different
// peripheral functional pins. By default all pins come up as GPIO
// inputs after reset.
//
// Caution:
// Only one GPIO pin should be enabled for SDAA operation.
// Only one GPIO pin shoudl be enabled for SCLA operation.
// Comment out other unwanted lines.
void I2CA_Init(void)
{
// Initialize I2C
I2caRegs.I2CSAR = 0x0050; // Slave address - EEPROM control code
#if (CPU_FRQ_150MHZ) // Default - For 150MHz SYSCLKOUT
I2caRegs.I2CPSC.all = 14; // Prescaler - need 7-12 Mhz on module clk (150/15 = 10MHz)
#endif
#if (CPU_FRQ_100MHZ) // For 100 MHz SYSCLKOUT
I2caRegs.I2CPSC.all = 9; // Prescaler - need 7-12 Mhz on module clk (100/10 = 10MHz)
#endif
I2caRegs.I2CCLKL = 10; // NOTE: must be non zero
I2caRegs.I2CCLKH = 5; // NOTE: must be non zero
I2caRegs.I2CIER.all = 0x24; // Enable SCD & ARDY interrupts
I2caRegs.I2CMDR.all = 0x0020; // Take I2C out of reset
// Stop I2C when suspended
I2caRegs.I2CFFTX.all = 0x6000; // Enable FIFO mode and TXFIFO
I2caRegs.I2CFFRX.all = 0x2040; // Enable RXFIFO, clear RXFFINT,
return;
}
Uint16 I2cWriteRegister(Uint16 i2cAddr,Uint16 reg,Uint16 data )
{
if (I2caRegs.I2CMDR.bit.STP == 1)
{
return I2C_STP_NOT_READY_ERROR;
}
I2caRegs.I2CSAR = i2cAddr;
if (I2caRegs.I2CSTR.bit.BB == 1)
{
return I2C_BUS_BUSY_ERROR;
}
I2caRegs.I2CCNT = 2;
// Setup data to send
I2caRegs.I2CDXR = reg;
I2caRegs.I2CDXR = data;
// Send start as master transmitter
I2caRegs.I2CMDR.all = 0x6E20;
DSP28x_usDelay(100000);
return I2C_SUCCESS;
}
void Led_Test()
{
Uint16 led,i;
InitI2CGpio();
I2CA_Init();
// I2CA_WriteConfig(&I2cMsgConfigOut1);
I2cWriteRegister(0x50,I2C_CONFIG_REG,I2C_OUTPUT_ALL);
DSP28x_usDelay(100000);
for(i=0;i<8;i++)
{
//地址 =LED_I2CADDR; 1为配置px为输出; 输出的数据是:1<<((led%7)+1)1<<((led%7)+1)
I2cWriteRegister(0x50,1,1<<((led%8))); //light one led
DSP28x_usDelay(2000000);
DSP28x_usDelay(2000000);
led++;
#if 0
if(led==0x07)
{
led=0;
flag=0;
}
else
led++;
#endif
}
}
//===========================================================================
// End of file.
//===========================================================================