STM32的集成电路总线I2C

一、I2C简介

1.I2C总线特点

        (1)两线制

                I2C只需要SDA、SCL两根线来完成数据的传输和外围器件的扩展,器件地址采用软件寻址方式。

        (2)多主机总线

                I2C是一个真正的多主机总线,如果2个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据被破坏。每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只能有一个主机。

        (3)传输速率

                数据传输速率相对SPI不高,串行的8位双向数据传输速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。

        (4)传输方式

                是同步半双工通信机制,总线上传输数据的最小单位是1个字节(8bit),高位先行,每传送完1个字节,接收器必须发送一个应答位。

2.I2C总线术语

  • 主机:初始化发送,产生时钟信号和终止发送的器件。
  • 从机:被主机寻址的器件。
  • 多主机:同时有多于一个主机尝试控制总线,但不破坏信息。
  • 仲裁:是在一个有多个主机同时尝试控制总线,但只允许其中一个控制总线并使信息不被破坏的过程。
  • 同步:两个或多个器件同步时钟信号的过程。
  • 地址:主机用于区分不同从机而分配的地址。
  • SDA:I2C通信时用于数据传输的信号线。
  • SCL:I2C通信时用于时钟传输的信号线。

3.I2C硬件构成

         将所有I2C设备的SDA线连在一起、SCL线连在一起,两线均要配置成开漏输出模式,SDA、SCL各接一个上拉电阻,一般为4.7KΩ左右。

4.位传输

        (1)数据有效性

        I2C总线以串行方式传输数据,数据传输是按照时钟节拍进行的。协议标准规定,SCL为高电平时,SDA上的数据必须保持不变;SCL为低电平时,SDA的电平状态才能改变。即SCL为高电平时数据有效。

        (2)起始信号和停止信号

        协议规定,SCL为高电平 时,SDA产生下降沿,表示起始信号,SDA产生上升沿,表示停止信号。

        (3)重复开始信号

        在I2C总线上,由主机产生一个起始信号后,在产生一个停止信号之前,再产生一个起始信号,称为重复起始信号。

        (4)应答信号和非应答信号

        协议规定,发送器每发送一个字节数据,接收器必须产生一个应答信号或非应答信号。实现方法是:发送器发送完8位数据后,在第9个时钟信号将SDA线拉高,接收器将SDA拉低,产生一个应答信号,或者保持SDA为高,产生一个非应答信号。

5.数据传输格式

        一般情况下,一个标准的I2C通信由4部分组成,起始信号从机地址传输数据传输停止信号,每次通信的数据字节数是没有限制的。

        (1)I2C总线寻址约定

                I2C采用软件方法实现从机寻址来简化总线连接,协议规定了起始信号后的第1个字节为寻址字节。I2C总线支持7位寻址模式和10位寻址模式。常用的7位寻址模式中,寻址字节由从机的7位地址位(D7~D1)和1位读写位(D0)组成,D0=1为读,D0=0为写。

        (2)数据传输模式

                主机从从机读N个字节:主机产生一个起始信号,然后发送一个寻址字节,读写位置1表示读,主机拉高SDA。从机检测到与自己相同的地址,产生一个应答信号。之后从机开始发送数据,每发送一个字节数据,主机产生一个应答信号。当数据传输完毕,主机产生一个非应答信号结束数据传输,主机产生一个停止信号结束通信,或者产生一个重复起始信号进入下一次数据传输。

                主机向从机写N个字节:主机产生一个起始信号,然后发送一个寻址字节,读写位置0表示写,主机拉高SDA。从机检测到与自己相同的地址,产生一个应答信号。之后主机开始发送数据,每发送一个字节数据,从机产生一个应答信号。当数据传输完毕,从机产生应答或非应答信号均可,此时主机再产生一个停止信号结束通信,或者产生一个重复起始信号进入下一次数据传输。

                主机指定地址从从机读N个字节:当主机在访问类似存储器器件(如EEPROM,一种掉电后数据不丢失的存储芯片)时,主机除了发送寻址字节来选择从机外,还要发送从机的存储地址来选择读写的位置。实际上很多器件、模块都有多个寄存器,都要指定相应的寄存器才能读取所需的数据。实现方法是先写后读,具体步骤是在上述读的基础上,在读之前先写存储地址,需要用到重复起始信号(本质就是一个起始信号)。具体如图所示:

                主机指定地址向从机写N个字节:类似的,需要指定存储位置再进行写操作,具体如下: 

二、STM32的I2C外设

        STM32的I2C外设具有4种工作模式:主发送器模式、主接收器模式、从发送器模式、从接收器模式。其重要特征如下:

  • 支持不同的通信速度。标准速度高达100KHz,快速高达400KHz。
  • 完善的通信功能。可由硬件自动执行时钟生成、起始和终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担。
  • 完善的错误监测。主模式时的仲裁丢失,地址/数据传输后的应答错误,检测到错位的起始或停止条件,禁止拉长时钟功能时的上溢或下溢。
  • 具有2个中断向量。一个用于地址/数据通信中断,一个用于通信出错中断。
  • 具有单字节缓冲器的DMA
  • 兼容系统管理总线(SMBus)

        STM32的I2C内部结构如图所示:

三、STM32 I2C外设的通信方式

        1.I2C主模式

                STM32的I2C模块作为主机使用,启动数据传输并产生时钟信号。

         (1)主模式发送

                I2C主模式发送示意图如图所示:

                在使用STM32的I2C外设时,需要遵循上图中的时序逻辑,等待相应事件的发生再执行下一步,各事件的意义说明如下:

  • EV5: 表示已检测到起始条件,可以开始发送寻址字节。
  • EV6:表示已发送从机地址,且已检测到从机的应答。
  • EV8_1:表示数据寄存器为空且上一个数据字节的传输已经完成,这意味着可以发送下一个数据字节。
  • EV8:表示数据寄存器为空,可以发送数据。
  • EV8_2:表示从机成功接收到主机发出的数据字节并发出应答信号,表明从机已准备好接收下一个数据字节。
  • EV9:暂不说明。
        (2)主模式接收 

                I2C主模式接收示意图如图所示:

                同样的,使用STM32的I2C外设时需要遵循上图中的时序逻辑,各事件的说明如下:

  • EV5: 表示已检测到起始条件,可以开始发送寻址字节。
  • EV6:表示已发送从机地址,且已检测到从机的应答。
  • EV7:表示数据寄存器不为空,即接收到了数据。
  • EV8_2:表示从机成功接收到主机发出的数据字节并发出应答信号,表明从机已准备好接收下一个数据字节。

        2.I2C从模式

        该模式即STM32的I2C模块作为从机,这里暂不介绍。 

四、STM32 I2C外设使用流程

        虽然不同器件实现的功能不同,但是只要遵循I2C协议,其通信方式都是一样的,配置流程也基本相同。对于STM32,首先要对I2C进行配置,使其能够正常工作,再结合不同器件的驱动程序,完成STM32与不同器件的数据传输。这里只介绍STM32作为主机的使用流程,从机可以是AT24XX系列EEPROM或MPU6050等器件,使用流程和参考代码如下:

  1. 配置GPIO。
  2. 配置I2C。
  3. 编写读取从机数据的函数。
  4. 编写向从机写数据的函数。
void I2C_Init(void)     //初始化STM32的I2C2模块,使其能够使用
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;	        //将PB10和PB11引脚初始化为复用
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);				 
                                                            
	

	I2C_InitTypeDef I2C_InitStructure;						
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;				//模式,选择为I2C模式
	I2C_InitStructure.I2C_ClockSpeed = 50000;				//时钟速度,选择为50KHz
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;		//时钟占空比,选择占空比为1/3
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;				//应答,选择使能
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	
                                                   //应答地址,选择7位,从机模式下才有效
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;				//自身地址,从机模式下才有效
	I2C_Init(I2C2, &I2C_InitStructure);	
	
	I2C_Cmd(I2C2, ENABLE);									//使能I2C2,开始运行
}



void Write_Byte(uint8_t 存储地址, uint8_t Data)  //向从机写一个字节数据,存储地址不一定为8位
{
	I2C_GenerateSTART(I2C2, ENABLE);										
    //产生起始条件
    
	while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	
    //等待EV5,即等待检测到起始条件

	I2C_Send7bitAddress(I2C2, 从机地址, I2C_Direction_Transmitter);	
    //发送从机地址

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
	//等待EV6,即等待发送从机地址并受到从机应答信号
	
	I2C_SendData(I2C2, 存储地址);											
    //发送存储地址

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);			
    //等待EV8,即等待数据寄存器为空,可以发送数据
	
	I2C_SendData(I2C2, Data);												
    //发送数据

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);				
    //等待EV8_2,即等待从机接收到数据并传回应答信号
	
	I2C_GenerateSTOP(I2C2, ENABLE);											
    //产生停止信号
}

uint8_t Read_Byte(uint8_t 数据所在地址) //从从机读取一个字节数据,数据所在地址不一定为8位
{
	uint8_t Data;
    //用于暂存数据
	
	I2C_GenerateSTART(I2C2, ENABLE);										
    //产生起始条件

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
    //等待EV5,即等待检测到起始条件
	
	I2C_Send7bitAddress(I2C2, 从机地址, I2C_Direction_Transmitter);	
    //发送从机地址

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
    //等待EV6,即等待发送从机地址并受到从机应答信号
	
	I2C_SendData(I2C2, 数据所在地址);											
    //发送从机的数据所在地址

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
    //等待EV8_2,即等待从机接收到数据并传回应答信号
	
	I2C_GenerateSTART(I2C2, ENABLE);										
    //产生重复起始条件

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);					
    //等待EV5,即等待检测到起始条件
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);		
    //发送从机地址

    while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);
    //等待EV6,即等待发送从机地址并受到从机应答信号
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);									
    //在接收最后一个字节之前提前将应答失能,之后从机不再传输数据

	I2C_GenerateSTOP(I2C2, ENABLE);											
    //在接收最后一个字节之前提前申请停止条件

	while (I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);			
    //等待EV7,即等待数据寄存器不为空,收到了数据

	Data = I2C_ReceiveData(I2C2);											
    //将接收到的数据存到Data变量中
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);									
    //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
	
	return Data;
}

代码仅供参考,具体使用需要结合相关从机器件的开发文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值