I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
1.它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
2.一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) , 一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数 据收发同步。
3.每个连接到总线的设备都有一个独立的地址,主机可以利用这个 地址进行不同设备之间的访问。
4.总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当 所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
5.多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由 哪个设备占用总线。 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高 速模式。
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、
仲裁、时钟同步和地址广播等环节。
I2C基本读写过程
主机写数据到从机:
• 当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况 表示通讯的起始。
• 当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的 停止。 • 起始和停止信号一般由主机产生。
数据有效性
I2C使用SDA信号线来传输数据,使用SCL信号线进行数据同步。
SDA数据线在SCL的每个时钟周期传输一位数据。
• SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时 表示数据“1”,为低电平时表示数据“0”。 •
当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平 切换,为下一次表示数据做好准备。
地址及数据方向
• I2C总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。设备地址可以是7位或10位。 •
紧跟设备地址的一个数据位R/W用来表示数据传输方向,数据方向位为 “1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
响应
I2C的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。
传输时主机产生时钟,在第9个时钟时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。
STM32的I2C特性及架构
软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。
硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
1.通讯引脚
STM32芯片有多个I2C外设,它们的I2C通讯信号引出到不同的GPIO引脚上,使用时必须配置到这些指定的引脚.
2.时钟控制逻辑
SCL线的时钟信号,由I2C接口根据时钟控制寄存器(CCR)控制, 控制的参数主要为时钟频率。 •
可选择I2C通讯的“标准/快速”模式,这两个模式分别I2C对应 100/400Kbit/s的通讯速率。 •
在快速模式下可选择SCL时钟的占空比,可选Tlow/Thigh=2或 Tlow/Thigh=16/9模式。 •
CCR寄存器中12位的配置因子CCR,它与I2C外设的输入时钟源共同 作用,产生SCL时钟。STM32的I2C外设输入时钟源为PCLK1。
3.数据控制逻辑
I2C的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC寄存器以及SDA数据线。
• 当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去; •
当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。
使用I2C外设通讯时,在通讯的不同阶段它会对“状态寄存器(SR1及SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。
1.主发送器
在发送了地址和清除了ADDR位后, 主设备通过内部移位寄存器将字节从DR寄存器发送到SDA线上。
主设备等待,直到TxE被清除,(见图245的EV8)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了INEVFEN和ITBUFEN位,则产生一个中断。
如果TxE被置位并且在上一次数据发送结束之前没有写新的数据字节到DR寄存器,则BTF被硬件置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。关闭通信在DR寄存器中写入最后一个字节后,通过设置STOP位产生一个停止条件(见图245的EV8_2),然后I2C接口将自动回到从模式(M/S位清除)。
2.主接收器
在发送地址和清除ADDR之后,I2C接口进入主接收器模式。在此模式下,I2C接口从SDA线接收数据字节,并通过内部移位寄存器送至DR寄存器。在每个字节后,I2C接口依次执行以下操作:
● 如果ACK位被置位,发出一个应答脉冲。
● 硬件设置RxNE=1,如果设置了INEVFEN和ITBUFEN位,则会产生一个中断(见图246的EV7)。
如果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位被清除)
3.从发送器
在接收到地址和清除ADDR位后,从发送器将字节从DR寄存器经由内部移位寄存器发送到SDA线上。
从设备保持SCL为低电平,直到ADDR位被清除并且待发送数据已写入DR寄存器。(见下图中的EV1和EV3)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果TxE位被置位,但在下一个数据发送结束之前没有新数据写入到I2C_DR寄存器,则BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。
4.从接收器
在接收到地址并清除ADDR后,从接收器将通过内部移位寄存器从SDA线接收到的字节存进DR
寄存器。I2C接口在接收到每个字节后都执行下列操作:
● 如果设置了ACK位,则产生一个应答脉冲
● 硬件设置RxNE=1。如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果RxNE被置位,并且在接收新的数据结束之前DR寄存器未被读出,BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器将清除BTF位。
关闭从通信
在传输完最后一个数据字节后,主设备产生一个停止条件, I2
C接口检测到这一条件时:
● 设置STOPF=1,如果设置了ITEVFEN位,则产生一个中断。
24C02 的 SCL 和 SDA 分别连在 STM32F1 的 PB6 和 PB7 上的
24C02 (EEPROM)芯片
本实验板中的 EEPROM 芯片 (型号:AT24C02) 的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。EEPROM 芯片的设备地址一共有 7 位,其中高 4 位固定为:1010 b,低 3 位则由 A0/A1/A2 信号线的电平决定,见图EEPROM 设备地址 ,图中的 R/W 是读写方向位,与地址无关。
按照我们此处的连接,A0/A1/A2 均为 0,所以 EEPROM 的 7 位设备地址是:101 0000b,即 0x50。
由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当 R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1时,表示读方向,加上 7 位地址,其值为“0xA0”,常称该值为“读地址”。
EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当引脚为低电平时,可写入数据,我们直接接地,不使用写保护功能。
(1) 配置通讯使用的目标引脚为开漏模式;
(2) 使能 I2C 外设的时钟;
(3) 配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;
(4) 编写基本 I2C 按字节收发的函数;
(5) 编写读写 EEPROM 存储内容的函数;
(6) 编写测试程序,对读写数据进行校验。
串口初始化
//部分代码使用宏定义,具体请看.h文件
void GPIOx_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_X,ENABLE); //开时钟
GPIO_InitTypeDef GPIO_InitStuct;
GPIO_InitStuct.GPIO_Mode = GPIO_Mode_X; //开漏输出
GPIO_InitStuct.GPIO_Pin = GPIO_Pin_SCL|GPIO_Pin_SDA;
GPIO_InitStuct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_X,&GPIO_InitStuct);
}
I2C初始化
//部分代码使用宏定义,具体请看.h文件
void Hardware_Config(void)
{
GPIOx_Config();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //开IIC的时钟
I2C_DeInit(EEPROM_I2C_X); //将外设 I2Cx 寄存器重设为缺省值 即初始化
I2C_InitTypeDef I2C_InitStuct;
I2C_InitStuct.I2C_Mode = I2C_Mode_I2C; //设置 I2C 为 I2C 模式
I2C_InitStuct.I2C_DutyCycle = I2C_DutyCycle_2; //I2C 快速模式 Tlow / Thigh = 2
I2C_InitStuct.I2C_OwnAddress1 = I2Cx_OWN_ADDRESS7; //该参数用来设置第一个设备自身地址,它可以是一个 7 位地址或者一个 10 位地址。
I2C_InitStuct.I2C_Ack =I2C_Ack_Enable; //使能应答(ACK)
I2C_InitStuct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位应答地址
I2C_InitStuct.I2C_ClockSpeed = I2C_Speed; //该参数用来设置时钟频率,这个值不能高于 400KHz。
I2C_Init(EEPROM_I2C_X,&I2C_InitStuct);
I2C_Cmd(EEPROM_I2C_X, ENABLE); //I2C使能
}
向EEPROM写入一个字节
//具体实现过程看7位主发送器 数据时序图
void EEPROM_Bety_Write(uint8_t addr,uint8_t date) //写入一个字节
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE); //产生起始信号
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); //产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //产生EV6事件
I2C_SendData(EEPROM_I2C_X, addr); //EV6事件被检测发送要访问的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTING)==ERROR){;} 产生EV8事件
I2C_SendData(EEPROM_I2C_X, date); //EV8事件被检测,发送要存储的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR); //传输完成
I2C_GenerateSTOP(EEPROM_I2C_X, ENABLE); //产生停止信号
EEPROM_WaitForWriteEnd();等待EEPROM响应函数,自己定义
}
写入多个字节(一次最多8bit)
/**写入多个字节,一次不超过8字节
*addr:要写入EEPROM的地址
*date:要写入EEPROM的数据
*numByteToWrite:写入数据的大小
*/
//具体实现过程看7位主发送器 数据时序图
void EEPROM_page_Write(uint8_t addr,uint8_t *date,uint8_t numByteToWrite)
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE);//产生起始信号
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); //产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);//产生EV6事件
I2C_SendData(EEPROM_I2C_X, addr); //EV6事件被检测发送要访问的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTING)==ERROR);//产生EV8事件
while(numByteToWrite)
{
I2C_SendData(EEPROM_I2C_X, *date);//EV8事件被检测,发送要存储的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR); //传输完成
numByteToWrite--;
date++;
}
I2C_GenerateSTOP(EEPROM_I2C_X, ENABLE); //产生停止信号
EEPROM_WaitForWriteEnd();
}
读取EEPROM的数据
//具体实现过程看住接收时序图
void EEPROM_Read(uint8_t addr,uint8_t *date,uint8_t numByteToRead) //读取数据
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE); //产生起始信号
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); //产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //产生EV6事件
I2C_SendData(I2C1, addr); //EV6事件被检测发送要访问的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTING)==ERROR){;}//产生EV8事件
//第二次起始信号
//产生起始信号
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE);
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Receiver);//EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)==ERROR);
while(numByteToRead)
{
if(numByteToRead == 1)
{
//如果是最后一个字节
I2C_AcknowledgeConfig(EEPROM_I2C_X,DISABLE);
}
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_RECEIVED)==ERROR);//EV7事件被检测到
//开始接收数据
*date = I2C_ReceiveData(EEPROM_I2C_X);
date++;
numByteToRead--;
}
I2C_GenerateSTOP(EEPROM_I2C_X,ENABLE);//数据完成
I2C_AcknowledgeConfig(EEPROM_I2C_X,ENABLE); //重新使能ACK
}
//等待响应函数
void EEPROM_WaitForWriteEnd(void)
{
do
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE); //产生一个开始信号
while(I2C_GetFlagStatus(EEPROM_I2C_X,I2C_FLAG_SB)==RESET); //检测标志位设置与否 I2C_FLAG_SB:起始位标志位(主模式)
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
}
while(I2C_GetFlagStatus(EEPROM_I2C_X,I2C_FLAG_ADDR)==RESET); //等待直到
I2C_GenerateSTOP(EEPROM_I2C_X,ENABLE); //产生一个结束信号
}
示例代码
.c文件
#include "Hardware.h"
/*
IIC硬件控制
6时钟线
7数据线
*/
/*
配置步骤
初始化IIC相关的GPIO口
配置IIC的工作模式
编写IIC写入函数
编写IIC读取函数
使用Read函数以及write函数进行数据检验
编写page w
*/
uint16_t EEPROM_ADDRESS;
void GPIOx_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_X,ENABLE); //开时钟
GPIO_InitTypeDef GPIO_InitStuct;
GPIO_InitStuct.GPIO_Mode = GPIO_Mode_X; //开漏输出
GPIO_InitStuct.GPIO_Pin = GPIO_Pin_SCL|GPIO_Pin_SDA;
GPIO_InitStuct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_X,&GPIO_InitStuct);
}
void Hardware_Config(void)
{
GPIOx_Config();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //开IIC的时钟
I2C_DeInit(EEPROM_I2C_X); //将外设 I2Cx 寄存器重设为缺省值 即初始化
I2C_InitTypeDef I2C_InitStuct;
I2C_InitStuct.I2C_Mode = I2C_Mode_I2C; //设置 I2C 为 I2C 模式
I2C_InitStuct.I2C_DutyCycle = I2C_DutyCycle_2; //I2C 快速模式 Tlow / Thigh = 2
I2C_InitStuct.I2C_OwnAddress1 = I2Cx_OWN_ADDRESS7; //该参数用来设置第一个设备自身地址,它可以是一个 7 位地址或者一个 10 位地址。
I2C_InitStuct.I2C_Ack =I2C_Ack_Enable; //使能应答(ACK)
I2C_InitStuct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位应答地址
I2C_InitStuct.I2C_ClockSpeed = I2C_Speed; //该参数用来设置时钟频率,这个值不能高于 400KHz。
I2C_Init(EEPROM_I2C_X,&I2C_InitStuct);
I2C_Cmd(EEPROM_I2C_X, ENABLE); //I2C使能
}
void EEPROM_Bety_Write(uint8_t addr,uint8_t date) //写入一个字节
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE); //产生起始信号
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); //产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //产生EV6事件
I2C_SendData(EEPROM_I2C_X, addr); //EV6事件被检测发送要访问的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTING)==ERROR){;} 产生EV8事件
I2C_SendData(EEPROM_I2C_X, date); //EV8事件被检测,发送要存储的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR); //传输完成
I2C_GenerateSTOP(EEPROM_I2C_X, ENABLE); //产生停止信号
EEPROM_WaitForWriteEnd();
}
/**写入多个字节,一次不超过8字节
*addr:要写入EEPROM的地址
*date:要写入EEPROM的数据
*numByteToWrite:写入数据的大小
*/
void EEPROM_page_Write(uint8_t addr,uint8_t *date,uint8_t numByteToWrite)
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE);//产生起始信号
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); //产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);//产生EV6事件
I2C_SendData(EEPROM_I2C_X, addr); //EV6事件被检测发送要访问的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTING)==ERROR);//产生EV8事件
while(numByteToWrite)
{
I2C_SendData(EEPROM_I2C_X, *date);//EV8事件被检测,发送要存储的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR); //传输完成
numByteToWrite--;
date++;
}
I2C_GenerateSTOP(EEPROM_I2C_X, ENABLE); //产生停止信号
EEPROM_WaitForWriteEnd();
}
void EEPROM_Read(uint8_t addr,uint8_t *date,uint8_t numByteToRead) //读取数据
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE); //产生起始信号
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); //产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //产生EV6事件
I2C_SendData(I2C1, addr); //EV6事件被检测发送要访问的地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_TRANSMITTING)==ERROR){;}//产生EV8事件
//第二次起始信号
//产生起始信号
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE);
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//产生EV5事件
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Receiver);//EV事件被检测发送设备地址
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)==ERROR);
while(numByteToRead)
{
if(numByteToRead == 1)
{
//如果是最后一个字节
I2C_AcknowledgeConfig(EEPROM_I2C_X,DISABLE);
}
while(I2C_CheckEvent(EEPROM_I2C_X,I2C_EVENT_MASTER_BYTE_RECEIVED)==ERROR);//EV7事件被检测到
//开始接收数据
*date = I2C_ReceiveData(EEPROM_I2C_X);
date++;
numByteToRead--;
}
I2C_GenerateSTOP(EEPROM_I2C_X,ENABLE);//数据完成
I2C_AcknowledgeConfig(EEPROM_I2C_X,ENABLE); //重新使能ACK
}
void EEPROM_WaitForWriteEnd(void)
{
do
{
I2C_GenerateSTART(EEPROM_I2C_X,ENABLE); //产生一个开始信号
while(I2C_GetFlagStatus(EEPROM_I2C_X,I2C_FLAG_SB)==RESET); //检测标志位设置与否 I2C_FLAG_SB:起始位标志位(主模式)
I2C_Send7bitAddress(EEPROM_I2C_X,EEPROM_Block0_ADDRESS,I2C_Direction_Transmitter); //EV事件被检测发送设备地址
}
while(I2C_GetFlagStatus(EEPROM_I2C_X,I2C_FLAG_ADDR)==RESET); //等待直到
I2C_GenerateSTOP(EEPROM_I2C_X,ENABLE); //产生一个结束信号
}
.h文件
#ifndef _HARDWART_H_
#define _HARDWART_H_
#include "stm32f10x.h"
/*GPIO设置*/
#define RCC_APB2Periph_GPIO_X RCC_APB2Periph_GPIOB
#define GPIO_Mode_X GPIO_Mode_AF_OD //复用开漏输出
#define GPIO_Pin_SCL GPIO_Pin_6 //时钟线
#define GPIO_Pin_SDA GPIO_Pin_7 //数据线
#define GPIO_X GPIOB
/* I2C设置*/
#define I2Cx_OWN_ADDRESS7 0X0A /* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
#define EEPROM_Block0_ADDRESS 0xA0 //EEPROM地址
#define I2C_Speed 400000
#define EEPROM_I2C_X I2C1
void Hardware_Config(void);
void EEPROM_Bety_Write(uint8_t addr,uint8_t date); //向EEPROM写入一个字节数据
void EEPROM_WaitForWriteEnd(void); //等待数据发送,STM32数据过快导致
void EEPROM_Read(uint8_t addr,uint8_t *date,uint8_t numByteToRead); //向EEPROM读取数据
void EEPROM_page_Write(uint8_t addr,uint8_t *date,uint8_t numByteToWrite); 向EEPROM写入8字节数据
#endif
main.c文件
#include "main.h"
int main()
{
uint8_t date[10];
uint8_t date1[10]={1,2,3,4,5,6,7,8};
u8 i;
Usart1_Init(115200);
printf("abc\r\n");
Hardware_Config();
EEPROM_Bety_Write(11,0X50);
EEPROM_Read(11,date,1);
// EEPROM_page_Write(uint8_t addr,uint8_t *date,uint8_t numByteToWrite);
//注意地址对齐,否则数据会出错 addr%numByteToWrite==0
EEPROM_page_Write(8,date1,8);
EEPROM_Read(8,date,8);
printf("\r\n接收到的数据0x%x\r\n",date[0]);
for(i=0;i<8;i++)
{
printf("\r\n接收到的数据%d\r\n",date[i]);
}
while(1)
{
;
}
}