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