STM32F103_I2C硬件模拟

I2C简介

I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

I2C物理层的特点

STM32F103_I2C硬件模拟_第1张图片

1.它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
2.一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) , 一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数 据收发同步。
3.每个连接到总线的设备都有一个独立的地址,主机可以利用这个 地址进行不同设备之间的访问。
4.总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当 所有设备都空闲都输出高阻态时,由上拉电阻把总线拉成高电平

5.多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由 哪个设备占用总线。 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高 速模式。

I2C的协议层

I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、
仲裁、时钟同步和地址广播等环节。
I2C基本读写过程
主机写数据到从机:
STM32F103_I2C硬件模拟_第2张图片
STM32F103_I2C硬件模拟_第3张图片

I2C基本读写过程
STM32F103_I2C硬件模拟_第4张图片
STM32F103_I2C硬件模拟_第5张图片
通讯复合格式:

STM32F103_I2C硬件模拟_第6张图片
STM32F103_I2C硬件模拟_第7张图片

通讯的起始和停止信号

STM32F103_I2C硬件模拟_第8张图片

• 当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况 表示通讯的起始。
• 当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的 停止。 • 起始和停止信号一般由主机产生。

数据有效性

I2C使用SDA信号线来传输数据,使用SCL信号线进行数据同步。
SDA数据线在SCL的每个时钟周期传输一位数据。
STM32F103_I2C硬件模拟_第9张图片

• SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时 表示数据“1”,为低电平时表示数据“0”。 •
当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平 切换,为下一次表示数据做好准备。

地址及数据方向

• I2C总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。设备地址可以是7位10位。 •
紧跟设备地址的一个数据位R/W用来表示数据传输方向,数据方向位为 “1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。

STM32F103_I2C硬件模拟_第10张图片

响应

I2C的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。STM32F103_I2C硬件模拟_第11张图片
传输时主机产生时钟,在第9个时钟时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。

STM32的I2C特性及架构
软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。
硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
STM32F103_I2C硬件模拟_第12张图片

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信号线采样到的数据一位一位地存储到“数据寄存器”中。

STM32的I2C通讯过程

使用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位清除)。
STM32F103_I2C硬件模拟_第13张图片

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位被清除)
STM32F103_I2C硬件模拟_第14张图片

3.从发送器

在接收到地址和清除ADDR位后,从发送器将字节从DR寄存器经由内部移位寄存器发送到SDA线上。
从设备保持SCL为低电平,直到ADDR位被清除并且待发送数据已写入DR寄存器。(见下图中的EV1和EV3)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果TxE位被置位,但在下一个数据发送结束之前没有新数据写入到I2C_DR寄存器,则BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。
STM32F103_I2C硬件模拟_第15张图片
4.从接收器
在接收到地址并清除ADDR后,从接收器将通过内部移位寄存器从SDA线接收到的字节存进DR
寄存器。I2C接口在接收到每个字节后都执行下列操作:
● 如果设置了ACK位,则产生一个应答脉冲
● 硬件设置RxNE=1。如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果RxNE被置位,并且在接收新的数据结束之前DR寄存器未被读出,BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器将清除BTF位。
STM32F103_I2C硬件模拟_第16张图片
关闭从通信
在传输完最后一个数据字节后,主设备产生一个停止条件, I2
C接口检测到这一条件时:
● 设置STOPF=1,如果设置了ITEVFEN位,则产生一个中断。

硬件设计


24C02 的 SCL 和 SDA 分别连在 STM32F1 的 PB6 和 PB7 上的
STM32F103_I2C硬件模拟_第17张图片
24C02 (EEPROM)芯片
STM32F103_I2C硬件模拟_第18张图片
本实验板中的 EEPROM 芯片 (型号:AT24C02) 的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。EEPROM 芯片的设备地址一共有 7 位,其中高 4 位固定为:1010 b,低 3 位则由 A0/A1/A2 信号线的电平决定,见图EEPROM 设备地址 ,图中的 R/W 是读写方向位,与地址无关。
STM32F103_I2C硬件模拟_第19张图片

按照我们此处的连接,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)
{
;
}
}

你可能感兴趣的:(stm32,嵌入式)