IIC是一种电路板级的半双工多组通信总线
IIC 即Inter-Integrated Circuit
在同一块电路板上用于芯片和芯片之间通信(用于多芯片之间的通信)
半双工:
由于通信介质的限制
两根线:一根线用来传数据,另一根线用来同步时钟(SDA:数据总线,SCL:时钟总线)
两两之间都可以进行通信,但是必须要由一个芯片开始发起
板级:
在同一块电路板上用于芯片和芯片之间通信(用于多芯片之间的通信)
半双工:
由于通信介质的限制
两根线:一根线用来传数据,另一根线用来同步时钟(SDA:数据总线,SCL:时钟总线)
两两之间都可以进行通信,但是必须要由一个芯片开始发起
IIC串行总线的组成和工作原理
1.采用串行总线技术可以使系统硬件设计大大简化,系统的体积减小,可靠性提高。
同时,系统的更改和扩充极为容易。
用于板级的通信(芯片和芯片之间的通信)
用于板级和设备之间的通信可以用rs232,485,can总线等等来通信
板级通信距离不会太长
下面来介绍下IIC的通信原理
1. 通信的发起和结束都由主机控制
2. 每个从设备都有一个地址
3. 传输数据的时候,sda对应的数据信号的宽度 要比scl的高电平的宽度要宽,
sda跳变只能在scl处于低电平的时候
4. 在scl处于高电平的时候,如果sda产生了高->低的跳变,一次通信开始,低->高的跳变,一次通信结束
5. 数据发送,一次只发1个字节,没法送一个byte打的数据的时候,都要回一个ack数据位
《MPU-6050_DataSheet_V3 4.pdf》描述 从设备
查找从设备地址
所以AD0值是0。
从设备地址是0x68
内部寄存器地址可以参考《上课用资料\i2c\mpu6050寄存器.doc》
从上图可以看出,4412提供4个寄存器来完成所有的IIC操作。SDA线上的数据从IICDS寄存器经过移位寄存器发出,或通过移位寄存器传入IICDS寄存器;IICADD寄存器中保存4412当做从机时的地址;IICCON、IICSTAT两个寄存器用来控制或标识各种状态,比如选择工作工作模式,发出S信号、P信号,决定是否发出ACK信号,检测是否接收到ACK信号。
主机是初始化总线的数据传输并产生允许传输的时钟信号
的器件此时任何被寻址的器件都被认为是从机
Exynos4412精简指令集微处理器支持4个IIC总线控制器。为了能使连接在总线上的主和从设备之间传输数据,专用的数据线SDA和时钟信号线SCL被使用,他们都是双向的。
如果工作在多主机的IIC总线模式,多个4412处理器将从从机那接收数据或发送数据给从机。在IIC总线上的主机端4412会启动或终止一个数据传输。4412的IIC总线控制器会用一个标准的IIC总线仲裁机制去实现多主机和多从机传输数据。
通过控制如下寄存器以实现IIC总线上的多主机操作:
控制寄存器: I2CCON
状态寄存器: I2CSTAT
Tx/Rx数据偏移寄存器: I2CDS
地址寄存器: I2CADD
如果I2C总线空闲,那么SCL和SDA信号线将都为高电平。在SCL为高电平期间,如果SDA有由高到低电平的跳变,那么将启动一个起始信号,如果SDA有由低到高电平的跳变,将启动一个结束信号。
主机端的设备总是提供起始和停止信号的一端。在起始信号被发出后,一个数据字节的前7位被当作地址通过SDA线被传输。这个地制值决定了总线上的主设备将要选择那个从设备作为传输对象,bit8决定传输数据的方向(是读还是写)。
I2C总线上的数据(即在SDA上传输的数据)都是以8位字节传输的,在总线上传输操作的过程中,对发送或接收的数据字节数是没有限制的。I2C总线上的主/从设备发送数据总是以一个数据的高位开始传输(即MSB方式),传输完一个字节后,应答信号紧接其后。
1. 开始和停止条件
当4412的I2C接口空闲时,它往往工作在从机模式。或者说,4412的的i2c接口在SDA线上察觉到一个起始信号之前它应该工作在从机模式。当控制器改变4412的i2c接口的工作模式为主机模式后,SDA线上发起数据传输并且控制器会产生SCL时钟信号。
开始条件通过SDA线进行串行的字节传输,一个停止信号终止数据传输,停止信号是指SCL在高电平器件SDA线有从低到高电平的跳变,主机端产生起始和停止条件。当主、从设备产生一个起始信号后,I2C总线将进入忙状态。这里需要说明的是上述主从设备都有可能作为主机端。
当一个主机发送了一个起始信号后,它也应该发送一个从机地址以通知总线上的从设备。这个地址字节的低7位表示从设备地址,高位表示传输数据的方向,即主机将要进行读还是写。当高位是0时,它将发起一个写操作(发送操作);当高位是1时,它将发起一个读数据的请求(接收操作)。
主机端发起一个结束信号以完成传输操作,如果主机端想在总线上继续进行数据的传输,它将发出另外一个起始信号和从设备地址。用这样的方式,它们可以用各种各样的格式进行读写操作。
【7】是否回ack
发送模式下,都是 1
接受模式下,再等待接受data之前,要这个位置0,表示不会回复ack,
【5】使能中断
【4】写0 清中断,写1 无效,
每次数据传输完毕,都要通过判断该位 ,如果是1 表示中断被添加,表示对方接受成功
【5】
置1 就发start信号 s
置0 发送stop信号 p
【4】长期置1
从设备地址
读写的数据 缓冲寄存器
父节点GPX3 27
typedef struct
{
unsigned int I2CCON;
unsigned int I2CSTAT;
unsigned int I2CADD;
unsigned int I2CDS;
unsigned int I2CLC;
}i2c5;
#define I2C5 (*(volatile i2c5 *)0x138B0000 )
#include "exynos_4412.h"
//#include "led.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 0xD0 //IIC写入时的地址字节数据,+1为读取
int adc_num;
void delay_ms(unsigned int time)
{
int i, j;
while(time--)
{
for (i = 0; i < 5; i++)
for (j = 0; j < 514; j++);
}
}
void mydelay_ms(int time)
{
int i, j;
while(time--)
{
for (i = 0; i < 5; i++)
for (j = 0; j < 514; j++);
}
}
void do_irq()
{
static int a = 1;
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //获取中断号
switch(irq_num)
{
case 57:
printf("in the irq_handler\n");
if(a)
led_on(1);
else
led_off(1);
a = !a;
EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1)); //清GPIO中断标志位
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25); //清GIC中断标志位
break;
case 75:
printf("in the wdt interrupt\n");
WDT.WTCNT = 180000;
WDT.WTCLRINT = 0;
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (1 << 11);
break;
case 42:
//printf("in the adc interrupt!\n");
adc_num = ADCDAT&0xfff;
printf("adc = %d\n",adc_num);
CLRINTADC = 0;
IECR2 = IECR2 | (1 << 19);
//42/32
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (1 << 10);
break;
case 76:
printf("in the alarm interrupt!\n");
RTCINTP = RTCINTP | (1 << 1);
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 12); //清GIC中断标志位
break;
case 77:
printf("in the tic interrupt!\n");
RTCINTP = RTCINTP | (1 << 0);
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 13); //清GIC中断标志位
break;
}
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num; //清cpu中断标志位
}
@brief iic read a byte program body
@param[in] slave_addr, addr, &data
@return None
void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data)
{
I2C5.I2CDS = slave_addr; //将从机地址写入I2CDS寄存器中
I2C5.I2CCON = (1 << 7)|(1 << 6)|(1 << 5); //设置时钟并使能中断
I2C5.I2CSTAT = 0xf0; //[7:6]设置为0b11,主机发送模式;
//往[5:4]位写0b11,即产生启动信号,发出IICDS寄存器中的地址
while(!(I2C5.I2CCON & (1 << 4))); // 等待传输结束,传输结束后,I2CCON [4]位为1,标识有中断发生;
// 此位为1时,SCL线被拉低,此时I2C传输停止;
I2C5.I2CDS = addr; //写命令值
I2C5.I2CCON = I2C5.I2CCON & (~(1 << 4));// I2CCON [4]位清0,继续传输
while(!(I2C5.I2CCON & (1 << 4)));// 等待传输结束
I2C5.I2CSTAT = 0xD0; // I2CSTAT[5:4]位写0b01,发出停止信号
I2C5.I2CDS = slave_addr | 1; //表示要读出数据
I2C5.I2CCON = (1 << 7)|(1 << 6) |(1 << 5) ; //设置时钟并使能中断
I2C5.I2CSTAT = 0xb0;//[7:6]位0b10,主机接收模式;
//往[5:4]位写0b11,即产生启动信号,发出IICDS寄存器中的地址
I2C5.I2CCON = I2C5.I2CCON & (~(1 << 4));
while(!(I2C5.I2CCON & (1 << 4)));//等待传输结束,接收数据
I2C5.I2CCON &= ~((1<<7)|(1 << 4));/* Resume the operation & no ack*/
// I2CCON [4]位清0,继续传输,接收数据,
// 主机接收器接收到后一字节数据后,不发出应答信号 no ack
// 从机发送器释放SDA线,以允许主机发出P信号,停止传输;
while(!(I2C5.I2CCON & (1 << 4)));// 等待传输结束
I2C5.I2CSTAT = 0x90;
*data = I2C5.I2CDS;
I2C5.I2CCON &= ~(1<<4); /*clean interrupt pending bit */
mydelay_ms(10);
*data = I2C5.I2CDS;
}
@brief iic write a byte program body
@param[in] slave_addr, addr, data
@return None
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{
I2C5.I2CDS = slave_addr;
I2C5.I2CCON = (1 << 7)|(1 << 6)|(1 << 5) ;
I2C5.I2CSTAT = 0xf0;
while(!(I2C5.I2CCON & (1 << 4)));
I2C5.I2CDS = addr;
I2C5.I2CCON = I2C5.I2CCON & (~(1 << 4));
while(!(I2C5.I2CCON & (1 << 4)));
I2C5.I2CDS = data;
I2C5.I2CCON = I2C5.I2CCON & (~(1 << 4));
while(!(I2C5.I2CCON & (1 << 4)));
I2C5.I2CSTAT = 0xd0;
I2C5.I2CCON = I2C5.I2CCON & (~(1 << 4));
mydelay_ms(10);
}
void MPU6050_Init ()
{
iic_write(SlaveAddress, PWR_MGMT_1, 0x00);
iic_write(SlaveAddress, SMPLRT_DIV, 0x07);
iic_write(SlaveAddress, CONFIG, 0x06);
iic_write(SlaveAddress, GYRO_CONFIG, 0x18);
iic_write(SlaveAddress, ACCEL_CONFIG, 0x01);
}
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;
}
/*
* 裸机代码,不同于LINUX 应用层, 一定加循环控制
*/
int main()
{
int data;
unsigned char zvalue;
GPB.CON = (GPB.CON & ~(0xff<<8)) | 0x33<<8; // GPBCON[3], I2C_5_SCL GPBCON[2], I2C_5_SDAn
mydelay_ms(100);
uart_init();
I2C5.I2CSTAT = 0xD0;
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
data = get_data(GYRO_ZOUT_H);
printf(" GYRO --> Z <---:Hex: %x", data);
data = get_data(GYRO_XOUT_H);
printf(" GYRO --> X <---:Hex: %x", data);
printf("\n");
mydelay_ms(1000);
}
return 0;
}