精华内容
下载资源
问答
  • 【组件】i2c抽象/模拟i2c

    万次阅读 2017-12-23 09:27:26
    关于i2c的使用,并不陌生,C51、ARM、MSP430等,都基本集成硬件i2c,或者不集成i2c的,可以根据总线时序图使用普通IO口翻转模拟一根i2c总线。对于当下流行的stm32饱受诟病的硬件i2c,相信很多人都是使用模拟i2c。...

    1.写在前面

    i2c总线是由PHILIPS公司开发的一种简单、双向二线制同步串行总线。关于i2c的使用,并不陌生,C51、ARM、MSP430等,都基本集成硬件i2c,或者不集成i2c的,可以根据总线时序图使用普通IO口翻转模拟一根i2c总线。对于当下流行的stm32饱受诟病的硬件i2c,相信很多人都是使用模拟i2c。模拟i2c的源码比较多,大多都是大同小异,对于各类例程,提供的模拟i2c似乎都不是太规范(个人见解),特别是一根i2c总线挂多个外设、模拟多根i2c总线、以及更换一个i2c外设时,都需要大幅度修改源码、复制源码、重新调试时序等重复的工作。在阅读过Linux设备驱动框架和RT-Thread的驱动框架,发现在总线分层上处理就特别好,完美解决了上述提及的问题。参考RT-Thread和Linux下的模拟i2c,整理修改在裸机上使用。

    2.Linux、RT-Thread设备驱动模型

    1)模型分为总线驱动和设备驱动;

    2)  总线驱动与外设驱动分离,方便一根总线挂多个外设,方便移植;

    3)  底层(与硬件相关)与上层分离,方便添加总线及移植到不同处理器,移植到其他处理器,只需重新实现硬件相关的“寄存器”层即可;

    3.MCU下裸机形式i2c总线抽象

    此部分实现源码为:i2c_core.c  i2c_core.h

    1)i2c总线抽象对外接口(API)

    “i2c_bus_xfer”为i2c封装对外的API,函数原型如下,提供一个函数模型,具体需要实例化函数指针。

    int i2c_bus_xfer(struct i2c_dev_device *dev,struct i2c_dev_message msgs[],unsigned int num)
    {
    	int size;
    	
    	size = dev->xfer(dev,msgs,num);	
    	return size;
    }

    a)此函数即作为驱动外设的对外接口,所有操作通过此函数接口,与底层总线实现分离,如EEPROM、RTC、温度传感器等;

    b)一个对外函数已经实现90%的情况使用,对应一些特殊情况,后期再完善或增加API。

    a)struct i2c_dev_device *i2c_dev

    2)i2c总线抽象API参数

    a)i2c_dev:i2c设备指针,类型为“struct i2c_dev_device”,驱动一个i2c外设时,首先要对此指针设备初始化;

    b)msgs:i2c一帧数据,发送数据及存放返回数据的缓存;

    c)num:数据帧数量。

    3)struct i2c_dev_device

    该结构体为关键,调用API驱动外设时,首先对此初始化(类似于Linux/RT-Thread注册设备)。完整的设备包括两部分,数据操作函数和i2c相关信息(如硬件i2c或者模拟i2c)。因此“struct i2c_dev_device”的原型为:

    struct i2c_dev_device
    {
            int (*xfer)(struct i2c_dev_device *dev,struct i2c_dev_message msgs[],unsigned int num);
            void *i2c_phy;
    };

    a)第一个参数是函数指针,数据收发通过此函数指针调用实体函数实现;

    b)第二个参数是一个void指针,初始化时指向我们使用的物理i2c(硬件/模拟),使用时可强制转换为对应的类型。

    4)xfer

    该函数与i2c总线设备对外接口函数“i2c_bus_xfer”具有相同的参数,形参参数参考此项的第2点,初始化时实例化指向实体函数。

    5)struct i2c_dev_message

    “struct i2c_dev_message”为i2c总线访问外设的一帧数据信息,包括发送数据、外设从地址、访问标识等。原型如下:

    struct i2c_dev_message
    {
    	unsigned short 	addr;
    	unsigned short	flags;
    	unsigned short	size;
    	unsigned char	*buff;
    	unsigned char   retries;		
    };

    a)addr:i2c外设从机地址,常用为7位,10位较少用;

    b)flags:标识,发送、接收、应答、地址位选择等标识;几种标识如下:

    #define I2C_BUS_WR             0x0000
    #define I2C_BUS_RD             (1u << 0)
    #define I2C_BUS_ADDR_10BIT     (1u << 2)
    #define I2C_BUS_NO_START      (1u << 4)
    #define I2C_BUS_IGNORE_NACK    (1u << 5)
    #define I2C_BUS_NO_READ_ACK    (1u << 6)

    c)size:发送的数据大小,或者接收的缓存大小;

    d)buff:缓存区;

    e)retries:i2c启动失败时,重启的次数。

    4.模拟i2c抽象

    对于模拟i2c,在以往的实现方式中,基本是时序图和外设代码混合在一起,增加外设或者使用新的i2c外设时,需要对模拟2ic代码进行较大工作量的修改,或者以“复制”的方式实现一套新的i2c总线。但同理,可以把模拟i2c时序部分代码抽象出来,以“复用”代码的形式实现。此部分实现源码为:i2c_bitops.c  i2c_bitops.h

    1)模拟i2c抽象对外接口

    根据上述封装的对外API,使用时,首先需要实现入口参数“i2c_dev”实例化,用模拟i2c即是调用模拟i2c相关接口。

    int i2c_bitops_bus_xfer(struct ops_i2c_dev *i2c_bus,struct i2c_dev_message msgs[],unsigned long num)
    {
    	struct i2c_dev_message *msg;
    	unsigned long i;
    	unsigned short ignore_nack;
    	int ret;
    	
    	ignore_nack = msg->flags & I2C_BUS_IGNORE_NACK;
    	i2c_bitops_start(i2c_bus);							
    	for (i = 0; i < num; i++)
        	{
    		msg = &msgs[i];
           		if (!(msg->flags & I2C_BUS_NO_START))
           		{
                		if (i)
                		{
                    		i2c_bitops_restart(i2c_bus);	
                		}
    			ret = i2c_bitops_send_address(i2c_bus,msg);
    			if ((ret != 0) && !ignore_nack)
    				goto out;
    		}
    		if (msg->flags & I2C_BUS_RD)
           		{//read
    			ret = i2c_bitops_bus_read(i2c_bus,msg);
    			if(ret < msg->size)
    			{
    				ret = -1;
    				goto out;
    			}
    		}
    		else
    		{//write
    			ret = i2c_bitops_bus_write(i2c_bus,msg);
    			if(ret < msg->size)
    			{
    				ret = -1;
    				goto out;
    			}
    		}
    	}
    	ret = i;
    out:
    	i2c_bitops_stop(i2c_bus);
    		
    	return ret;
    }
    int ops_i2c_bus_xfer(struct i2c_dev_device *i2c_dev,struct i2c_dev_message msgs[],unsigned int num)
    {
    	return(i2c_bitops_bus_xfer((struct ops_i2c_dev*)(i2c_dev->i2c_phy),msgs,num));
    }

    a)模拟一根i2c总线时,对外的操作函数都通过上诉函数;i2c信息帧相关参数由上层调用传递进入,此处主要增加“struct ops_i2c_dev”的封装;

    b)该函数使用到的函,其中入口参数为“struct ops_i2c_dev”类型的都是模拟i2c相关;

    d)模拟i2c封装实现主要针对“struct ops_i2c_dev”原型的实例化。

    2)struct ops_i2c_dev

    “struct ops_i2c_dev”原型如下:

    struct ops_i2c_dev
    {
            void (*set_sda)(int8_t state);
            void (*set_scl)(int8_t state);
            int8_t (*get_sda)(void);
            int8_t (*get_scl)(void);
            void (*delayus)(uint32_t us);
    };

    a)set_sda:数据线输出;

    b)set_scl:时钟线输出;

    c)get_sda:数据线输入(捕获);

    d)get_scl:时钟线输入(捕获);

    e)delayus:延时函数;

    要实现一个模拟i2c,只需将上诉函数指针的实体实现即可,具体看后面描述。

    3)模拟i2c时序

    以产生i2c起始信号函数为例子,简要分析:

    static voidi2c_bitops_start(struct ops_i2c_dev *i2c_bus)
    {
        i2c_bus->set_sda(0);                                          
        i2c_bus->delayus(3);
        i2c_bus->set_scl(0);                                                       
    }        

    入口参数为struct ops_i2c_dev *i2c_bus,其实就是i2c_bitops_bus_xfer应用层函数传入的参数,最终是在此调用,底层需要实现的就是io模拟的输入/输出状态函数。

    其他函数,如

    static void i2c_bitops_restart(struct ops_i2c_dev *i2c_bus)
    static chari2c_bitops_wait_ack(struct ops_i2c_dev *i2c_bus)
    static int i2c_bitops_send_byte(struct ops_i2c_dev*i2c_bus,unsigned char data)

    等等,入口参数都是i2c_bus,时序实现与常规裸机程序设计是一致的,不同的是函数指针的分离调用,具体看附件源码。

    4)标识位

    在以往的模拟i2c或者硬件i2c中,操作外设时都有各类情况,如读和写方向的切换、连续操作(不需启动i2c总线,如写EEPROM,先写地址再写数据)等。对于这类情况,我们处理办法是选择相关的宏标识即可,具体实现由“中间层”实现,让i2c外设驱动起来更简单!以上述对外函数为例:

    a)通过标识位判断是读还是写状态

    if (msg->flags & I2C_BUS_RD)
    {//read
            ret = i2c_bitops_bus_read(i2c_bus,msg);
            if(ret < msg->size)
            {
                    ret = -1;
                    goto out;
            }
    }

    b)应答状态标识

    ignore_nack = msg->flags & I2C_BUS_IGNORE_NACK;

    5)读写函数

    读写函数最终是通过io口1bit的翻转模拟出时序,从而获得数据,这部分与常规模拟i2c一致,通过函数指针方式操作。主要实现接口函数:

    static unsigned long i2c_bitops_bus_write(struct ops_i2c_dev *i2c_bus,struct i2c_dev_message *msg);
    static unsigned long i2c_bitops_bus_read(struct ops_i2c_dev *i2c_bus,struct i2c_dev_message *msg);

    5.模拟i2c总线实现

    此部分实现源码为:i2c_hw.c  i2c_hw.h

    以stm32f1为硬件平台,采用上述模拟i2c封装,实现一根模拟i2c总线。

    1)实现struct ops_i2c_dev函数实体

    除了“delayus”函数外,其余为io翻转,以“set_sda”和“delayus”为例,实现如下:

    static void gpio_set_sda(int8_t state)
    {
            if (state)
                    I2C1_SDA_PORT->BSRR = I2C1_SDA_PIN;
            else
                    I2C1_SDA_PORT->BRR = I2C1_SDA_PIN;
    }
    static void gpio_delayus(uint32_t us)
    {
    #if 0  
            volatile int32_t i;
    	
            for (; us > 0; us--)
            {
                    i = 30;  //mini 17
                    while(i--);
            }
    #else
            Delayus(us);
    #endif
    }

    a)为例提高速率,上诉代码采用寄存器方式操作,可以用库函数操作io口;

    b)延时可以用硬件定时器延时,或者软件延时,具体根据cpu时钟计算;

    c)其他源码看附件中“i2c_hw.c”

    2)初始化一根模拟i2c总线

    void stm32f1xx_i2c_init(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;										
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);		
    
    	GPIO_InitStructure.GPIO_Pin = I2C1_SDA_PIN | I2C1_SCL_PIN;
      	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;	   		
      	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	   		
      	GPIO_Init(I2C1_SDA_PORT, &GPIO_InitStructure);								   				
    	I2C1_SDA_PORT->BSRR = I2C1_SDA_PIN;												
    	I2C1_SCL_PORT->BSRR = I2C1_SCL_PIN;
    	
    	//device init
    	ops_i2c1_dev.set_sda = gpio_set_sda;
    	ops_i2c1_dev.get_sda = gpio_get_sda;
    	ops_i2c1_dev.set_scl = gpio_set_scl;
    	ops_i2c1_dev.get_scl = gpio_get_scl;
    	ops_i2c1_dev.delayus = gpio_delayus;
    		
    	i2c1_dev.i2c_phy 	 = &ops_i2c1_dev;
    	i2c1_dev.xfer 		 = ops_i2c_bus_xfer; 
    }

    a)i2c io初始化;

    b)i2c设备实例化,其中“ops_i2c1_dev”和“i2c1_dev”即是我们定义的总线设备,后面使用该总线时主要通过“i2c1_dev”实现对底层的调用。

    6.驱动EEPROM(AT24C16)

    此部分实现源码为:24clxx.c  24clxx.h

    上面总线完成后,驱动一个i2c外设可以说就是信手拈来的事情了,而且模拟i2c总线抽象出来后,不需在做重复调试时序的工作。

    假设初始化的i2c设备为i2c1_dev。

    1)  写EEPROM。写一个字节,页写算法详细见源码附件(24clxx.c):

    char ee_24clxx_writebyte(u16 addr,u8 data)
    {
         struct i2c_dev_message ee24_msg[1];
         u8	buf[3];
         u8  slave_addr;
         if(EEPROM_MODEL > 16)
         {					  
    	  slave_addr =EE24CLXX_SLAVE_ADDR;
    	  buf[0] = (addr >>8)& 0xff;   
    	  buf[1] = addr & 0xff;
    	  buf[2] = data;
              ee24_msg[0].size  = 3;
         }
         else
         {
              slave_addr = EE24CLXX_SLAVE_ADDR | (addr>>8);
    	  buf[0] = addr & 0xff;
    	  buf[1] = data;
    	  ee24_msg[0].size = 2;
         }
         ee24_msg[0].addr = slave_addr;
         ee24_msg[0].flags = I2C_BUS_WR;
         ee24_msg[0].buff = buf;
         i2c_bus_xfer(&i2c1_dev,ee24_msg,1);
    		
         return 0;
    }

    2)读EEPROM

    voidee_24clxx_readbytes(u16 read_ddr, char* pbuffer, u16 read_size)
    { 
         struct i2c_dev_message ee24_msg[2];
         u8     buf[2];
         u8     slave_addr;
         if(EEPROM_MODEL > 16)
         {
              slave_addr =EE24CLXX_SLAVE_ADDR;
              buf[0] = (read_ddr>>8)& 0xff;
              buf[1] = read_ddr& 0xff;
              ee24_msg[0].size  = 2;
         }
         else
         {
              slave_addr =EE24CLXX_SLAVE_ADDR | (read_ddr>>8);
              buf[0] = read_ddr & 0xff;
              ee24_msg[0].size  = 1;
         }
         ee24_msg[0].buff  = buf;
         ee24_msg[0].addr  = slave_addr;
         ee24_msg[0].flags = I2C_BUS_WR;
         ee24_msg[1].addr  = slave_addr;
         ee24_msg[1].flags = I2C_BUS_RD;
         ee24_msg[1].buff  = (u8*)pbuffer;
         ee24_msg[1].size  = read_size;
         i2c_bus_xfer(&i2c1_dev,ee24_msg,2);
    }

    3)注意事项

    驱动一个外设相对容易了,注意的事项就是标识位部分。

    a)此处外设地址(addr),是实际地址,不含读写位(7bit),比如AT24C16外设地址为0x50,可能大家平常用的是0xA0,因为包括读写位;

    b)写数据时,如果以2帧i2c_dev_message消息发送,需要注意“I2C_BUS_NO_START”宏,此宏标识意思是不需要再次启动i2c了,一般看i2c外设手册时序图可知道。如写EEPROM是先写地址,然后写数据这个过程是连续的,此时就需用到“I2C_BUS_NO_START”标识。程序可改成这样:

    char ee_24clxx_writebyte(u16 addr,u8 data)
    {
         struct i2c_dev_message ee24_msg[2];
         u8     buf[2];
         u8 slave_addr;
         if(EEPROM_MODEL > 16)
         {                                   
              slave_addr =EE24CLXX_SLAVE_ADDR;
              buf[0] = (addr>>8)& 0xff;  
              buf[1] = addr &0xff;
              ee24_msg[0].size  = 2;
         }
         else
         {
               slave_addr =EE24CLXX_SLAVE_ADDR | (addr>>8);
               buf[0] = addr &0xff;
               ee24_msg[0].size  = 1;
         }
         ee24_msg[0].addr = slave_addr;
         ee24_msg[0].flags = I2C_BUS_WR;
         ee24_msg[0].buff  = buf;
         ee24_msg[1].addr = slave_addr;
         ee24_msg[1].flags = I2C_BUS_WR |I2C_BUS_NO_START;
         ee24_msg[1].buff  = &data;
         ee24_msg[1].size  = 1;
         i2c_bus_xfer(&i2c1_dev,ee24_msg,2);
              
         return 0;
    }

    4)其他

    理解之后,或者使用过Linux、RT-Thread的驱动框架的,再驱动其他i2c外设,就是很容易的事情了,剩下的就是配置寄存器、应用算法的问题了。

    7.总结

    1)整体思路比较易理解,本质就是函数指针,将与硬件底层无关的部分抽象出来,相关联的地方分层明确,通过函数指针的方式进行调用。

    2)事务分离,通用、重复的事情交给总线处理,特殊任务留给外设驱动。

    8.相关例子

    【1】LM75A温度传感器使用:

     https://blog.csdn.net/qq_20553613/article/details/79140266

    【2】LP55231 LED驱动使用:

    https://blog.csdn.net/qq_20553613/article/details/78933482

    9.源码

    【1】  https://github.com/Prry/drivers-for-mcu

    10.参考

    【1】  https://github.com/RT-Thread/rt-thread

    【2】  https://blog.csdn.net/qq_20553613/article/details/78550427

    展开全文
  • I2C详解(一)

    万次阅读 多人点赞 2019-01-03 17:22:21
    I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时串行总线,常用于微控制器与外设之间的连接。要想了解详细I2C协议,目前最新的I2C标准协议是2014年第6版本,如下: I2C-...

    I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时串行总线,常用于微控制器与外设之间的连接。要想了解详细I2C协议,目前最新的I2C标准协议是2014年第6版本,如下:
    I2C-Bus Standards Specification


    硬件连接

    I2C仅需两根线就可以支持一主多从或者多主连接,I2C使用两个双向开漏线,配合上拉电阻进行连接,关于上拉电阻阻值大小有最大值和最小值的限制,具体计算请参考 I2C-上拉电阻计算

    • SDA:串行数据线
    • SCL:串行时钟线

    在这里插入图片描述

    在这里插入图片描述

    • 硬件拉低(Open-Drain Pulling Low)

    下图代表了总线电平拉低过程,当总线要传输低电平’0’时,Logic会控制FET使其导通VBUS总线连接到地,将总线拉低,注意在快速模式下最大的灌电流不超过3mA,这限制了上拉电阻的最小值。

    在这里插入图片描述

    • 硬件拉高(Open-Drain Releasing Bus)

    下图代表了总线电平高电平过程,当总线要传输高电平’1’时,Logic会控制FET使其关断VBUS总线上拉到电源,将总线拉高。

    在这里插入图片描述


    软件通讯协议

    I2C是串行传输总线,按照下面格式进行一位一位传输。协议如下:
    在这里插入图片描述


    总线速度

    双向传输总线:

    • 标准模式(Standard-mode):速率高达100kbit/s
    • 快速模式(Fast-mode):速率高达400kbit/s
    • 快速模式+(Fast-mode Plus):速率高达1Mbit/s。
    • 高速模式(High-speed mode):速率高达3.4Mbit/s

    单向传输总线:

    • 超快速模式(Ultra Fast-mode):速率高达5Mbit/s

    起始和停止条件及重复起始条件(START and STOP conditions and Repeated start condition):
    起始和终止条件都是由主机(master)发起产生。总线在起始条件之后处于忙碌状态,在停止条件之后又处于空闲状态。

    • 起始条件:SCL线是高电平时,SDA线从高电平向低电平切换。
    • 停止条件:SCL线是高电平时,SDA线从低电平向高电平切换。
    • 重复起始条件:和起始条件相似,重复起始条件发生在停止条件之前。主机想继续给从机发送消息时,一个字节传输完成后可以发送重复起始条件,而不是产生停止条件。
      在这里插入图片描述

    字节格式
    SDA数据线上的每个字节必须是8位,每次传输的字节数量没有限制。每个字节后必须跟一个响应位(ACK)。首先传输的数据是最高位(MSB),SDA上的数据必须在SCL高电平周期时保持稳定,数据的高低电平翻转变化发生在SCL低电平时期。
    在这里插入图片描述
    以传输Byte:1010 1010 (0xAAh)为例,SDA SCL传输时序如下所示:
    在这里插入图片描述


    响应ACK(Acknowledge)和非响应NACK(Not Acknowledge)
    每个字节传输必须带响应位,相关的响应时钟也由主机产生,在响应的时钟脉冲期间(第9个时钟周期),发送端释放SDA线,接收端把SDA拉低。以上图传输101010101为例,SCL第9位时钟高电平信号期间,SDA拉低其代表了有ACK响应位。
    当在SCL第9位时钟高电平信号期间,SDA仍然保持高电平,这种情况定义为NACK非响应位。这种情况下,主机可以直接产生STOP条件终止以后的传输或者继续重新START开始一个新的传输。以下情况会导致出现NACK位:

    • 接收机没有发送机响应的地址,接收端没有任何ACK发送给发射机
    • 由于接收机正在忙碌处理实时程序导致接无法接收或者发送
    • 传输过程中,接收机识别不了发送机的数据或命令
    • 接收机无法接收
    • 主机接收完成读取数据后,要发送NACK结束告知从机
      以下图例代表NACK时序:
      在这里插入图片描述

    7-bit 地址格式和读写位
    一个7-bit的地址是从最高位(MSB) 开始发送的,这个地址后面会紧跟1-bit(R/W)的操作符,1表示读操作,0表示写操作。 接下来的一个bit是NACK/ACK,当这个帧中前面8 bit发送完后,接收端的设备获得SDA控制权,此时接收设备应该在第9个时钟脉冲之前回复一个ACK(将SDA拉低)以表示接收正常,如果接收设备没有将SDA拉低,则说明接收设备可能没有收到数据(如寻址的设备不存在或设备忙)或无法解析收到的消息,如果是这样,则由master来决定如何处理(stop或repeated start condition)。

    在这里插入图片描述

    展开全文
  • I2C库函数

    千次阅读 2018-08-08 17:10:35
    1.void I2C_DeInit(I2C_TypeDef* I2Cx) 功能:将I2Cx外设寄存器重设为默认值 注释:该函数调用了RCC_APB1PeriphResetCmd来进行挂载于APB上外设的寄存器的复位 例如:I2C_DeInit();   2.void I2C_Init(I2C_...

    1.void I2C_DeInit(I2C_TypeDef* I2Cx)

    功能:将I2Cx外设寄存器重设为默认值

    注释:该函数调用了RCC_APB1PeriphResetCmd来进行挂载于APB上外设的寄存器的复位

    例如:I2C_DeInit();

     

    2.void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct)

    功能:根据I2C_InitStruct中指定的参数初始化I2Cx外设

    例如:I2C_Init(I2C1 , &I2C_InitStructure);

     

    3.void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct)

    功能:根据默认值填充每个I2C_InitStruct成员

    注释:直接向结构体成员赋值,不涉及寄存器写入的操作

    例如:I2C_StructInit(&I2C_InitStructure);

     

    4.void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能I2C外设

    注释:1. 在从模式下,如果清除使能位时通讯正在进行,在当前通讯结束后,I2C模块被禁用并返回空闲状态;

                  由于在通讯结束后发生PE=0 (Peripheral enable),所有的位被清除

               2. 在主模式下,通讯结束之前,绝不能清除该位

    例如:I2C_Cmd(I2C1 , ENABLE);

     

    5.void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能指定I2C的DMA请求

    例如:I2C_DMACmd(I2C1 , ENABLE);

     

    6.void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:指定下一个DMA传输是否为最后一个

    注释:该位在主接收模式使用, 使得在最后一次接收数据时可以产生一个NACK

    例如:I2C_DMALastTransferCmd(I2C1 , ENABLE);

     

    7.void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:生成I2Cx通信起始信号

    例如:I2C_GenerateSTART(I2C1 , ENABLE);

     

    8.void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:生成I2Cx通信停止信号

    注释:当设置了STOP、START或PEC位,在硬件清除这个位之前,软件不要执行任何对I2C_CR1的写操作,

               否则有可能会第2次设置STOP、START或PEC位;

               PEC:数据包出错检测 (Packet error checking)

    例如:I2C_GenerateSTOP(I2C1 , ENABLE);

     

    9.void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能指定的I2C应答功能

    注释:在接收到一个字节后返回一个应答(匹配的地址或数据)

    例如:I2C_AcknowledgeConfig(I2C1 , ENABLE);

     

    10.void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address)

    功能:配置指定的I2C自身地址2

    注释: I2C_OwnAddress1 在结构体内被赋值;

              双地址模式使能,OAR2才会被识别;

              I2C_OwnAddress_x是STM32设备本身的地址,当STM32作为主设备,不用关心该地址设置,随意设置数即可,

              但是如果STM32作为从设备使用时,必须进行配置

    例如:I2C_OwnAddress2Config(I2C1 , 0x7F);

     

    11.void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能双地址模式

    注释:双地址模式的使能使得OAR2才会被识别

    例如:I2C_DualAddressCmd(I2C1 , ENABLE);

     

    12.void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能广播呼叫

    注释:以应答还是非应答响应地址00h

    例如:I2C_GeneralCallCmd(I2C1 , ENABLE);

     

    13.void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState)

    功能:使能或者失能指定的I2C中断

    注释:I2C_IT_BUF:缓冲器中断使能          I2C_IT_EVT:事件中断使能          I2C_IT_ERR:出错中断使能

    例如:I2C_ITConfig(I2C1 , I2C_IT_BUF);

     

    14.void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)

    功能:通过I2Cx外设发送数据字节

    注释:在从模式下,地址不会被拷贝进数据寄存器DR;

               硬件不管理写冲突(如果TxE=0,仍能写入数据寄存器);

               如果在处理ACK脉冲时发生ARLO事件,接收到的字节不会被拷贝到数据寄存器里,因此不能读到它

    例如:I2C_SendData(I2C1 , 0x01);

     

    15.uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)

    功能:获取I2Cx外设最近接收的数据

    注释:注意数据被覆盖的问题

    例如:Data = I2C_ReceiveData(I2C1);

     

    16.void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)

    功能:发送地址以选中从设备

    注释:同数据发送相同,地址也是数据的一种

    例如:I2C_Send7bitAddress(I2C1 , 0xA0 , I2C_Direction_Transmitter);

     

    17.uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register)

    功能:读取指定的I2C寄存器并返回其值

    例如:Register = I2C_ReadRegister(I2C1 , I2C_Register_CR1);

     

    18.void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能指定的I2C软件重置

    注释:该位可以用于总线繁忙(BUSY位为’1’),但是在总线上又没有检测到停止条件时使用

    例如:I2C_SoftwareResetCmd(I2C1 , ENABLE);

     

    19.void I2C_NACKPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_NACKPosition)

    功能:在主接收模式下选择指定的I2C NACK位置

              下一个字节是最后一个收到的字节 / 当前字节是最后一个收到的字节

    注释:当要接收的数据数量等于2时,这个函数在I2C主接收模式下非常有用;

               在这种情况下,应该在数据接收开始之前调用这个函数(使用参数I2C_NACKPosition_Next);

               为了NACK第2个字节,必须在清除ADDR位之后清除ACK位;

               该函数配置与I2C_PECPositionConfig相同的寄存器位(POS),但它用于I2C模式,

               而I2C_PECPositionConfig()用于SMBUS模式

    例如:I2C_NACKPositionConfig(I2C1 , I2C_NACKPosition_Next);

     

    20.void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert)

    功能:为指定的I2C驱动SMBus提醒 (SMBus alert)引脚的高或低

    注释:提醒响应地址头紧跟在NACK还是ACK信号后面

    例如:I2C_SMBusAlertConfig(I2C1 , I2C_SMBusAlert_Low);

     

    21.void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能指定的I2C PEC传输    (PEC:数据包出错检测 Packet error checking)

    注释:配置有无PEC传输;仲裁丢失时,PEC的计算失效

    例如:I2C_TransmitPEC(I2C1 , ENABLE);

     

    22.void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition)

    功能:选择指定的I2C PEC位置

    注释:指示下一个字节为PEC还是当前字节为PEC;

               POS位只能用在2字节的接收配置中,必须在接收数据之前配置;

               为了NACK第2个字节,必须在清除ADDR位之后清除ACK位;

               为了检测第2个字节的PEC,必须在配置了POS位之后,拉伸ADDR事件时设置PEC位;

               该函数配置了与I2C_NACKPositionConfig()相同的位(POS),但是用于SMBUS模式,

               而I2C_NACKPositionConfig()用于I2C模式

    例如:I2C_PECPositionConfig(I2C1 , I2C_PECPosition_Next);

     

    23.void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能传输字节的PEC值计算

    例如:I2C_CalculatePEC(I2C1 , ENABLE);

     

    24.uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx)

    功能:返回指定I2C的PEC值

    注释:当ENPEC=1时,读取PEC[7:0]存放内部的PEC的值

    例如:PEC = I2C_GetPEC(I2C1);

     

    25.void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能指定的I2C ARP(地址解析协议ARP)

    注释:地址解析协议(通过给每个从设备动态地分配一个新的唯一地址,可以解决SMBus的从地址冲突)

    例如:I2C_ARPCmd(I2C1 , ENABLE);

     

    26.void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)

    功能:使能或者失能指定的I2C时钟时钟延长(从模式)

    注释:该位用于当ADDR或BTF标志被置位,在从模式下禁止时钟延长,直到它被软件复位

              BTF:字节发送结束 (Byte transfer finished)

              ADDR:地址已被发送(主模式)/地址匹配(从模式) (Address sent (master mode)/matched (slave mode))

    例如:I2C_StretchClockCmd(I2C1 , ENABLE);

     

    27.void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle)

    功能:配置指定的I2C快速模式时的占空比

    注释:CCR寄存器只有在关闭I2C时(PE=0)才能设置

    例如:I2C_FastModeDutyCycleConfig(I2C1 , I2C_DutyCycle_2);

     

    28.下面是三种I2C状态监控函数:

    (1).ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)

    基本状态监测

    功能:检查最后一个I2Cx事件是否等于作为参数传递的事件

    例如:errorstatus = I2C_CheckEvent(I2C1 , I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED);

    注释:

     

    (2).uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx)

    高级状态监控

    功能:返回最后一个I2Cx事件

    例如:event = I2C_GetLastEvent(I2C1);

    注释:

     

    (3).FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)

    基于标志位的状态监控

    功能:检查是否设置了指定的I2C标志

    例如:flagstatus = I2C_GetFlagStatus(I2C1 , I2C_FLAG_DUALF);

    注释:

     

    29.void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)

    功能:清除I2Cx的挂起标志

    例如:I2C_ClearFlag(I2C1 , I2C_FLAG_SMBALERT);

     

    30.ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT)

    功能:获取中断标志位,检查指定的I2C中断是否发生

    例如:status = I2C_GetITStatus(I2C1 , I2C_IT_SMBALERT);

     

    31.void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT)

    功能:清除I2Cx的中断挂起位

    例如:I2C_ClearITPendingBit(I2C1 , I2C_IT_SMBALERT); 

     

    展开全文
  • 如何判断I2C从机的设备地址

    千次阅读 2018-12-28 16:41:33
    大家在使用Ginkgo USB-I2C适配器,有个非常重要的信息就是所控制的从机地址必须要知道,否则就无法正确的和I2C从机进行通信,一般来说,在I2C设备的数据手册中都会明确提出这个设备的I2C地址是多少,不过在有的时候...

    大家在使用Ginkgo USB-I2C适配器,有个非常重要的信息就是所控制的从机地址必须要知道,否则就无法正确的和I2C从机进行通信,一般来说,在I2C设备的数据手册中都会明确提出这个设备的I2C地址是多少,不过在有的时候,可能手边没有这个设备的地址信息,但是也想和它建立通信,这种情况怎么办呢?下面我们就可以用我们的适配器并配合我们提供的一个小工具来找到这个设备的地址。
    实现的基本原理,我们先假设这个I2C从机设备是7bit模式地址,然后我们以此发送所有地址数据,然后判断从机是否正常应答,如果正常应答了,说明这个地址就是这个I2C从机的地址,否则就不是他的地址。
    软件界面如下:
    1.png 
    从软件输出的信息可以看到,当发送0xD0地址的时候,从机正常应答了,而发送其他地址的时候从机都没应答,这就能说明从机设备地址就是0xD0.
    软件下载:
     Ginkgo_USB-I2C_AutoAddr_Setup_v1.0.0.rar (8.82 MB, 下载次数: 142) 
    点击进入纬图官方淘宝店!

    展开全文
  • STM32的I2C通信

    万次阅读 多人点赞 2017-08-07 12:49:35
    STM32的两个GPIO引脚,分别用于SCL和SDA,按照I2C规约的时序,像控制LED灯那样控制引脚输出,若是接收数据时则读取SDA线上的电平,那就可以实现I2C通信了,这也是我们在51单片机上的“软件模拟协议”做法。...
  • I2C设备地址(7位地址左移)

    千次阅读 2017-07-17 17:26:55
    鹏程万里--扬帆起航 目录视图 摘要视图 订阅 从创业到再就业,浅述对程序员职业生涯的看法 征文 | 你会为 AI 转型么?...I2C设备地址 2013-04-16 09:10 10925人阅读 评论(0) 收藏 举
  • i2c设备的读写地址换算

    千次阅读 2011-09-09 09:22:19
    #define MAX_17040_BATTERY_I2C_ADDR (0x36) #define MAX_17040_BATTERY_WRITE_ADDR (MAX_17040_BATTERY_I2C_ADDR #define MAX_17040_BATTERY_READ_ADDR ((MAX_17040_BATTERY_I2C_ADD
  • 如果在用户空间控制I2C设备,需要访问/dev目录中所提供的接口,在使用I2C设备之前必须要载入i2c-dev设备。 每一个已经注册的IC设备都有一个编号,该编号从0开始计算。可以查看/sys/class/i2c-dev目录以获得I2C适配器...
  • 一、I2C接口技术 1.I2C总线系统组成 2.I2C总线的状态及信号 3.I2C总线基本操作 4.启动和停止条件 5.I2C总线数据传输格式 二、I2C总线上拉电阻的估算与选取 三、树莓派与AT24C02接口实验电路及Python SMBus串行I2C ...
  • 为什么I2C从机地址要左移一位

    万次阅读 2017-02-23 20:37:54
    最开始比较疑惑,当MCU做主机与一些I2C接口的从机(比如一些数字传感器,大多数仍为I2C接口)通信时,无论是用IO模拟I2C协议还是使用I2C模块,在发送从机地址时为什么都需要将从机手册的I2C地址左移一位发出去呢?...
  • I2C通信和ADC

    千次阅读 2016-10-03 22:48:52
    I2C接口I2C接口只有两根线,SCL和SDA: SCL:时钟线,传输时钟信号,由主设备向从设备传输时钟信号, SDA:数据线,传输数据信号,主从设备之间相互传递数据的通道 I2C属于串行通信,数据以bit为单位在SDA线上串行...
  • 详解Linux-I2C驱动

    千次阅读 2016-02-25 18:06:54
    1.2 I2C 1.3 硬件 1.4 软件 1.5 参考二、LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总线特性 2.3 开始和停止条件 2.4 数据传输格式 2.5 响应 2.6 总线仲裁三、LinuxI2C驱动--解析EEPROM的读写 ...
  • STM32F4模拟i2c

    千次阅读 2018-06-27 00:13:30
      STM32F4系列出来已有好几年,价格上也越来越...在之前“i2c模型/模拟i2c”文章中有使用到模拟i2c,现在将其移植到F4系列的MCU上。F1中用的是标准库,F4中用的是HAL库,函数API有差别,但使用思维是一样的。 ...
  • STM32 通用GPIO模拟I2C实现

    千次阅读 2019-09-30 11:21:47
    STM32 模拟I2C实现 #通用GPIO模拟I2C通信实现样例 1 GPIO初始化 `` #ifdef HW_I2C1 //硬件I2C初始化 //PA8-I2C1_SCL GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; ...
  • I2C通信协议详细讲解

    千次阅读 多人点赞 2020-12-13 23:08:59
    I2C协议讲解讲解流程我们为什么要学习I2C通信I2C协议简介:I2C物理层特点I2C协议层写数据读数据读和写数据通讯的起始和停止信号地址及数据方向 讲解流程 我们为什么要学习I2C通信 Stm32的最常用的板间通信有很多,有...
  • 物联网之STM32开发八(I2C总线通信)

    千次阅读 2018-08-04 17:57:47
    STM32-I2C总线通信 内容概要 I2C总线通信原理 三轴加速度传感器mpu6050介绍 I2C通信实例 I2C总线通信原理 内容概要: I2C总线简介 I2C总线协议 I2C总线读写操作 STM32F0-I2C控制器特性 I2C总线简介: I2...
  • I2C通讯

    千次阅读 2009-12-02 09:34:00
    I2C通讯[ 2007-4-28 4:40:00 | By: CANopen ] 二线制总线,Philips推出的一种串行总线方式,通过SDA(串行数据线)及SCL(串行时钟线)构成I2C总线在IC器件之间通讯,通过软件寻址识别每个器件而不需要片选线;...
  • <!-- @page {margin:0.79in} p {margin-bottom:0.08in} --> tegra adapter driver adapter设备的注册 staticstructresource i2c_resource1[] = { [0]= { ...INT_I2C, ...INT_I2C,
  • I2C通讯协议详解

    千次阅读 多人点赞 2020-04-05 20:57:02
    I2C协议总结两个方面物理层电气特性协议层I2C基本读写过程#写过程通讯复合格式通讯信号的判断通讯的起始和停止信号数据有效性地址及数据方向响应信号通讯过程部分代码讲解总结**顺便找一下玩过stm32+ESP8266+onenet...
  • stm32软件模拟i2c通讯读取lm75a温度

    千次阅读 2019-03-24 21:25:50
    stm32硬件i2c有着一些bug,此外对于i2c这种通用的串行通信协议,从源头掌握和使用显然更加靠谱一些,当然,对于arm,还是直接操作寄存器来得方便的多。 1、I2C协议 1.1 i2c串行总线概述 采用串行总线技术可以使系统...
  • i2c 编程接口

    万次阅读 2012-10-17 14:04:43
    1、通信接口 i2c发送或者接收一次数据都以数据包 struct i2c_msg 封装 struct i2c_msg { ...#define I2C_M_TEN 0x0010 // 十位地址标志 #define I2C_M_RD 0x0001 // 接收数据标志 __u16 len;
  • I2C知识总结

    万次阅读 2016-10-29 16:07:39
    I2C(Inter-Integrated Circuit,集成电路总线)  I2C总线为PHILIPS公司推出的串行通信总线,具有接线少,控制方式简单,通信速率高等优点。采用数据线SDA和时钟线SCL构成通信线路,各器件可通过并联到总线上实现...
  • I2C和EEPROM

    千次阅读 多人点赞 2017-10-15 15:38:42
    前几章我们学了一种通信协议叫做UART异步串口通信,这节课我们要来学习第二种常用的通信协议I2CI2C总线是由PHILIPS公司开发的两线式串行总线,多用于连接微处理器及其外围设备。I2C总线的主要特点是接口方式简单,...
  • I2C总线性能介绍

    千次阅读 2012-10-22 15:45:12
    I2C总线性能介绍 在线问答: [问:zhongshan] 请介绍一下I2C总线的应用领域和应用注意事项。 [答:Bruce] I2C总线是一个通用的串行总线,可以在很多领域中应用,比如计算机...
  • STM32 IO口模拟I2C+驱动MPU6050

    万次阅读 多人点赞 2017-07-18 22:03:04
    STM32 I2C GPIO MPU6060 陀螺仪
  • Linux I2C设备驱动编写

    千次阅读 2017-05-02 18:06:44
    版权声明:本文为博主原创文章,未经博主允许不得转载。 ...I2C adapter ...SMBus 与 I2C的区别 ...I2C driverI2C client ...在Linux驱动中I2C系统中主要...I2C driver 某个I2C设备的设备驱动,可以以driver理解。 I2C cl
  • STM32系统学习——I2C (读写EEPROM)

    万次阅读 多人点赞 2017-12-08 13:54:24
    I2C 通讯协议(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。 在计算机科学里,大部分复杂的...
  • 本文作为i2c驱动分析的第一部分,主要讲述: 1、I2C总线原理。 2、I2C架构概述。 3、I2C代码在内核中的结构。 4、Algorithm中的传输函数master_xfer。 5、总线驱动注册和探测函数。
  • I2C(smbus、pmbus)和SPI协议分析

    万次阅读 多人点赞 2016-06-17 20:10:33
    I2C和SPI作为两种非常常用的低速外部总线,已经不是什么新新技术了。 有些知识点,明白了,一段时间不用了,又忘记了。所以决定乘最近正好碰过这两个东西,还是写下来以备后患。I2CI2C是以前的飞利浦半导体制定的...
  • Linux下使用IIC总线 读写i2c从设备寄存器 by 韩大卫 @吉林师范大学 handawei@jusontech.com 转载请务必表明出处 ******************* ********************************************** 2012.7.16 1,本文给...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 457,644
精华内容 183,057
关键字:

如何计算i2c地址