MCU_IIC总线

转载自https://blog.csdn.net/rayone_/article/details/81207363

/*******************************************************************************************************************

*Author:JYW

*Time:2019_02_14

*Describe:学而有益故转以习之

***************************************************************************************************/

什么是IIC呢:

        一个时钟线SCL和一个数据线SDA;半双工通信;

       在我看来就是用于IC与IC之间的通信,两条线上可以挂在多个设备,每个设备的地址不同,至于主机如何确定从机地址,每个IC都有相应的IIC地址,主机对设备进行扫描,如果扫描到了相应设备,该设备会发出应答,进而进行通信;

       “比如IIC总线上接了两块单片机,一块是主机,另一块是从机,如何定义从机的这个地址”
如果是带IIC的单片机,会有地址寄存器,寄存器里的值即为它作为从机时的地址

       如果是不带IIC的单片机,没有地址寄存器。所以一般不用做从机。以AT89C51为例,用它的普通IO口模拟IIC时序时,一般都是用作主机

IIC怎么用:

       首先要理解IIC协议:空闲状态、开始信号、停止信号、应答信号、数据的有效性、数据传输

       空闲状态:SDA和SCL两条信号线同时处于高电平

       起始信号:当SCL为高期间,SDA由高到低的跳变

       停止信号:当SCL为高期间,SDA由低到高的跳变

       应答信号ACK:

       IIC总线上的所有数据都是以8位字节传送的,发送器每发送一个字节,就在第9个时钟脉冲期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

       因此一个完整的字节数据传输需要9个时钟脉冲。如果从机作为接收方向主机发送非应答信号,主机方就认为此次数据传输失败;如果是主机作为接收方,在从机发送器发送完一个字节数据后,向从机发送了非应答信号,从机就认为数据传输结束,并释放SDA线。不论是以上哪种情况都会终止数据传输,这时主机或是产生停止信号释放总线或是产生重新开始信号,开始一次新的通信。 

数据的有效性:

        I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

        数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定;我认为就是SDA的脉宽大于SCL的脉宽即可

数据的传输:

        在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。

/*******************************

        IIC驱动程序
********************************/

//初始化IIC

void IIC_Init(void)

{      

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高

}

//产生IIC起始信号

void IIC_Start(void)

{

SDA_OUT();     //sda线输出

IIC_SDA=1;      

IIC_SCL=1;

delay_us(4);

  IIC_SDA=0;//START:when CLK is high,DATA change form high to low

delay_us(4);

IIC_SCL=0;//钳住I2C总线,准备发送或接收数据

}   

//产生IIC停止信号

void IIC_Stop(void)

{

SDA_OUT();//sda线输出

IIC_SCL=0;

IIC_SDA=0;//STOP:when CLK is high DATA change form low to high

  delay_us(4);

IIC_SCL=1;

IIC_SDA=1;//发送I2C总线结束信号

delay_us(4);    

}

//等待应答信号到来

//返回值:1,接收应答失败

//        0,接收应答成功

u8 IIC_Wait_Ack(void)

{

u8 ucErrTime=0;

SDA_IN();      //SDA设置为输入  

IIC_SDA=1;delay_us(1);    

IIC_SCL=1;delay_us(1);  

while(READ_SDA)

{

ucErrTime++;

if(ucErrTime>250)

{

IIC_Stop();

return 1;

}

}

IIC_SCL=0;//时钟输出0    

return 0;  

}

//产生ACK应答

void IIC_Ack(void)

{

IIC_SCL=0;

SDA_OUT();

IIC_SDA=0;

delay_us(2);

IIC_SCL=1;

delay_us(2);

IIC_SCL=0;

}

//不产生ACK应答     

void IIC_NAck(void)

{

IIC_SCL=0;

SDA_OUT();

IIC_SDA=1;

delay_us(2);

IIC_SCL=1;

delay_us(2);

IIC_SCL=0;

}        

//IIC发送一个字节

//返回从机有无应答

//1,有应答

//0,无应答   

void IIC_Send_Byte(u8 txd)

{                        

    u8 t;   

SDA_OUT();     

    IIC_SCL=0;//拉低时钟开始数据传输

    for(t=0;t<8;t++)

    {              

        //IIC_SDA=(txd&0x80)>>7;

if((txd&0x80)>>7)

IIC_SDA=1;

else

IIC_SDA=0;

txd<<=1;   

delay_us(2);   //对TEA5767这三个延时都是必须的

IIC_SCL=1;

delay_us(2);

//以上都在准备数据,只需等待SCL 的下降沿跳变即数据将被发送

IIC_SCL=0;

delay_us(2);

    }  

}     

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   

u8 IIC_Read_Byte(unsigned char ack)

{

unsigned char i,receive=0;

SDA_IN();//SDA设置为输入

    for(i=0;i<8;i++ )

{

        IIC_SCL=0;

        delay_us(2);

IIC_SCL=1;

        receive<<=1;

        if(READ_SDA)receive++;   

delay_us(1);

    }  

    if (!ack)

        IIC_NAck();//发送nACK

    else

        IIC_Ack(); //发送ACK   

    return receive;

}
--------------------- 
作者:Rayone_ 
来源:CSDN 
原文:https://blog.csdn.net/Rayone_/article/details/81207363 

猜你喜欢

转载自blog.csdn.net/jiangxiaoweijxw/article/details/87298099