1.与硬件IIC的比较
1.1.使用灵活
可使用任意2个IO口实现,不用受芯片管脚限制;
1.2.速率快
通过调整延时,可以实现超过400k的速率,实际测试最大速率接近800k;
1.3.容错性强
硬件IIC在通信出错后,无法自行恢复,模拟IIC则可以迅速恢复;
2.底层接口函数
2.1. I2C_Start
static uint8_t I2C_Start(void)
{
SDA_H;//拉高数据线
SCL_H;//拉高时钟线
I2C_DELAY;
if(SDA_read == RESET)
//SDA线为低电平则总线忙,退出
{
return I2C_BUS_BUSY;
}
SDA_L;//产生下降沿
I2C_DELAY;
SCL_L;//拉低时钟线
I2C_DELAY;
if(SDA_read == SET)
//SDA线为高电平则总线出错,退出
{
return I2C_BUS_ERROR;
}
return I2C_BUS_READY;
}
2.2. I2C_Stop
static void I2C_Stop(void)
{
SCL_L;
I2C_DELAY;
SDA_L;//拉低数据线
I2C_DELAY;
SCL_H;//拉高时钟线
I2C_DELAY;
SDA_H;//产生上升沿
I2C_DELAY;
}
2.3. I2C_SendACK
static void I2C_SendACK(void)
{
SCL_L;
I2C_DELAY;
SDA_L;//应答=0
I2C_DELAY;
SCL_H;//拉高时钟线
I2C_DELAY;
SCL_L;//拉低时钟线
I2C_DELAY;
}
2.4. I2C_SendNACK
static void I2C_SendNACK(void)
{
SCL_L;
I2C_DELAY;
SDA_H;//应答=1
I2C_DELAY;
SCL_H;//拉高时钟线
I2C_DELAY;
SCL_L;//拉低时钟线
I2C_DELAY;
}
2.5. I2C_WaitAck
static uint8_t I2C_WaitAck(void)//1:ACK;0:NoACK
{
//接收从机的应答
SDA_H;
I2C_DELAY;
SCL_H;//拉高时钟线
I2C_DELAY;
if(SDA_read)//读应答信号
{
SCL_L;//拉低时钟线
return I2C_NACK;
}
else
{
SCL_L;//拉低时钟线
return I2C_ACK;
}
}
2.6. I2C_Delay
static void I2C_Delay(void)//调整波特率
{
uint16_t i= 5;//=5~400k,16~200k,38~100k,81~50k
while(i--);//屏蔽本行,对应约800k速率
}
2.7. I2C_SendByte(uint8_tData)
static void I2C_SendByte(uint8_t Data)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
SCL_L;//拉低时钟线
I2C_DELAY;
if(Data & 0x80)//移出数据的最高位
{
SDA_H;
}
else
{
SDA_L;
}
Data <<= 1;
I2C_DELAY;
SCL_H;//拉高时钟线
I2C_DELAY;
}
SCL_L;
I2C_DELAY;
}
2.8. I2C_RecvByte(void)
static uint8_t I2C_RecvByte(void)
{
uint8_t i, Dat = 0;
SDA_H;//准备读取数据
SCL_L;
I2C_DELAY;
for(i = 0; i < 8; i++)
{
SCL_H;//拉高时钟线,让从机准备好数据
I2C_DELAY;
Dat <<= 1;
if(SDA_read)//读数据
{
Dat |= 0x01;
}
SCL_L;//拉低时钟线
I2C_DELAY;
}
return Dat;
}
3.硬件接口(AT24cxx)
3.1. I2C_GPIO_Configuration
void I2C_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(I2C_SDA_GPIO_CLK | I2C_SDA_GPIO_CLK, ENABLE);
#if 1
//重启从器件,引脚配置为PP方式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;
GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_PIN);
GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_PIN);
GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_PIN);
GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_PIN);
#endif
//配置引脚为OC模式,依赖上拉电阻实现数据功能
GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
}
3.2. I2C_Write_Buf_to_AT24cxx
uint8_t I2C_Write_Buf_to_AT24cxx(uint8_t AT24cxx_addr, uint16_t start_addr, uint16_t len, uint8_t* buf, uint8_t longaddr)
{
uint8_t i= 0;
uint8_t page, addr;
addr = start_addr & 0xff;
page = (start_addr >> 8) & 0x0f;
//1.协议开始
if(I2C_Start())
{
debug_out(("I2C_W_1\r\n"));
return 1;
}
//2.1.发送设备地址
I2C_SendByte(AT24cxx_addr | I2C_Direction_Transmitter);
//2.2.接收ACK
if(!I2C_WaitAck())
{
debug_out(("I2C_W_2\r\n"));
I2C_Stop();
return 1;
}
if(longaddr)//写入地址高位
{
//3.1.发送写入地址高位
I2C_SendByte(page);
//3.2.接收ACK
I2C_WaitAck();
}
//3.3.发送写入地址低位
I2C_SendByte(addr);
//3.4.接收ACK
I2C_WaitAck();
//4.写入数据并接收ACK
for(i = 0; i < len; i++)
{
//4.1.写入数据
I2C_SendByte(buf[i]);
//4.2.接收ACK数据
if(!I2C_WaitAck())//内部寄存器数据
{
debug_out(("I2C_W_3\r\n"));
I2C_Stop();//发送停止信号
return 1;
}
}
//5.协议结束
I2C_Stop();//发送停止信号
return 0;
}
3.2. I2C_Read_Buf_form_AT24cxx
uint8_t I2C_Read_Buf_form_AT24cxx(uint8_t AT24cxx_addr, uint16_t start_addr, uint16_t len, uint8_t* buf, uint8_t longaddr)
{
uint8_t page, addr;
addr = start_addr & 0xff;
page = (start_addr >> 8) & 0x0f;
//1.协议开始
if(I2C_Start())
{
debug_out(("I2C_r_1\r\n"));
return 1;
}
//2.1.发送设备地址(写地址)
I2C_SendByte(AT24cxx_addr | I2C_Direction_Transmitter);
//2.2.接收ACK
if(!I2C_WaitAck())
{
I2C_Stop();
debug_out(("I2C_r_2\r\n"));
return 1;
}
if(longaddr)//写入地址高位
{
//3.1.发送写入地址高位
I2C_SendByte(page);
//3.2.接收ACK
I2C_WaitAck();
}
//3.3.发送写入地址低位
I2C_SendByte(addr);
//3.4.接收ACK
I2C_WaitAck();
//4.协议再次启动
I2C_Start();
//5.1.发送设备地址(读地址)
I2C_SendByte(AT24cxx_addr | I2C_Direction_Receiver);
//5.2.接收ACK
I2C_WaitAck();
//6.读取数据并发送ACK或NACK
while(len)
{
//6.1.读取数据
*buf = I2C_RecvByte();
buf++;
len--;
//6.2.发送ACK或NACK指令
if(len)//仍有数据需要读取,发送ACK
{
I2C_SendACK();
}
else//数据已经读取完毕,发送NACK
{
I2C_SendNACK();
}
}
//7.协议结束
I2C_Stop();
return 0;
}
4.测试程序
4.测试程序
4.1.初始化
I2C_GPIO_Configuration();
4.2.写入测试
#define IIC_SIZE 256
if(AT_24cxx_Write_Buf(xg_data,0,IIC_SIZE))
{
err++;
}
4.3.读取测试
#define IIC_SIZE 256
if(AT_24cxx_Read_Buf(tmp_buf,0,IIC_SIZE))
{
err++;
}
4.4.数据验证
for(k = 0; k < IIC_SIZE; k++)
{
if(tmp_buf[k] != xg_data[k])
{
err++;
}
}
4.5.周期测试
if(count++ == 100)
{
relay = DWT_Time_Relay(0);
count = 0;
j++;
sprintf((char*)tmp_buf,"运行%dk次,用时%dms,出错=%d\r\n",j, (relay/1000), err);
USB_Put_Str_PC((uint8_t *)tmp_buf);
////USB_Put_MultByte_PC((uint8_t *)tmp_buf, IIC_SIZE);
DWT_Delayms(100);
DWT_Time_Start(0);
}
4.6.实际运行时间:
24C02,共256字节,写入用时330ms,读取用时6ms。