IICpb6pb7模拟.总线冲突,判低电平控制

2023-10-11 13:59

本文主要是介绍IICpb6pb7模拟.总线冲突,判低电平控制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

版权声明:本文为博主原创文章,允许转载,但希望标注转载来源。 https://blog.csdn.net/qq_38410730/article/details/80312357

IIC的基本介绍

IIC的简介

IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。它是半双工通信方式。

  • IIC总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。
  • IIC总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。

一般情况下,数据线SDA和时钟线SCL都是处于上拉电阻状态。因为:在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。

STM32的IIC接口

目前绝大多数的MCU都附带IIC总线接口,STM32也不例外。但是在本文中,我们不使用STM32的硬件IIC来读取24C02,而是通过软件的方式来模拟。

原因是因为:STM32的硬件IIC非常复杂,更重要的是它并不稳定,故不推荐使用。

 

IIC协议

IIC总线在传输数据的过程中一共有三种类型信号,分别为:开始信号、结束信号和应答信号。这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。同时我们还要介绍其空闲状态、数据的有效性、数据传输。

先来看一下IIC总线的时序图:

这可能会比较复杂,可以先看一份简化了的时序图:

空闲状态

当IIC总线的数据线SDA和时钟线SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。 

起始信号与停止信号

  • 起始信号:当时钟线SCL为高期间,数据线SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号;
  • 停止信号:当时钟线SCL为高期间,数据线SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。

应答信号

发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 

  • 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
  • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 

对于反馈有效应答位ACK的要求是:接收器在第9个时钟脉冲之前的低电平期间将数据线SDA拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放数据线SDA,以便主控接收器发送一个停止信号P。

数据有效性

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

即:数据在时钟线SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。

数据的传达

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

延时时间

 

IIC总线的数据传送

IIC总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(地址通过物理接地或者拉高),主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。

也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。

  • 主设备往从设备中写数据。数据传输格式如下:

淡蓝色部分表示数据由主机向从机传送,粉红色部分则表示数据由从机向主机传送。

写用0来表示(低电平),读用1来表示(高电平)。

  • 主设备从从设备中读数据。数据传输格式如下:

在从机产生响应时,主机从发送变成接收,从机从接收变成发送。之后,数据由从机发送,主机接收,每个应答由主机产生,时钟信号仍由主机产生。若主机要终止本次传输,则发送一个非应答信号,接着主机产生停止条件。

  •  主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下:

在多主的通信系统中,总线上有多个节点,它们都有自己的寻址地址,可以作为从节点被别的节点访问,同时它们都可以作为主节点向其它的节点发送控制字节和传送数据。但是如果有两个或两个以上的节点都向总线上发送启动信号并开始传送数据,这样就形成了冲突。要解决这种冲突,就要进行仲裁的判决,这就是I2C总线上的仲裁。

I2C总线上的仲裁分两部分:SCL线的同步和SDA线的仲裁。

这部分就暂时不介绍了,想要了解:可以参考链接浅谈I2C总线或I2C总线协议图解。

 

IIC底层驱动程序分析

现拟采用PB6、PB7来模拟IIC时序,其中:PB6为时钟线,PB7为数据线。

首先进行一些必要的宏定义:

  1.  

    //IO方向设置

  2.  

    #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}

  3.  

    #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

  4.  

     

  5.  

    //IO操作函数

  6.  

    #define IIC_SCL PBout(6) //SCL

  7.  

    #define IIC_SDA PBout(7) //SDA

  8.  

    #define READ_SDA PBin(7) //输入SDA

  9.  

     

  10.  

    //IIC所有操作函数

  11.  

    void IIC_Init(void); //初始化IIC的IO口

  12.  

    void IIC_Start(void); //发送IIC开始信号

  13.  

    void IIC_Stop(void); //发送IIC停止信号

  14.  

    void IIC_Send_Byte(u8 txd); //IIC发送一个字节

  15.  

    u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节

  16.  

    u8 IIC_Wait_Ack(void); //IIC等待ACK信号

  17.  

    void IIC_Ack(void); //IIC发送ACK信号

  18.  

    void IIC_NAck(void); //IIC不发送ACK信号

由于IIC是半双工通信方式,因而数据线SDA可能会数据输入,也可能是数据输出,需要定义IIC_SDA来进行输出、READ_SDA来进行输入,与此同时就要对IO口进行模式配置:SDA_IN()和SDA_OUT()。

而时钟线SCL一直是输出的,所以就没有数据线SDA麻烦了。

  1.  

    //初始化IIC

  2.  

    void IIC_Init(void)

  3.  

    {

  4.  

    GPIO_InitTypeDef GPIO_InitStructure;

  5.  

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

  6.  

     

  7.  

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;

  8.  

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

  9.  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  10.  

    GPIO_Init(GPIOB, &GPIO_InitStructure);

  11.  

    GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高,空闲状态

  12.  

    }

  13.  

    //产生IIC起始信号

  14.  

    void IIC_Start(void)

  15.  

    {

  16.  

    SDA_OUT(); //sda线输出

  17.  

    IIC_SDA=1;

  18.  

    IIC_SCL=1;

  19.  

    delay_us(4);

  20.  

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

  21.  

    delay_us(4);

  22.  

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

  23.  

    }

  24.  

    //产生IIC停止信号

  25.  

    void IIC_Stop(void)

  26.  

    {

  27.  

    SDA_OUT();    //sda线输出

  28.  

    IIC_SCL=0;

  29.  

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

  30.  

    delay_us(4);

  31.  

    IIC_SCL=1;

  32.  

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

  33.  

    delay_us(4);

  34.  

    }

  35.  

    //发送数据后,等待应答信号到来

  36.  

    //返回值:1,接收应答失败,IIC直接退出

  37.  

    // 0,接收应答成功,什么都不做

  38.  

    u8 IIC_Wait_Ack(void)

  39.  

    {

  40.  

    u8 ucErrTime=0;

  41.  

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

  42.  

    IIC_SDA=1;delay_us(1);

  43.  

    IIC_SCL=1;delay_us(1);

  44.  

    while(READ_SDA)

  45.  

    {

  46.  

    ucErrTime++;

  47.  

    if(ucErrTime>250)

  48.  

    {

  49.  

    IIC_Stop();

  50.  

    return 1;

  51.  

    }

  52.  

    }

  53.  

    IIC_SCL=0;    //时钟输出0

  54.  

    return 0;

  55.  

    }

  56.  

    //产生ACK应答

  57.  

    void IIC_Ack(void)

  58.  

    {

  59.  

    IIC_SCL=0;

  60.  

    SDA_OUT();

  61.  

    IIC_SDA=0;

  62.  

    delay_us(2);

  63.  

    IIC_SCL=1;

  64.  

    delay_us(2);

  65.  

    IIC_SCL=0;

  66.  

    }

  67.  

    //不产生ACK应答

  68.  

    void IIC_NAck(void)

  69.  

    {

  70.  

    IIC_SCL=0;

  71.  

    SDA_OUT();

  72.  

    IIC_SDA=1;

  73.  

    delay_us(2);

  74.  

    IIC_SCL=1;

  75.  

    delay_us(2);

  76.  

    IIC_SCL=0;

  77.  

    }

  78.  

    //IIC发送一个字节

  79.  

    //返回从机有无应答

  80.  

    //1,有应答

  81.  

    //0,无应答

  82.  

    void IIC_Send_Byte(u8 txd)

  83.  

    {

  84.  

    u8 t;

  85.  

    SDA_OUT();

  86.  

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

  87.  

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

  88.  

    {

  89.  

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

  90.  

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

  91.  

    IIC_SDA=1;

  92.  

    else

  93.  

    IIC_SDA=0;

  94.  

    txd<<=1;

  95.  

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

  96.  

    IIC_SCL=1;

  97.  

    delay_us(2);

  98.  

    IIC_SCL=0;

  99.  

    delay_us(2);

  100.  

    }

  101.  

    }

  102.  

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

  103.  

    u8 IIC_Read_Byte(unsigned char ack)

  104.  

    {

  105.  

    unsigned char i,receive=0;

  106.  

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

  107.  

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

  108.  

    {

  109.  

    IIC_SCL=0;

  110.  

    delay_us(2);

  111.  

    IIC_SCL=1;

  112.  

    receive<<=1;

  113.  

    if(READ_SDA)receive++;

  114.  

    delay_us(1);

  115.  

    }

  116.  

    if (!ack)

  117.  

    IIC_NAck();        //发送nACK

  118.  

    else

  119.  

    IIC_Ack();         //发送ACK

  120.  

    return receive;

  121.  

    }

这里是通过普通IO口(PB6、PB7)来模拟IIC时序的程序,其实本质上都是严格按照IIC的时序图进行的,认真读,仔细对比,应该是没有什么困难的。

就提一下:IIC_Read_Byte()函数,这个函数的参数表示读取一个字节之后,需要给对方应答信号或非应答信号。

 

普通IO口模拟IIC时序读取24C02

24C02芯片介绍

EEPROM (Electrically Erasable Programmable read only memory),带电可擦可编程只读存储器——一种掉电后数据不丢失的存储芯片。 

24Cxx芯片是EEPROM芯片的一种,它是基于IIC总线的存储器件,遵循二线制协议,由于其具有接口方便,体积小,数据掉电不丢失等特点,在仪器仪表及工业自动化控制中得到大量的应用。24Cxx在电路的作用主要是在掉电的情况下保存数据。

本文使用的是24C02芯片,总容量是2k个bit(256个字节)。这里芯片名称里的02代表着总容量。

24C02芯片的引脚分布和具体的作用见下图:

 

24C02芯片的引脚说明
引脚名称说明
A0-A2地址输入线
SDA数据线
SCL时钟线
WP写保护
GND、VCC提供电源

下图是本文中24C02和STM32的引脚连接图:

从图中可以看出:A0、A1、A2都为0。

对于并联在一条IIC总线上的每个IC都有唯一的地址。那么看一下从器件地址,可以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容:

根据图中的内容:如果是写24C02的时候,从器件地址为10100000(0xA0);读24C02的时候,从器件地址为10100001(0xA1)。

24C02芯片的时序图

这部分的内容应结合上文:I2C总线的数据传送的内容一起理解。

24C02字节写时序

对24C02芯片进行写字节操作的时候,步骤如下:

  1. 开始位,后面紧跟从器件地址位(0xA0),等待应答,这是为了在IIC总线上确定24C02的从地址位置;
  2. 确定操作24C02的地址,等待应答,也就是将字节写入到24C02中256个字节中的位置;
  3. 确定需要写入24C02芯片的字节,等待应答,停止位。

24C02字节读时序

对24C02芯片进行读字节操作的时候,步骤如下:

  1. 开始位,后面紧跟从器件地址位(0xA0),等待应答,这是为了在IIC总线上确定24C02的从地址位置;
  2. 确定操作24C02的地址,等待应答,也就是从24C02中256个字节中读取字节的位置;
  3. 再次开始位,后面紧跟从器件地址位(0xA1),等待应答;
  4. 获取从24C02芯片中读取的字节,发出非应答信号,停止位。

读取24C02芯片程序

  1.  

    #define AT24C01 127

  2.  

    #define AT24C02 255

  3.  

    #define AT24C04 511

  4.  

    #define AT24C08 1023

  5.  

    #define AT24C16 2047

  6.  

    #define AT24C32 4095

  7.  

    #define AT24C64 8191

  8.  

    #define AT24C128 16383

  9.  

    #define AT24C256 32767

  10.  

    //Mini STM32开发板使用的是24c02,所以定义EE_TYPE为AT24C02

  11.  

    #define EE_TYPE AT24C02

  1.  

    //初始化IIC接口

  2.  

    void AT24CXX_Init(void)

  3.  

    {

  4.  

    IIC_Init();

  5.  

    }

  6.  

    //在AT24CXX指定地址读出一个数据

  7.  

    //ReadAddr:开始读数的地址

  8.  

    //返回值 :读到的数据

  9.  

    u8 AT24CXX_ReadOneByte(u16 ReadAddr)

  10.  

    {

  11.  

    u8 temp=0;

  12.  

        IIC_Start();

  13.  

    if(EE_TYPE>AT24C16)            //为了兼容24Cxx中其他的版本

  14.  

    {

  15.  

    IIC_Send_Byte(0XA0); //发送写命令

  16.  

    IIC_Wait_Ack();

  17.  

    IIC_Send_Byte(ReadAddr>>8);    //发送高地址

  18.  

    IIC_Wait_Ack();

  19.  

    }else     IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据

  20.  

     

  21.  

    IIC_Wait_Ack();

  22.  

        IIC_Send_Byte(ReadAddr%256); //发送低地址

  23.  

    IIC_Wait_Ack();

  24.  

    IIC_Start();

  25.  

    IIC_Send_Byte(0XA1); //进入接收模式

  26.  

    IIC_Wait_Ack();

  27.  

        temp=IIC_Read_Byte(0);     //读一个字节,非应答信号信号

  28.  

        IIC_Stop();        //产生一个停止条件

  29.  

    return temp;

  30.  

    }

  31.  

    //在AT24CXX指定地址写入一个数据

  32.  

    //WriteAddr :写入数据的目的地址

  33.  

    //DataToWrite:要写入的数据

  34.  

    void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)

  35.  

    {

  36.  

        IIC_Start();

  37.  

    if(EE_TYPE>AT24C16)

  38.  

    {

  39.  

    IIC_Send_Byte(0XA0); //发送写命令

  40.  

    IIC_Wait_Ack();

  41.  

    IIC_Send_Byte(WriteAddr>>8);    //发送高地址

  42.  

    }else

  43.  

    {

  44.  

    IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据

  45.  

    }

  46.  

    IIC_Wait_Ack();

  47.  

        IIC_Send_Byte(WriteAddr%256); //发送低地址

  48.  

    IIC_Wait_Ack();

  49.  

    IIC_Send_Byte(DataToWrite); //发送字节

  50.  

    IIC_Wait_Ack();

  51.  

        IIC_Stop();    //产生一个停止条件

  52.  

    delay_ms(10);

  53.  

    }

  54.  

    //在AT24CXX里面的指定地址开始写入长度为Len的数据

  55.  

    //该函数用于写入16bit或者32bit的数据.

  56.  

    //WriteAddr :开始写入的地址

  57.  

    //DataToWrite:数据数组首地址

  58.  

    //Len :要写入数据的长度2,4

  59.  

    void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)

  60.  

    {

  61.  

    u8 t;

  62.  

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

  63.  

    {

  64.  

    AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);

  65.  

    }

  66.  

    }

  67.  

     

  68.  

    //在AT24CXX里面的指定地址开始读出长度为Len的数据

  69.  

    //该函数用于读出16bit或者32bit的数据.

  70.  

    //ReadAddr :开始读出的地址

  71.  

    //返回值 :数据

  72.  

    //Len :要读出数据的长度2,4

  73.  

    u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)

  74.  

    {

  75.  

    u8 t;

  76.  

    u32 temp=0;

  77.  

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

  78.  

    {

  79.  

    temp<<=8;

  80.  

    temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);

  81.  

    }

  82.  

    return temp;

  83.  

    }

  84.  

    //检查AT24CXX是否正常

  85.  

    //这里用了24XX的最后一个地址(255)来存储标志字.

  86.  

    //如果用其他24C系列,这个地址要修改

  87.  

    //返回1:检测失败

  88.  

    //返回0:检测成功

  89.  

    u8 AT24CXX_Check(void)

  90.  

    {

  91.  

    u8 temp;

  92.  

    temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX

  93.  

    if(temp==0X55)return 0;

  94.  

    else//排除第一次初始化的情况

  95.  

    {

  96.  

    AT24CXX_WriteOneByte(255,0X55);

  97.  

    temp=AT24CXX_ReadOneByte(255);

  98.  

    if(temp==0X55)return 0;

  99.  

    }

  100.  

    return 1;

  101.  

    }

  102.  

     

  103.  

    //在AT24CXX里面的指定地址开始读出指定个数的数据

  104.  

    //ReadAddr :开始读出的地址 对24c02为0~255

  105.  

    //pBuffer :数据数组首地址

  106.  

    //NumToRead:要读出数据的个数

  107.  

    void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)

  108.  

    {

  109.  

    while(NumToRead)

  110.  

    {

  111.  

    *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);

  112.  

    NumToRead--;

  113.  

    }

  114.  

    }

  115.  

    //在AT24CXX里面的指定地址开始写入指定个数的数据

  116.  

    //WriteAddr :开始写入的地址 对24c02为0~255

  117.  

    //pBuffer :数据数组首地址

  118.  

    //NumToWrite:要写入数据的个数

  119.  

    void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)

  120.  

    {

  121.  

    while(NumToWrite--)

  122.  

    {

  123.  

    AT24CXX_WriteOneByte(WriteAddr,*pBuffer);

  124.  

    WriteAddr++;

  125.  

    pBuffer++;

  126.  

    }

  127.  

    }

  1.  

    //要写入到24c02的字符串数组

  2.  

    const u8 TEXT_Buffer[]={"WarShipSTM32 IIC TEST"};

  3.  

    #define SIZE sizeof(TEXT_Buffer)

  4.  

     

  5.  

    int main(void)

  6.  

    {

  7.  

    u8 key;

  8.  

    u16 i=0;

  9.  

    u8 datatemp[SIZE];

  10.  

    delay_init(); //延时函数初始化

  11.  

          NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级

  12.  

    uart_init(115200); //串口初始化为115200

  13.  

    LED_Init(); //初始化与LED连接的硬件接口

  14.  

    LCD_Init(); //初始化LCD

  15.  

    KEY_Init(); //按键初始化

  16.  

    AT24CXX_Init(); //IIC初始化

  17.  

     

  18.  

    POINT_COLOR=RED;//设置字体为红色

  19.  

    LCD_ShowString(30,50,200,16,16,"WarShip STM32");

  20.  

    LCD_ShowString(30,70,200,16,16,"IIC TEST");

  21.  

    LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

  22.  

    LCD_ShowString(30,110,200,16,16,"2015/1/15");

  23.  

    LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read"); //显示提示信息

  24.  

    while(AT24CXX_Check())//检测不到24c02

  25.  

    {

  26.  

    LCD_ShowString(30,150,200,16,16,"24C02 Check Failed!");

  27.  

    delay_ms(500);

  28.  

    LCD_ShowString(30,150,200,16,16,"Please Check! ");

  29.  

    delay_ms(500);

  30.  

    LED0=!LED0;//DS0闪烁

  31.  

    }

  32.  

    LCD_ShowString(30,150,200,16,16,"24C02 Ready!");

  33.  

    POINT_COLOR=BLUE;//设置字体为蓝色

  34.  

    while(1)

  35.  

    {

  36.  

    key=KEY_Scan(0);

  37.  

    if(key==KEY1_PRES)//KEY_UP按下,写入24C02

  38.  

    {

  39.  

    LCD_Fill(0,170,239,319,WHITE);//清除半屏

  40.  

    LCD_ShowString(30,170,200,16,16,"Start Write 24C02....");

  41.  

    AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);

  42.  

    LCD_ShowString(30,170,200,16,16,"24C02 Write Finished!");//提示传送完成

  43.  

    }

  44.  

    if(key==KEY0_PRES)//KEY1按下,读取字符串并显示

  45.  

    {

  46.  

    LCD_ShowString(30,170,200,16,16,"Start Read 24C02.... ");

  47.  

    AT24CXX_Read(0,datatemp,SIZE);

  48.  

    LCD_ShowString(30,170,200,16,16,"The Data Readed Is: ");//提示传送完成

  49.  

    LCD_ShowString(30,190,200,16,16,datatemp);//显示读到的字符串

  50.  

    }

  51.  

    i++;

  52.  

    delay_ms(10);

  53.  

    if(i==20)

  54.  

    {

  55.  

    LED0=!LED0;//提示系统正在运行

  56.  

    i=0;

  57.  

    }

  58.  

    }

  59.  

    }

 

IIC总结

  1. 进行数据传送时,在SCL为高电平期间,SDA线上电平必须保持稳定,只有SCL为低时,才允许SDA线上电平改变状态。并且每个字节传送时都是高位在前;
  2. 对于应答信号,ACK=0时为有效应答位,说明从机已经成功接收到该字节,若为1则说明接受不成功;
  3. 如果从机需要延迟下一个数据字节开始传送的时间,可以通过把SCL电平拉低并保持来强制主机进入等待状态;
  4. 主机完成一次通信后还想继续占用总线在进行一次通信,而又不释放总线,就要利用重启动信号。它既作为前一次数据传输的结束,又作为后一次传输的开始;
  5. 总线冲突时,按“低电平优先”的仲裁原则,把总线判给在数据线上先发送低电平的主器件;
  6. 在特殊情况下,若需禁止所有发生在I2C总线上的通信,可采用封锁或关闭总线,具体操作为在总线上的任一器件将SCL锁定在低电平即可;
  7. SDA仲裁和SCL时钟同步处理过程没有先后关系,而是同时进行的。

这篇关于IICpb6pb7模拟.总线冲突,判低电平控制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/188531

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码  2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法,这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列 算法分析 这道题其实就是要将连续且相同的字符替换成字符重复的次数+

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品

模拟实现vector中的常见接口

insert void insert(iterator pos, const T& x){if (_finish == _endofstorage){int n = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + n;//防止迭代