嵌入式学习随笔一:基础通信协议——IIC协议

学习嵌入式,除了最基本的C语言学习以外,非常重要也是最最基本的知识要点就是弄透设备之间的通讯协议,其中主要讨论的有IIC总线、SPI总线、UART串口通讯和CAN总线四种通讯方式。

IIC总线协议(Inter-Integrated Circuit)是设备间最简单的通信协议之一,它是一种串行数据总线,只由两根线组成数据线(SDA)和时钟线(SCL),这两根线上可以连接多个设备,可实现一对一,一对多和多对多。由于只有一根数据线,所以是一种半双工通信方式,同一时间只能一端发送另一端接收,控制时钟线的作为主机。在数据传输的过程中采用的是高位(MSB)在前低位(LSB)在后的传输方式(小端模式)。

一.IIC协议时序

时序是IIC总线协议在编程上的敲门砖,弄懂了时序图才能进行下一步的工作,具体的图形可以在网上百度,这里就只做文字说明,时序信号分为起始信号、终止信号、应答信号、非应答信号。在开始数据传输之前,即总线处于闲置状态时,SDA和SCL的初始电平都被上拉电阻控制为高电平,而且不同状态的改变都是通过SDA信号线的改变实现的。 

(1)起始信号:SCL保持高电平期间(保持一定的延时 1us即可),SDA由高电平变为低电平,编程为:SCL=1;SDA=1;delay();SDA=0;SCL=0;(SCL拉低可进行数据传输)

(2)终止信号:SCL保持高电平期间(保持一定的延时 1us即可),SDA由低电平变为高电平,编程为:SCL=1;SDA=0;delay();SDA=1,SCL=0;

注:数据传输时,SDA的电平改变只发生在SCL在低电平时期。

(3)应答信号I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个ACK(在SCL信号线在高电平期间,把数据总线的电平拉低)来表示数据成功接收。编程实现为:SCL=1;SDA=0;SCL=0;

(4)非应答信号:与应答信号相反,在SCL信号线在高电平期间,把数据总线的电平拉低高,编程实现为SCL=1;SDA=1;SCL=0;

注:在一字节数据或者信号传输完成后将SCL拉低,使总线进入等待状态,等待后续操作,发送停止信号后结束数据传输。

二.数据传输

数据发送过程:发送设备地址——发送ROM地址——发送起始信号——发送数据——每一个字节后(判断接收设备是否应答)——数据发送完之后——停止信号

数据读取过程:发送设备地址——发送ROM地址——发送设备地址+1(进入接收模式)——发送起始信号——接收数据——每一个字节后(发送是否应答)——数据接收完之后——停止信号

编码实现过程为:(这里应用了位带操作,可以直接给SDA和SCL赋予0或1)

void IIC_SendByte(unsigned char dat)   //写数据
{
    unsigned char i;
    SDA_OUT();                        //将SDA设置为输出模式
    for (i = 0; i < 8; i++)
    {
        if((dat<<i)&0x80)
        {
            SDA = 1;
        }
        else
        {
            SDA = 0;
        }
        SCL = 1;       //开始让数据维持稳定
        delay_us(1);  

        SCL = 0;
        delay_us(1);
    }

    SDA = 1; //释放总线  , 发送完8位,主机置高电平
    SCL = 1;
    delay_us(1);
    if (SDA)        //SDA 低电平    从机回馈低电平
    {
        ack = 0;   //0 == ack  代表无ack信号, 从机不应答,发送不成功
    }
    else
    {
        ack = 1;   //从机应答,发送成功
    }
    SCL = 0;
    delay_us(5);
}
unsigned char IIC_RecvByte(void)   //读数据(接收数据)
{
    unsigned char i, receive = 0;
    SDA_IN();                    //将SDA设置为输入模式             
    for (i = 0; i < 8; i++)
    {
        SCL = 0;       // 告诉数据可以变化  SDA  脉冲线   
                     //只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
        delay_us(1);
        SCL = 1;       // 数据保持稳定, 开始读
        delay_us(1);
        receive<<=1;
        if (SDA)
        {
            receive = receive + 1;
        }
    }
    SCL = 0;
    delay_us(10);
    return receive;
}
void IIC_ACK(void)         //应答信号
{
    SDA = 0;               //拉低SDA
    SCL = 1;               
    delay_us(1);
    SCL = 0;            
}
void IIC_NOACK(void)       // 非应答信号
{
    SDA = 1;
    SCL = 1;               拉高SDA
    delay_us(1);
    SCL = 0;
}

每发送和接收完成一位的数据,需要将SCL拉高一定延时,保持数据的稳定。

以AT24C02为例,设计IIC总线的数据传输程序:

unsigned char AT24CXX_WriteStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num)//写数据
{
    unsigned char i;
    IIC_Start();                               //开始传输
    IIC_SendByte(devaddr);                     //发送设备地址
    if (0 == ack)                              //非应答
    {
        return 0;
    }
    IIC_SendByte(romaddr);                     //发送存储器地址
    if (0 == ack) 
    {
        return 0;
    }
    for (i = 0; i < num; i++)                  //发送数据,num为数据的字节数
    {
        IIC_SendByte(*s);
        if (0 == ack) 
        {
            return 0;
        }
        s++;                                   //发送一个字节之后发送下一个字节
    }
    IIC_Stop();
    return 1;
}
unsigned char AT24CXX_ReadStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num)
{
    unsigned char i;
    IIC_Start();
    IIC_SendByte(devaddr);
    if (0 == ack)        //无应答 返回0 失败
    {
        return 0;
    }
    IIC_SendByte(romaddr);
    if (0 == ack) 
    {
        return 0;
    }

    IIC_Start();
    IIC_SendByte(devaddr + 1);                    //最低位写1,进入接收模式
    if (0 == ack) 
    {
        return 0;
    }
    for (i = 0; i < num - 1; i++)                 //倒数第二个数据退出循环
    {
        *s = IIC_RecvByte();
        IIC_ACK();
        s++;
    }
    *s = IIC_RecvByte();                         //将最后一字节数据赋值给指针
    IIC_NOACK();
    IIC_Stop();
    return 1;
}

 总结上述的过程在每发送一个数据,无论是地址信号还是所要传输的数据,都应该用应答信号进行简单的判断,数据传输开始前,要发送相应的设备地址和存储地址再进行数据的发送和读取,设备地址的最后一位为数据传输类型,0为发送模式,1为接收模式。

三.IIC多设备通信

虽然IIC总线只有两根线,但是在两根线上可以挂载多个设备。

IIC总线协议规定,在总线启动后,第一个字节的数据为设备地址,高7位为寻址地址,其中高四位为设备标识,接下来三位为片选,最低位为读写标志,0为写操作,1为读操作,按设备地址来看,最多可挂载2^7-1=127个设备。但由于嵌入式设备的专用性,理论上不会设计多设备挂载的情况。

当发生多个设备间的通信时,需要进行总线仲裁保证通信的有效进行。

主机只能在总线空闲的时候启动传输。两个或多个主机可能在起始条件的最小持续内产生一个起始条件,结果在总线上产生一个规定的起始条件。

当SCL线是高电平时,仲裁在SDA线发生:这样,在其他主机发送低电平时,发送高电平的主机将断开它的数据输出级,因为总线上的电平和它自己的电平不同。仲裁可以持续多位。从地址位开始,同一个器件的话接着就是数据位(如果主机-发送器),或者比较相应位(如果主机-接收器)。IIC总线的地址和数据信息由赢得仲裁的主机决定,在这个过程中不会丢失信息。

仲裁不能在下面情况之间进行:

1)重复起始条件和数据位;

2)停止条件和数据位;

3)重复起始条件和停止条件。

四.IIC的主要应用——AT24C02芯片

在嵌入式产品中,IIC应用最广泛的当属EEPROM,用于小数据的存储,在简单的IO口编程中,需要根据对应的输入输出去设置SDA引脚的输入输出模式(如果搭载有系统,读取EEPROM中的数据时可以直接调用系统层的驱动即可实现,SDA始终保持输出模式即可),而SCL信号线都是有主机控制,所以设置为输出即可,由于有上拉电阻的原因,所以这里的输入模式应该为上拉输入(GPIO_Mode_IPU),输出模式为开漏输出(GPIO_Mode_AF_OD)。

EEPROM在读取传输过程中,可以设置为按字节或按页读写(根据不同的芯片,页的大小不同,具体参考芯片手册),要提一下的是EEPROM不能跨页写,可以跨页读。

五.实际应用中的时序测试

图为EE存储中的实际时序图,从起始信号到8个bit位的传输,再由从机回复一个低电平的应答信号之后再继续传输下一个字节,直到所有字节发送完之后以停止位结束该数据帧的传输,释放总线后SDA和SCL都处于高电平。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值