IIC通信协议

一、IIC简介

        IIC是一种半双工、同步、单端的两线串行通信接口,需要串行数据线SDA和串行时钟线SCL,使用时,两线需要通过上拉电阻接入电源,通信时钟频率通常为100KHz、400KHz。IIC通信支持多主、多从,总线上产生串行时钟的设备是主设备,控制IIC通信状态,产生START、STOP条件。

        每次通信都开始与START条件,结束于STOP条件,通信字节数没有限制,信息以字节为单位传输,第9bit由信息接收方产生应答。

  • 通信协议

1、起始和停止条件

起始信号:SCL高、SDA由高拉低

结束信号:SCL高、SDA由低拉高

2、位传输

数据位:SCL高,SDA有效;SCL低,SDA进行数据切换

3、应答

接收设备每接收到一字节数据,就要产生一个应答信号,接收器拉低SDA表示应答

4、寻址操作

IIC设备通常有8位地址信息,用于确定数据的接收方,其中LSB表示读写方向,1为读,0为写。

5、写数据时序

起始信号;

从设备地址(7bit)+写方向位(0);

等待ACK;

发送数据,每发送8bit,要等待对面ACK,可以重复发送N个8bit;

停止信号;

6、读数据时序

起始信号;

从设备地址(7bit)+读方向位(1);

等待ACK;

接收数据,每接收8bit,要发送ACK,可以重复接收N个8bit;

不想接收时,发送NACK;

停止信号;

7、读写时序,很多外设内部有存储器地址,所以通常要混合读写

起始信号;

从设备地址(7bit)+写方向位(0);

等待ACK;

发送从设备内部存储器地址;

等待ACK;

重发起始信号;

从设备地址(7bit)+读或写方向位;

........后续为正常读写操作,时序同正常读写相同

二、IIC读写EEPROM

1、AT24C02

        AT24C02存储器采用256x8bit组织结构,具有两线串行接口,支持IIC通信,时钟频率为1MHz(5V)或400KHz(1.8V, 2.5V, 2.7V)。可按字节写,也支持页写功能,每页8字节,提供硬件写保护,内部写周期最大5ms。读取时支持字节读,另外支持地址自增功能,可以实现随机读和序列读。

        A2、A1、A0为地址输入引脚,总线上可级联8块AT24C02。

2、字节写

        时序如图,在主器件发送停止条件终止通信后,EEPROM会进入内部写周期,此时任何输入都无效,直到退出写周期EEPROM才会应答。

3、页写

        页写的初始化与字节写相同,只是主器件不会在第一个数据发送后就产生停止条件,而是发送多个字节数据,EEPROM对每个数据响应ACK,最终仍由主器件发送停止条件终止通信。

每写入一个字节,AT24C02会将字地址的低3bit自动加1,保证每次数据写入都维持在页内,如果主器件发送数据超过8字节,将回转到页首,覆盖之前写入的数据。

4、应答查询

        主器件发送停止条件终止写操作后,EEPROM会进入内部写周期,通过应答查询可以查看EEPROM是否恢复。应答查询是指:循环发送起始条件和器件地址(读写位根据需要),直到EEPROM应答ACK,说明EEPROM恢复。

5、随机读

        时序如图,主器件首先按字节写方式进行初始化,EEPROM应答字地址后,主器件重发起始条件,然后发送器件地址(读写位置1),EEPROM应答后发送1字节数据,主器件发送停止条件

6、当前地址读

        EEPROM被访问后,会记录字地址加1的值,当读到最后一页最后一个地址后会回转到0地址,所以写入一次后,可以不发送字地址直接读。

7、顺序读

主器件按上述两种方式读取1字节数据后,如果不响应NACK,响应ACK后,EEPROM会自增字地址,并随时钟继续发送后续数据,当读到存储器末尾,会回转到0字节,直到主器件发送停止条件终止通信。

三、AT24C02读写源码

1、说明

        使用stm32f103 外设I2C1驱动AT24C02,引脚PB6、PB7,仅实现基本读写功能,未考虑异常情况

2、源码

eeprom_hardiic.c

​
#include "eeprom_hardiic.h"

void EEPROM_Init(void)
{
	I2C_InitTypeDef stIIC;
	GPIO_InitTypeDef stGPIO;
	
	RCC_APB2PeriphClockCmd(EEPROM_GPIO_CLK, ENABLE);
	RCC_APB1PeriphClockCmd(EEPROM_IIC_CLK, ENABLE);
	
	stGPIO.GPIO_Mode = GPIO_Mode_AF_OD;
	stGPIO.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	stGPIO.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(EEPROM_GPIO_PORT, &stGPIO);
	
	stIIC.I2C_Ack = I2C_Ack_Enable;
	stIIC.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	stIIC.I2C_ClockSpeed = 50000;
	stIIC.I2C_DutyCycle = I2C_DutyCycle_2;
	stIIC.I2C_Mode = I2C_Mode_I2C;
	stIIC.I2C_OwnAddress1 = 00;
	
	I2C_Init(EEPROM_IIC, &stIIC);
	
	I2C_Cmd(EEPROM_IIC, ENABLE);
}

void EEPROM_ReadBytes(uint8_t *pReadBuf, uint8_t ucAddress, uint8_t ucSize)
{
	uint8_t i;
	
	while(I2C_GetFlagStatus(EEPROM_IIC, I2C_FLAG_BUSY) == SET);
	
	I2C_GenerateSTART(EEPROM_IIC, ENABLE);
	while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
	
	I2C_Send7bitAddress(EEPROM_IIC, EEPROM_ADDRESS, I2C_Direction_Transmitter);
	while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
	
	I2C_SendData(EEPROM_IIC, ucAddress);
	while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
	
	I2C_GenerateSTART(EEPROM_IIC, ENABLE);
	while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	
	
	I2C_Send7bitAddress(EEPROM_IIC, EEPROM_ADDRESS, I2C_Direction_Receiver);
	while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);
	
	for (i = 0; i < ucSize; ++i)
	{
		if (i == ucSize-1)
		{
			I2C_AcknowledgeConfig(EEPROM_IIC, DISABLE);
			I2C_GenerateSTOP(EEPROM_IIC, ENABLE);
		}
		while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);
		pReadBuf[i] = I2C_ReceiveData(EEPROM_IIC);
	}
	
	I2C_AcknowledgeConfig(EEPROM_IIC, ENABLE);
}

void EEPROM_WriteBytes(const uint8_t *pWriteBuf, uint8_t ucAddress, uint8_t ucSize)
{
	uint8_t i;
	uint8_t ucAddr = ucAddress;
	
	while(I2C_GetFlagStatus(EEPROM_IIC, I2C_FLAG_BUSY) == SET);

	for (i = 0; i < ucSize; ++i)
	{
		if (i == 0 || (ucAddr & (EEPROM_PAGE_SIZE-1)) == 0)
		{
			I2C_GenerateSTOP(EEPROM_IIC, ENABLE);
			
			EEPROM_WaitWriteEnd();
			
			I2C_GenerateSTART(EEPROM_IIC, ENABLE);
			while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
	
			I2C_Send7bitAddress(EEPROM_IIC, EEPROM_ADDRESS, I2C_Direction_Transmitter);
			while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
			
			I2C_SendData(EEPROM_IIC, ucAddr);
			while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);			
		}
		
		I2C_SendData(EEPROM_IIC, pWriteBuf[i]);
		while(I2C_CheckEvent(EEPROM_IIC, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
		
		ucAddr++;
	}
	
	I2C_GenerateSTOP(EEPROM_IIC, ENABLE);
}	

void EEPROM_WaitWriteEnd(void)
{
	do{
		I2C_GenerateSTART(EEPROM_IIC, ENABLE);
		while(I2C_GetFlagStatus(EEPROM_IIC, I2C_FLAG_SB) == RESET);

		I2C_Send7bitAddress(EEPROM_IIC, EEPROM_ADDRESS, I2C_Direction_Transmitter);
	}while(I2C_GetFlagStatus(EEPROM_IIC, I2C_FLAG_ADDR) == RESET);
	
	I2C_GenerateSTOP(EEPROM_IIC, ENABLE);
}

​

eeprom_hardiic.h

​
#ifndef _EEPROM_HARDIIC_H
#define _EEPROM_HARDIIC_H

#include "stm32f10x.h"

#define EEPROM_ADDRESS 			0xA0
#define EEPROM_PAGE_SIZE		8
#define EEPROM_SIZE					256

#define EEPROM_IIC					I2C1
#define EEPROM_IIC_CLK			RCC_APB1Periph_I2C1
#define EEPROM_GPIO_CLK			RCC_APB2Periph_GPIOB
#define EEPROM_GPIO_PORT		GPIOB

void EEPROM_Init(void);
void EEPROM_ReadBytes(uint8_t *pReadBuf, uint8_t ucAddress, uint8_t ucSize);
void EEPROM_WriteBytes(const uint8_t *pWriteBuf, uint8_t ucAddress, uint8_t ucSize);
void EEPROM_WaitWriteEnd(void);

#endif /* _EEPROM_HARDIIC_H */

​

测试相关

static uint8_t ucArray[2] = {0xA5, 0x5A};
static uint8_t ucRead[2] = {0x00, 0x00};

uint8_t test(void)
{
	uint8_t addr = 0x00;
	for (int i = 0; i < EEPROM_SIZE; i += 2)
	{
		printf("i = %d\r\n", i);
		EEPROM_WriteBytes(ucArray, addr, 2);
		EEPROM_WaitWriteEnd();
		//delay_ms(5);
		EEPROM_ReadBytes(ucRead, addr, 2);
		if (ucRead[0] != 0xA5 || ucRead[1] != 0x5A)
		{
			printf("0x%2x read data: 0x%2x, 0x%2x\r\n", addr, ucRead[0], ucRead[1]);
			return 0;
		}
		ucRead[0] = 0x00;
		ucRead[1] = 0x00;
		addr += 2;
	}
	return 1;
}

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值