DSP

STM32使用模拟IIC实现24C02读取

2019-07-13 18:22发布

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 "));
return 1;
}


//2.1.发送设备地址
I2C_SendByte(AT24cxx_addr | I2C_Direction_Transmitter);
//2.2.接收ACK
if(!I2C_WaitAck())
{
debug_out(("I2C_W_2 "));
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 "));
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 "));
return 1;
}


//2.1.发送设备地址(写地址)
I2C_SendByte(AT24cxx_addr | I2C_Direction_Transmitter);
//2.2.接收ACK
if(!I2C_WaitAck())
{
I2C_Stop();
debug_out(("I2C_r_2 "));
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 ",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。