---恢復內容開始---
這篇文章是寫給一個學弟看的,關於IIC,關於24C02的單字節寫入\讀取..頁寫入和讀取,,學弟總是害怕協議,,,我總是對人家說,本來就這樣的,協議就是人家這樣規定的,,,如果你早生幾十年你也可能規定個IIC協議......
我的單片機和24C02通信,,,我的單片機就叫主機,,,24C02叫從機
先看IIC
IIC協議規定開始傳輸數據的時候要先發一個起始信號,,,目的應該是告訴從機要開始通信了,准備准備
終止信號就是拜拜啦,再見!
起始信號就是 在SCL在高電平期間SDA來一個下降沿,,終止信號就是在SCL在高電平期間SDA來一個上升沿(所以協議上才會說,在正常傳輸數據的時候,只有在SCL為低電平的時候,數據線SDA的高低電平狀態才允許改變,要不然豈不是和起始信號或者終止信號沖突了)
/******************************************************************* 起動總線函數 函數原型: void Start_I2c(); 功能: 啟動I2C總線,即發送I2C起始條件. ********************************************************************/ void Start_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(); }
void Stop_I2c() { SDA=0; /*發送結束條件的數據信號*/ _Nop(); /*發送結束條件的時鍾信號*/ SCL=1; /*結束條件建立時間大於4μs*/ _Nop(); _Nop(); _Nop(); _Nop(); _Nop(); SDA=1; /*發送I2C總線結束信號*/ _Nop(); _Nop(); _Nop(); _Nop(); }
發送完起始信號就能傳輸數據了
下面是程序
void SendByte(unsigned char c) { unsigned char BitCnt; //SCL=0; 起始信號最后是SCL=0;所以不用寫了 for(BitCnt=0;BitCnt<8;BitCnt++) /*要傳送的數據長度為8位*/ { if((c<<BitCnt)&0x80)/*判斷發送位*/ { 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;//沒有接收到應答信號 else ack=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();發送停止信號
接收程序如下
unsigned char RcvByte() { unsigned char retc; unsigned char BitCnt; 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決定) ********************************************************************/ void Ack_I2c(bit a) { if(a==0)SDA=0; /*在此發出應答或非應答信號 */ else SDA=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 **/ unsigned char WriteData(unsigned char Data,unsigned char Address) { 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 **/ unsigned char ReadData(unsigned char Address) { unsigned char Data =0; Start_I2c(); SendByte(0xa0);//最后一位為0 if(ack==0)return(0); SendByte(Address); //發送地址 if(ack==0)return(0); Start_I2c(); SendByte(0xa1);//最后一位為1 if(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 **/ unsigned char PageWrite(unsigned char *Data,unsigned char Address,unsigned char cnt) { 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 **/ unsigned char PageRead(unsigned char *Data,unsigned char Address,unsigned char cnt) { Start_I2c(); SendByte(0xa0);//最后一位為0 if(ack==0)return(0); SendByte(Address); //發送要讀的地址 if(ack==0)return(0); Start_I2c(); SendByte(0xa1);//最后一位為1 if(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