這篇文章是寫給一個學弟看的,關于IIC,關于24C02的單字節寫入\讀取..頁寫入和讀取,,學弟總是害怕協議,,,我總是對人家說,本來就這樣的,協議就是人家這樣規定的,,,如果你早生幾十年你也可能規定個IIC協議......
我的單片機和24C02通信,,,我的單片機就叫主機,,,24C02叫從機
先看IIC
IIC協議規定開始傳輸數據的時候要先發一個起始信號,,,目的應該是告訴從機要開始通信了,準備準備
終止信號就是拜拜啦,再見!
起始信號就是 在SCL在高電平期間SDA來一個下降沿,,終止信號就是在SCL在高電平期間SDA來一個上升沿(所以協議上才會說,在正常傳輸數據的時候,只有在SCL為低電平的時候,數據線SDA的高低電平狀態才允許改變,要不然豈不是和起始信號或者終止信號沖突了)
/*******************************************************************
起動總線函數
函數原型: void? Start_I2c();
功能:? ? 啟動I2C總線,即發送I2C起始條件.
********************************************************************/voidStart_I2c()
{
SDA=1;/*發送起始條件的數據信號*/_Nop();
SCL=1;
_Nop();/*起始條件建立時間大于4.7us,延時*/_Nop();
_Nop();
_Nop();
_Nop();
SDA=0;/*發送起始信號*/_Nop();/*起始條件鎖定時間大于4μs*/_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;/*鉗住I2C總線,準備發送或接收數據*/_Nop();
_Nop();
}
voidStop_I2c()
{
SDA=0;/*發送結束條件的數據信號*/_Nop();/*發送結束條件的時鐘信號*/SCL=1;/*結束條件建立時間大于4μs*/_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1;/*發送I2C總線結束信號*/_Nop();
_Nop();
_Nop();
_Nop();
}
發送完起始信號就能傳輸數據了
下面是程序
voidSendByte(unsignedcharc)
{
unsignedcharBitCnt;//SCL=0; 起始信號最后是SCL=0;所以不用寫了for(BitCnt=0;BitCnt<8;BitCnt++)/*要傳送的數據長度為8位*/{if((c<
SDA=1;
}else{
SDA=0;
}
_Nop();
SCL=1;/*置時鐘線為高,通知被控器開始接收數據位*/_Nop();
_Nop();/*保證時鐘高電平周期大于4μs*/_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1;/*8位發送完后釋放數據線,準備接收應答位*/_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();if(SDA==1)/*判斷是否接收到應答信號*/ack=0;//沒有接收到應答信號elseack=1;//接收到應答信號SCL=0;
_Nop();
_Nop();
}
現在說一下接收,,,假設上面發送完0xaa以后,從機就返回給我們數據(11001100, 0xcc),當然SCL為低電平的時候模塊準備數據,,SCL為高電平的時候,從機就把數據放在了SDA上,這樣循環8次,一個8位數據就過來了
整體上應該是
Start_I2c();起始信號程序
SendByte(0xaa);
判斷下ack是不是等于1,應答了(是繼續執行還是停止看自己了)
Data = RcvByte();//接收數據
Ack_I2c(1);//發送非應答,就是SDA=1;,這個程序在下面
Stop_I2c();發送停止信號
接收程序如下
unsignedcharRcvByte()
{
unsignedcharretc;
unsignedcharBitCnt;
retc=0;
SDA=1;/*置數據線為輸入方式*/for(BitCnt=0;BitCnt<8;BitCnt++)
{
_Nop();
SCL=0;/*置時鐘線為低,準備接收數據位*/_Nop();
_Nop();/*時鐘低電平周期大于4.7μs*/_Nop();
_Nop();
_Nop();
SCL=1;/*置時鐘線為高使數據線上數據有效*/_Nop();
_Nop();
retc=retc<<1;if(SDA==1)retc=retc+1;/*讀數據位,接收的數據位放入retc中*/_Nop();
_Nop();
}
SCL=0;
_Nop();
_Nop();return(retc);
}
應答或者非應答程序如下
/********************************************************************
應答子函數
函數原型:? void Ack_I2c(bit a);
功能:? ? ? 主控器進行應答信號(可以是應答0或非應答1信號,由位參數a決定)
********************************************************************/voidAck_I2c(bit a)
{if(a==0)SDA=0;/*在此發出應答或非應答信號*/elseSDA=1;
_Nop();
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();/*時鐘低電平周期大于4μs*/_Nop();
_Nop();
_Nop();
SCL=0;/*清時鐘線,鉗住I2C總線以便繼續接收*/_Nop();
_Nop();
}
IIC其實就這樣了,主要看支持IIC通信的芯片的資料了,寫好這些就是IIC通用的了
資料鏈接
https://wenku.baidu.com/view/3fc8558002d276a200292ef9.html
現在看芯片資料如何寫進去一個字節
關于器件的地址
寫就是0xa0;;;;讀就是0xa1
所以寫函數就是
/**
* @brief? 向24C02寫數據
* @param? Data--數據
* @param? Address--地址
* @param? None
* @retval None
* @example
**/unsignedcharWriteData(unsignedcharData,unsignedcharAddress)
{
Start_I2c();
SendByte(0xa0);//最后一位為0寫入if(ack==0)return(0);
SendByte(Address);//發送地址if(ack==0)return(0);
SendByte(Data);//發送數據if(ack==0)return(0);
Stop_I2c();//結束總線return(1);
}
關于應答哈我的SendByte(unsigned char? c)函數里面發送完8位數據后就寫了應答,然后把應答標志給ack,,后面直接判斷的ack
現在想想為什么叫應答...直接說判斷從機正沒正確接收到數據就完了唄,就是把SDA拉高,然后把SCL拉高,等一會然后判斷SDA引腳有沒有被從機拉低,拉低了就說明好了......沒拉低從機可能接收的數據不正確
_Nop();
_Nop();
SDA=1;??????????????? /*8位發送完后釋放數據線,準備接收應答位*/
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)/*判斷是否接收到應答信號*/
ack=0;//沒有接收到應答信號
else
ack=1;//接收到應答信號
SCL=0;
_Nop();
_Nop();
}
再看從任意地址讀一個數據
注意哈第一個發送的器件地址是0xa0,后面的是0xa1
所以程序如下
/**
* @brief? 從24C02讀出數據
* @param? None
* @param? Address--地址
* @param? None
* @retval 讀到的數據
* @example
**/unsignedcharReadData(unsignedcharAddress)
{
unsignedcharData =0;
Start_I2c();
SendByte(0xa0);//最后一位為0if(ack==0)return(0);
SendByte(Address);//發送地址if(ack==0)return(0);
Start_I2c();
SendByte(0xa1);//最后一位為1if(ack==0)return(0);
Data=RcvByte();
Ack_I2c(1);//發送非就答位Stop_I2c();//結束總線return(Data);
}
現在看頁寫
把程序先放上,對了寫的時候的開始地址最好是0,8,16,24,32,40,68,,,,8的倍數,要不然數據可能有錯誤,當然我用的芯片頁寫最多一次能寫入8個字節.....感覺有點少哈......可以在現在的基礎上再做一個函數實現哈,,或者用寫單字節的for循環下....
/**
* @brief? 向24C02寫數據----頁寫,,,最多一次寫入8個字節,多了會覆蓋前面的
* @param? Data--數據
* @param? StartAddress--開始的地址--最大255
* @param? None
* @retval None
* @example
**/unsignedcharPageWrite(unsignedchar*Data,unsignedcharAddress,unsignedcharcnt)
{
Start_I2c();
SendByte(0xa0);//最后一位為0寫入if(ack==0)return(0);
SendByte(Address);//發送地址if(ack==0)return(0);while(cnt--)
{
SendByte(*Data++);//發送數據if(ack==0)return(0);
DelayMs(10);
}
Stop_I2c();//結束總線return(1);
}
現在看頁讀
看程序
/**
* @brief? 從24C02讀出數據----頁讀
* @param? Data--數據指針
* @param? StartAddress--開始的地址--最大255
* @param? None
* @retval None
* @example
**/unsignedcharPageRead(unsignedchar*Data,unsignedcharAddress,unsignedcharcnt)
{
Start_I2c();
SendByte(0xa0);//最后一位為0if(ack==0)return(0);
SendByte(Address);//發送要讀的地址if(ack==0)return(0);
Start_I2c();
SendByte(0xa1);//最后一位為1if(ack==0)return(0);while(cnt--)
{*Data? =RcvByte();
Data++;
Ack_I2c(0);//發送應答位DelayMs(10);
}
Ack_I2c(1);//發送非應答位Stop_I2c();//結束總線return(1);
}
說一下讀的時候最好開始讀取的地址是8的倍數,讀取的數據個數也是8的倍數,,,我測試的如果不是這樣有時候,第二次頁讀的時候就會讀錯........
這芯片和8干上了............
還有一個立即讀,,,看明白就行,就是立即返回當前讀地址加1后的那個數據
源碼鏈接
鏈接:http://pan.baidu.com/s/1i4M7BId%20密碼:r9ov