I2C

一、简介

    (1)物理层

            

            是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通信总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。

            一个I2C总线只使用两条总线,一条双向串行数据线(SDA),一条串行时钟线(SCL)。数据线用来传输数据,时钟线用于数据收发同步。

            每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。

            总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。

            多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。

            具有三种传输模式:标准模式传输速率为100Kbit/s ; 快速模式传输速率为400Kbit/s ; 高速模式下可达3.4Mbit/s。但目前最常用的就是快速模式,而大多数I2C设备尚不支持高速模式。

    (2)协议层

            I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

            1》I2C基本读写过程

                 主机写数据到从机:

                 

                 主机由从机读数据:

                 

                 通讯符合格式:

                 

                EEPROM有设备地址,内部有存储器地址,如果从EEPROM第2个存储单元格地址读取数据,则:

                    首先,往EEPROM写入要读取的存储单元格的地址;

                    然后,接收EEPROM返回来的第2个存储单元格的内容。

            2》起始和停止信号

                 

            3》数据有效性

                 

            4》地址及数据方向

                 

                8位设备写地址 = (7位设备地址 << 1)  |  0 ;

                8位设备读地址 = (7位设备地址 << 1)  |  1 。

            5》响应

                 

二、

三、固件库

    (1)初始化结构体

            typedef struct

            {

                  uint32_t   I2C_ClockSpeed;                   //设置SCL时钟频率,此值要低于40 0000。

                  uint16_t   I2C_Mode;                             //指定工作模式。

                                                                                  I2C_Mode_I2C (I2C模式)
                                                                                  I2C_Mode_SMBusDevice(SMBUS从机模式)
                                                                                  I2C_Mode_SMBusHost  (SMBUS主机模式)

                  uint16_t   I2C_DutyCycle;                      //指定时钟占空比。

                                                                                  I2C_DutyCycle_16_9 (low/high = 16 :9)
                                                                                  I2C_DutyCycle_2    (low/high = 2 :1)

                  uint16_t   I2C_OwnAddress1;                //指定自身的I2C设备地址。

                  uint16_t   I2C_Ack;                                //使能或关闭响应(一般使能)。

                                                                                I2C_Ack_Enable (使能)
                                                                                I2C_Ack_Disable(禁用)

                  uint16_t   I2C_AcknowledgedAddress;  //指定设备地址长度。

                                                                                I2C_AcknowledgedAddress_7bit (7位设备地址)
                                                                                I2C_AcknowledgedAddress_10bit(10位设备地址)

            }I2C_InitTypeDef;

    (2)常用固件库函数

            1》产生起始信号

                  void   I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);

            2》产生终止信号

                  void   I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

            3》发送7位设备地址

                  void   I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

                  参数:

                          I2C_Direction ---> I2C_Direction_Transmitter(发送方) ; I2C_Direction_Receiver(接收方)。

            4》发送数据

                  void   I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

            5》接收数据

                  uint8_t   I2C_ReceiveData(I2C_TypeDef* I2Cx);  

            6》获取状态标志

                  FlagStatus   I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);

            7》获取中断状态标志

                 ITStatus   I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);

四、AT24C02介绍

    容量为2048bits,256个字节,8个字节为一页,共32页。

    设备地址:

          

    写入数据时序图:

        1》按字节写入

           

        2》按页写入

           

    读取数据时序图:

        1》读取当前地址的数据(极少使用)

           

        2》随机读取数据

           

        3》顺序读取数据

           

 

五、程序

/*i2c.h文件*/

#ifndef __I2C_H__
#define __I2C_H__


#include "stm32l1xx.h"

#define EEPROM_PAGE_SIZE 8   //ÿҳ8¸ö×Ö½Ú

void I2C_Config(void);
uint8_t EEPROM_Write_Byte(uint8_t addr,uint8_t data);
uint8_t EEPROM_Read_Byte(uint8_t addr,uint8_t *data);
uint8_t EEPROM_Write_Page(uint8_t addr,uint8_t *data,uint8_t size);
uint8_t EEPROM_Read_Buff(uint8_t addr,uint8_t *data,uint16_t size);
uint8_t EEPROM_Write_Buff(uint8_t addr, uint8_t* data, uint16_t size);


#endif
 

/*i2c.c文件*/

#include "./I2C/i2c.h"

#define TIME_OUT 0x000FFFFF
uint32_t count_wait = TIME_OUT;

static uint8_t Wait_For_Standby(void);

/*
    SCL --- PB8 ; SDA --- PB9 
*/

void I2C_Config(void)
{
    GPIO_InitTypeDef initValue;
    I2C_InitTypeDef i2c1InitValue;
    
    //打开时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
    
    //配置GPIO的复用功能
    initValue.GPIO_Mode = GPIO_Mode_AF;
    initValue.GPIO_OType = GPIO_OType_OD;
    initValue.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
    initValue.GPIO_PuPd = GPIO_PuPd_NOPULL;
    initValue.GPIO_Speed = GPIO_Speed_40MHz;
    GPIO_Init(GPIOB,&initValue);
    
    //选择复用引脚
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_I2C1);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_I2C1);
    
    //初始化I2C串口
    i2c1InitValue.I2C_Ack = I2C_Ack_Enable;
    i2c1InitValue.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    i2c1InitValue.I2C_ClockSpeed = 400000;
    i2c1InitValue.I2C_DutyCycle = I2C_DutyCycle_2;
    i2c1InitValue.I2C_Mode = I2C_Mode_I2C;
    i2c1InitValue.I2C_OwnAddress1 = 0x78;
    I2C_Init(I2C1,&i2c1InitValue);
    
    //使能I2C
    I2C_Cmd(I2C1,ENABLE);
}

//往EEPROM写入一个字节的数据
//addr:要写入数据的单元格地址
//data:要写入的数据
uint8_t EEPROM_Write_Byte(uint8_t addr,uint8_t data)
{
    
    //产生起始信号
    I2C_GenerateSTART(I2C1,ENABLE);
    
    //等待EV5事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 1;
        }
    }
    
    //发送设备写地址
    I2C_Send7bitAddress(I2C1,0xA0,I2C_Direction_Transmitter);
    
    //等待EV6事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 2;
        }
    }
    
    //发送要写入数据的单元格地址
    I2C_SendData(I2C1,addr);
    
    //等待EV8_2事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 3;
        }
    }
    
    //发送要写入的数据
    I2C_SendData(I2C1,data);
    
    //等待EV8_2事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 4;
        }
    }
    
    //产生结束信号
    I2C_GenerateSTOP(I2C1,ENABLE);
    
    //等待接入完成
    return Wait_For_Standby();
}


//读取一个字节数据
uint8_t EEPROM_Read_Byte(uint8_t addr,uint8_t *data)
{
    //产生起始信号
    I2C_GenerateSTART(I2C1,ENABLE);
    
    //等待EV5事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 5;
        }
    }
    
    //发送设备写地址·
    I2C_Send7bitAddress(I2C1,0xA0,I2C_Direction_Transmitter);
    
    //等待EV6事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 6;
        }
    }
    
    //发送要读取的单元格地址
    I2C_SendData(I2C1,addr);
    
    //等待V8_2事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 7;
        }
    }
    
    //重新产生起始信号
    I2C_GenerateSTART(I2C1,ENABLE);
    
    //等待EV5事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 8;
        }
    }
    
    //发送设备读地址
    I2C_Send7bitAddress(I2C1,0xA1,I2C_Direction_Receiver);
    
    //等待EV6事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 9;
        }
    }
    
    
    //产生应答信号
    I2C_AcknowledgeConfig(I2C1,DISABLE);
    
    //等待EV7事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0)
        {
            return 10;
        }
    }
        
    //接收读取到的数据
    *data = I2C_ReceiveData(I2C1);
    
    //产生停止信号
    I2C_GenerateSTOP(I2C1,ENABLE);
    
    return 0;
    
}

//页写入
//data:要写入的数据的指针
//addr:要写入的存储单元格首地址
//size:要写入多少个数据(size小于等于8)
uint8_t EEPROM_Write_Page(uint8_t addr,uint8_t *data,uint8_t size)
{
    //产生起始信号
    I2C_GenerateSTART(I2C1,ENABLE);
    
    
    //等待EV5事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 12;
        }
    }    
    
    //发送设备写地址
    I2C_Send7bitAddress(I2C1,0xA0,I2C_Direction_Transmitter);
    
    //等待EV6事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 13;
        }
    }
    
    //发送单元格首地址
    I2C_SendData(I2C1,addr);
    
        
    //等待EV8_2事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 14;
        }
    }
    
    //发送要写入的数据
    while(size--)
    {
        I2C_SendData(I2C1,*data);
        
        //等待EV8_2事件
        count_wait = TIME_OUT;
        while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
        {
            count_wait--;
            if(count_wait == 0 )
            {
                return 15;
            }
        }
        
        data++;
    }
    
    //产生终止信号
    I2C_GenerateSTOP(I2C1,ENABLE);
    
    //等待内部时序完成
    return Wait_For_Standby();
}

//顺序读取数据
//addr:要读取的单元格首地址
//data:存放数据的指针
//size:要读取多少个数据
uint8_t EEPROM_Read_Buff(uint8_t addr,uint8_t *data,uint16_t size)
{
    //产生起始信号
    I2C_GenerateSTART(I2C1,ENABLE);
    
    
    //等待EV5事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 16;
        }
    }    
    
    //发送设备写地址
    I2C_Send7bitAddress(I2C1,0xA0,I2C_Direction_Transmitter);
    
    
    //等待EV6事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 17;
        }
    }
    
    //发送要读取的单元格首地址
    I2C_SendData(I2C1,addr);
    
        
    //等待EV8_2事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 18;
        }
    }
    
    //重新产生起始信号
    I2C_GenerateSTART(I2C1,ENABLE);
    
    //等待EV5事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 16;
        }
    }    
    
    //发送设备读地址
    I2C_Send7bitAddress(I2C1,0xA1,I2C_Direction_Receiver);
    
    //等待EV6事件
    count_wait = TIME_OUT;
    while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
    {
        count_wait--;
        if(count_wait == 0 )
        {
            return 20;
        }
    }
    
    
    //读取数据
    while(size--)
    {
        if(size == 0)
        {
            //产生非应答信号
            I2C_AcknowledgeConfig(I2C1,DISABLE);
        }
        else
        {
            //产生应答信号
            I2C_AcknowledgeConfig(I2C1,ENABLE);
        }
        
            
        //等待EV7事件
        count_wait = TIME_OUT;    
        while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
        {
            count_wait--;
            if(count_wait == 0 )
            {
                return 21;
            }
        }
        
        //读取数据
        *data = I2C_ReceiveData(I2C1);
        
        data++;
    }
    
    //产生终止信号
    I2C_GenerateSTOP(I2C1,ENABLE);

    return 0;
}


//分页写入
//addr:要写入数据的单元格首地址
//data:要写入的数据的指针
//size:要写入多少个数据
uint8_t EEPROM_Write_Buff(uint8_t addr, uint8_t* data, uint16_t size)
{
    uint8_t single_adrr = addr%EEPROM_PAGE_SIZE;
    
    if(single_adrr == 0)
    {    
        uint8_t num_of_page = size/EEPROM_PAGE_SIZE;    
        uint8_t single_byte = size%EEPROM_PAGE_SIZE;
        
        while(num_of_page--)
        {
            //调用页写入函数
            EEPROM_Write_Page(addr, data, EEPROM_PAGE_SIZE);
            
            //等待写入完成
            Wait_For_Standby();
            
            addr += EEPROM_PAGE_SIZE;
            data += EEPROM_PAGE_SIZE;
        }
            
        //调用页写入函数
        EEPROM_Write_Page(addr, data, single_byte);
        
        //等待写入完成
        Wait_For_Standby();    
    }
    else //addr不对齐
    {
        uint8_t num_of_page,single_byte,sheng_yu;
        //第一次写入
        uint8_t first_size = EEPROM_PAGE_SIZE - single_adrr;
        
       //调用页写入函数
        EEPROM_Write_Page(addr, data, first_size);
        
        //等待写入完成
        Wait_For_Standby();
        
        addr += first_size;
        data += first_size;
        
        //剩下要写入的数据
        sheng_yu = size - first_size;
        
        num_of_page = sheng_yu/EEPROM_PAGE_SIZE;    
        single_byte = sheng_yu%EEPROM_PAGE_SIZE;    
    
        
        while(num_of_page--)
        {
            //调用页写入函数
            EEPROM_Write_Page(addr, data, EEPROM_PAGE_SIZE);
            
            //等待写入完成
            Wait_For_Standby();
            
            addr += EEPROM_PAGE_SIZE;
            data += EEPROM_PAGE_SIZE;
        }
            
        /调用页写入函数
        EEPROM_Write_Page(addr, data, single_byte);
        
       //等待写入完成
        Wait_For_Standby();        
    
    }
    
    return 0;

}

//等待EEPROM内部写入操作完成
//0表示正常等待完成,非0表示等待不到响应信号
uint8_t Wait_For_Standby(void)
{
    uint32_t check_count = 0xFFFFF;
    
    while(check_count--)
    {
        //产生起始信号
        I2C_GenerateSTART(I2C1, ENABLE);
        
        //等待EV5事件
        count_wait = TIME_OUT;
        while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
        {
            count_wait--;
            if(count_wait == 0 )
            {
                return 11;
            }
        }    
        
        //发送设备写地址
        I2C_Send7bitAddress(I2C1, 0xA0, I2C_Direction_Transmitter);
        
        //等待EV6事件
        count_wait = TIME_OUT;
        while(count_wait--)
        {
            //若检测到响应,说明内部写时序完成,跳出等待函数
            if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)
            {
                //退出前停止本次通讯
                I2C_GenerateSTOP(I2C1, ENABLE);    
                return 0;
            }            
        }        
    }
    
    //退出前停止本次通讯
    I2C_GenerateSTOP(I2C1, ENABLE);    
    return 1;
}

 

/*main.c文件*/

#include "stm32l1xx.h"
#include "./I2C/i2c.h"

uint8_t test[500*1024];
#define TEST_SIZE   256

int main(void)
{    
    uint16_t i;
    uint8_t data[TEST_SIZE];
    uint8_t buff[TEST_SIZE];
    
    //给buff赋值
    for(i=0;i<TEST_SIZE;i++)
    {
        buff[i]=i;
    }
    
    if(test[0])
    {
        //printf("\r\n页读写测试结束,读取到的数据为:\r\n");

    }

    I2C_Config();
    
    //写入测试
    EEPROM_Write_Byte(0x01,0x12);
        
    EEPROM_Read_Byte(0x01,data);
    
    //printf("\r\n单字节读写测试结束,data=0x%x\n",data[0]);
        
    EEPROM_Write_Buff(0x00, buff, TEST_SIZE);
    
    EEPROM_Read_Buff(0x00,data, TEST_SIZE);
    
    //printf("\r\n页写入测试结束,读取到的数据为:\r\n");

    for(i=0;i<TEST_SIZE;i++)
    {
        //printf("0x%02x ",data[i]);
    }
    
    while(1)
    {    
        
    }    
}
 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值