|
一,為了那“可惡”的目的
2011 年初,小白菜的工作比較清閑了,于是小白菜就開始IIC 學(xué)習(xí)之路。
之前在用 IIC 總線(口線模擬)時,總感覺IIC 移植時不夠簡介易懂,使用時,函數(shù)不
能夠適應(yīng)所有IIC 操作,于是小白菜想改寫一下IIC 總線操作,使之成為真正的萬能IIC 總
線驅(qū)動。于是小白菜找到目標(biāo)了:
一是編寫一個移植性極其好的 IIC 總線操作,只需要簡單的改動就能完成移植。
二是編寫兩個函數(shù),一個是向器件發(fā)送數(shù)據(jù)函數(shù),一個是從器件中讀取數(shù)據(jù)函數(shù)。并且
這兩個函數(shù)真正適應(yīng)所有不同的IIC 器伯的操作。
二,過程抽象
為了達(dá)到以上的目的,小白菜需要先對 IIC 進(jìn)行一次抽象。
1 單片機(jī)向器件發(fā)送數(shù)據(jù)時的抽象過程
(這里的抽象指的是抽象出不依賴具體器件的 IIC 操作)
發(fā)送時的流程:
(1) MCU 啟動總線
(2) 發(fā)送器件IIC 地址 接收 ACK 信號
(3) 發(fā)送寄存器地址1 接收 ACK 信號 [發(fā)送寄存器地址2 接收 ACK 信號]
有些器件可能沒有寄存器地址(小白菜還沒有遇到過),所以該步可能不需要。
(4) 發(fā)送數(shù)據(jù)1 接收 ACK 信號
發(fā)送數(shù)據(jù)2 接收 ACK 信號
發(fā)送數(shù)據(jù)3 接收 ACK 信號
……
(5) MCU 關(guān)閉總線,發(fā)送完成。
我們來具體的分析一下,
第(1)步啟動總線,
第(2)步發(fā)送 1B 數(shù)據(jù)(IIC 地址),接收一個ACK 信號
第(3)步發(fā)送 1B 數(shù)據(jù)(寄存器地址1),接收一個ACK 信號,發(fā)送1B 數(shù)據(jù)(寄存器地址2),
接收一個ACK 信號
第(4)步發(fā)送 1B 數(shù)據(jù)(數(shù)據(jù)1),接收一個ACK 信號,發(fā)送1B 數(shù)據(jù)(數(shù)據(jù)2),接收一個
ACK 信號……
第(5)步關(guān)閉總線
寫到這里,有些人可能看出來了,其實(shí)發(fā)送時不論數(shù)據(jù)或地址,都是一個相同過程,啟
動總線后,MCU 發(fā)送一個字節(jié),等一個ACK 信號,再發(fā)送一個字節(jié),等一個ACK 信號,
發(fā)送……,哎、發(fā)送完了,得了,關(guān)閉總線。
繼續(xù)進(jìn)行抽象,我們可以把第(2)、(3)、(4)步進(jìn)行合并,得到IIC 寫操作抽象:
第(1)步啟動總線,
第(2) (3) (4)步發(fā)送 1B 數(shù)據(jù),接收一個ACK 信號,直到發(fā)送完成
第(5)步關(guān)閉總線
到這里之后,似乎是大功告成了,但是小白菜一想,這么多數(shù)據(jù),參數(shù)肯定是用指針+
數(shù)據(jù)字節(jié)數(shù)還進(jìn)行傳遞,可是像讀寫EEPROM 這樣的器件,地址就丙三字節(jié),但一次寫入
的數(shù)據(jù)可能有很多個字節(jié),于是小白菜把(2) (3) 合在一起,把數(shù)據(jù)發(fā)送部分(4)單獨(dú)拿出來。
小白菜得到了最終的 IIC 寫操作的抽象
第(1)步啟動總線,
第(2) (3)步發(fā)送 1B 數(shù)據(jù),接收一個ACK 信號,直到發(fā)送完成(發(fā)送地址)
第(4)步發(fā)送 1B 數(shù)據(jù),接收一個ACK 信號,直到發(fā)送完成 (發(fā)送數(shù)據(jù))
第(5)步關(guān)閉總線
小白菜據(jù)此寫出了函數(shù)名及形參和流程圖(就是上面的抽象,所以嘛,就不寫了)。
extern uint8 IIC_MCU_Send_Str(uint8 *PAddr, uint8 AddrNum, uint8 *PDataAddr, uint16 DataNum)
*PAddr :I 第1 批發(fā)送的數(shù)據(jù)的首地址。這部分IC 地址以及子地址。PAddr[0]中存放IIC
地址,后面的存放子地址。
AddrNum :IIC 以及子地址的字節(jié)數(shù)。不可為0.
*PDataAddr :第2 批發(fā)送的數(shù)據(jù)的首地址。這部分是發(fā)送的數(shù)據(jù)。
DataNum :第2 批要發(fā)送的字節(jié)數(shù)(最大為65536 個字節(jié))。為0 時不發(fā)送這一部分。
2 單片機(jī)從有寄存器的IIC 器件讀取數(shù)據(jù)時的抽象過程:
發(fā)送時的流程:
(1) MCU 啟動總線
(2) 發(fā)送器件IIC 地址 接收 ACK 信號
(3) 發(fā)送寄存器地址1 接收 ACK 信號 [發(fā)送寄存器地址2 接收 ACK 信號]
有些器件可能沒有寄存器地址(小白菜還沒有遇到過),所以該步可能不需要。
(4) MCU 重新啟動總線
(5) 發(fā)送器件IIC 地址(最低位置1 以表明是讀操作) 接收 ACK 信號
(6) 接收數(shù)據(jù)1 發(fā)送 ACK 信號
接收數(shù)據(jù)2 發(fā)送 ACK 信號
接收數(shù)據(jù)3 發(fā)送 ACK 信號
……
(7) 接收最后一字節(jié)數(shù)據(jù) 發(fā)送非ACK 信號
(8) MCU 關(guān)閉總線,接收完成。
根據(jù)該流程,我們可以清楚地得到讀取時的抽象:
第(1)步 MCU 啟動總線
第(2) (3) 步發(fā)送 1B 數(shù)據(jù),接收一個ACK 信號,直到發(fā)送完成(發(fā)送地址)
第(4) 步 MCU 重新啟動總線
第(5) 步發(fā)送器件IIC 地址(最低位置1 以表明是讀操作) 接收 ACK 信號
第(6) 步接收前面的字節(jié),發(fā)送ACK 信號
第(7) 步接收最后一字節(jié)數(shù)據(jù),發(fā)送非ACK 信號
第(8) 步 MCU 關(guān)閉總線,接收完成。
雖然這里步驟多了,但是,函數(shù)參數(shù)也用不了幾個,首先要知道地址吧,還要知道數(shù)據(jù)讀出
來后存放在哪里吧,小白菜想了想,這個函數(shù)的參數(shù)和寫操作函數(shù)的參數(shù)一樣就行了,
于是小白菜據(jù)寫出了函數(shù)名及形參和流程圖(就是上面的抽象,所以嘛,你懂得)
extern uint8 IIC_MCU_Rcv_Str(uint8 *PAddr, uint8 AddrNum, uint8 *PDataAddr, uint16
DataNum)
*PAddr :發(fā)送的數(shù)據(jù)的首地址。這部分IC 地址以及子地址。PAddr[0]中存放IIC 地址,
后面的存放子地址。
AddrNum :IIC 以及子地址的字節(jié)數(shù)。不可為0.
*PDataAddr :存放所接收數(shù)據(jù)的首地址
DataNum :要接收的字節(jié)數(shù)。
三 奮筆疾書 + 代碼移植
小白菜開始了寫代碼了。因?yàn)槭切“撞寺铮砸婚_始也不知道哪些地方在移植時需要
修改,于是就開始先寫代碼,可能在寫的時候就能知道了。寫啊寫,寫啊寫,終于讓小白菜
寫完了。
寫著寫著還真讓小白菜找出了哪里需要移植了。口線需要更改吧,不同的單片機(jī),頭
文件不一樣吧,還得有延時函數(shù)需要改吧……
于是小白菜把在移植時需要更改的地方做了一個“表”,放在H 文件中的“移植修改”
部分。這樣,在修改時就可以只修改H 文件中的一個地方,就能快速的完成移植。
現(xiàn)在想想,小白菜有的時候也不是那么菜嘛(偷笑 ing)。
到現(xiàn)在為止,小白菜的目的已經(jīng)達(dá)到了。突然,后背一涼,心里一個想法冒出來了,
剛寫完的代碼還沒測試就飄飄然了!哎,被勝利沖昏了頭腦。于是小白菜自已測試了一番,
Bug 還真是有,改改更健康~~
四使用說明
4.1 移植修改
移植修改都在 H 文件中的移植修改部分。下面進(jìn)行具體說明。
//----------------------------------------------------------------------------//
// 編號:1
// 名稱:
// 功能:單片機(jī)寄存器頭文件,例如reg51.h
//----------------------------------------------------------------------------//
#include "ATT703x.H"
4.1.1 這部分是請您把使用的單片機(jī)的頭文件包含進(jìn)來。大蝦們用過MCU 多了,知道不同
的MCU,其寄存器定義是不一樣滴,不是所有的51 單片機(jī)都用Reg51.H 或Reg52.H 頭文
件的。
//----------------------------------------------------------------------------//
// 編號:2
// 名稱:SDA, SCL
// 功能:模擬I2C 數(shù)據(jù)傳送位
//----------------------------------------------------------------------------//
#if defined(IIC_IO_ENABLE)
sbit SDA = P0^0; // 模擬I2C 數(shù)據(jù)傳送位。
sbit SCL = P2^6; // 模擬I2C 時鐘控制位。
#endif
4.1.2 SDA 和SCL 口線定義。這里就是您用的口線,如果您告訴我您不知道怎么改,好吧,
你贏了……
//----------------------------------------------------------------------------//
// 編號:3
// 名稱:IIC_Delay_1US()
// 功能:精確的1 微秒延時函數(shù)。請根據(jù)您所用的單片機(jī)來正確設(shè)置。
// :如果您的系統(tǒng)中有精確的微妙級延時函數(shù),那么您可以直接使用。
// :例如,您的延時函數(shù)是Delay_1us(),那么您可以使用下句
// :#define IIC_Delay_1us() Delay_1us()
// :來實(shí)現(xiàn)延時。
//----------------------------------------------------------------------------//
#defineIIC_Delay_500ns() _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
#define IIC_Delay_1US() IIC_Delay_500ns();IIC_Delay_500ns();
4.1.3 軟件延時函數(shù),這里是標(biāo)準(zhǔn)IIC,不是快速IIC。1us 的延時怎么做呢?當(dāng)然是nop 函
數(shù)了。如果您不知道一個nop 的執(zhí)行時間,那么說明您需要好好看看手冊了。
4.1.4 好多單片機(jī)都需要設(shè)置時鐘,設(shè)置GPIO 狀態(tài),所以在使用IIC 之前,請一定確保MCU
先初始化完畢。
4.2 函數(shù)說明
4.2.1 MCU 向IIC 器件發(fā)送多字節(jié)數(shù)據(jù)函數(shù)
//----------------------------------------------------------------------------//
// MCU 向IIC 器件發(fā)送多字節(jié)數(shù)據(jù)函數(shù)(對外提供服務(wù))
//函數(shù)名稱:IIC_MCU_Send_Str
//函數(shù)功能:MCU 向IIC 從器件發(fā)送多字節(jié)數(shù)據(jù)。本函數(shù)是寫IIC 從器件的抽象函數(shù)。
//入口參數(shù):
// *PAddr: IIC 地址以及子地址。PAddr[0]中存放IIC 地址,后面的存放子地址。
// AddrNum : IIC 以及子地址的字節(jié)數(shù)。不可為0.
//
// *PDataAddr: 第 2 批發(fā)送的數(shù)據(jù)的首地址。這部分是發(fā)送的數(shù)據(jù)。
// DataNum : 第 2 批要發(fā)送的字節(jié)數(shù)(最大為65536 個字節(jié))。為0 時不發(fā)送這一部分。
//出口參數(shù):0 = 操作成功,1 = 操作出錯。
//重要說明:這是一個從啟動IIC 總線到發(fā)送數(shù)據(jù)再到最后結(jié)束總線為止的完整的發(fā)送過程。
// 數(shù)據(jù)發(fā)送的順序是先發(fā)送PAddr[0],最后發(fā)送PAddr[AddrNum - 1],然后發(fā)送
// PDataAddr[0],最后發(fā)送PDataAddr[DataNum - 1]。
// 一般地,PAddr 用于發(fā)送器件IIC 地址和子地址,PDataAddr 用于發(fā)送數(shù)據(jù)。
// 本函數(shù)對有無子地址的IIC 器件都適用。
//----------------------------------------------------------------------------//
extern uint8 IIC_MCU_Send_Str(uint8 *PAddr, uint8 AddrNum, uint8 *PDataAddr, uint16
DataNum)
應(yīng)用示例:
從 0x00 字節(jié)地址開始寫AT24C02,寫入10 個字節(jié)數(shù)(這里不考慮頁寫等待,因?yàn)楹?br />
IIC 寫無關(guān)),這10B 數(shù)據(jù)存放在unsigned char Buf[10]中,寫入時要求Buf[0]寫入0x00 字節(jié)
地址,Buf[1]寫入0x01 字節(jié)地址……。
A2、A1、A0 全接地。(有人說我沒說明WP 的接法……我只有一個問題,你是來砸場
子的么!!!)
首先組織 IIC 地址,設(shè)置一數(shù)組unsigned char Addr[2],其中Addr[0] = 0xA0,Addr[1] = 0x00;
Addr [0]中存放的是(二進(jìn)制表示) 1 0 1 0 A2 A1 A0 0(LSB)
Addr [1]中存放的是(二進(jìn)制表示) a7 a6 a5 a4 a3 a2 a1 a0(LSB)
調(diào)用時 IIC_MCU_Send_Str(Addr, 2, Buf, 10);
您還應(yīng)當(dāng)查看一下函數(shù)的返回值,是0 表示操作成功,否則操作失敗。
4.2.1 MCU 從有子地址的IIC 器件中接收多字節(jié)函數(shù)
//----------------------------------------------------------------------------//
// MCU 從有子地址的IIC 器件中接收多字節(jié)函數(shù)(對外接口)
//函數(shù)名稱:IICMCURcvStr
//函數(shù)功能:本函數(shù)用于有子地址的IIC 器件的讀操作。
//入口參數(shù):
// *PAddr: IIC 地址以及子地址。PAddr[0]中存放IIC 地址,后面的存放子地址。
// AddrNum : IIC 以及子地址的字節(jié)數(shù)。為0 時出錯.
// *PDataAddr:存放所接收數(shù)據(jù)的首地址
// DataNum : 要接收的字節(jié)數(shù)。合法值1-65535。為0 時出錯。
//
//出口參數(shù):0 = 操作成功,1 = 操作出錯。
//重要說明:
// 讀取的第一個數(shù)據(jù)存放在PDataAddr[0]中,第一個存放在PDataAddr[1]中……
// 有子地址的IIC 器件的讀操作是:
// MCU 先啟動總線,然后發(fā)送器件的IIC 地址和需要操作的子地址
//(這一部分就是*PAddr),之后重新啟動總線,再次發(fā)送器件的IIC 地址且最低位置1 以表
明是讀
// 操作,等待應(yīng)答后便開始接收數(shù)據(jù)(這一部分是*PDataAddr),最后關(guān)閉總線。
//----------------------------------------------------------------------------//
extern uint8 IIC_MCU_Rcv_Str(uint8 *PAddr, uint8 AddrNum, uint8 *PDataAddr, uint16
DataNum)
讀取的第一個數(shù)據(jù)存放在 PDataAddr[0]中,第一個存放在PDataAddr[1]中……
應(yīng)用示例:
從 0x00 字節(jié)地址開始讀AT24C02,讀10 個字節(jié)數(shù)并且存放在unsigned char Buf[10]中,
A2、A1、A0 全接地。(有人說我沒說明WP 的接法……還提這個問題!!)
首先組織 IIC 地址,設(shè)置一數(shù)組unsigned char Addr[2],其中Addr[0] = 0xA0,Addr[1] = 0x00;
Addr [0]中存放的是(二進(jìn)制表示) 1 0 1 0 A2 A1 A0 0(LSB)
Addr [1]中存放的是(二進(jìn)制表示) a7 a6 a5 a4 a3 a2 a1 a0(LSB)
調(diào)用時 IIC_MCU_Rcv_Str (Addr, 2, Buf, 10);
這樣,Buf[0]是讀取到的0x00 字節(jié)地址的數(shù)據(jù),Buf[1]是0x01 字節(jié)地址的數(shù)據(jù)……
您還應(yīng)當(dāng)查看一下函數(shù)的返回值,是 0 表示操作成功,否則操作失敗。
五 最后的廢話
好吧,有人會說這個實(shí)現(xiàn)的有點(diǎn)羅嗦,不如直接以IIC 地址,寄存器地址做參數(shù)來的方
便;雖然有時候我也這么覺得。
但是、但是、但是什么呢?下期見!
3htech
我是一顆小白菜
程序下載:
IIC.rar
(94 KB, 下載次數(shù): 53)
2014-12-19 02:37 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|
評分
-
查看全部評分
|