1、PIC32参考资源

PIC32系列参考手册 中文版  链接地址:PIC32系列参考手册 第24章 I2C模块

image.png

2、IIC通讯简介

I2C模块是用于同其他外设或单片机器件进行通信的串行接口。可在以下任意 I2C系统中工作:

• 作为从器件

• 在单主机系统中作为主器件 (从器件也可同时工作)

• 在多主机系统中作为主 / 从器件 (提供总线冲突检测和仲裁)

相互独立的I2C主器件逻辑和I2C从器件逻辑,它们根据各自的事件产生中断。

 I2C模块具有以下主要特性:

• 主器件和从器件逻辑相互独立

• 支持多主机系统,可以防止在仲裁时丢失报文

• 在从模式下可检测 7 位和 10 位器件地址,并可配置地址掩码

• 检测I2C协议中定义的广播呼叫地址

• 具有自动 SCLx 时钟延长功能,为处理器提供延时以响应从器件的数据请求

• 支持 100 kHz 和 400 kHz 总线规范

• 支持严格I2C保留地址规则

image.png

3、控制和状态寄存器

PIC32MX I2C模块包含以下特殊功能寄存器

• I2CxCON:I2C模块控制寄存器

• I2CxSTAT:I2C模块状态寄存器

• I2CxMSK :地址掩码寄存器(指定 I2CxADD 中的哪些位可以忽略,从而提供了多地址支持)

• I2CxRCV:接收缓冲寄存器 (只读)

• I2CxTRN:发送寄存器 (读 / 写)

• I2CxADD:地址寄存器 (保存从器件地址)

• I2CxBRG:波特率发生器重载寄存器(保存I2C模块波特率发生器的波特率发生器重载值)

每个 I 2 C 模块还具有以下用于中断控制的相关位:

• I2CxMIF:主器件中断标志状态位 (在 IFC0 和 IFC1 INT 寄存器中)

• I2CxSIF:从器件中断标志状态位 (在 IFS0 和 IFS1 INT 寄存器中)

• I2CxBIF:总线冲突中断标志状态位 (在 IFS0 和 IFS1 INT 寄存器中)

• I2CxMIE:主器件中断允许控制位 (在 IEC0 和 IEC1 INT 寄存器中)

• I2CxSIE:从器件中断允许控制位 (在 IEC0 和 IEC1 INT 寄存器中)

• I2CxBIE:总线冲突中断允许控制位 (在 IEC0 和 IEC1 INT 寄存器中)

• I2CxIP<2:0>:中断优先级控制位 (在 IPC6 和 IPC8 INT 寄存器中)

• I2CxIS<1:0>:中断子优先级控制位 (在 IPC6 和 IPC8 INT 寄存器中)

I2CXCON :I2C控制寄存器

image.png

 bit 15 ON:I2C使能位

1 = 使能 I2C模块,并将 SDA 和 SCL 引脚配置为串口引脚

0 = 禁止 I 2 C 模块;所有 I 2 C 引脚由端口功能控制

注: 使用 1:1 PBCLK 分频比时,在清零模块 ON 位的指令之后,用户的软件不应立即在 SYSCLK周期中读 / 写外设的 SFR。

bit 14 FRZ :DEBUG(调试)模式冻结控制位(仅在 DEBUG(调试)模式下可读 / 写;其他情况下读为 0)

1 = 处于DEBUG(调试)模式时模块停止工作

0 = 处于DEBUG(调试)模式时模块不停止工作

注: FRZ 仅在调试异常模式下可写,在正常模式下强制为 0。

bit 13 SIDL :IDLE (空闲)模式停止位

1 = 当器件进入 IDLE (空闲)模式时,模块停止工作

0 = 在 IDLE (空闲)模式下模块继续工作

bit 12 SCLREL:SCL 释放控制位 仅适用于 I 2 C 从模式,模块复位和 (ON = 0)会设置 SCLREL = 1

如果 STREN = 0:

1 = 释放时钟

0 = 保持时钟为低电平 (时钟延长)

注: 在从器件发送开始时自动清零。

如果 STREN = 1:

1 = 释放时钟

0 = 保持时钟为低电平(时钟延长)。用户可以将该位设定为 0,强制在下一次 SCL 低电平时产生时钟延长。

注: 在从器件发送开始时自动清零;在从器件接收结束时自动清零。

bit 11 STRICT :严格 I 2 C 保留地址规则使能位

1 = 强制实行严格保留寻址规则。器件不响应保留地址空间或产生处于保留地址空间中的地址。

0 = 不使能严格 I 2 C 保留地址规则

bit 10 A10M :10 位从器件地址标志位

1 = I2CxADD 为 10 位从器件地址

0 = I2CxADD 为 7 位从器件地址

bit 9 DISSLW :压摆率控制禁止位

1 = 标准速度模式下禁止压摆率控制 (100 kHz); 1 MHz 模式下也禁止

0 = 高速模式下使能压摆率控制 (400 kHz)

bit 8 SMEN :SMBus 输入电平禁止位

1 = 使能输入逻辑以使门限值符合 SMBus 规范

0 = 禁止 SMBus 特定输入

bit 7 GCEN :广播呼叫使能位,仅适用于 I 2 C 从模式

1 = 允许在 I2CSR 中接收到广播呼叫地址时产生中断。已使能模块接收。

0 = 禁止广播呼叫地址

bit 6 STREN :SCL 时钟延长使能位,仅适用于 I 2 C 从模式;与 SCLREL 位配合使用。

1 = 使能时钟延长

0 = 禁止时钟延长

bit 5 ACKDT :应答数据位

仅适用于I 2 C主模式;适用于主模式接收期间。当用户在接收结束时发出一个应答序列时将发送的值。

1 = 发送 NACK

0 = 发送 ACK

bit 4 ACKEN :应答序列使能位,仅适用于 I 2 C 主模式;适用于主模式接收期间

1 = 在 SDA 和 SCL 引脚上发出应答序列,并发送 ACKDT数据位;由模块清零

0 = 应答序列空闲

bit 3 RCEN :接收使能位,仅适用于 I 2 C 主模式

1 = 使能 I 2 C 接收模式,在接收完 8 位数据字节时由模块自动清零

0 = 接收序列不在进行中

bit 2 PEN :停止条件使能位,仅适用于 I 2 C 主模式

1 = 在 SDA 和 SCL 引脚上发出停止条件;由模块清零

0 = 停止条件空闲

bit 1 RSEN :重复启动条件使能位,仅适用于 I 2 C 主模式

1 = 在 SDA 和 SCL 引脚上发出重复启动条件;由模块清零

0 = 重复启动条件空闲

bit 0 SEN :启动条件使能位,仅适用于 I 2 C 主模式

1 = 在 SDA 和 SCL 引脚上发出启动条件;由模块清零

0 = 启动条件空闲

I2CXSTAT:I2C状态寄存器

image.png

 bit 15 ACKSTAT :应答状态位

适用于 I 2 C 主模式和从模式;适用于发送和接收操作。

1 = 未接收到应答

0 = 接收到应答

bit 14 TRSTAT :发送状态位

仅适用于 I 2 C 主模式;适用于主发送模式。

1 = 主器件正在进行发送 (8 位 + ACK)

0 = 主器件不在进行发送

bit 13-11 保留:写入 0 ;忽略读操作

bit 10 BCL :主器件总线冲突检测位

当 I 2 C 模块被禁止 (ON = 0)时清零。

1 = 主器件工作期间检测到总线冲突

0 = 未检测到冲突

bit 9 GCSTAT :广播呼叫状态位,在检测到停止条件之后清零。

1 = 接收到广播呼叫地址

0 = 未接收到广播呼叫地址

bit 8 ADD10:10 位地址状态位,在检测到停止条件之后清零。

1 = 10 位地址匹配

0 = 10 位地址不匹配

bit 7 IWCOL :写冲突检测位

1 = 因为 I 2 C 模块忙,尝试写 I2CxTRN 寄存器失败。必须用软件清零。

0 = 未发生冲突

bit 6 I2COV :I 2 C 接收溢出状态位

1 = 当 I2CxRCV 寄存器仍然保存原先字节的情况下接收到了新字节。在发送模式下 I2COV 为 “无关位”。必须用软件清零。

0 = 未溢出

bit 5 D/A :数据 / 地址位,仅对从模式操作有效。

1 = 指示上次接收或发送的字节为数据

0 = 指示上次接收或发送的字节为地址

bit 4 P :停止位

当检测到启动、重复启动或停止条件时更新;当 I 2 C 模块被禁止 (ON = 0)时清零。

1 = 指示上次检测到停止位

0 = 上次未检测到停止位

bit 3 S :启动位

当检测到启动、重复启动或停止条件时更新;当 I 2 C 模块被禁止 (ON = 0)时清零。

1 = 指示上次检测到启动 (或重复启动)位

0 = 上次未检测到启动位

bit 2 R/W :读 / 写信息位,仅对从模式操作有效。

1 = 读——指示数据传输自从器件输出

0 = 写——指示数据传输输入到从器件

bit 1 RBF : 接收缓冲区满状态位

1 = 接收完成; I2CxRCV 为满

0 = 接收未完成; I2CxRCV 为空

bit 0 TBF : 发送缓冲区满状态位

1 = 发送正在进行中; I2CxTRN 为满 (8 位数据)

0 = 发送完成; I2CxTRN 为空

I2CXADD :I 2 C 从器件地址寄存器

image.png

 bit 9-0 ADD<9:0> :I 2 C 从器件地址位,主模式或从模式

I2CXMSK :I 2 C 地址掩码寄存器

image.png

 bit 9-0 MSK<9:0> :I 2 C 地址掩码位

1 = 将传入地址匹配序列中的特定位单元强制设为 “无关位”。

0 = 地址位单元必须与传入的 I 2 C 地址匹配序列匹配。

I2CXBRG :I2C波特率发生器寄存器

image.png

 bit 11-0 I2CxBRG<11:0> :I 2 C 波特率发生器值位,外设时钟的分频函数。

I2CXTRN :I 2 C 发送数据寄存器

image.png

bit 7-0 I2CTXDATA<7:0> :I 2 C 发送数据缓冲区位

I2CxRCV :I 2 C 接收数据寄存器

image.png

  bit 7-0 I2CRXDATA<7:0> :I 2 C 接收数据缓冲区位

IFS0 :中断标志状态寄存器 0

image.png

 bit 31 I2C1MIF:I 2 C 主器件中断标志状态位

bit 30 I2C1SIF:I 2 C 从器件中断标志状态位

bit 29 I2C1BIF:I 2 C 总线冲突中断标志状态位

1 = 产生了中断请求

0 = 未产生中断请求

IEC0 :中断允许控制寄存器 0

image.png

 bit 31 I2C1MIE:I 2 C 主器件中断允许控制位

bit 30 I2C1SIE:I 2 C 从器件中断允许控制位

bit 29 I2C1BIE:I 2 C 总线冲突中断允许控制位

1 = 允许中断

0 = 禁止中断

IPC6 :中断优先级控制寄存器 6

image.png

 bit 12-10 I2C1IP<2:0> :I 2 C1 中断优先级位

111 = 中断优先级为 7

110 = 中断优先级为 6

101 = 中断优先级为 5

100 = 中断优先级为 4

011 = 中断优先级为 3

010 = 中断优先级为 2

001 = 中断优先级为 1

000 = 禁止中断

bit 9-8 I2C1IS<1:0> :I 2 C1 子优先级位

11 = 中断子优先级为 3

10 = 中断子优先级为 2

01 = 中断子优先级为 1

00 = 中断子优先级为 0

4、I2C总线协议

当某个主器件要启动数据传输时,它首先发送它要与之进行通信的器件地址。所有的器件均会监听,看是否是自己的地址。在该地址中,bit 0 指定主器件是要自从器件读数据还是向从器件写数据。外接上拉电阻以确保在没有器件将数据线拉低时线路能保持高电平。

• 只有在总线不忙时才能启动数据传输。

• 在数据传输期间,只要 SCLx 时钟线为高电平,数据线就必须保持稳定。在 SCLx 时钟线为高电平时,数据线的电平变化将被解析为启动或停止条件。

启动数据传输 (S )

在总线空闲状态之后,当时钟(SCLx)为高电平时,SDAx 线从高电平跳变到低电平产生启动条件。所有数据传输都必须以启动条件开始。

停止数据传输 (P )

当时钟(SCLx)为高电平时,SDAx 线从低电平跳变到高电平产生停止条件。所有数据传输都必须以停止条件结束。

重复启动 (R )

在等待状态之后,当时钟(SCLx)为高电平时,SDAx 线从高电平跳变到低电平产生重复启动条件。重复启动条件使主器件可在不放弃总线控制的情况下更改所寻址从器件的总线方向。

数据有效 (D )

在启动条件之后,如果 SDAx 线在时钟信号的高电平期间保持稳定,则 SDAx 线的状态代表有效数据。每个 SCLx 时钟传送一位数据。

应答 (A )或不应答 (N )

所有的数据字节传输都必须由接收器进行应答 (ACK)或不应答 (NACK)。接收器将 SDAx 线拉低则发出 ACK,释放 SDAx 线则发出 NACK。应答为一位周期,使用一个 SCLx 时钟。

等待 / 数据无效 (Q )

数据线上的数据必须在时钟信号的低电平期间改变。器件也可以通过在 SCLx 上驱动低电平来延长时钟的低电平时间,使得总线处于等待状态。

总线空闲 (I )

在停止条件之后、启动条件之前的时间内,数据线和时钟线都保持高电平。

image.png

 使能I2C操作

通过将 ON (I2CxCON<15>)位置 1 来使能模块。

当模块使能时,主器件和从器件功能同时工作,并根据软件或总线事件作出响应。总线操作使用两个引脚。它们是 SCLx 引脚 (时钟线)和 SDAx 引脚 (数据线)。I2C 模块可产生三种中断信号:从器件中断(I2CxSIF)、主器件中断(I2CxMIF)和总线冲突中断 (I2CxBIF)。

I2C发送和接收寄存器

I2CxTRN 是写入发送数据的寄存器。在报文的处理过程中, I2CxTRN 寄存器将移出各个数据位。因此,除非总线空闲,否则不能写入 I2CxTRN。

主器件或从器件正在接收的数据移入一个不可访问的移位寄存器 I2CxRSR。当接收到完整字节时,字节将传送到 I2CxRCV 寄存器。

如果在软件从 I2CxRCV 寄存器中读取前一字节之前,模块接收到另一完整字节,则发生接收器溢出, I2COV 位 (I2CxSTAT<6>)置 1。 I2CxRSR 中的字节将丢失。

I2C波特率发生器

波特率发生器的重载值包含在 I2CxBRG 寄存器中。在写入 I2CxTRN 时,波特率发生器会自动开始计数。

FSCK = (PBCLK) / ((I2CxBRG+2) * 2)

I2CBRG = (PBCLK / (2 *FSCK)) – 2

在单主机环境中作为主器件进行通信

使用I2C来与I2C外设(例如I2C串行存储器)进行通信。在I2C系统中,主器件控制总线上所有数据通信的序列。

通过以下步骤产生序列。

1. 通过将 ON 位 (I2CxCON<15>)设置为 1 开启模块。

2. 在 SDAx 和 SCLx 上发出一个启动条件。

3. 向从器件发送带有写操作指示的 I 2 C 器件地址字节。

4. 等待并验证来自从器件的应答。

5. 向从器件发送串行存储器地址高字节。

6. 等待并验证来自从器件的应答。

7. 向从器件发送串行存储器地址低字节。

8. 等待并验证来自从器件的应答。

9. 在 SDAx 和 SCLx 上发出一个重复启动条件。

10. 向从器件发送带有读操作指示的器件地址字节。

11. 等待并验证来自从器件的应答。

12. 使能主器件接收,以接收串行存储器数据。

13. 在数据字节接收结束时产生 ACK 或 NACK 条件。

14. 在 SDAx 和 SCLx 上产生一个停止条件。

image.png

 产生启动总线事件:软件应将启动使能位 SEN(I2CxCON<0>)置 1。在置 1 启动位之前,软件可以检查 P 状态位 (I2CxSTAT<4>)来确保总线处于空闲状态。

向从器件发送数据:将适当的值写入 I2CxTRN 寄存器即可发送一个数据字节、一个 7 位器件地址字节或一个 10 位地址的第二个字节。

接收来自从器件的数据:主器件在发送了 R/W 位值为 1 的从器件地址之后,即可接收来自从器件的数据。这可通过将接收使能位 RCEN(I2CxCON<3>)置 1 来使能。主器件逻辑开始产生时钟,并且在SCLx的每个下降沿之前,对SDAx线进行采样并将数据移入I2CxRSR。

应答产生:将应答使能位 ACKEN (I2CxCON<4>)置 1,将允许产生主器件应答序列。

产生停止总线事件:将停止使能位 PEN (I2CxCON<2>)置 1,将允许产生主器件停止序列。

产生重复启动总线事件:将重复启动使能位 RSEN(I2CxCON<1>)置 1,将允许产生主器件重复启动序列

节能模式和调试模式下的I2C操作

主模式工作时的休眠:当器件进入 SLEEP (休眠)模式时,模块的所有时钟源都会被关闭。波特率发生器会由于时钟停止而停止。

从模式工作时的休眠:在器件处于 SLEEP (休眠)模式时, I 2 C 模块可以在从模式下继续工作。

空闲模式:当器件进入 IDLE(空闲)模式时,所有 PBCLK 时钟源继续工作。如果 I2CxSIDL = 0,则模块将在 IDLE (空闲)模式下继续工作。如果 I2CxSIDL = 1,则模块将在 IDLE (空闲)模式下停止工作。

调试模式下的操作:当 FRZ = 0 时,即使应用程序在 DEBUG(调试)模式下暂停,I 2 C 模块也会继续运行。当 FRZ = 1 且应用程序在 DEBUG(调试)模式下暂停时,模块将冻结其操作,并且不更改 I 2 C 模块的状态。

复位的影响

复位(POR 和 WDT 等)会禁止 I 2 C 模块并终止所有正在进行和待执行的报文活动。

5、用Harmony生成I2C工程代码

1、在Available Components组件中将I2C1添加到Project Graph中;

image.png

  2、组件添加后,组件初始内容如下;

image.png

3、生成的代码与原始代码存在差异,需进行确认;

4、代码生成后需要的操作;

        1、系统初始化完成后添加I2C初始化函数;

        2、添加应用层EEPROM读写函数;

5、编译运行将代码烧录到开发板中;

点击编译按钮,编译提示BUILD SUCCESSFUL,点击烧录,提示Programming/Verify complete,观察EEPROM读写状态,若读写正常则LED闪烁,若读写不一致,则LED熄灭。

I2C配置说明

image.png

 Operating Mode:器件工作模式,可选Master或Slave模式,主从模式选择;

I2C Clock Frequency:模块工作频率;

Disable Slew Rate Control:禁止斜率控制;

SMBus Input Levels:SMBus输入电平禁止位;

Stop in Idle Mode bit:空闲时器件停止选择;

I2C Baud Rate(Hz):I2C器件工作频率;

Include Force Write I2C Function (Master Mode Only -Ignore NACK from Slave):强制写I2C;

6、实际代码分析

plib_i2c_master.c

//I2C初始化
void I2C1_Initialize(void)
{
    /* Disable the I2C Master interrupt */
    IEC0CLR = _IEC0_I2C1MIE_MASK; //主器件中断允许控制位 (在IEC0和IEC1 INT寄存器中)
 
    /* Disable the I2C Bus collision interrupt */
    IEC0CLR = _IEC0_I2C1BIE_MASK; //总线冲突中断允许控制位 (在IEC0和IEC1 INT寄存器中)
 
    I2C1BRG = 93; //波特率发生器寄存器 400k
 
    I2C1CONCLR = _I2C1CON_SIDL_MASK; //IDLE(空闲)模式停止位
    I2C1CONCLR = _I2C1CON_DISSLW_MASK; //压摆率控制禁止位
    I2C1CONCLR = _I2C1CON_SMEN_MASK; //SMBus输入电平禁止位
 
    /* Clear master interrupt flag */
    IFS0CLR = _IFS0_I2C1MIF_MASK; //主器件中断标志状态位
 
    /* Clear fault interrupt flag */
    IFS0CLR = _IFS0_I2C1BIF_MASK; //总线冲突中断标志状态位
 
    /* Turn on the I2C module */
    I2C1CONSET = _I2C1CON_ON_MASK; //I2C使能位
 
    /* Set the initial state of the I2C state machine */
    i2c1Obj.state = I2C_STATE_IDLE;
}
 
/* I2C state machine */
static void I2C1_TransferSM(void)
{
    IFS0CLR = _IFS0_I2C1MIF_MASK;
 
    switch (i2c1Obj.state)
    {
        case I2C_STATE_START_CONDITION: //开始连接
            /* Generate Start Condition */
            I2C1CONSET = _I2C1CON_SEN_MASK; //启动条件使能位
            IEC0SET = _IEC0_I2C1MIE_MASK; //主器件中断允许控制位
            IEC0SET = _IEC0_I2C1BIE_MASK; //总线冲突中断允许控制位
            i2c1Obj.state = I2C_STATE_ADDR_BYTE_1_SEND;
            break;
 
        case I2C_STATE_ADDR_BYTE_1_SEND: //发送第一字节地址
            /* Is transmit buffer full? */
            if (!(I2C1STAT & _I2C1STAT_TBF_MASK)) //发送缓冲区满状态位
            {
                if (i2c1Obj.address > 0x007F) //有两个字节的地址,10位
                {
                    /* Transmit the MSB 2 bits of the 10-bit slave address, with R/W = 0 */
                    I2C1TRN = ( 0xF0 | (((uint8_t*)&i2c1Obj.address)[1] << 1)); //发送数据寄存器
 
                    i2c1Obj.state = I2C_STATE_ADDR_BYTE_2_SEND;
                }
                else //只有一个字节的地址 7位
                {
                    /* 8-bit addressing mode */
                    I2C1TRN = ((i2c1Obj.address << 1) | i2c1Obj.transferType);
 
                    if (i2c1Obj.transferType == I2C_TRANSFER_TYPE_WRITE) //读写
                    {
                        i2c1Obj.state = I2C_STATE_WRITE;
                    }
                    else
                    {
                        i2c1Obj.state = I2C_STATE_READ;
                    }
                }
            }
            break;
 
        case I2C_STATE_ADDR_BYTE_2_SEND: //第二个字节地址
            /* Transmit the 2nd byte of the 10-bit slave address */
            if (!(I2C1STAT & _I2C1STAT_ACKSTAT_MASK)) //应答状态位
            {
                if (!(I2C1STAT & _I2C1STAT_TBF_MASK))
                {
                    /* Transmit the remaining 8-bits of the 10-bit address */
                    I2C1TRN = i2c1Obj.address; //低的8位
 
                    if (i2c1Obj.transferType == I2C_TRANSFER_TYPE_WRITE)
                    {
                        i2c1Obj.state = I2C_STATE_WRITE;
                    }
                    else
                    {
                        i2c1Obj.state = I2C_STATE_READ_10BIT_MODE;
                    }
                }
            }
            else //发送第一个字节地址后,没有收到应答
            {
                /* NAK received. Generate Stop Condition. */
                i2c1Obj.error = I2C_ERROR_NACK; //无应答错误
                I2C1CONSET = _I2C1CON_PEN_MASK; //停止条件使能位
                i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
            }
            break;
 
        case I2C_STATE_READ_10BIT_MODE: //发送10位地址后,读取10bit模式
            if (!(I2C1STAT & _I2C1STAT_ACKSTAT_MASK)) //应答位状态
            {
                /* Generate repeated start condition */
                I2C1CONSET = _I2C1CON_RSEN_MASK; //重复启动条件使能位
                i2c1Obj.state = I2C_STATE_ADDR_BYTE_1_SEND_10BIT_ONLY;
            }
            else
            {
                /* NAK received. Generate Stop Condition. */
                i2c1Obj.error = I2C_ERROR_NACK;
                I2C1CONSET = _I2C1CON_PEN_MASK;
                i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
            }
            break;
 
        case I2C_STATE_ADDR_BYTE_1_SEND_10BIT_ONLY:
            /* Is transmit buffer full? */
            if (!(I2C1STAT & _I2C1STAT_TBF_MASK)) //发送缓冲区满状态位
            {
                /* Transmit the first byte of the 10-bit slave address, with R/W = 1 */
                I2C1TRN = ( 0xF1 | ((((uint8_t*)&i2c1Obj.address)[1] << 1)));
                i2c1Obj.state = I2C_STATE_READ;
            }
            else
            {
                /* NAK received. Generate Stop Condition. */
                i2c1Obj.error = I2C_ERROR_NACK;
                I2C1CONSET = _I2C1CON_PEN_MASK;
                i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
            }
            break;
 
        case I2C_STATE_WRITE:
            if (!(I2C1STAT & _I2C1STAT_ACKSTAT_MASK)) //应答位状态
            {
                /* ACK received */
                if (i2c1Obj.writeCount < i2c1Obj.writeSize)
                {
                    if (!(I2C1STAT & _I2C1STAT_TBF_MASK)) //发送缓冲区满状态位
                    {
                        /* Transmit the data from writeBuffer[] */
                        I2C1TRN = i2c1Obj.writeBuffer[i2c1Obj.writeCount++];
                    }
                }
                else
                {
                    if (i2c1Obj.readCount < i2c1Obj.readSize)
                    {
                        /* Generate repeated start condition */
                        I2C1CONSET = _I2C1CON_RSEN_MASK; //重复启动条件使能位
 
                        i2c1Obj.transferType = I2C_TRANSFER_TYPE_READ;
 
                        if (i2c1Obj.address > 0x007F) //10bit地址
                        {
                            /* Send the I2C slave address with R/W = 1 */
                            i2c1Obj.state = I2C_STATE_ADDR_BYTE_1_SEND_10BIT_ONLY;
                        }
                        else
                        {
                            /* Send the I2C slave address with R/W = 1 */
                            i2c1Obj.state = I2C_STATE_ADDR_BYTE_1_SEND;
                        }
 
                    }
                    else //数据接收完成
                    {
                        /* Transfer Complete. Generate Stop Condition */
                        I2C1CONSET = _I2C1CON_PEN_MASK;
                        i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
                    }
                }
            }
            else
            {
                /* NAK received. Generate Stop Condition. */
                i2c1Obj.error = I2C_ERROR_NACK;
                I2C1CONSET = _I2C1CON_PEN_MASK;
                i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
            }
            break;
 
        case I2C_STATE_READ:
            if (!(I2C1STAT & _I2C1STAT_ACKSTAT_MASK)) //应答位状态
            {
                /* Slave ACK'd the device address. Enable receiver. */
                I2C1CONSET = _I2C1CON_RCEN_MASK; //接收使能位
                i2c1Obj.state = I2C_STATE_READ_BYTE;
            }
            else
            {
                /* NAK received. Generate Stop Condition. */
                i2c1Obj.error = I2C_ERROR_NACK;
                I2C1CONSET = _I2C1CON_PEN_MASK;
                i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
            }
            break;
        case I2C_STATE_READ_BYTE:
            /* Data received from the slave */
            if (I2C1STAT & _I2C1STAT_RBF_MASK) //接收缓冲区满状态位
            {
                i2c1Obj.readBuffer[i2c1Obj.readCount++] = I2C1RCV; //接收数据寄存器
                if (i2c1Obj.readCount == i2c1Obj.readSize)
                {
                    /* Send NAK */
                    I2C1CONSET = _I2C1CON_ACKDT_MASK; //应答数据位 发送NACK
                    I2C1CONSET = _I2C1CON_ACKEN_MASK; //应答序列使能位
                }
                else
                {
                    /* Send ACK */
                    I2C1CONCLR = _I2C1CON_ACKDT_MASK; //应答数据位 发送ACK
                    I2C1CONSET = _I2C1CON_ACKEN_MASK; //应答序列使能位
                }
                i2c1Obj.state = I2C_STATE_WAIT_ACK_COMPLETE;
            }
            break;
        case I2C_STATE_WAIT_ACK_COMPLETE:
            /* ACK or NAK sent to the I2C slave */
            if (i2c1Obj.readCount < i2c1Obj.readSize)
            {
                /* Enable receiver */
                I2C1CONSET = _I2C1CON_RCEN_MASK; //接收使能位
                i2c1Obj.state = I2C_STATE_READ_BYTE;
            }
            else
            {
                /* Generate Stop Condition */
                I2C1CONSET = _I2C1CON_PEN_MASK; //停止条件使能位
                i2c1Obj.state = I2C_STATE_WAIT_STOP_CONDITION_COMPLETE;
            }
            break;
        case I2C_STATE_WAIT_STOP_CONDITION_COMPLETE:
            i2c1Obj.state = I2C_STATE_IDLE;
            IEC0CLR = _IEC0_I2C1MIE_MASK; //主器件中断允许控制位
            IEC0CLR = _IEC0_I2C1BIE_MASK; //总线冲突中断允许控制位
            if (i2c1Obj.callback != NULL)
            {
                i2c1Obj.callback(i2c1Obj.context);
            }
            break;
        default:
            break;
    }
}
void I2C1_CallbackRegister(I2C_CALLBACK callback, uintptr_t contextHandle)
{
    if (callback == NULL)
    {
        return;
    }
    i2c1Obj.callback = callback;
    i2c1Obj.context = contextHandle;
}
bool I2C1_IsBusy(void)
{
    if( (i2c1Obj.state != I2C_STATE_IDLE ) || (I2C1CON & 0x0000001F) || //
        (I2C1STAT & _I2C1STAT_TRSTAT_MASK) || (I2C1STAT & _I2C1STAT_S_MASK) ) //发送状态位,启动位
    {
        return true;
    }
    else
    {
        return false;
    }
}
bool I2C1_Read(uint16_t address, uint8_t* rdata, size_t rlength)
{
    /* State machine must be idle and I2C module should not have detected a start bit on the bus */
    if((i2c1Obj.state != I2C_STATE_IDLE) || (I2C1STAT & _I2C1STAT_S_MASK)) //非空闲,检测到启动
    {
        return false;
    }
    i2c1Obj.address             = address;
    i2c1Obj.readBuffer          = rdata;
    i2c1Obj.readSize            = rlength;
    i2c1Obj.writeBuffer         = NULL;
    i2c1Obj.writeSize           = 0;
    i2c1Obj.writeCount          = 0;
    i2c1Obj.readCount           = 0;
    i2c1Obj.transferType        = I2C_TRANSFER_TYPE_READ;
    i2c1Obj.error               = I2C_ERROR_NONE;
    i2c1Obj.state               = I2C_STATE_ADDR_BYTE_1_SEND;
    I2C1CONSET                  = _I2C1CON_SEN_MASK; //启动条件使能位
    IEC0SET                     = _IEC0_I2C1MIE_MASK; //主器件中断允许控制位
    IEC0SET                     = _IEC0_I2C1BIE_MASK; //总线冲突中断允许控制位
    return true;
}
bool I2C1_Write(uint16_t address, uint8_t* wdata, size_t wlength)
{
    /* State machine must be idle and I2C module should not have detected a start bit on the bus */
    if((i2c1Obj.state != I2C_STATE_IDLE) || (I2C1STAT & _I2C1STAT_S_MASK))
    {
        return false;
    }
    i2c1Obj.address             = address;
    i2c1Obj.readBuffer          = NULL;
    i2c1Obj.readSize            = 0;
    i2c1Obj.writeBuffer         = wdata;
    i2c1Obj.writeSize           = wlength;
    i2c1Obj.writeCount          = 0;
    i2c1Obj.readCount           = 0;
    i2c1Obj.transferType        = I2C_TRANSFER_TYPE_WRITE;
    i2c1Obj.error               = I2C_ERROR_NONE;
    i2c1Obj.state               = I2C_STATE_ADDR_BYTE_1_SEND;
    I2C1CONSET                  = _I2C1CON_SEN_MASK; //启动条件使能位
    IEC0SET                     = _IEC0_I2C1MIE_MASK; //主器件中断允许控制位
    IEC0SET                     = _IEC0_I2C1BIE_MASK; //总线冲突中断允许控制位
    return true;
}
bool I2C1_WriteRead(uint16_t address, uint8_t* wdata, size_t wlength, uint8_t* rdata, size_t rlength)
{
    /* State machine must be idle and I2C module should not have detected a start bit on the bus */
    if((i2c1Obj.state != I2C_STATE_IDLE) || (I2C1STAT & _I2C1STAT_S_MASK))
    {
        return false;
    }
    i2c1Obj.address             = address;
    i2c1Obj.readBuffer          = rdata;
    i2c1Obj.readSize            = rlength;
    i2c1Obj.writeBuffer         = wdata;
    i2c1Obj.writeSize           = wlength;
    i2c1Obj.writeCount          = 0;
    i2c1Obj.readCount           = 0;
    i2c1Obj.transferType        = I2C_TRANSFER_TYPE_WRITE;
    i2c1Obj.error               = I2C_ERROR_NONE;
    i2c1Obj.state               = I2C_STATE_ADDR_BYTE_1_SEND;
    I2C1CONSET                  = _I2C1CON_SEN_MASK;
    IEC0SET                     = _IEC0_I2C1MIE_MASK;
    IEC0SET                     = _IEC0_I2C1BIE_MASK;
    return true;
}
I2C_ERROR I2C1_ErrorGet(void)
{
    I2C_ERROR error;
    error = i2c1Obj.error;
    i2c1Obj.error = I2C_ERROR_NONE;
    return error;
}
bool I2C1_TransferSetup(I2C_TRANSFER_SETUP* setup, uint32_t srcClkFreq )
{
    uint32_t baudValue;
    uint32_t i2cClkSpeed;
    if (setup == NULL)
    {
        return false;
    }
    i2cClkSpeed = setup->clkSpeed;
    /* Maximum I2C clock speed cannot be greater than 1 MHz */
    if (i2cClkSpeed > 1000000) //1Mhz
    {
        return false;
    }
    if( srcClkFreq == 0)
    {
        srcClkFreq = 80000000UL;
    }
    baudValue = ((float)((float)srcClkFreq/2.0) * (1/(float)i2cClkSpeed - 0.000000130)) - 1;
    /* I2CxBRG value cannot be from 0 to 3 or more than the size of the baud rate register */
    if ((baudValue < 4) || (baudValue > 4095))
    {
        return false;
    }
    I2C1BRG = baudValue; //波特率发生器值
    /* Enable slew rate for 400 kHz clock speed; disable for all other speeds */
    if (i2cClkSpeed == 400000)
    {
        I2C1CONCLR = _I2C1CON_DISSLW_MASK;; //压摆率控制禁止位
    }
    else
    {
        I2C1CONSET = _I2C1CON_DISSLW_MASK;;
    }
    return true;
}
static void I2C1_BUS_InterruptHandler(void)
{
    /* Clear the bus collision error status bit */
    I2C1STATCLR = _I2C1STAT_BCL_MASK; //主器件总线冲突检测位
    /* ACK the bus interrupt */
    IFS0CLR = _IFS0_I2C1BIF_MASK; //总线冲突中断标志状态位
    i2c1Obj.state = I2C_STATE_IDLE;
    i2c1Obj.error = I2C_ERROR_BUS_COLLISION;
    if (i2c1Obj.callback != NULL)
    {
        i2c1Obj.callback(i2c1Obj.context);
    }
}
static void I2C1_MASTER_InterruptHandler(void)
{
    I2C1_TransferSM();
}
void I2C_1_InterruptHandler(void)
{
    if ((IFS0 & _IFS0_I2C1BIF_MASK) && (IEC0 & _IEC0_I2C1BIE_MASK))
    {
        I2C1_BUS_InterruptHandler();
    }
    else if ((IFS0 & _IFS0_I2C1MIF_MASK) && (IEC0 & _IEC0_I2C1MIE_MASK))
    {
        I2C1_MASTER_InterruptHandler();
    }
}

interrupts.c

//中断服务函数
void __ISR(_I2C_1_VECTOR, ipl1SOFT) I2C_1_Handler (void)
{
    I2C_1_InterruptHandler();
}

app.c

#define APP_AT24MAC_DEVICE_ADDR             0x0050
#define APP_AT24MAC_MEMORY_ADDR             0x00
#define APP_AT24MAC_MEMORY_ADDR1            0x00
#define APP_TRANSMIT_DATA_LENGTH            6
#define APP_ACK_DATA_LENGTH                 1
#define APP_RECEIVE_DUMMY_WRITE_LENGTH      2
#define APP_RECEIVE_DATA_LENGTH             4
 
 
 uint8_t testTxData[APP_TRANSMIT_DATA_LENGTH] =
{
    0x01,0x02,0x03,0x04,0x05,0x06,
};
 
 uint8_t  testRxData[APP_RECEIVE_DATA_LENGTH];
 
typedef enum
{
    APP_STATE_EEPROM_STATUS_VERIFY,
    APP_STATE_EEPROM_WRITE,
    APP_STATE_EEPROM_WAIT_WRITE_COMPLETE,
    APP_STATE_EEPROM_CHECK_INTERNAL_WRITE_STATUS,
    APP_STATE_EEPROM_READ,
    APP_STATE_EEPROM_WAIT_READ_COMPLETE,
    APP_STATE_VERIFY,
    APP_STATE_IDLE,
    APP_STATE_XFER_SUCCESSFUL,
    APP_STATE_XFER_ERROR
 
} APP_STATES;
 
typedef enum
{
    APP_TRANSFER_STATUS_IN_PROGRESS,
    APP_TRANSFER_STATUS_SUCCESS,
    APP_TRANSFER_STATUS_ERROR,
    APP_TRANSFER_STATUS_IDLE,
 
} APP_TRANSFER_STATUS;
 
void APP_I2CCallback(uintptr_t context )
{
    APP_TRANSFER_STATUS* transferStatus = (APP_TRANSFER_STATUS*)context;
 
    if(I2C1_ErrorGet() == I2C_ERROR_NONE)
    {
        if (transferStatus)
        {
            *transferStatus = APP_TRANSFER_STATUS_SUCCESS;
        }
    }
    else
    {
        if (transferStatus)
        {
            *transferStatus = APP_TRANSFER_STATUS_ERROR;
        }
    }
}
 
 
void App_Task(void)
{
    uint8_t ackData = 0;
    APP_STATES state = APP_STATE_EEPROM_STATUS_VERIFY;
    volatile APP_TRANSFER_STATUS transferStatus = APP_TRANSFER_STATUS_ERROR;
    
    
	I2C1_Initialize();
 
	while(1)
	{
		switch (state)
        {
            case APP_STATE_EEPROM_STATUS_VERIFY:
 
                /* Register the TWIHS Callback with transfer status as context */
                I2C1_CallbackRegister( APP_I2CCallback, (uintptr_t)&transferStatus );
 
               /* Verify if EEPROM is ready to accept new requests */
                transferStatus = APP_TRANSFER_STATUS_IN_PROGRESS;
                I2C1_Write(APP_AT24MAC_DEVICE_ADDR, &ackData, APP_ACK_DATA_LENGTH);
 
                state = APP_STATE_EEPROM_WRITE;
                break;
 
            case APP_STATE_EEPROM_WRITE:
 
                if (transferStatus == APP_TRANSFER_STATUS_SUCCESS)
                {
                    /* Write data to EEPROM */
                    transferStatus = APP_TRANSFER_STATUS_IN_PROGRESS;
                    I2C1_Write(APP_AT24MAC_DEVICE_ADDR, &testTxData[0], APP_TRANSMIT_DATA_LENGTH);
                    state = APP_STATE_EEPROM_WAIT_WRITE_COMPLETE;
                }
                else if (transferStatus == APP_TRANSFER_STATUS_ERROR)
                {
                    /* EEPROM is not ready to accept new requests */
                    state = APP_STATE_XFER_ERROR;
                }
                break;
 
            case APP_STATE_EEPROM_WAIT_WRITE_COMPLETE:
 
                if (transferStatus == APP_TRANSFER_STATUS_SUCCESS)
                {
                    /* Read the status of internal write cycle */
                    transferStatus = APP_TRANSFER_STATUS_IN_PROGRESS;
                    I2C1_Write(APP_AT24MAC_DEVICE_ADDR, &ackData, APP_ACK_DATA_LENGTH);
                    state = APP_STATE_EEPROM_CHECK_INTERNAL_WRITE_STATUS;
                }
                else if (transferStatus == APP_TRANSFER_STATUS_ERROR)
                {
                    state = APP_STATE_XFER_ERROR;
                }
                break;
 
             case APP_STATE_EEPROM_CHECK_INTERNAL_WRITE_STATUS:
 
                if (transferStatus == APP_TRANSFER_STATUS_SUCCESS)
                {
                    state = APP_STATE_EEPROM_READ;
                }
                else if (transferStatus == APP_TRANSFER_STATUS_ERROR)
                {
                    /* EEPROM's internal write cycle is not complete. Keep checking. */
                    transferStatus = APP_TRANSFER_STATUS_IN_PROGRESS;
                    I2C1_Write(APP_AT24MAC_DEVICE_ADDR, &ackData, APP_ACK_DATA_LENGTH);
                }
                break;
            case APP_STATE_EEPROM_READ:
                transferStatus = APP_TRANSFER_STATUS_IN_PROGRESS;
                /* Read the data from the page written earlier */
                I2C1_WriteRead(APP_AT24MAC_DEVICE_ADDR, &testTxData[0], 2,  &testRxData[0], APP_RECEIVE_DATA_LENGTH);
                state = APP_STATE_EEPROM_WAIT_READ_COMPLETE;
                break;
            case APP_STATE_EEPROM_WAIT_READ_COMPLETE:
                if (transferStatus == APP_TRANSFER_STATUS_SUCCESS)
                {
                    state = APP_STATE_VERIFY;
                }
                else if (transferStatus == APP_TRANSFER_STATUS_ERROR)
                {
                    state = APP_STATE_XFER_ERROR;
                }
                break;
            case APP_STATE_VERIFY:
                if (memcmp(&testTxData[2], &testRxData[0], APP_RECEIVE_DATA_LENGTH) != 0)
                {
                    /* It means received data is not same as transmitted data */
                    state = APP_STATE_XFER_ERROR;
                }
                else
                {
                    /* It means received data is same as transmitted data */
                    state = APP_STATE_XFER_SUCCESSFUL;
                }
                break;
            case APP_STATE_XFER_SUCCESSFUL:
            {
                GPIO_RB0_Toggle();
				Delay_xmS(1000);
                break;
            }
            case APP_STATE_XFER_ERROR:
            {
                GPIO_RB0_Clear();
                break;
            }
            default:
                break;
        }
	}
}

7、实验验证

点击编译按钮,编译提示BUILD SUCCESSFUL,点击烧录,提示Programming/Verify complete,

使用Debug观察变量数值,读出数据与写入数据一致,LED灯闪烁。

image.png

另一种IIC驱动工程代码,有时间将在后续提供。

image.png

————————————————

版权声明:本文为CSDN博主「Huangtop」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Huangtop/article/details/119651512