欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

標(biāo)題: 小白菜的IIC學(xué)習(xí)之路 [打印本頁]

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

程序下載: IIC.rar (94 KB, 下載次數(shù): 53)

作者: qq704661078    時(shí)間: 2015-1-31 21:25
樓下高手,謝謝分享!!
作者: fontex    時(shí)間: 2015-2-7 09:02
只能說謝謝。
作者: xsynet    時(shí)間: 2015-2-10 11:20
只能說謝謝了
作者: xsynet    時(shí)間: 2015-2-10 11:20
樓主強(qiáng)大,學(xué)習(xí)了
作者: 496111160    時(shí)間: 2015-2-21 16:47
只能說謝謝。
作者: 496111160    時(shí)間: 2015-2-21 16:53
學(xué)習(xí)一下,看看效果
作者: fuermalin    時(shí)間: 2015-2-23 19:50
謝謝樓主
學(xué)習(xí)一下
作者: 1101459202    時(shí)間: 2016-3-6 08:57

謝謝樓主
學(xué)習(xí)一下
作者: davedoff86    時(shí)間: 2016-9-29 13:16
好用, 學(xué)習(xí)了
作者: lyseg    時(shí)間: 2017-4-4 12:03
謝謝分享!!
作者: insurace    時(shí)間: 2019-7-16 10:03
感謝樓主
作者: jemery1030    時(shí)間: 2019-7-17 20:00
感謝再感謝。




歡迎光臨 (http://www.raoushi.com/bbs/) Powered by Discuz! X3.1