PIC单片机 IIC通信及实现

IIC通信

  IIC是一种只需要2根数据线就可以实现数据通信的总线式结构。IIC采用主从式通信方式,通信过程完全由主设备决定。完整的通信流程是主设备发送起始信号给从设备,再发送地址来选中从设备,然后就可以开始进行数据传输,当传输结束后由主设备发送结束信号告知从设备通信结束。

(示例代码中,PORTA为所用单片机的8位引脚对应的寄存器名称,采用PORTA<7>作为数据位SDA、PORTA<6>作为时钟位SCL)

image-20210915200730739
起始信号

  起始信号由两个下降沿组成,SDA信号先从1置0,然后SCL再从1置0,由此告知从设备要开始通信了。代码如下:

/*************************************************************
启动总线函数
函数原型: void start_i2c();
Function: start on the I2C bus
*************************************************************/
void start_i2c()
{
    
    
    PORTA |= 0b10000000; //SDA=1,发送启始条件的数据信号
    delay_10us();
    PORTA |= 0b01000000; //SCL=1
    delay_10us();
    PORTA &= 0b01111111; //SDA=0,发送起始信号
    delay_10us();
    PORTA &= 0b10111111; //SCL=0,钳住I2C总线,准备发送数据或接收数据
    delay_10us();
}
终止信号

  起始信号由两个上升沿,SCL信号先从0置1,然后SDA再从0置1,由此告知从设备要结束通信。代码如下:

/*************************************************************
停止总线函数
函数原型: void stop_i2c();
Function: stop the I2C bus
**************************************************************/
void stop_i2c()
{
    
    
    PORTA &= 0b01111111; //SDA=0,发送结束条件的数据信号
    delay_10us();
    PORTA |= 0b01000000; //SCL=1
    delay_10us();
    PORTA |= 0b10000000; //SDA=1
    delay_10us();
}
发送8位数据

  无论发送地址还是数据,都是通过相同方式一位一位地发送,并且要从高位开始。首先是将时钟线SCL置0,意味着数据线SDA要开始准备信息来发送。

  在每一位数据发送前时钟线都要有一段低电平。当数据线SDA置为要发送的电平后SCl置1,从设备读取此时SDA电平值作为一个bit,随后SCL置0,SDA置为下一个待发送位,重复上述步骤,循环8次即完成一次8bit数据传输。

/*====================================================
字节数据传送函数
函数原型: void send_byte(uchar c);
Function: 将数据C发送出去,可以是地址,也可以是数据,发完后等待回应,并对此状态
位进行操作(不应答或非应答都使ack=0 ),发送数据正常,ack=1;ack=0
表示被控器无应答或损坏。
====================================================*/
void send_byte(uint8_t c)
{
    
    
    uint8_t bit_count;
    for (bit_count = 0; bit_count < 8; bit_count++)
    {
    
    
        if (c & 0x80)
        {
    
    
            PORTA |= 0b10000000; //SDA=1
        }
        else
        {
    
    
            PORTA &= 0b01111111; //SDA=0
        }
        delay_10us();
        PORTA |= 0b01000000; //SCL=1
        delay_10us();
        PORTA &= 0b10111111; //SCL=0
        c <<= 1;

        delay_10us();
    }
}
等待应答

  为了确保从设备收到了主设备发送来的数据,每次发送后主设备都要等待从设备回复ACK信号才能进行后续通话。

  在等待ACK的过程中,主设备要先释放总线SDA,将总线权限交给从设备,即SDA置1。后续如果检测到SDA被置0了就说明从设备答复了ACK信号。

/*====================================================
等待从设备应答函数
函数原型: void wait_ack();
Function: 等待从设备对主设备发送行为的应答
====================================================*/
uint8_t wait_ack()
{
    
    
    uint8_t re = 0;

    PORTA |= 0b10000000; //SDA=1,释放总线,将总线权限交给从设备
    delay_10us();
    PORTA |= 0b01000000; //SCL=1
    TRISA = 0b10000000;
    delay_10us();

    if ((PORTA & 0b10000000) == 0)
    {
    
    
        re = 1; //ACK
    }
    else
    {
    
    
        re = 0; //NACK
    }

    TRISA &= 0b01111111;
    PORTA &= 0b10111111; //SCL=0
    delay_10us();
    return re;
}
发送ACK

  主设备发送完数据需要从设备答复ACK信号,而从设备发送给主设备数据后同样需要主设备给其答复ACK信号以确保收到。

/*====================================================
给从设备发送应答函数
函数原型: void wait_ack();
Function: 给从设备对主设备发送成功接收的应答
====================================================*/
void send_ack()
{
    
    
    PORTA &= 0b01111111; //SDA=0;
    delay_10us();
    PORTA |= 0b01000000; //SCL=1
    delay_10us();
    PORTA &= 0b10111111; //SCL=0
    delay_10us();
}
接收8位数据

  与发送数据时相同,SCL置1表示读取此时SDA电平值作为一个bit,循环8次得到8位数据值。

/*================================================
字节数据接收函数
函数原型:uchar receive_byte();
FUNCTION: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数。
====================================================*/
uint8_t receive_byte()
{
    
    
    uint8_t retc = 0;
    PORTA |= 0b10000000; //SDA=1,释放总线,将总线权限交给从设备
    TRISA |= 0b10000000;
    delay_10us();

    uint8_t bit_count;
    for (bit_count = 0; bit_count < 8; bit_count++)
    {
    
    
        retc = retc * 2;     //retc<<1
        PORTA &= 0b10111111; //SCL=0
        delay_10us();
        PORTA |= 0b01000000; //SCL=1
        delay_10us();

        if (PORTA & 0b10000000)
        {
    
    
            retc++;
        }
        delay_10us();
    }

    TRISA &= 0b01111111;
    PORTA &= 0b10111111; //SCL=0
    delay_10us();

    return (retc);
}
通话总程序

  开启通信——>等待应答——>发送地址和传输方向(前7bit为地址,后1bit表示数据方向)——>发送传感器器件配置信息——>等待应答——>再次开启通信——>发送地址和传输方向——>等待应答——>接收数据——>回复ACK——>结束通信

/*====================================================
IIC通话函数
函数原型: void iic_sensor(void);
Function: 以IIC协议与温度传感器进行通话,获取温度数据
====================================================*/
void iic_sensor()
{
    
    
    start_i2c();
    send_byte(0b10010010); //发送地址值,并且设置为输入
    wait_ack();
    send_byte(0x00); //发送配置信息
    wait_ack();

    start_i2c();
    send_byte(0b10010011); //发送地址值,并且设置为输出
    wait_ack();
    temp[1] = receive_byte();
    send_ack();
    temp[0] = receive_byte();
    send_ack();

    stop_i2c(); //通话结束
}

猜你喜欢

转载自blog.csdn.net/qq_45753394/article/details/120320884