STM32第七章: IIC(I2C Inter-Intergrated Circuit 集成电路总线)、 IIC时序图(IIC协议)、 I2C模拟时序、STM32F4XX的IIC通信、重载print

目录

一.    IIC(I2C Inter-Intergrated Circuit 集成电路总线)

二.    IIC时序图(IIC协议)

三.    I2C模拟时序

四、STM32F4XX的IIC通信

五、重载printf()


一.    IIC(I2C Inter-Intergrated Circuit 集成电路总线)

    由Pillphs公司发明的。
    IIC也是属于通信的一种,并且也是串行通信(按bit收发数据)。
    
    IIC属于串行总线通信
        只有一根数据线        SDA:Serial DAta 串行数据线
        但是还有一根时钟线    SCL:Serial CLock串行时钟线
        
    SDA:Serial DAta 串行数据线
        数据传输按bit位,先传送最高bit(MSB)
        
    SCL:Serial CLock串行时钟线
        传递的是时钟信号,为什么需要时钟线?用来同步信号的。
        
        同步:约定好发送数据只能在时钟的低跳变时,接收(采样)数据只能在时钟的高跳变时。
        
    所以,IIC是半双工的通信,因为只有一根数据线,在发送数据的时候就不能接收,否则收的数据就是自己发送出去的。IIC通信设备都会挂载在SDA和SCL总线上,或者说在SDA和SCL总线上会挂载很多的设备。那么在任意时刻,只能有一个设备向总线上发送数据,但是接收没有限制,都可以接收。
    为了让数据精准到达(而不是广播的形式发送)我们给IIC总线上的每一个设备都给一个唯一的地址,这个地址就是设备地址,用来区分不同的IIC设备的。

二.    IIC时序图(IIC协议)

IIC数据通信的大概流程:

    a.    总线空闲(空闲指没有数据通信时总线的状态)
        我们约定:IIC总线在空闲的时候,SDA和SCL都处于高电平(通过在总线上上接一个上拉电阻即可)
        接下来,如果有一个设备需要给另一个设备发送数据的话,就需要一个起始信号
         
    b.    起始信号:用来表示我要往总线上发送数据啦。
        SCL时钟线保持高电平
        SDA数据线从高到低产生一个低跳变
    
        模拟IIC的起始信号:
        
        //空闲
        SCL = 1;
        SDA = 1;
        delay();
        
        //起始信号
        SDA = 0;
        delay();
    
        有没有可能两个设备同时发送起始信号呢?
        有可能,所以需要总线仲裁:决定谁的起始信号有效。
        
        比如:在发送起始信号前,判断IIC总线是否空闲。
        
        int time_out = SCL_T;        //超时时间为一个SCL周期
        while(SCL == 1 && SDA == 1 && time_out--);
        如果一个SCL周期内,SCL和SDA都是高电平,那么说明就没有人往总线上发送数据。
        
    c.    发送数据
        数据包含用户真正要发送的数据,也包括设备地址(指定通信方)。
        
        因为总线上有多个设备,其中一个发起一个起始信号,表示它要与总线上的某个设备或者多个设备进行通信,具体与谁进行通信?如果不指定,总线上所有的设备都可以收到数据。
        
        所以IIC协议规定:每一个IIC总线上的设备都必须要有一个IIC设备地址(7bit/10bit)。
        并且,同一个IIC总线上的设备,地址必须不一样。

        
        IIC中数据(包括设备地址)的发送都是按8bit进行发送的
        一个完整的设备地址 = 7bit设备地址 + 1bit读写位(占最低位bit0)
            bit0:0    W 表示我要从指定地址上的IIC设备上写入数据
            bit0:1  R 表示我要从指定地址上的IIC设备上读取数据
        
            例子:
                设备B的地址是101 1010,CPU要发送数据0x55给设备A
                
                CPU: START  10110100 01010101
                 A :
                 
            发送完一个字节的数据之后,对方必须要返回一个ACK(应答位)
                CPU: START  10110100   01010101
                 A :                   ACK           ACK
            
            ACK:在SDA数据线上的第9个时钟周期,接收方给SDA一个低电平。

                    问题:就是如果数据的最后一个bit本身就是一个低电平,那么SDA线此时的电平状态就是0,这个时候,不管接收方应答还是不应答,发送方可能会认为对方应答。
                  
  解决:发送方在发送完8bit的数据后,一般会释放SDA数据线(SDA = 1)。    
            在第9个时钟周期时,接收方就会给SDA一个低电平表示应答(表示我收到啦)。
            
            
        数据发送规则:
            数据发送起始就是根据要发送的数据的bit位的情况给SDA线低电平or高电平。
            先发送MSB(最高位)。
            发送数据时,更改数据线的要求如下:
                IIC协议规定:
                    在SCL时钟线低跳变的时候,可以改变SDA数据线的电平
                    所以发送是下降沿触发,每一个下降沿可以发送一个bit的数据
                    在SCL时钟线高跳变的时候,SDA数据线保持稳定。
                    所以接收是上升沿触发,每一个上升沿到来,接收方就会去SDA上采集1bit的数据。

                    
                
    d.    停止信号
        SCL保持高电平
        SDA从低电平到高电平跳变

 所以一帧数据:

         发送:
            START + data(7bit addr + 1bit0(W))         + data(8bit data)        + .... + STOP
                                              1bit ACK                     1bit ACK 

        接收:
            START + data(7bit addr + 1bit1(R))                        1bit ACK             1bit NA + STOP 
                                              1bit ACK data(8bit data)            + .... +    

问题:
        SDA线一般是由谁来发送数据就由谁来控制,那么SCL时钟线应该由谁来控制呢?
        谁控制都可以,只要不同时控制,但是很多的设备,不具备输出时钟的功能。
        因为它可能没有时钟单元,所以,像STM32中一般是由CPU作为时钟输出(控制)者。

        
        所以我们通过谁控制SCL线将其区分成不同的角色:
            IIC主设备:Master
                产生IIC时钟输出的设备,它控制IIC总线的传输速率。
                
            IIC从设备:Slave
                被动接收IIC时钟的设备。
                
            细分:

                Master-Send 主发        Master-Receive 主收
                Slave-Send  从发        Slave-Receive  从收

        IIC总线上的时钟频率一般在几十K ~400Khz频率越低通信速度就越慢,但是也越稳定

三.    I2C模拟时序

     /*
        IIC_Send_Start:发送IIC起始信号
    */
    void IIC_Send_Start(void)
    {
        //空闲
        SCL = 1;
        SDA = 1;
        delay(IIC_T);    //IIC_T:IIC时钟信号的周期
        
        //起始信号
        SDA = 0;
        delay(IIC_T);        
    }

    /*
        IIC_Send_Stop:发送IIC停止信号
    */
    void IIC_Send_Stop(void)
    {
        SCL = 1;
        SDA = 0;
        delay(IIC_T);
        
        SDA = 1;
        delay(IIC_T);
    }

    /*
        IIC_Send_Byte:将一个字节的数据发送出去
            @ch:要发送的数据,1个字节
            @返回值:发送成功返回1(接收方回复了一个ACK表示发送成功)
                    失败返回0(接收方并没有回复ACK)
    */
    int IIC_Send_Byte(uint8_t ch)
    {
        //MSB(最高位)先发,并且是在SCL下降沿的时候发送1bit
        int i;
        for(i = 7;i >= 0;i--)            //8个SCL时钟周期发送8bit
        {
            SCL = 0;                    //周期开始
            SDA = (ch >> i) & 0x01;        //发送一个bit的数据
            delay(IIC_T/2);                //等待半个时钟周期
            SCL = 1;                    //上升沿产生
            delay(IIC_T/2);                //延时等待一会让对方有时间接收
        }
        
        //发送发在发送完8bit数据之后,一般要先释放数据线
        //同时在第9个时钟周期,等待接收方回应一个ACK低电平
        SCL = 0;                        //第9个周期开始
        SDA = 1;                        //发送方先释放数据线
        delay(IIC_T/2);                    //等待接收方应答
        SCL = 1;                        
        if(SDA)
        {
            return 0;                    //代表无人应答,发送失败
        }
        else
        {
            return 1;                    //代表有人应答,发送成功
        }
        delay(IIC_T/2);    
    }

    /*
        IIC_Recv_Byte:从IIC总线上接收一个字节
            @返回值:将接收到的字节返回
    */
    uint8_t IIC_Recv_Byte(void)
    {
    
    }
    
    /*
        IIC_Write_Datas:向指定的IIC设备写入数据
            @addr:7bit的目标IIC设备地址
            @str:要发送的数据字符串
            @len:要发送的数据字符串的长度
            @返回值:发送成功返回1,发送失败返回0
    */
    uint8_t IIC_Write_Datas(uint8_t addr,uint8_t *str,uint32_t len)
    {
    
    }

四、STM32F4XX的IIC通信

 

       

五、重载printf()

        为了更好的调试代码,重载ptintf。重载ptintf()实际是重载fputc()

int fputc(int c,FILE *stream)
{
    USART_SendData(USART1,c & 0xFF);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
    return 0;
}

版权声明:本文为CSDN博主「歌咏^0^」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_58885663/article/details/121599535

目录

一.    IIC(I2C Inter-Intergrated Circuit 集成电路总线)

二.    IIC时序图(IIC协议)

三.    I2C模拟时序

四、STM32F4XX的IIC通信

五、重载printf()


一.    IIC(I2C Inter-Intergrated Circuit 集成电路总线)

    由Pillphs公司发明的。
    IIC也是属于通信的一种,并且也是串行通信(按bit收发数据)。
    
    IIC属于串行总线通信
        只有一根数据线        SDA:Serial DAta 串行数据线
        但是还有一根时钟线    SCL:Serial CLock串行时钟线
        
    SDA:Serial DAta 串行数据线
        数据传输按bit位,先传送最高bit(MSB)
        
    SCL:Serial CLock串行时钟线
        传递的是时钟信号,为什么需要时钟线?用来同步信号的。
        
        同步:约定好发送数据只能在时钟的低跳变时,接收(采样)数据只能在时钟的高跳变时。
        
    所以,IIC是半双工的通信,因为只有一根数据线,在发送数据的时候就不能接收,否则收的数据就是自己发送出去的。IIC通信设备都会挂载在SDA和SCL总线上,或者说在SDA和SCL总线上会挂载很多的设备。那么在任意时刻,只能有一个设备向总线上发送数据,但是接收没有限制,都可以接收。
    为了让数据精准到达(而不是广播的形式发送)我们给IIC总线上的每一个设备都给一个唯一的地址,这个地址就是设备地址,用来区分不同的IIC设备的。

二.    IIC时序图(IIC协议)

IIC数据通信的大概流程:

    a.    总线空闲(空闲指没有数据通信时总线的状态)
        我们约定:IIC总线在空闲的时候,SDA和SCL都处于高电平(通过在总线上上接一个上拉电阻即可)
        接下来,如果有一个设备需要给另一个设备发送数据的话,就需要一个起始信号
         
    b.    起始信号:用来表示我要往总线上发送数据啦。
        SCL时钟线保持高电平
        SDA数据线从高到低产生一个低跳变
    
        模拟IIC的起始信号:
        
        //空闲
        SCL = 1;
        SDA = 1;
        delay();
        
        //起始信号
        SDA = 0;
        delay();
    
        有没有可能两个设备同时发送起始信号呢?
        有可能,所以需要总线仲裁:决定谁的起始信号有效。
        
        比如:在发送起始信号前,判断IIC总线是否空闲。
        
        int time_out = SCL_T;        //超时时间为一个SCL周期
        while(SCL == 1 && SDA == 1 && time_out--);
        如果一个SCL周期内,SCL和SDA都是高电平,那么说明就没有人往总线上发送数据。
        
    c.    发送数据
        数据包含用户真正要发送的数据,也包括设备地址(指定通信方)。
        
        因为总线上有多个设备,其中一个发起一个起始信号,表示它要与总线上的某个设备或者多个设备进行通信,具体与谁进行通信?如果不指定,总线上所有的设备都可以收到数据。
        
        所以IIC协议规定:每一个IIC总线上的设备都必须要有一个IIC设备地址(7bit/10bit)。
        并且,同一个IIC总线上的设备,地址必须不一样。

        
        IIC中数据(包括设备地址)的发送都是按8bit进行发送的
        一个完整的设备地址 = 7bit设备地址 + 1bit读写位(占最低位bit0)
            bit0:0    W 表示我要从指定地址上的IIC设备上写入数据
            bit0:1  R 表示我要从指定地址上的IIC设备上读取数据
        
            例子:
                设备B的地址是101 1010,CPU要发送数据0x55给设备A
                
                CPU: START  10110100 01010101
                 A :
                 
            发送完一个字节的数据之后,对方必须要返回一个ACK(应答位)
                CPU: START  10110100   01010101
                 A :                   ACK           ACK
            
            ACK:在SDA数据线上的第9个时钟周期,接收方给SDA一个低电平。

                    问题:就是如果数据的最后一个bit本身就是一个低电平,那么SDA线此时的电平状态就是0,这个时候,不管接收方应答还是不应答,发送方可能会认为对方应答。
                  
  解决:发送方在发送完8bit的数据后,一般会释放SDA数据线(SDA = 1)。    
            在第9个时钟周期时,接收方就会给SDA一个低电平表示应答(表示我收到啦)。
            
            
        数据发送规则:
            数据发送起始就是根据要发送的数据的bit位的情况给SDA线低电平or高电平。
            先发送MSB(最高位)。
            发送数据时,更改数据线的要求如下:
                IIC协议规定:
                    在SCL时钟线低跳变的时候,可以改变SDA数据线的电平
                    所以发送是下降沿触发,每一个下降沿可以发送一个bit的数据
                    在SCL时钟线高跳变的时候,SDA数据线保持稳定。
                    所以接收是上升沿触发,每一个上升沿到来,接收方就会去SDA上采集1bit的数据。

                    
                
    d.    停止信号
        SCL保持高电平
        SDA从低电平到高电平跳变

 所以一帧数据:

         发送:
            START + data(7bit addr + 1bit0(W))         + data(8bit data)        + .... + STOP
                                              1bit ACK                     1bit ACK 

        接收:
            START + data(7bit addr + 1bit1(R))                        1bit ACK             1bit NA + STOP 
                                              1bit ACK data(8bit data)            + .... +    

问题:
        SDA线一般是由谁来发送数据就由谁来控制,那么SCL时钟线应该由谁来控制呢?
        谁控制都可以,只要不同时控制,但是很多的设备,不具备输出时钟的功能。
        因为它可能没有时钟单元,所以,像STM32中一般是由CPU作为时钟输出(控制)者。

        
        所以我们通过谁控制SCL线将其区分成不同的角色:
            IIC主设备:Master
                产生IIC时钟输出的设备,它控制IIC总线的传输速率。
                
            IIC从设备:Slave
                被动接收IIC时钟的设备。
                
            细分:

                Master-Send 主发        Master-Receive 主收
                Slave-Send  从发        Slave-Receive  从收

        IIC总线上的时钟频率一般在几十K ~400Khz频率越低通信速度就越慢,但是也越稳定

三.    I2C模拟时序

     /*
        IIC_Send_Start:发送IIC起始信号
    */
    void IIC_Send_Start(void)
    {
        //空闲
        SCL = 1;
        SDA = 1;
        delay(IIC_T);    //IIC_T:IIC时钟信号的周期
        
        //起始信号
        SDA = 0;
        delay(IIC_T);        
    }

    /*
        IIC_Send_Stop:发送IIC停止信号
    */
    void IIC_Send_Stop(void)
    {
        SCL = 1;
        SDA = 0;
        delay(IIC_T);
        
        SDA = 1;
        delay(IIC_T);
    }

    /*
        IIC_Send_Byte:将一个字节的数据发送出去
            @ch:要发送的数据,1个字节
            @返回值:发送成功返回1(接收方回复了一个ACK表示发送成功)
                    失败返回0(接收方并没有回复ACK)
    */
    int IIC_Send_Byte(uint8_t ch)
    {
        //MSB(最高位)先发,并且是在SCL下降沿的时候发送1bit
        int i;
        for(i = 7;i >= 0;i--)            //8个SCL时钟周期发送8bit
        {
            SCL = 0;                    //周期开始
            SDA = (ch >> i) & 0x01;        //发送一个bit的数据
            delay(IIC_T/2);                //等待半个时钟周期
            SCL = 1;                    //上升沿产生
            delay(IIC_T/2);                //延时等待一会让对方有时间接收
        }
        
        //发送发在发送完8bit数据之后,一般要先释放数据线
        //同时在第9个时钟周期,等待接收方回应一个ACK低电平
        SCL = 0;                        //第9个周期开始
        SDA = 1;                        //发送方先释放数据线
        delay(IIC_T/2);                    //等待接收方应答
        SCL = 1;                        
        if(SDA)
        {
            return 0;                    //代表无人应答,发送失败
        }
        else
        {
            return 1;                    //代表有人应答,发送成功
        }
        delay(IIC_T/2);    
    }

    /*
        IIC_Recv_Byte:从IIC总线上接收一个字节
            @返回值:将接收到的字节返回
    */
    uint8_t IIC_Recv_Byte(void)
    {
    
    }
    
    /*
        IIC_Write_Datas:向指定的IIC设备写入数据
            @addr:7bit的目标IIC设备地址
            @str:要发送的数据字符串
            @len:要发送的数据字符串的长度
            @返回值:发送成功返回1,发送失败返回0
    */
    uint8_t IIC_Write_Datas(uint8_t addr,uint8_t *str,uint32_t len)
    {
    
    }

四、STM32F4XX的IIC通信

 

       

五、重载printf()

        为了更好的调试代码,重载ptintf。重载ptintf()实际是重载fputc()

int fputc(int c,FILE *stream)
{
    USART_SendData(USART1,c & 0xFF);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
    return 0;
}

版权声明:本文为CSDN博主「歌咏^0^」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_58885663/article/details/121599535

生成海报
点赞 0

歌咏^0^

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

智能车浅谈——抗干扰技术软件篇

软件抗干扰技术 前面介绍了一些硬件抗干扰技术,不难发现,要做好一个完美的硬件系统是需要花大心思的,而且很多硬件抗扰方案都需要借助一些元器件来实现,这在一定程度上增加了费用,

十六届智能车全向组算法开源(一)

系列文章目录 之前也把自己做的全向所有电路都开源了,内容也包含完整的原理图PDF,想了解的可以看看往期博客。 硬件开源第一章 硬件开源第二章 文章目录 目录 系列文章目录 文章目录 前言 一、智能车比赛常用的赛