yuangaoping的个人空间 https://bbs.21ic.com/?587159 [收藏] [复制] [RSS]

日志

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

已有 559 次阅读2016-11-18 16:30 |个人分类:STM32|系统分类:单片机| STM32, IIC, 模拟

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。


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)