I2C是由Philips公司發明的一種串行數據通信協議,僅使用兩根信號線:SerialClock(簡稱SCL)和SerialData(簡稱SDA)。I2C是總線結構,1個Master,1個或多個Slave,各Slave設備以7位地址區分,地址后面再跟1位讀寫位,表示讀(=1)或者寫(=0),所以我們有時也可看到8位形式的設備地址,此時每個設備有讀、寫兩個地址,高7位地址其實是相同的。 I2C數據格式如下: 無數據:SCL=1,SDA=1; 開始位(Start):當SCL=1時,SDA由1向0跳變; 停止位(Stop):當SCL=1時,SDA由0向1跳變; 數據位:當SCL由0向1跳變時,由發送方控制SDA,此時SDA為有效數據,不可隨意改變SDA; 當SCL保持為0時,SDA上的數據可隨意改變; 地址位:定義同數據位,但只由Master發給Slave; 應答位(ACK):當發送方傳送完8位時,發送方釋放SDA,由接收方控制SDA,且SDA=0; 否應答位(NACK):當發送方傳送完8位時,發送方釋放SDA,由接收方控制SDA,且SDA=1。 當數據為單字節傳送時,格式為: 開始位,8位地址位(含1位讀寫位),應答,8位數據,應答,停止位。 當數據為一串字節傳送時,格式為: 開始位,8位地址位(含1位讀寫位),應答,8位數據,應答,8位數據,應答,……,8位數據,應答,停止位。 需要注意的是: 1,SCL一直由Master控制,SDA依照數據傳送的方向,讀數據時由Slave控制SDA,寫數據時由Master控制SDA。當8位數據傳送完畢之后,應答位或者否應答位的SDA控制權與數據位傳送時相反。 2,開始位“Start”和停止位“Stop”,只能由Master來發出。 3,地址的8位傳送完畢后,成功配置地址的Slave設備必須發送“ACK”。否則否則一定時間之后Master視為超時,將放棄數據傳送,發送“Stop”。 4,當寫數據的時候,Master每發送完8個數據位,Slave設備如果還有空間接受下一個字節應該回答“ACK”,Slave設備如果沒有空間接受更多的字節應該回答“NACK”,Master當收到“NACK”或者一定時間之后沒收到任何數據將視為超時,此時Master放棄數據傳送,發送“Stop”。 5,當讀數據的時候,Slave設備每發送完8個數據位,如果Master希望繼續讀下一個字節,Master應該回答“ACK”以提示Slave準備下一個數據,如果Master不希望讀取更多字節,Master應該回答“NACK”以提示Slave設備準備接收Stop信號。 6,當Master速度過快Slave端來不及處理時,Slave設備可以拉低SCL不放(SCL=0將發生“線與”)以阻止Master發送更多的數據。此時Master將視情況減慢或結束數據傳送。 7,I2C規程運用主/從雙向通訊。器件發送數據到總線上,則定義為發送器,器件接收據 定義為接收器。主器件和從器件都可以工作于接收和發送狀態。 總線必須由主器件(通常 為微控制器)控制,主器件產生串行時鐘(SCL)控制總線的傳輸方向,并產生起始和停止 條件。SDA線上的數據狀態僅在SCL為低電平的期間才能改變,SCL為高電平的期間,SDA 狀態的改變被用來表示起始和停止條件。 在實際應用中,并沒有強制規定數據接收方必須對于發送的8位數據做出回應,尤其是在Master和Slave端都是用GPIO軟件模擬的方法來實現的情況下,編程者可以事先約定數據傳送的長度,不發送ACK,有時可以起到減少系統開銷的效果。
源碼:
/********************************************************************/ void i2c_init(void) { PACNT_init; PADDR_init; PADAT_init;
SCL_high; SDA_high; }
/********************************************************************/ uint8 i2c_write(uint8 slave_address, uint8 *buffer, int byte_count, int freq) { uint8 out_mask = 0x80; uint8 value = 0x00; uint8 send_byte = 0x00; uint8 status = 0x81; int count = 8; int clk_count = 0; int i = 0;
/* Set delay value based on frequency. */ int D = (int) ((4000/freq) - 14);
slave_address = (slave_address & 0xFE); i2c_start(); delay(500); send_byte = slave_address; for(i = 0; i <= byte_count; i++) { count = 8; out_mask = 0x80; /* Send data bytes one bit at a time. */ while(count > 0) { value = ((send_byte & out_mask) ? 1 : 0); if (value == 1) { PADAT_init; SDA_high;} else { PADAT_init; SDA_low;} delay(D); PADAT_init; SCL_high; /* Clock stretching wait statement. Wait until clock is released by slave. Only effects program on first iteration. */ while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;} delay(2*D); PADAT_init; SCL_low; delay(D); out_mask >>= 1; count--; } PADAT_init; SDA_high; /* Let go of data pin. */ delay(D); if (((GPIO_PADAT & 0x0400) ? 1 : 0) == 1) { status = 0xA1; /* Transfer complete, bus busy, acknowledge not received. */ break; } /* If not acknowledged, exit loop. */ PADAT_init; SCL_high; delay(2*D); PADAT_init; SCL_low; status = 0xA0; /* Transfer complete, bus busy, acknowledge received. */ delay(D); send_byte = buffer[i]; } PADAT_init; SDA_high; SCL_low; delay(100); return(status); } /********************************************************************/ uint8 i2c_read(uint8 slave_address, uint8 *buffer, int byte_count, int freq) { uint8 input_byte = 0x00; uint8 value = 0x00; uint8 out_mask = 0x80; uint8 status = 0x81; int count = 8; int clk_count = 0; int i = 0;
/* Set delay value based on frequency. */ int D = (int) ((4000/freq) - 14);
slave_address = (slave_address | 0x01); i2c_start(); delay(500); /********** Write Address Procedure **********/ while(count > 0) { value = ((slave_address & out_mask) ? 1 : 0); if (value == 1) { PADAT_init; SDA_high;} else { PADAT_init; SDA_low;} delay(D); PADAT_init; SCL_high; /* Clock stretching wait. Wait until clock is released by slave. */ while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;} delay(2*D); PADAT_init; SCL_low; delay(D); out_mask >>= 1; count--; } PADAT_init; SDA_high; /* Let go of data pin. */ delay(D); SCL_high; delay(2*D); /* If not acknowleged, set status accordingly and exit read process. */ if (((GPIO_PADAT & 0x0400) ? 1 : 0) == 1) { status = 0xA1; return(status);} PADAT_init; SCL_low; delay(D);
/********** Begin Read Procedure **********/
/* Release SDA and SCL to initiate transfer. */ PADAT_init; SDA_high; SCL_high; for(i = 0; i < byte_count; i++) { count = 8; input_byte = 0x00; PADAT_init; SCL_high; /* Clock stretching wait. Wait until clock is released by slave. */ while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;} /* Loop for bit-by-bit read of data. */ while(count > 0) { PADAT_init; SCL_high; delay(D); delay(4); /* Required to make read and write clocks the same freq. */ if ((GPIO_PADAT & 0x0600) == 0x0600) input_byte++; delay(D); PADAT_init; SCL_low; delay(2*D); if (count == 1) break; else input_byte <<= 1; count--; }
/* Write input byte to "read_buffer". */ buffer[i] = input_byte; if(i == (byte_count - 1)) break; /* Below is the acknowledge procedure. */ PADAT_init; SDA_low; delay(D); SCL_high; delay(2*D); PADAT_init; SCL_low; delay(D); SDA_high; status = 0xA0; }
/* Standard protocol calls for the last read byte to not receive an acknowledge from the master. */ PADAT_init; SDA_high; SCL_high; delay(2*D); PADAT_init; SCL_low; delay(D); SDA_high; status = 0xA1; return(status); } /********************************************************************/ void i2c_start(void) { int clk_count = 0; uint8 compare = 0x00;
PADAT_init; SDA_high; delay(100);
PADAT_init; SCL_high; delay(100);
/* Clock stretching wait. Wait until clock is released by slave. */ while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;} PADAT_init; SDA_low; delay(100); PADAT_init; SCL_low; delay(100); } /********************************************************************/ uint8 i2c_stop(void) { uint8 status = 0x00; int clk_count = 0; PADAT_init; SCL_low; delay(100); PADAT_init; SDA_low; delay(100); PADAT_init; SCL_high;
/* Clock stretching wait statement. Wait until clock is released by slave. */ while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;} delay(100); PADAT_init; SDA_high; status = 0x81; /* Set bus idle. */ return(status); } /********************************************************************/ void delay(int value) { int clk_count = 0; while (clk_count < value) {clk_count++;} } /********************************************************************/ 第二個例子 函數定義: gpio_iic.h: #ifndef __IIC_GPIO__ #define __IIC_GPIO__ void delay(); /* 設置scl引腳電平,0低電平,1高電平,其他值無效,返回值一直為0,留著它用。 */ int set_scl( int value ); /* 得到scl引腳電平,0低電平,1高電平,必須是這兩個值,其他函數需要調用。 */ int get_scl(); /* 設置sda引腳電平,0低電平,1高電平,其他值無效,返回值一直為0,留著它用。 */ int set_sda( int value ); /* 得到sda引腳電平,0低電平,1高電平,必須是這兩個值,其他函數需要調用。 */ int get_sda(); /* 重新發送iic start位,這個是在傳送數據過程中使用。 */ void iic_restart();
/* 發送iic start位,這里假設總線空閑,此時SDL與SCL都為高電平。 */ void iic_start(); /* 發送stop位,這里假設scl此時為低電平。 */ void iic_stop(); /* 發送一個bit0,這里假設scl此時為低電平,sda電平不定。 */ void send_bit0(); /* 發送一個bit 1,這里假設scl此時為低電平,sda電平不定。 */ void send_bit1(); /* 接收一個bit位,返回值只能是0或1。 */ int receive_bit(); /* 發送ACK,實際上是發送一個bit0. */ void send_ack(); /* 接收ACK。 */ int receive_ack(); /* 接收一個字節。 */ char receive_byte(); /* 接收一個buf,返回值總是為0,它不能保證從器件一定能收到ACK,也不能保證從器件正在工作。 */ int receive_buf( char *buf, int buf_size ); /* 發送一個字節,返回ACK的值,發送時,沒有收到ACK會重試n次,這是常。 */ int send_byte( char data_byte ); /* 發送一個buf,返回值是成功發送,收到ACK的字節數量。 */ int send_buf( char *buf, int buf_size ); #endif gpio_iic.c:
#include "gpio_iic.h" //#define __80C52__ #define __MINI2440__ #ifdef __MINI2440__ #include #include #include #endif #ifdef __80C52__ #include sbit SCL = P1^0; sbit SDA = P1^1; #endif void delay() { #ifdef __MINI2440__ udelay(1); #endif #ifdef __80C52__ int i = 0; for( i = 0; i < 10000; i ++ ); #endif } /* 設置scl引腳電平,0低電平,1高電平,其他值無效,返回值一直為0,留著它用。 */ int set_scl( int value ) {
#ifdef __MINI2440__ // s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA); // s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL); s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPIO_OUTPUT); switch( value ) { case 0: s3c2410_gpio_setpin(S3C2410_GPE(14), 0); // IICSCL break; case 1: s3c2410_gpio_setpin(S3C2410_GPE(14), 1); // IICSCL break; default: break; } #endif #ifdef __80C52__ if ( 0 == value ) SCL = 0; else if ( 1 == value ) SCL = 1; #endif return 0; } /* 得到scl引腳電平,0低電平,1高電平,必須是這兩個值,其他函數需要調用。 */ int get_scl() { #ifdef __MINI2440__ s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPIO_INPUT); return s3c2410_gpio_getpin(S3C2410_GPE(14)) > 0 ? 1: 0; #endif
#ifdef __80C52__ return SCL; #endif } /* 設置sda引腳電平,0低電平,1高電平,其他值無效,返回值一直為0,留著它用。 */ int set_sda( int value ) { #ifdef __MINI2440__ s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPIO_OUTPUT); switch( value ) { case 0: s3c2410_gpio_setpin(S3C2410_GPE(15), 0); // IICSDA break; case 1: s3c2410_gpio_setpin(S3C2410_GPE(15), 1); // IICSDA break; default: break; } #endif #ifdef __80C52__ if ( 0 == value ) SDA = 0; else if ( 1 == value ) SDA = 1; #endif return 0; } /* 得到sda引腳電平,0低電平,1高電平,必須是這兩個值,其他函數需要調用。 */ int get_sda() { #ifdef __MINI2440__ int sda_pin = 0; s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPIO_INPUT); sda_pin = s3c2410_gpio_getpin(S3C2410_GPE(15)) > 0 ? 1:0; return sda_pin; #endif #ifdef __80C52__ return SDA; #endif } /* 重新發送iic start位,這個是在傳送數據過程中使用。 */ void iic_restart() { set_scl( 0 ); delay(); set_sda( 1 ); delay(); set_scl( 1 ); delay(); set_sda( 0 ); delay(); set_scl( 0 ); delay(); }
/* 發送iic start位,這里假設總線空閑,此時SDL與SCL都為高電平。 */ void iic_start() { set_sda( 0 ); delay(); set_scl( 0 ); delay(); }
/* 發送stop位,這里假設scl此時為低電平。 */ void iic_stop() { set_sda( 0 ); delay(); set_scl( 1 ); delay(); set_sda( 1 ); delay(); } /* 發送一個bit0,這里假設scl此時為低電平,sda電平不定。 */ void send_bit0() { set_sda( 0 ); delay(); set_scl( 1 ); delay(); set_scl( 0 ); delay(); }
/* 發送一個bit 1,這里假設scl此時為低電平,sda電平不定。 */ void send_bit1() { set_sda( 1 ); delay(); set_scl( 1 ); delay(); set_scl( 0 ); delay(); } /* 接收一個bit位,返回值只能是0或1。 */ int receive_bit() { int value = -1; set_sda( 1 ); delay(); set_scl( 0 ); delay(); set_scl( 1 ); delay(); value = get_sda(); set_scl( 0 ); return value; } /* 發送ACK,實際上是發送一個bit0. */ void send_ack( ) { send_bit0(); } /* 接收ACK。 */ int receive_ack() { int ack = 1; set_sda( 1 ); delay(); set_scl( 1 ); delay(); ack = get_sda(); delay(); set_scl( 0 ); return ack; } /* 接收一個字節。 */ char receive_byte( ) { int i = 0; int recv_data = 0; for ( i = 0; i < 8; i++ ) { recv_data = recv_data | receive_bit(); if ( 7 == i ) break; recv_data <<= 1; } send_ack(); return recv_data; } /* 接收一個buf,返回值總是為0,它不能保證從器件一定能收到ACK,也不能保證從器件正在工作。 */ int receive_buf( char *buf, int buf_size ) { int i = 0; for( i = 0; i < buf_size; i ++ ) { buf[i] = receive_byte(); } return buf_size; } /* 發送一個字節,返回ACK的值,發送時,沒有收到ACK會重試n次,這是常。 */ int send_byte( char data_byte ) { int retry_count = 8;//重試次數。 int i = 0; int ack = 1; char send_data = 0; send_data = data_byte; do{ for ( i = 0; i < 8; i ++ ) { if ( 0x80 & send_data ) send_bit1(); else send_bit0(); send_data <<= 1; } ack = receive_ack(); if ( 0 == ack ) break; send_data = data_byte; retry_count --; } while( retry_count >= 0 ); return ack; } /* 發送一個buf,返回值是成功發送,收到ACK的字節數量。 */ int send_buf( char *buf, int buf_size ) { int i = 0; int count = 0; for ( i = 0; i < buf_size; i ++ ) { if (0 != send_byte( buf[i] ) ) break; count ++; } return count; } 下面是可以用來讀寫at24c02的測試代碼,在mini2440板測試通過, static void m24c02_send( int addr, char *buf, int buf_size) { int count = 0; int rev = -1; iic_start(); rev = send_byte( 0xa0 ); rev = send_byte( (addr >> 0) & 0xff ); count = send_buf( buf, buf_size ); iic_stop(); } static void m24c02_recv( int addr , char *buf, int buf_size ) { iic_start(); send_byte( 0xa0 ); send_byte( (addr >> 0) & 0xff ); iic_restart(); send_byte( 0xa1 ); receive_buf( buf, buf_size ); iic_stop(); }
(1)基礎宏定義 #define GPIO_SCL S3C2410_GPF3 #define GPIO_SDA S3C2410_GPF0 #define GPIO_SDA_OUTP S3C2410_GPF0_OUTP //設定SDA輸出 #define GPIO_SDA_INP S3C2410_GPF0_INP //設定SDA輸入 #define GPIO_SCL_OUTP S3C2410_GPF3_OUTP //設定SCL輸出 void I2C_SCL_OUTP( void ) { s3c2410_gpio_cfgpin(GPIO_SCL,GPIO_SCL_OUTP); } void I2C_SCL_Output(u8 value) { if(value) { s3c2410_gpio_setpin(GPIO_SCL,value); } else { s3c2410_gpio_setpin(GPIO_SCL,value ); } } void I2C_SDA_Mode(u8 v_mode) //SDA輸出方向 { if(v_mode) { s3c2410_gpio_cfgpin(GPIO_SDA, GPIO_SDA_OUTP); } else { s3c2410_gpio_cfgpin(GPIO_SDA, GPIO_SDA_INP); } } void I2C_SDA_Output(u8 value) { if(value) { s3c2410_gpio_setpin(GPIO_SDA,value); } else { s3c2410_gpio_setpin(GPIO_SDA,value ); } } u8 I2C_SDA_Read(void) //SDA讀數據 { return s3c2410_gpio_getpin(GPIO_SDA); } (2)基礎段 void I2C_Init(void) { I2C_SDA_Output(1); I2C_SCL_Output(1); //默認拉高 } void I2C_Wait(void) { u16 i; for(i=0;i<200;i++); } void I2C_Start(void) { I2C_SDA_Output(1); I2C_SCL_Output(1); I2C_Wait(); I2C_SDA_Output(0); I2C_Wait(); I2C_SCL_Output(0); } void I2C_Stop(void) { I2C_SDA_Output(0); I2C_Wait(); I2C_SCL_Output(1); I2C_Wait(); I2C_SDA_Output(1); }
(3)讀寫單個字節的段 u8 I2C_Send_Byte(u8 bytedata) { u8 i,ack; I2C_SDA_Mode(1); //SDA輸出 I2C_SCL_OUTP(); for (i = 0; i < 8; i++) { if (bytedata & 0x80) { I2C_SDA_Output(1); } else { I2C_SDA_Output(0); } bytedata <<= 1; I2C_SCL_Output(1); udelay(3); I2C_SCL_Output(0); udelay(1); } I2C_SDA_Output(1); //release udelay(3); I2C_SDA_Mode(0); //設定SDA輸入 I2C_SCL_Output(1); udelay(3); ack = I2C_SDA_Read(); //讀應答 I2C_SDA_Mode(1); I2C_SCL_Output(0); udelay(3); return ack; } u8 I2C_Receive_Byte(void) { u8 i; u8 bytedata = 0x00; u8 temp; I2C_SDA_Mode(0); for ( i = 0; i < 8; i++) { I2C_SCL_Output(1); udelay(3); bytedata <<= 1; temp = I2C_SDA_Read(); printk("reda SDA'value is:%d\n",temp); if (temp) bytedata |= 0x01; printk(" bytedata is:%x\n",bytedata); I2C_SCL_Output(0); udelay(1); } I2C_SDA_Mode(1); return bytedata; } (4)讀寫單個字節的I2C應用函數 u8 I2C_Byte_Write(u8 device_ID,u8 address,u8 bytedata) { u8 ack; printk("device_ID is:%x\n",device_ID); printk("address is:%x\n",address); printk("date is:%x\n",bytedata); I2C_Start(); ack=I2C_Send_Byte(device_ID); printk("ack is:%d\n",ack); if(ack) I2C_Stop(); I2C_Send_Byte(address); I2C_Send_Byte(bytedata); I2C_Stop(); I2C_Wait(); return 0; } u8 I2C_Byte_Read(u8 device_ID,u8 address) { u8 bytedata; I2C_Start(); I2C_Send_Byte(device_ID); I2C_Send_Byte(address); I2C_Start(); I2C_Send_Byte(device_ID+1); bytedata = I2C_Receive_Byte(); //讀單個字節,不需要再發應答 I2C_Stop(); return bytedata; } |