IIC通信(STM32F4与AT24C02的通信)

来源:互联网 发布:java web开发需求 编辑:程序博客网 时间:2024/06/01 23:20

IIC通信

IIC的使用场合今天就不累赘了,直接入主题,使用IIC是关键在于他的时序图,以下为IIC时序图的解读:

时序图:

记得IIC通信是一种电平的跳变,在写代码时不必太纠结通信速率,延时合适就好,可以从上图得到什么信息呢,可不可以这么认为,时钟线SCL拉高的期间数据线SDA就不再变化了,是不是就可以得出这么一个结论:时钟线拉高的时候才能从外围设备读数据或是写数据给对方,而且时钟线呈等长时间的高或低电平,其实实际工程中不需要这么严格,比如可以这样,这个周期为10us,下个周期为15或7或8us都是可以的,所以我们在写代码的时候应该将注意力放在电平的跳变上,这里在举个例子:在写操作时,先将数据线设置为你想要的电平,延时一会,再将时钟线拉高使其电平数据保持有效,再延时一会,将时钟线拉低,到这里就算写完了一位数据;再比如读数据时,先将时钟线拉高,确保即将读的数据是有效的,然后延时一会就可以读了,读完后再将时钟先拉低延时为下一次读做准备。
虽然IIC通信对于不同的设备有点略微的不同,具体的根据硬件的时序图来,基本分为这几个步骤:1.启动  2. 对数据的操作 3. 停止    其中对数据的操作又分为读和写,对于这些步骤,我们看图说话。
启动与停止:
这是启动和停止的图示,可以看出什么呢,可不可以这样认为,启动这样子实现,将时钟线与数据线都拉高,然后先将数据线拉低,再将时钟线拉低,通讯就这样开始了;通讯停止的话则刚刚好反过来,将两者拉低,然后先将SCL拉高,再将SDA拉高,这样通讯就停止了。

写操作:


           从图中看出,写入数据之前,需要做一些准备工作,寻设备地址和写设备内存地址,每发送一个字节(高位先发MSB)还有个应答信号ACK,设备地址(DEVICE ADDRESS)是以8为发送,但他并不是8为地址,最低为是R/W命令,意思是你想对这个设备进行读或写操作,1是读,0是写,这样的外设芯片手册里都可以找的到,其次就是写即将要对选定的设备的哪块内存进行读或写操作,即内存地址(WORD ADDRESS),然后才是真正的写数据data,需要再次提醒的是不管写地址还是数据,写一个字节后对方就会回应一个应答信号(有应答低电平,无应答高电平,无应答则认为数据传输出错了),更加具体的细节将再代码中备注。

读操作:



读操作相对写来说,感觉更复杂一点,因为它多了两个步骤,读操作是这样的,先和写操作一样,寻设备(本质还是写),再写地址,然后再启动一下,启动后再写一次
设备内存地址(特别注意这次写地址带有读操作命令),然后就可以开始读数据了,需要特别注意的是每读一个字节的给对方一个应答信号ACK(高电平),想要结束这次读操作,须在读到的最后一个字节不给应答信号,再给STOP信号结束这次读操作。
下面展现出IIC的功能代码,可自行移植:
void IIC_Start(void)//启动信号,将时钟线与数据线都拉高,然后先将数据线拉低,再将时钟线拉低,通讯就这样开始了{        SDA_OUT;//将作为数据线的那个IO引脚设置为输出模式        I2C_SDA_OUT = 1;//拉高数据线        I2C_CLK = 1;//拉高时钟线        delay_us(5);//适当延时,不能太低,高了也不好,低了芯片识别不了,久了通讯速率太慢。        I2C_SDA_OUT = 0;        delay_us(5);        I2C_CLK = 0;}
void IIC_Stop(void){        SDA_OUT;        I2C_SDA_OUT = 0;        I2C_CLK = 0;        delay_us(5);        I2C_CLK = 1;        delay_us(5);        I2C_SDA_OUT = 1;}uint8_t Wait_Ack(void)//写数据时等待对方应答{        uint8_t ack = 0, i= 0;        SDA_IN;                I2C_CLK = 0;        delay_us(5);                I2C_CLK = 1;        delay_us(2);        while(I2C_SDA_IN)        {                i++;                ack = 1;                if(i > 100)                        printf("ack fail\r\n");        }         ack = 0;                I2C_CLK = 0;          return ack;      }void IIC_Ack(void)//读数据的时候给对方应答{        SDA_OUT;        I2C_CLK = 0;        delay_us(5);        I2C_SDA_OUT = 0;        delay_us(5);        I2C_CLK = 1;        delay_us(5);}void IIC_Nack(void)//读数据即将结束的时候不给对方应答{        SDA_OUT;        I2C_CLK = 0;        delay_us(5);        I2C_SDA_OUT = 1;        delay_us(5);        I2C_CLK = 1;        delay_us(5);}void IIC_Send_byte(uint8_t data)//发送一个字节{        uint8_t  i;        SDA_OUT;        I2C_CLK = 0;        delay_us(5);        for(i = 0; i < 8; i++)        {                if(data & (1 << (7 - i)))                        I2C_SDA_OUT = 1;                else                        I2C_SDA_OUT = 0;                delay_us(2);                                I2C_CLK = 1;                delay_us(5);                          I2C_CLK = 0;                 delay_us(5);        }}uint8_t IIC_Read_byte(void)//读取一个字节{        uint8_t  i;        uint8_t data = 0;        I2C_CLK = 0;        delay_us(5);        SDA_IN;        for(i = 0; i < 8; i++)        {                I2C_CLK = 1;                delay_us(2);                if(I2C_SDA_IN)                {                        data <<= 1;                        data |= 1;                }                else                {                        data <<= 1;                 }               delay_us(3);               I2C_CLK = 0;               delay_us(5);        }        return data;}void IIC_Write(uint8_t addr, uint8_t* buf, uint8_t len)//写数据,写的时候只能页写入,这里是基于AT24C02的(每页8个字节),不同的芯片可能不一样,{        uint8_t i, j;        uint8_t page = 0;        for(i = 0; i < (len/8); i++)        {                     IIC_Start();                delay_us(5);                IIC_Send_byte(0xA0);                if(Wait_Ack())                {                        printf("start fail");                        return;                }                   IIC_Send_byte((addr/8*8)+ i*8);//写入的地址必须是8的整数倍,因为页写入                if(Wait_Ack())//无应答则结束,否则继续                {                        printf("write addr fail");                        return;                }                   for(j = 0; j < 8; j++)                {                       IIC_Send_byte(buf[ i*8 + j ]);                         if(Wait_Ack())                        {                                printf("write data fail");                                return;                        }                }                IIC_Stop();                page = i;                delay_ms(50);//´这个延时很关键,经多次测试,要足够长,不然会应答错误(通讯出错)        }}uint8_t  IIC_Read(uint8_t addr, uint8_t* buf, uint8_t len){        uint8_t i;        uint8_t ack = 0;        IIC_Start();        delay_us(2);        IIC_Send_byte(0xA0);//寻设备,写命令        if(Wait_Ack())        {                printf("find device fail");                ack = 1;        }        IIC_Send_byte(addr/8*8);        if(Wait_Ack())        {                printf("write addr fail");                ack = 1;        }        IIC_Start();//再次启动        IIC_Send_byte(0xA1)//读命令        if(Wait_Ack())        {                printf("read device fail");                ack = 1;        }        for(i = 0; i < len; i++)//读操作不分页读取,一直读,地址会自动往后偏移        {                buf[i] = IIC_Read_byte();                if(i == (len - 1))                        IIC_Nack();                else                        IIC_Ack();        }        if(!ack)                printf("read succeed!\r\n");        IIC_Stop();        return ack;}






原创粉丝点击