一、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;
}