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;
}