01-STM8L052R8 I2C读写函数实现

1. I2C简介

 I2C 是很常见的一种总线协议,I2C 是 NXP 公司设计的,I2C 使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是 SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候 SCL 和 SDA 处于高电平。I2C 总线标准模式下速度可以达到 100Kb/S,快速模式下可以达到 400Kb/S。I2C 总线工作是按照一定的协议来运行的,接下来就看一下 I2C 协议。I2C 是支持多从机的,也就是一个 I2C 控制器下可以挂多个 I2C 从设备,这些不同的 I2C从设备有不同的器件地址,这样 I2C 主控制器就可以通过 I2C 设备的器件地址访问指定的 I2C设备了,一个 I2C 总线连接多个 I2C 设备如下图所示
在这里插入图片描述
 图中 SDA 和 SCL 这两根线必须要接一个上拉电阻,一般是 4.7K。其余的 I2C 从器件都挂接到 SDA 和 SCL 这两根线上,这样就可以通过 SDA 和 SCL 这两根线来访问多个 I2C设备。

2. I2C通信协议

2.1 起始位

  I2C 通信起始标志,通过这个起始位就可以告诉 I2C 从机,“我”要开始进行 I2C 通信了。在 SCL 为高电平的时候,SDA 出现下降沿就表示为起始位,如下图所示:
在这里插入图片描述

2.2 终止位

 停止位就是停止 I2C 通信的标志位,和起始位的功能相反。在 SCL 为高电平的时候,SDA出现上升沿就表示为停止位,如下图 所示:
在这里插入图片描述

2.3 数据传输

 I2C 总线在数据传输的时候要保证在 SCL 高电平期间,SDA 上的数据稳定,因此 SDA 上的数据变化只能在 SCL 低电平期间发生,如下图所示:
在这里插入图片描述

2.4 应答信号

 当 I2C 主机发送完 8 位数据以后会将 SDA 设置为输入状态,等待 I2C 从机应答,也就是等到 I2C 从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完 8 位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将 SDA 拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
在这里插入图片描述
 下图是一帧完整的I2C数据
在这里插入图片描述

2.5 写时序

 I2C写时序如下图中右侧上面两小图所示。每一副小图中横线上策表示主机发送的信号或数据,横线下册表示从机发送的信号或数据。
 I2C多次写时序过程如下:
  1. 主机发送起始信号,然后发送七位从机地址和写标志位
  2. 从机给一个应答信号
  3. 主机发送八位的寄存器地址
  4. 从机给一个应答信号
  5. 主机发送八位的数据
  6. 从机给一个应答信号
  … 循环执行 5 和 6 两步 …
  7. 数据发送完成时,主机发送停止信号,结束数据传输
在这里插入图片描述

2.6 读时序

 I2C多次读时序过程如下:
  1. 主机发送起始信号,然后发送七位从机地址和 写 标志位
  2. 从机给一个应答信号
  3. 主机发送八位的寄存器地址
  4. 从机给一个应答信号
  5. 主机再次发送起始信号,然后发送七位从机地址和 读 标志位
  6. 从机给一个应答信号
  7. 从机发送八位的数据
  8. 主机给一个应答信号
  … 循环执行 7 和 8 两步 …
  9. 主机不想继续读取数据时,从机发送8为数据后,主机不应答,主机发送停止信号结束传输过程
在这里插入图片描述

3. STM8L052R8 I2C读写示例

3.1 i2c.h

#ifndef _I2C_H_
#define _I2C_H_

#define I2C_INTERFACE		I2C1
#define I2C_CLK				CLK_Peripheral_I2C1

#define I2C_SCL_PORT		GPIOC
#define I2C_SCL_PIN			GPIO_Pin_1

#define I2C_SDA_PORT		GPIOC
#define I2C_SDA_PIN			GPIO_Pin_0

void i2c_init(void);
void i2c_master_read(uint8_t addr, uint8_t *buf, uint8_t len);
void i2c_master_write(uint8_t addr, uint8_t *buf, uint8_t len);

#endif	/* i2c.h */

3.2 i2c_init(初始化)

void i2c_init(void)
{
	I2C_DeInit(I2C_INTERFACE);
	
	GPIO_Init(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_Mode_Out_PP_High_Slow);	// I2C1_SCL -> PC1
	GPIO_Init(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_Mode_Out_PP_High_Slow);	// I2C1_SDA -> PC0
	
	CLK_PeripheralClockConfig(I2C_CLK, ENABLE);		// 使能I2C1时钟
	
	// 速率 100kHz,7位
	I2C_Init(I2C_INTERFACE, 10000, 0x01, I2C_Mode_I2C, I2C_DutyCycle_2, I2C_Ack_Enable, I2C_AcknowledgedAddress_7bit);
	
	I2C_Cmd(I2C_INTERFACE, ENABLE);		// 使能I2C1
}

3.3 发送多个字节

芯片手册发送流程如下:
在这里插入图片描述

/*
* 功   能: i2c主机发送数据
* @param1: 从机地址/从机寄存器地址
* @param2: 要写入的数据
* @param4: 要写入数据的长度,单位字节
*/
void i2c_master_write(uint8_t addr, uint8_t *buf, uint8_t len)
{
 	//printf("write test 1\n");
 	delay(100);
 	while( I2C_GetFlagStatus(I2C_INTERFACE, I2C_FLAG_BUSY) );	// 等待空闲

   // 产生一个起始信号。测试EV5,检测从器件返回一个应答信号
   I2C_GenerateSTART(I2C_INTERFACE, ENABLE);					
   while( !I2C_CheckEvent(I2C_INTERFACE,I2C_EVENT_MASTER_MODE_SELECT) );
   //printf("write test 2\n");
   
   // 设置I2C从器件地址,I2C主设备为写模式。测试EV6,检测从器件返回一个应答信号
   I2C_Send7bitAddress(I2C_INTERFACE, addr<<1, I2C_Direction_Transmitter);	
   while( !I2C_CheckEvent(I2C_INTERFACE, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) );
   //printf("write test 3\n");

   //2C_ClearFlag(I2C_INTERFACE, I2C_FLAG_ADDRESSSENTMATCHED);	// must add
  
   // 发送数据,检测EV8_2从机返回一个应答信号
   while(len > 0)
   {
   	I2C_SendData(I2C_INTERFACE, *buf);     
   	while( !I2C_CheckEvent(I2C_INTERFACE, I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
   	
   	if(len == 1)		// 只有一个数据
   	{
   		break;		// 跳出循环,发送停止信号
   	}
   	else				// 有多个数据,循环发送
   	{
   		len--;
   		buf++;
   	}
   }
  
  // 发送停止信号
  I2C_GenerateSTOP(I2C_INTERFACE, ENABLE);
}

3.4 读取多个字节

芯片手册读取流程如下:
在这里插入图片描述
在这里插入图片描述

/*
 * 功   能: i2c主机读取数据
 * @param1: 从机地址
 * @param2: 存放读取数据的缓存区
 * @param4: 读取数据的长度,单位字节
 */
void i2c_master_read(uint8_t addr, uint8_t *buf, uint8_t len)
{
#if 0	
	while( I2C_GetFlagStatus(I2C_INTERFACE, I2C_FLAG_BUSY) );	// 等待空闲
  
	// 产生一个起始信号。测试EV5,检测从器件返回一个应答信号
	I2C_GenerateSTART(I2C_INTERFACE, ENABLE);					
	while( !I2C_CheckEvent(I2C_INTERFACE,I2C_EVENT_MASTER_MODE_SELECT) );	

	// 设置I2C从器件地址,I2C主设备为写模式。测试EV6,检测从器件返回一个应答信号
	I2C_Send7bitAddress(I2C_INTERFACE, addr, I2C_Direction_Transmitter);	
	while( !I2C_CheckEvent(I2C_INTERFACE,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) );
	
	I2C_ClearFlag(I2C_INTERFACE, I2C_FLAG_ADDRESSSENTMATCHED);	// must add
	
	// 寄存器地址,检测EV8_2从机返回一个应答信号
	I2C_SendData(I2C_INTERFACE, *reg_addr);                                                    
	while( !I2C_CheckEvent(I2C_INTERFACE, I2C_EVENT_MASTER_BYTE_TRANSMITTED) );
#endif
	
	// 产生一个起始信号。测试EV5,检测从器件返回一个应答信号
	I2C_GenerateSTART(I2C_INTERFACE, ENABLE);					
	while( !I2C_CheckEvent(I2C_INTERFACE,I2C_EVENT_MASTER_MODE_SELECT) );	
	//printf("1\n");
	
	// 设置I2C从器件地址,I2C主设备为读模式。测试EV6,检测从器件返回一个应答信号
	I2C_Send7bitAddress(I2C_INTERFACE, addr<<1, I2C_Direction_Receiver);	
	while( !I2C_CheckEvent(I2C_INTERFACE, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) );
	//printf("2\n");
	
	//I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);// must add
	
	while(len > 0)
	{
		// 测试EV6
		while( !I2C_CheckEvent(I2C_INTERFACE, I2C_EVENT_MASTER_BYTE_RECEIVED) );
		*buf = I2C_ReceiveData(I2C_INTERFACE);
		
		//printf("3\n");
		if(len  == 1)	
		{
			// 读到最后一个字节,无需应答,直接发送停止位
			I2C_AcknowledgeConfig(I2C_INTERFACE, DISABLE);
			I2C_GenerateSTOP(I2C_INTERFACE, ENABLE);
		}
		else
		{
			// 不是最后一个字节,发送应答信号,字节数减1,指向下一个存储空间
			I2C_AcknowledgeConfig(I2C_INTERFACE, ENABLE);
			buf++;			
		}
		len--;
	}
	I2C_AcknowledgeConfig(I2C_INTERFACE, ENABLE);
}
发布了57 篇原创文章 · 获赞 64 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_36310253/article/details/103160619