第十六章 I2C

第十六章 I2C

目录

[第十六章 I2C](#第十六章 I2C)

[1 I2C简介](#1 I2C简介)

[2 I2C从模式](#2 I2C从模式)

[3 I2C主模式](#3 I2C主模式)

[4 SDA/SCL线控制](#4 SDA/SCL线控制)

[5 I2C中断请求](#5 I2C中断请求)

[6 例程设计](#6 例程设计)

[6.1 IIC_IntTransmit例程](#6.1 IIC_IntTransmit例程)

[6.2 IIC_Software例程](#6.2 IIC_Software例程)

[7 下载验证](#7 下载验证)

[7.1 IIC_IntTransmit例程](#7.1 IIC_IntTransmit例程)

[7.2 IIC_Software例程](#7.2 IIC_Software例程)


1 I2C简介

I2C(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器以及其外围设备。它是由数据线 SDA 和时钟线 SCL 构成的串行总线,可发送和接收数据,在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送。

I2C 总线有如下特点:

  • 总线由数据线 SDA 和时钟线 SCL 构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。
  • 总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时序就可以实现微控制器与器件之间的通信。
  • 数据线 SDA 和时钟线 SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电压,所以当总线空闲的时候,这两条线路都是高电平。
  • 总线上数据的传输速率在标准模式下可达 100kbit/s 在快速模式下可达 400kbit/s 在高速模式下可达 3.4Mbit/s。
  • 总线支持设备连接。在使用 I2C 通信总线时,可以有多个具备 I2C 通信能力的设备挂载在上面,同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容 400pF 的限制决定。I2C 总线挂载多个器件的示意图,如下图所示:

I2C 总线挂载多个器件

下面来学习I2C 总线协议,I2C 总线时序图如下所示:

I2C 总线时序图

为了便于大家更好的了解 I2C 协议,我们从起始信号、停止信号、应答信号、数据有效性、数据传输以及空闲状态等 6 个方面讲解。

① 起始信号

当 SCL 为高电平期间,SDA 由高到低的跳变。起始信号是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。

② 停止信号

当 SCL 为高电平期间,SDA 由低到高的跳变。停止信号也是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态。

③ 应答信号

发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

观察上图标号③就可以发现,有效应答的要求是从机在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主机,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放SDA 线,以便主机接收器发送一个停止信号。

④ 数据有效性

I2C 总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。数据在 SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定。

⑤ 数据传输

在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行时钟的配合下,在 SDA 上逐位地串行传送每一位数据。数据位的传输是边沿触发。

⑥ 空闲状态

I2C 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

了解前面的知识后,下面介绍一下 I2C 的基本的读写通讯过程,包括主机写数据到从机即写操作,主机到从机读取数据即读操作。下面先看一下写操作通讯过程图。

写操作通讯过程

主机首先在 I2C 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的 8bit 数据,所有从机接收到该 8bit 数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。

主机在总线上接收到有应答信号后,才能继续向从机发送数据。

注意:I2C 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。

接着讲解一下 I2C 总线的读操作过程,先看一下读操作通讯过程图。

读操作通讯过程图

主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的 8bit 数据,从机接收到数据验证是否是自身的地址。 那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回 8bit 数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。

2 I2C从模式

默认情况下,I2C 接口总是工作在从模式。从从模式切换到主模式,需要产生一个起始条件。为了产生正确的时序,必须在 I2C_CR2 寄存器中设定该模块的输入时钟。输入时钟的频率必须至少是:

标准模式下为:2MHz

快速模式下为:4MHz

一旦检测到起始条件,在 SDA线上接收到的地址被送到移位寄存器。然后与芯片自己的地址 OAR1和 OAR2(当 ENDUAL=1)或者广播呼叫地址(如果 ENGC=1)相比较。

注:在 10 位地址模式时,比较包括头段序列(11110xx0),其中的 xx 是地址的两个最高有效位。头段或地址不匹配:I2C 接口将其忽略并等待另一个起始条件。

头段匹配(仅 10 位模式):如果 ACK 位被置'1',I2C 接口产生一个应答脉冲并等待 8 位从地址。地址匹配:I2C 接口产生以下时序:

如果 ACK 被置'1',则产生一个应答脉冲硬件设置 ADDR 位;如果设置了 ITEVFEN 位,则产生一个中断如果 ENDUAL=1,软件必须读 DUALF 位,以确认响应了哪个从地址。在 10 位模式,接收到地址序列后,从设备总是处于接收器模式。在收到与地址匹配的头序列并且最低位为'1'(即 11110xx1)后,当接收到重复的起始条件时,将进入发送器模式。在从模式下 TRA 位指示当前是处于接收器模式还是发送器模式。

从发送器

在接收到地址和清除 ADDR 位后,从发送器将字节从 DR 寄存器经由内部移位寄存器发送到 SDA 线上。

从设备保持 SCL 为低电平,直到 ADDR 位被清除并且待发送数据已写入 DR 寄存器。(见下图中的EV1 和 EV3)。当收到应答脉冲时:TxE 位被硬件置位,如果设置了 ITEVFEN 和 ITBUFEN 位,则产生一个中断。

如果 TxE 位被置位,但在下一个数据发送结束之前没有新数据写入到 I2C_DR 寄存器,则 BTF 位被置位,在清除 BTF 之前 I2C 接口将保持 SCL 为低电平;读出 I2C_SR1 之后再写入 I2C_DR 寄存器将清除 BTF 位。

从发送器的传送序列图

说明:S=Start(起始条件),S,=重复的起始条件,P=Stop(停止条件),A=应,NA=非响应 EVx=事件(ITEVFEN=1 时产生中断)

EV1:ADDR=1,读 SR1 然后读 SR2 将清除该事件。

EV3-1:TxE=1,移位寄存器空,数据寄存器空,写 DR。

EV3:TxE=1,移位寄存器非空,数据寄存空,写 DR 将清除该事件。

EV3-2:AF=1,在 SR1 寄存器的 AF 位写'0'可清除 AF 位。

注:

1-EV1 和 EV3 1 事件拉长 SCL 低的时间,直到对应的软件序列结束。

2-EV3 的软件序列必须在当前字节传输结束之前完成。

从接收器

在接收到地址并清除 ADDR 后,从接收器将通过内部移位寄存器从 SDA 线接收到的字节存进 DR 寄存器。I2C 接口在接收到每个字节后都执行下列操作:

如果设置了 ACK 位,则产生一个应答脉冲硬件设置 RxNE=1。如果设置了 ITEVFEN 和 ITBUFEN 位,则产生一个中断。

如果 RxNE 被置位,并且在接收新的数据结束之前 DR 寄存器未被读出,BTF 位被置位,在清除BTF 之前 I2C 接口将保持 SCL 为低电平;读出 I2C_SR1 之后再写入 I2C_DR 寄存器将清除 BTF 位。

从接收器的传送序列图

说明:S=Start(起始条件),S,=重复的起始条件,P=Stop(停止条件),A=响应,NA=非响应,EVx=事件(ITEVFEN=1 时产生中断)

EV1:ADDR=1,读 SR1 然后读 SR2 将清除该事件。

EV2:RxNE=1,读 DR 将消除该事件。

EV4:STOPF=1,读 SR1 然后写 CR1 寄存器将清除该事件。

注:

1.EV1 事件拉长 SCL 低的时间,直到对应的软件序列结束。

2.EV2 的软件序列必须在当前字节传输结束之前完成。关闭从通信在传输完最后一个数据字节后,主设备产生一个停止条件,I2C 接口检测到这一条件时:

  • 设置 STOPF=1,如果设置了 ITEVFEN 位,则产生一个中断。然后 I2C 接口等待读 SR1 寄存器,再写 CR1 寄存器。

3 I2C主模式

在主模式时,I2C 接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。当通过 START 位在总线上产生了起始条件,设备就进入了主模式。

以下是主模式所要求的操作顺序:

  1. 在 I2C_CR2 寄存器中设定该模块的输入时钟以产生正确的时序
  2. 配置时钟控制寄存器
  3. 配置上升时间寄存器
  4. 编程 I2C_CR1 寄存器启动外设
  5. 置 I2C_CR1 寄存器中的 START 位为 1,产生起始条件
  6. I2C 模块的输入时钟频率必须至少是:
  7. 标准模式下为:2MHz
  8. 快速模式下为:4MHz
  9. 起始条件

当 BUSY=0 时,设置 START=1,I2C 接口将产生一个开始条件并切换至主模式(M/SL 位置位)。

注:在主模式下,设置 START 位将在当前字节传输完后由硬件产生一个重开始条件。一旦发出开始条件:

SB 位被硬件置位,如果设置了 ITEVFEN 位,则会产生一个中断。然后主设备等待读 SR1 寄存器,紧跟着将从地址写入 DR 寄存器。

从地址的发送

从地址通过内部移位寄存器被送到 SDA 线上。

  • 在 10 位地址模式时,发送一个头段序列产生以下事件:
    • ADD10 位被硬件置位,如果设置了 ITEVFEN 位,则产生一个中断。然后主设备等待读 SR1 寄存器,再将第二个地址字节写入 DR 寄存器。

ADDR 位被硬件置位,如果设置了 ITEVFEN 位,则产生一个中断。随后主设备等待一次读 SR1 寄存器,跟着读 SR2 寄存器。

  • 在 7 位地址模式时,只需送出一个地址字节。

一旦该地址字节被送出:

    • − ADDR 位被硬件置位,如果设置了 ITEVFEN 位,则产生一个中断。

随后主设备等待一次读 SR1 寄存器,跟着读 SR2 寄存器。根据送出从地址的最低位,主设备决定进入发送器模式还是进入接收器模式。

  • 在 7 位地址模式时
    • − 要进入发送器模式,主设备发送从地址时置最低位为'0'。
    • − 要进入接收器模式,主设备发送从地址时置最低位为'1'。
  • 在 10 位地址模式时
    • − 要进入发送器模式,主设备先送头字节(11110xx0),然后送最低位为'0'的从地址。(这里 xx代表 10 位地址中的最高 2 位。)
    • − 要进入接收器模式,主设备先送头字节(11110xx0),然后送最低位为'1'的从地址。然后再重新发送一个开始条件,后面跟着头字节(11110xx1)(这里 xx 代表 10 位地址中的最高 2位。)

TRA 位指示主设备是在接收器模式还是发送器模式。

主发送器

在发送了地址和清除了 ADDR 位后,主设备通过内部移位寄存器将字节从 DR 寄存器发送到 SDA 线上。

主设备等待,直到 TxE 被清除。当收到应答脉冲时:

TxE 位被硬件置位,如果设置了 INEVFEN 和 ITBUFEN 位,则产生一个中断。如果 TxE 被置位并且在上一次数据发送结束之前没有写新的数据字节到 DR 寄存器,则 BTF 被硬件置位,在清除 BTF 之前 I2C 接口将保持 SCL 为低电平;读出 I2C_SR1 之后再写入 I2C_DR 寄存器将清除 BTF 位。

关闭通信

在 DR 寄存器中写入最后一个字节后,通过设置 STOP 位产生一个停止条件,然后 I2C 接口将自动回到从模式(M/S 位清除)。

注: 当 TxE 或 BTF 位置位时,停止条件应安排在出现 EV8_2 事件时。

主发送器传送序列图

说明:S=Star(起始条件),S;=重复的起始条件,P=Stop(停止条件),A=响应,NA=非响应,EVx=事件(ITEVFEN=1 时产生中断)。

EV5:SB=1,读 SR1 然后将地址写入 DR 寄存器将清除该事件。

EV6:ADDR=1,读 SR1 然后读 SR2 将清除该事件。

EV8 1:TxE=1,移位寄存器空,数据寄存器空,写 DR 寄存器。

EV8:TxE=1,移位寄存器非空,数据寄存器空,写入 DR 寄存器将清除该事件。

EV8 2:TxE=1,BTF=1,请求设置停止位。TxE 和 BTF 位由硬件在产生停止条件时清除。

EV9:ADDR10=1,读 SR1 然后写入 DR 寄存将清除该事件。

注:1-EV5、EV6、EV9、EV8 1 和 EV8 2 事件拉长 SCL 低的时间,直到对应的软件序列结束。

主接收器

在发送地址和清除 ADDR 之后,I2C 接口进入主接收器模式。在此模式下,I2C 接口从 SDA 线接收数据字节,并通过内部移位寄存器送至 DR 寄存器。在每个字节后,I2C 接口依次执行以下操作:

如果 ACK 位被置位,发出一个应答脉冲。

硬设置 RxNE=1, 如果设置了 INEVFEN 和 ITBUFEN 位,则会产生一个中断。

如果 RxNE 位被置位,并且在接收新数据结束前,DR 寄存器中的数据没有被读走,硬件将设置BTF=1,在清除 BTF 之前 I2C 接口将保持 SCL 为低电平;读出 I2C_SR1 之后再读出 I2C_DR 寄存器将清除 BTF 位。

关闭通信

主设备在从从设备接收到最后一个字节后发送一个 NACK。接收到 NACK 后,从设备释放对 SCL 和SDA 线的控制;主设备就可以发送一个停止/重起始条件。为了在收到最后一个字节后产生一个 NACK 脉冲,在读倒数第二个数据字节之后(在倒数第二个 RxNE 事件之后)必须清除 ACK 位。为了产生一个停止/重起始条件,软件必须在读倒数第二个数据字节之后(在倒数第二个 RxNE事件之后)设置 STOP/START 位。只接收一个字节时,刚好在 EV6 之后(EV6_1 时,清除 ADDR 之后)要关闭应答和停止条件的产生位。在产生了停止条件后,I2C 接口自动回到从模式(M/SL 位被清除)。

主接收器传送序列图

说明:S=Start(起始条件),S:=重复的起始条件,P=Stop(停止条件),A=响应,NA=非响应,EVx=事件(ITEVFEN=1 时产生中断)EV5:SB=1,读 SR1 然后将地址写入 DR 寄存器将除该事件。EV6:ADDR=1,读 SR1 然后读 SR2 将清除该事件。在 10位主接收模式下,该事件后应设置 CR2的 START=1.EV6 1:没有对应的事件标志,只适于接收 1 个字节的情况。恰好在 EV6 之后(即清除了 ADDR 之后),要洁除响应和停止条件的产生。

EV7:RxNE=1,读 DR 寄存器清除该事件。

EV7 1:RXNE=1,读 DR 寄存器清除该事件。设置 ACK=0 和 STOP 请求。

EV9:ADDR10=1,读 SR1 然后写入 DR 寄存将清除该事件。

  1. 如果收到一个单独的字节,则是 NA。

  2. EV5、EV6 和 E事件拉长 SCL 低电平,直到对应的软件序列结束。

  3. EV7 的软件序列必须在当前字节传输结束前完成。

EV6_1 或 EV7_1 的软件序列必须在当前传输字节的 ACK 脉冲之前完成。

4 SDA/SCL线控制

如果允许时钟延长:

  • 发送器模式:如果 TxE=1 且 BTF=1:I2C 接口在传输前保持时钟线为低,以等待软件读取SR1,然后把数据写进数据寄存器(缓冲器和移位寄存器都是空的)。
  • 接收器模式:如果 RxNE=1 且 BTF=1:I2C 接口在接收到数据字节后保持时钟线为低,以等待软件读 SR1,然后读数据寄存器 DR(缓冲器和移位寄存器都是满的)。
  • 如果在从模式中禁止时钟延长:
  • 如果 RxNE=1,在接收到下个字节前 DR 还没有被读出,则发生过载错。接收到的最后一个字节丢失。
  • 如果 TxE=1,在必须发送下个字节之前却没有新数据写进 DR,则发生欠载错。相同的字节将被重复发出。
  • 不控制重复写冲突。

5 I2C中断请求

下图列出了所有的 I2C 中断请求:

|---------------------|----------|-------------------|
| 中断事件 | 事件标志 | 开启控制位 |
| 起始位已发送 (主) | SB | ITEVFEN |
| 地址已发送 (主) 或地址匹配 (从) | ADDR | ITEVFEN |
| 10 位头段已发送 (主) | ADD10 | ITEVFEN |
| 已收到停止 (从) | STOPF | ITEVFEN |
| 数据字节传输完成 | BTF | ITEVFEN |
| 接收缓冲区非空 | RxNE | ITEVFEN 和 ITBUFEN |
| 发送缓冲区空 | TxE | ITEVFEN 和 ITBUFEN |
| 总线错误 | BERR | ITERREN |
| 仲裁丢失 (主) | ARLO | ITERREN |
| 响应失败 | AF | ITERREN |
| 过载 / 欠载 | OVR | ITERREN |
| PEC 错误 | PECERR | ITERREN |
| 超时 / Tlow 错误 | TIMEOUT | ITERREN |
| SMBus 提醒 | SMBALERT | - |

注:

1.SB、ADDR、ADD10、STOPF、BTF、RxNE 和 TxE 通过逻辑或汇到同一个中断通道中。

2.BERR、ARLO、AF、OVR、PECERR、TIMEOUT 和 SMBALERT 通过逻辑或汇到同一个中断通道中。

I2C 中断映射图

6 例程设计

6.1 IIC_IntTransmit例程

1.UART 模块:配置 USART1,将printf输出重定向到该串口,用于与用户交互和输出调试信息。

2.I2C 配置模块:使能 GPIOB 和 I2C1 时钟,配置引脚,初始化 I2C1,设置工作模式、时钟速度等参数。

复制代码
void IIC_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_I2C2, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    //Enable internal pull-up function
    GPIO_ForcePuPdCmd(GPIOB, ENABLE);
    GPIO_ForcePullUpConfig(GPIOB, GPIO_Pin_6);
    GPIO_ForcePullUpConfig(GPIOB, GPIO_Pin_7);

    I2C_DeInit(I2C1);
    I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle           = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_OwnAddress1         = 0xA0;
    I2C_InitStructure.I2C_ClockSpeed          = 100000;
    I2C_InitStructure.I2C_Ack                 = I2C_Ack_Enable;

    I2C_Init(I2C1, &I2C_InitStructure);
    I2C_Cmd(I2C1, ENABLE);
}

3.主设备测试模块:填充发送缓冲区,生成 I2C 起始信号,发送从设备地址和数据,最后生成停止信号。

复制代码
void IIC_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_I2C2, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    //Enable internal pull-up function
    GPIO_ForcePuPdCmd(GPIOB, ENABLE);
    GPIO_ForcePullUpConfig(GPIOB, GPIO_Pin_6);
    GPIO_ForcePullUpConfig(GPIOB, GPIO_Pin_7);

    I2C_DeInit(I2C1);
    I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle           = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_OwnAddress1         = 0xA0;
    I2C_InitStructure.I2C_ClockSpeed          = 100000;
    I2C_InitStructure.I2C_Ack                 = I2C_Ack_Enable;

    I2C_Init(I2C1, &I2C_InitStructure);
    I2C_Cmd(I2C1, ENABLE);
}

4.从设备测试模块:使能 I2C 中断,进入循环等待接收数据。若接收完成,打印数据并比较是否与发送数据一致。

复制代码
void IIC_SlaveTest(void)
{
    FillData();
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);
    I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);

    while (1)
    {
        if (RecvFlag == 1)
        {
            DataPrintf(RecvBuff, BUFF_SIZE);
            if (memcmp(RecvBuff, SendBuff, BUFF_SIZE) == 0)
            {
                printf("IIC slave int receive data success\n");
            }
            memset(RecvBuff, 0, BUFF_SIZE);
            RecvFlag = 0;
        }
    }
}

5.NVIC 配置模块:配置 I2C1 的事件和错误中断优先级,使能相应中断通道。

6.数据处理模块:包含数据填充、打印和获取命令等功能。

6.2 IIC_Software例程

该例程通过软件模拟I2C协议与EEPROM芯片通信,验证I2C驱动的正确性。程序首先初始化系统时钟并通过串口输出SYSCLK/HCLK/PCLK1/PCLK2/ADCCLK频率信息,随后配置I2C所需GPIO引脚(SCL/SDA),调用`ee_Test()`执行EEPROM测试:向指定地址写入256字节数据块,读取数据并校验正确性,同时测试多字节读写及错误处理以验证通信稳定性。主循环保持程序运行,确保测试持续进行。工作流程如下:

1.初始化系统:配置串口(波特率 115200)输出系统时钟频率(SYSCLK/HCLK/PCLK1/PCLK2/ADCCLK)。

2.I2C 配置:通过i2c_CfgGpio()初始化 I2C 所需 GPIO 引脚(如 SCL/SDA)。

复制代码
// main函数中的I2C GPIO配置调用
int main(void)
{
    // ...(初始化代码略)...

    i2c_CfgGpio(); // 初始化I2C所需的GPIO引脚(如SCL/SDA)

    ee_Test(); // 执行EEPROM测试

    while (1);
}

// 假设i2c_CfgGpio()函数实现(示例,实际在bsp_i2c_gpio.h中)
void i2c_CfgGpio(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 假设I2C使用GPIOB引脚

    // 配置SCL和SDA引脚为开漏输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // 假设SCL=PB6,SDA=PB7
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;       // 开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 上拉电阻使能(根据硬件设计选择)
    GPIO_SetBits(GPIOB, GPIO_Pin_6 | GPIO_Pin_7);
}

3.EEPROM 测试:调用ee_Test()执行软件 I2C 与 EEPROM 的通信测试(可能包括读写验证)。

复制代码
// main函数中的EEPROM测试调用
int main(void)
{
    // ...(初始化代码略)...

    ee_Test(); // 执行EEPROM读写测试

    while (1);
}

// 假设ee_Test()函数实现(示例,实际在bsp_i2c_ee.h中)
void ee_Test(void)
{
    uint8_t write_buf[BUFF_SIZE] = {0};
    uint8_t read_buf[BUFF_SIZE] = {0};
    uint16_t i;

    // 初始化数据缓冲区
    for (i = 0; i < BUFF_SIZE; i++)
    {
        write_buf[i] = i % 256; // 写入测试数据(0~255循环)
    }

    // 通过I2C写入EEPROM
    i2c_WriteBytes(EEPROM_ADDR, 0x00, write_buf, BUFF_SIZE);

    // 延时等待写入完成(EEPROM写入需要时间)
    delay_ms(10);

    // 通过I2C读取EEPROM数据
    i2c_ReadBytes(EEPROM_ADDR, 0x00, read_buf, BUFF_SIZE);

    // 验证数据一致性(示例:打印前10字节)
    printf("EEPROM Test: ");
    for (i = 0; i < 10; i++)
    {
        printf("%02X ", read_buf[i]);
    }
    printf("\n");
}

4. 主循环:保持程序运行,维持测试状态。

7 下载验证

7.1 IIC_IntTransmit例程

程序启动

  • 串口输出系统时钟频率信息,可确认时钟配置是否正确。
  • 显示测试提示,告知用户可通过输入 m 或 r 选择主设备发送或从设备接收测试模式。

主设备发送测试(输入 m)

  • 串口输出确认信息。
  • 显示即将发送的十六进制数据。
  • 程序控制 I2C 总线完成起始信号、地址及数据发送,最后发送停止信号。

从设备接收测试(输入 r)

  • 串口输出确认信息。
  • 使能 I2C 中断并进入等待。
  • 接收到 256 字节数据后,显示接收到的十六进制数据。
  • 比较接收和发送数据,若一致则输出成功信息,清空接收缓冲区并重置标志位。

7.2 IIC_Software例程

程序运行后,先完成延时与串口初始化,接着通过串口输出系统时钟频率信息,包括SYSCLK、HCLK、PCLK1、PCLK2和ADCCLK频率,并提示进行IIC软件测试。随后进行I2C所需GPIO引脚初始化,调用函数执行与EEPROM的通信测试,可能包含向指定地址写入数据块、读取数据并校验以及测试通信稳定性等操作。若测试正常,可认为I2C驱动功能正确;若有异常,可能会在后续测试流程中体现出来。最后程序进入无限循环,维持当前运行状态:

相关推荐
智商偏低6 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen7 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
白鱼不小白9 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D10 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术13 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt13 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘14 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
几个几个n16 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件
Despacito0o19 小时前
ESP32-s3摄像头驱动开发实战:从零搭建实时图像显示系统
人工智能·驱动开发·嵌入式硬件·音视频·嵌入式实时数据库
门思科技19 小时前
设计可靠 LoRaWAN 设备时需要考虑的关键能力
运维·服务器·网络·嵌入式硬件·物联网