51单片机、IIC从机模拟、IIC协议、iiC读写
1.思路
IIC大部分都是MCU对EEPROM进行读写数据,今天我们用一块51单片机模拟iic_slaver,另外一块mcu当主机,实现主机对iic_slaver的读写。主机向从机读写数据。
1.1写数据
(1)主机发送开始信号,从机检测开始信号(while(IPIN_SCL);while(PIN_SDA)😉.
(2)主机发送器件地址,从机检测器件地址是否正确,若正确从机应答,不正确从机不作回应。
(3)主机发送寄存器地址,从机收到并回应ack。
(4)主机发送8bit数据,从机接收8bit数据,并将数据保存。从机应答主机发送的8bit数据
(5)主机发送结束信号,从机检测结束信号。
1.2读数据
(1)开始信号,主机向从机发送start,从机等待start。
(2)主机发送器件地址,从机判断主机发送的器件地址是否匹配。
(3)主机发送奇存器地址,从机收到并回应ack
(4)知己再次发送开始信号。
(5)主机发送设备地址和读操作,从机判断地址是否匹配,若是则回应。
(6)从机在检测到SCL的情况下,向主机发送8bit数据
(7)等待并判断主机发送的应答
(8)主机发送结束信号,从机检测结束信号。
2.从机IIC协议
2.1起始信号和结束信号
主机发送的起始信号是在SCL线高电平期间,SDA从高电平跳转到低电平。所以从机的代码应该是等带SCL高电平,在SCL高电平期间,等待SDA从高电平跳转到低电平。
void slaver_wait_start()
{
while(!PIN_SCL); //等待高电平
while(PIN_SDA);
while(PIN_SCL);
}
void slaver_wait_stop()
{
while(!PIN_SCL);
while(!PIN_SDA);
}
2.2从机读取和写数据
void slaver_read_data()
{
u8 i;
g_u8_data = 0x00; //全局变量
for(i=0;i<8;i++)
{
while(!PIN_SCL);
if(PIN_SDA)
{
g_u8_data |= (0x80>>i);
}
while(PIN_SCL);
}
}
void slaver_write_data(u8 u8_byte)
{
u8 i;
for(i=0;i<8;i++)
{
while(PIN_SCL); //等待时钟线低电平
PIN_SDA = u8_byte & (0x80>>i);
while(!PIN_SCL);
while(PIN_SCL);
}
PIN_SDA = 1; //释放SDA信号线
}
2.3从机发送和接收ACK
void slaver_send_ack(u8 u8_ack)
{
PIN_SDA = u8_ack;
while(!PIN_SCL);
while(PIN_SCL);
PIN_SDA = 1;
}
int slaver_receive_ack()
{
while(!PIN_SCL);
if(PIN_SDA)
{
while(PIN_SCL);
return 1;
}
else
{
while(PIN_SCL);
return 0;
}
}
2.4判断主机发送读操作还是写操作
int slaver_device_addr()
{
slaver_wait_start();
slaver_read_data();
if(g_u8_data == g_u8_device_w_addr)
{
slaver_send_ack(0);
return 1;
}
else if(g_u8_data == g_u8_device_r_addr)
{
slaver_send_ack(0);
return 2;
}
else
{
return 0;
}
}
2.5寄存器地址
void slaver_reg_addr()
{
slaver_read_data();
g_u8_reg_addr = g_u8_data;
slaver_send_ack(0);
}
2.6主函数
主机在发送完器件地址、寄存器地址后,主机读数据比写数据要多发送个起始位和器件地址,所以我就判断是否有起始位,有起始位就读,没有就写,这样代码就不会卡死,抓取的波形也是正确的。主机的SCL接从机的SCL,主机的SDA接从机的SDA,接地。
sbit PIN_SCL = P1^4;
sbit PIN_SDA = P1^5;
u8 g_u8_device_w_addr = 0xa8;
u8 g_u8_device_r_addr = 0xa9;
u8 g_u8_data = 0x00;
u8 g_u8_reg_addr = 0x01;
u8 g_u8_buf[10];
void main()
{
while(1)
{
if(slaver_device_addr() == 1)
{
slaver_reg_addr();
while(!PIN_SCL);
if(PIN_SDA == 1) //主机读
{
if(slaver_device_addr() == 2)
{
slaver_write_data(g_u8_buf[0]);
if(slaver_receive_ack() == 1)
{
slaver_wait_stop();
}
}
}
else //主机写,从机读
{
slaver_read_data();
g_u8_buf[0] = g_u8_data;
slaver_send_ack(0);
slaver_wait_stop();
}
}
}
}
3.主机IIC协议
3.1起始信号和结束信号
void master_start()
{
PIN_SDA = 1;
PIN_SCL = 1;
PIN_SDA = 0;
PIN_SCL = 0;
}
void master_stop()
{
PIN_SDA = 0;
PIN_SCL = 1;
PIN_SDA = 1;
}
3.2主机写数据和读数据
void master_writedata(u8 u8_data)
{
u8 i;
for(i=0;i<8;i++)
{
PIN_SDA = u8_data(0x80>>i);
PIN_SCL = 1;
PIN_SCL = 0;
}
PIN_SCL = 0;
}
u8 master_readdata()
{
u8 i;
u8 u8_word = 0x00;
PIN_SDA = 1;
for(i=0;i<8;i++)
{
PIN_SCL = 1;
if(PIN_SDA)
{
u8_word |= (0x80 >>i);
}
PIN_SCL = 0;
}
return u8_word;
}
3.3主机发送和接收ACK
void master_write_ack(u8 u8_ackbit)
{
PIN_SCL = 0;
PIN_SDA = u8_ackbit;
PIN_SCL = 1;
PIN_SCL = 0;
}
BOOL master_check_ack()
{
BOOL result = FALSE;
u8 u8_ackbit = 0;
u16 u16_wait = 0;
PIN_SDA = 1; //释放总线,让从机发送ack
PIN_SCL = 1;
while(_i2c_read_data() && (u16_wait < Timeout))
{
u16_wait++;
}
if(!_i2c_read_data())
{
result = TRUE;
}
PIN_SCL = 0;
return result;
}
3.4主机写
BOOL master_write_byte(u8 * u8_buf,u8_num)
{
BOOL result = FALSE;
master_start();
master_writedata(0xa8);
if(!master_check_ack())
goto STOP;
master_writedata(0x00);
if(!master_check_ack())
goto STOP;
while(u8_num--)
{
master_writedata(*u8_buf--);
if(!master_check_ack())
goto STOP;
}
result = TRUE;
STOP:
master_stop();
return result;
}
3.5主机读
void master_read_byte(u8 * u8_byte,u8 num)
{
master_start();
master_writedata(0xa8);
if(!master_check_ack())
goto STOP;
master_writedata(0x00);
if(!master_check_ack())
goto STOP;
master_start();
master_writedata(0xa9);
if(!master_check_ack())
goto STOP;
while(u8_num--)
{
*u8_byte++ == master_readdata();
if(u8_num)
{
master_write_ack(0);
}
else
{
master_write_ack(1);
}
}
STOP:
master_stop();
}