IIC是由Philips公司發明的一種串行數據通信協議,僅使用兩根信號線:SerialClock(簡稱SCL)和SerialData(簡稱SDA)。
IIC是總線結構,1個Master,1個或多個Slave,各Slave設備以7位地址區分,地址后面再跟1位讀寫位,表示讀(=1)或者寫(=0),所以我們有時也可看到8位形式的設備地址,此時每個設備有讀、寫兩個地址,高7位地址其實是相同的。
IIC數據格式如下:
無數據: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將視情況減慢或結束數據傳送。
下面是本人編寫的單片機普通IO口模擬IIC總線的程序源代碼:
- /*
- ************************************************************************************
- Fuction:GPIO模擬iic bus,實現與AT24C02的數據通信
- PS:該源代碼是以STC89C52為平台編寫的,可通過修改包含的頭文件以適用於所有51系列單片機
- SD:Jason
- ************************************************************************************
- */
- #include <reg52.h>
- #include <string.h>
- sbit sda = P2^0;
- sbit scl = P2^1;
- unsigned char table1[11]={0,1,2,3,4,5,6,7,8,9};
- unsigned char table2[11];
- void init();
- void somenop();
- void delay(unsigned char);
- void start();
- void stop();
- void send_ack(unsigned char);
- unsigned char rec_ack();
- void write_byte(unsigned char);
- unsigned char read_byte();
- unsigned char write_add(unsigned char,unsigned char *,unsigned char);
- unsigned char read_add(unsigned char,unsigned char *,unsigned char);
- void main()
- {
- init();
- write_add(0x10,table1,10);
- delay(100);
- read_add(0x10,table2,10);
- while(1);
- }
- //端口初始化
- void init()
- {
- memset(table2,0,sizeof(table2));
- sda = 1;
- scl = 1;
- somenop();
- }
- //起始信號
- void start()
- {
- sda = 1;
- somenop();
- scl = 1;
- somenop();
- sda = 0;
- somenop();
- }
- //終止信號
- void stop()
- {
- sda = 0;
- somenop();
- scl = 1;
- somenop();
- sda = 1;
- somenop();
- }
- //主機發送應答信號
- void send_ack(unsigned char ack)
- {
- scl = 0;
- somenop();
- sda = ack;
- somenop();
- scl = 1;
- somenop();
- scl = 0;
- somenop();
- sda = 1;
- somenop();
- }
- //主機接收應答信號
- unsigned char rec_ack()
- {
- scl = 1;
- somenop();
- if(sda == 1)
- {
- scl = 0;
- delay(1);
- scl = 1;
- if(sda == 1)
- return 1;
- }
- else
- return 0;
- }
- //寫一個字節
- void write_byte(unsigned char dat)
- {
- unsigned char i,a;
- a = dat;
- for(i=0;i<8;i++)
- {
- a = a<<1;
- scl = 0;
- somenop();
- sda = CY;
- somenop();
- scl = 1;
- somenop();
- }
- scl = 0;
- somenop();
- sda = 1;
- somenop();
- }
- //讀一個字節
- unsigned char read_byte()
- {
- unsigned char i,a;
- scl = 0;
- somenop();
- sda = 1;
- somenop();
- for(i=0;i<8;i++)
- {
- scl = 1;
- somenop();
- a = (a<<1)|sda;
- scl = 0;
- somenop();
- }
- return a;
- }
- //向add地址寫入len長度的dat數據
- unsigned char write_add(unsigned char add,unsigned char *dat,unsigned char len)
- {
- unsigned char flag,i;
- start();
- write_byte(0xa0);
- if(rec_ack() == 0)
- {
- write_byte(add);
- if(rec_ack() == 0)
- {
- for(i=0;i<len;i++)
- {
- write_byte(dat[i]);
- if(rec_ack() == 1)
- {
- i = 0;
- break;
- }
- }
- if(i == len)
- flag = 1;
- }
- }
- stop();
- return flag;
- }
- //從add地址讀出len長度的數據給dat
- unsigned char read_add(unsigned char add,unsigned char *dat,unsigned char len)
- {
- unsigned char flag,i;
- start();
- write_byte(0xa0);
- if(rec_ack() == 0)
- {
- write_byte(add);
- if(rec_ack() == 0)
- {
- start();
- write_byte(0xa1);
- if(rec_ack() == 0)
- {
- for(i=0;i<len;i++)
- {
- dat[i] = read_byte();
- send_ack(0);
- }
- flag = 1;
- send_ack(1);
- somenop();
- }
- }
- }
- stop();
- return flag;
- }
- //delay 5us
- void somenop()
- {;;}
- //delay x ms
- void delay(unsigned char x)
- {
- unsigned char y,z;
- for(y=x;y>0;y--)
- for(z=110;z>0;z--);
- }