I2C接口开漏输出输入双向

十年热恋 提交于 2019-12-13 22:03:17

I2C接口

原创朝辞暮见 发布于2018-06-13 19:45:25 阅读数 8265  收藏

展开

一、I2C总线协议内容

1. I2C总线引脚定义

SDA (I2C数据引脚)

CLK (I2C数据引脚)

 

2. I2C总线物理连接

I2C总线物理连接如下图所示,SDA和CLK连接线上连有两个上拉电阻,当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低。

 

二、I2C总线的数据传送

1. 数据位的有效性规定

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

1 scl处于高电平的时候,如果sda处于低电平:传输数据0,sda处于高电平:传输数据1

2.在scl处于高电平的时候,sda用来传输数据,必须保持电平稳定

3.如果要产生数据即sda需要变化时,只能在scl处于低电平的时候

 

 

2. 起始和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。 

1. 信号的发起和终止,由主机发起,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。

 

2. scl处于高电平的时候,sda由高->低的跳变, 表示发起传输,产生起始信号s,

如果低->高跳变,表示终止传输,产生终止信号p

3. 在中间,可以正常传输数据

4. 连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号

5. 如果出现,从机在接收到一部分数据后需要对数据处理,如何因对这种情况?

接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平从而使主机处于等待状态【只有scl处于高电平的时候,主机才会去匹配sda的电平,接收下一个bit】。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。

 

3. 数据传送格式

1)字节传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)

主机每次发起通信后,第一个字节,往往是从设备的地址【7位】+RW【寻址信号】

 

1. 由于某种原因从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),它必须将数据线置于高电平,而由主机产生一个终止信号以结束总线的数据传送。

2. 如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节的“非应答”【sda处于高电平】通知主机,主机则应发出终止信号以结束数据的继续传送。

3   当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”【拉高sda】来实现的。然后,从机释放SDA线,以允许主机产生终止信号

 

2)数据帧格式

I2C总线上传送的数据信号是广义的,即包括地址信号,又包括真正的数据信号。

在起始信号后必须传送一个从机的地址(7位),第8位数数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。在总线的一次数据传送过程中,可以有以下几种组合方式:

a) 主机向从机发送数据:

注1:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。

A表示应答, A非表示非应答(高电平)。S表示起始信号,P表示终止信号。

注2:I2C总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号后的第一个字节)

b) 主机从从机中读取数据

c)主机从从机中既有读数据也有写数据

 

三、I2C发送与接收数据时序图

注:红色框代表主机拥有sda控制权,绿色框代表从机拥有sda控制权

 

1. 主机发送数据

2. 主机读取数据

 

四、Exynos4412-I2C控制器

1. I2C总线控制器概述

Exynos4412有9个I2C总线控制器,其中8个是通用I2C控制器,另1个I2C控制器是专门位HDMI提供。如下图所示:Exynos4412控制器通过读写寄存器来实现I2C通信,I2CCON寄存器和I2CSTAT寄存器用来配置、控制I2C控制器,并显示控制器状态。

 

2. I2C控制器寄存器详解

A)I2CCONn寄存器

B) I2CSTATn寄存器

 

C)I2CDSn 寄存器

该寄存器用来收发数据

 

五. I2C控制器流程

1. 配置主机发送模式

1)配置对应的I2C引脚的功能为SDA和SCL

2) 设置I2CCON[6]配置I2C发送时钟和中断使能

3)设置I2C发送使能,I2CSTAT[4]= 0b1

2. 将要通信的I2C从机的地址和读写位写入I2CDSn 寄存器

3. 将0xF0写入I2CSTAT寄存器

I2CSTAT[7:6] =0xb11设置4412模式为主机发送模式

I2CSTAT[5] = 0xb1,该位写1,发送开始信号

I2CSTAT[4] = 0xb1写1,使能I2C串口发送

4. I2C控制器发出开始信号后,在2中写入的I2CDS寄存器地址自动发送到SDA总线上,用来寻找从机。

5. 在ACK周期后,I2C控制器发生中断,I2CSTAT[4] 被自动置1,I2C传输暂停

6. I2C数据通信是否结束,结束跳转到10,没有结束跳转到7

7.  将要传输的数据写入I2CDS寄存器准备发送。

8. 清除中断标志位,通过向I2CCON[4]中写0实现

9. 清除中断标志位后,I2CDS寄存器内的数据就开始发送到SDA总线上。发送完成后,跳转到5。

10. 将0XD0写入I2CSTAT寄存器。

I2CSTAT[7:6] =0xb11设置4412模式为主机发送模式

I2CSTAT[5] = 0x0,该位写0,发送停止信号

I2CSTAT[4] = 0xb1,使能I2C串口发送

11. 清除中断标志位,通过向I2CCON[4]中写0实现

12. 延时等待一段时间,使得停止信号生效,I2C通信结束

 

I2C控制器主机发送模式流程图如下:

 

六、I2C接口读取MPU6050传感器数据测试实验

1. 实验目的:略

2. 实验原理:

 

 

3. 实验内容

Exynos4412通过I2C总线读取MPU6050的Z轴角速度结果并打印。首先要配置Exynos4412的I2C5(原理图连接I2C5)控制器,配置GPIO为I2C功能模式,再根据MPU6050芯片手册提供的芯片地址、寄存器说明和时序对MPU6050操作。

1)MPU6050的I2C地址

在电路图中AD0电平为0,所以MPU6050的I2C地址为0x68

 

2)在MPU6050手册中,有以下几种操作时许,分别是字节写时序、页写时序、当前地址读时序、随机地址读时序和顺序读时序。

A)字节写时序(Byte Write)如下图所示:

 

字节写时序依次要发送器件地址和读写位(AD + W)、数据写入地址(RA)和写入的8位数据(DATA)

 

A)字节读时序(Byte Read)如下图所示:

字节读时序依次要发送器件地址和读写位(AD + W)、数据读取地址(RA)。因为要转换数据流向,因此重新发送开始信号(S),接着发送器件地址和读写位(AD + R)、读取数据(DATA)。

七. 使用I2C控制器读取MPU6050数据示例代码如下:



#include "exynos_4412.h"


//****************************************
// MPU6050内部地址
//****************************************
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress 0x68 //IIC写入时的地址字节数据,+1为读取


void mydelay_ms(int time);
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data);
void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data);
void MPU6050_Init ();
int get_data(unsigned char addr);


/*-------------------------MAIN FUNCTION------------------------------*/
/**********************************************************************
 * @brief Main program body
 * @param[in] None
 * @return  int
 **********************************************************************/
int main(void)
{
int data;


unsigned char zvalue;


GPB.CON = (GPB.CON & ~(0xF<<12)) | 0x3<<12; // GPBCON[3], I2C_5_SCL
GPB.CON = (GPB.CON & ~(0xF<<8)) | 0x3<<8; // GPBCON[2], I2C_5_SDA


mydelay_ms(100);
uart_init();


/*---------------------------------------------------------------------*/
I2C5.I2CSTAT = 0xD0;        //设置I2CM Master 输出模式,停止信号,使能I2C串口输出
I2C5.I2CCON &= ~(1<<4); /*clean interrupt pending bit  */
/*---------------------------------------------------------------------*/


mydelay_ms(100);
MPU6050_Init();
mydelay_ms(100);


printf("\n********** I2C test!! ***********\n");


while(1)
{
//Turn on
GPX2.DAT |= 0x1 << 7;


data = get_data(GYRO_ZOUT_H);
printf(" GYRO --> Z <---:Hex: %0x", data);
printf("\n");


mydelay_ms(20);
//Turn off
GPX2.DAT &= ~(0x1 << 7);
mydelay_ms(500);
}
return 0;
}


void mydelay_ms(int time)
{
int i, j;
while(time--)
{
for (i = 0; i < 5; i++)
for (j = 0; j < 514; j++);
}
}




/**********************************************************************
 * 函数功能: iic 向特定地址写一个字节
 * @param[in] slave_addr,iic从机地址
 *  addr, 芯片内部特定地址
 *  data,写入的数据
 * @return  None
 **********************************************************************/
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{
I2C5.I2CDS = slave_addr << 1 | 0x0;
I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ,ENABLE RX/TX */
I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4;/*Master Trans mode ,START ,ENABLE RX/TX ,*/
while(!(I2C5.I2CCON & (1<<4)));


I2C5.I2CDS = addr;
I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.
while(!(I2C5.I2CCON & (1<<4)));


I2C5.I2CDS = data; // Data
I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.
while(!(I2C5.I2CCON & (1<<4)));


I2C5.I2CSTAT = 0xD0; //stop


I2C5.I2CCON &= ~(1<<4);


mydelay_ms(10);
}
/**********************************************************************
 * @brief iic 从特定地址读取一个字节的数据
 * @param[in] slave_addr,iic从机地址
 *  addr, 芯片内部特定地址
 * @return  None
 **********************************************************************/
void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data)
{
I2C5.I2CDS = slave_addr << 1 | 0x0;


I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ,ENABLE RX/TX */
I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4;/*Master Trans mode ,START ,ENABLE RX/TX ,*/
while(!(I2C5.I2CCON & (1<<4)));


I2C5.I2CDS = addr;
I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.
while(!(I2C5.I2CCON & (1<<4)));
I2C5.I2CSTAT = 0xD0; //stop




I2C5.I2CDS = slave_addr << 1 | 0x01; // Read
I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ,ENABLE RX/TX */


I2C5.I2CSTAT = 2<<6 | 1<<5 | 1<<4;/*Master receive mode ,START ,ENABLE RX/TX ,*/
while(!(I2C5.I2CCON & (1<<4)));


I2C5.I2CCON &= ~((1<<7) | (1<<4));/* Resume the operation  & no ack*/
while(!(I2C5.I2CCON & (1<<4)));//判断有没有收到data


I2C5.I2CSTAT = 0x90;
I2C5.I2CCON &= ~(1<<4); /*clean interrupt pending bit  */


*data = I2C5.I2CDS;
mydelay_ms(10);
}


/**********************************************************************
 * @brief MPU6050_Init program body
 * @param[in] None
 * @return  None
 **********************************************************************/
void MPU6050_Init ()
{
iic_write(SlaveAddress, PWR_MGMT_1, 0x00); //设置使用内部时钟8M
iic_write(SlaveAddress, SMPLRT_DIV, 0x07); //设置陀螺仪采样率
iic_write(SlaveAddress, CONFIG, 0x06);     //设置数字低通滤波器
iic_write(SlaveAddress, GYRO_CONFIG, 0x18);//设置陀螺仪量程+-2000度/s
iic_write(SlaveAddress, ACCEL_CONFIG, 0x01);//设置加速度量程+-2g
}


/**********************************************************************
 * @brief get MPU6050 data program body
 * @param[in] addr
 * @return  int
 **********************************************************************/
int get_data(unsigned char addr)
{
char data_h, data_l;
iic_read(SlaveAddress, addr, &data_h);
iic_read(SlaveAddress, addr+1, &data_l);
return (data_h<<8)|data_l;

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!