第十六章 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驱动功能正确;若有异常,可能会在后续测试流程中体现出来。最后程序进入无限循环,维持当前运行状态:

相关推荐
SY师弟2 小时前
台湾TEMI协会竞赛——1、龙舟机器人组装教学
c语言·单片机·嵌入式硬件·机器人·嵌入式·台湾temi协会
星宇CY2 小时前
STM32 定时器应用:从精准延时到智能控制的实战指南
stm32·单片机·嵌入式硬件
WIZnet 中国社区官方博客2 小时前
第二章 开发板与芯片介绍
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·系统框架·开发板介绍·硬件资源
学习噢学个屁2 小时前
基于STM32音频频谱分析设计
c语言·stm32·单片机·嵌入式硬件·音视频
电子科技圈3 小时前
IAR开发平台升级Arm和RISC-V开发工具链,加速现代嵌入式系统开发
arm开发·嵌入式硬件·设计模式·性能优化·软件工程·代码规范·risc-v
位东风5 小时前
【凌智视觉模块】rv1106 部署 ppocrv4 检测模型 rknn 推理
c++·人工智能·嵌入式硬件
智驾6 小时前
电机控制基础,小白入门篇
嵌入式硬件·电机·电机控制
Peter_Deng.7 小时前
单片机 - STM32 非阻塞式编程详解:以 LED 和按键为例(附超详细寄存器级代码)
stm32·单片机·嵌入式硬件
平凡灵感码头7 小时前
基于 STM32 的四路 PWM 控制智能小车运动的模块化控制程序
stm32·单片机·嵌入式硬件
情意绵绵6747 小时前
VAS1085Q奇力科技LED驱动芯片车规级线性芯片
单片机·嵌入式硬件·硬件架构·硬件工程·pcb工艺