日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

外设驱动库开发笔记8:GPIO模拟I2C驱动

發布時間:2024/7/23 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 外设驱动库开发笔记8:GPIO模拟I2C驱动 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

I2C總線簡單方便,是我們經常使用的一種總線。但有時候我們的MCU沒有足夠多的I2C控制器來實現我們的應用,所幸我可以使用普通的GPIO引腳來模擬低速的I2C總線通信。這一節我們就來實現使用軟件通過普通GPIO操作I2C設備的驅動。

1、功能概述

I2C總線使用兩條線:串行數據(SDA)和串行時鐘(SCL)。所有I2C主設備和從設備僅與這兩條線連接。每個設備可以是發射器,接收器或兩者。有些設備是主設備,它們生成總線時鐘并在總線上啟動通信,其他設備是從設備并響應總線上的命令。為了與特定設備通信,每個從設備必須具有總線上唯一的地址。I2C主設備(通常是微控制器)不需要地址,因為沒有其他設備向主設備發送命令。總線設備連接示意圖如下:

1.1、I2C的傳輸過程

I2C總線有標準、快速和高速多種速度模式;也有7位地址和10位地址多種地址格式,但不管什么樣的模式其數據傳輸格式都可以劃分為3個階段:起始階段、數據傳輸階段和終止階段。如下圖:

1.1.1、起始階段

I2C總線不工作的情況下,SDA(數據線)SCL(時鐘線)上的信號均為高電平。如果此時主機需要發起新的通信請求,那么需要首先通過SDASCL發出起始標志。當SCL為高電平時,SDA電平從高變低,這一變化表示完成了通信的起始條件。

在起始條件和數據通信之間,通常會有延時要求,具體的指標會在設備廠商的規格說明書中給出。

1.1.2、數據傳輸階段

I2C總線的數據通信是以字節(8)作為基本單位在SDA上進行串行傳輸的。一個字節的傳輸需要9個時鐘周期。其中,字節中每一位的傳輸都需要一個時鐘周期,當新的SCL到來時,SCL為低電平,此時數據發送方根據當前傳輸的數據位控制SDA的電平信號。如果傳輸的數據位為"1",就將SDA電平拉高;如果傳輸的數據位為"0",就將SDA的電平拉低。當SDA上的數據準備好之后,SCL由低變高,此時數據接收方將會在下一次SCL信號變低之前完成數據的接收。當8位數據發送完成后,數據接收方需要一個時鐘周期以使用SDA發送ACK信號,表明數據是否接收成功。當ACK信號為"0"時,說明接收成功;為"1"時,說明接收失敗。每個字節的傳輸都是由高位(MSB)到低位(LSB)依次進行傳輸。

I2C總線協議中規定,數據通信的第一個字節必須由主機發出,內容為此次通信的目標設備地址和數據通信的方向(/)。在這個字節中,第17位為目標設備地址,第0位為通信方向,當第0位為"1"時表示讀,即后續的數據由目標設備發出主機進行接收;當第0位為"0"時表示寫,即后續的數據由主機發出目標設備進行接收。在數據通信過程中,總是由數據接收方發出ACK信號。

1.1.3、終止階段

當主機完成數據通信,并終止本次傳輸時會發出終止信號。當SCL 是高電平時,SDA電平由低變高,這個變化意味著傳輸終止。

1.2I2C的傳輸格式

根據I2C總線的技術標準,I2C總線上的數據傳輸方式有3種:主站向從站寫數據方式;主站從從站讀數據方式;讀寫組合的方式。下面將就這幾種方式簡單說明。

1.2.1、寫數據格式

主站向從站寫數據方式是主棧發送數據給從站。傳輸方向沒有改變,從站接收主站發過來的每一個字節。具體格式如下圖:

1.2.2、讀數據格式

主站從從站讀數據方式,主站在發送第一個字節之后,立即接收從站數據。也就是說在第一次確認的時刻,主發送器變成了主接收器,從屬接收器變成了從屬發送器。第一個確認仍然由從站生成。主站則生成后續的確認。停止條件由主主站生成,它在停止條件之前發送一個非確認應答。具體格式如下圖:

1.2.3、讀寫組合格式

組合格式就是讀和寫是接連完成的。在傳輸中改變方向時,啟動條件和從地址都要重復,但R/W位要倒過來。如果主接收器發送一個重復啟動條件,它在重復啟動條件之前發送一個非確認應答,但不會有停止條件。具體格式如下圖:

2、驅動設計與實現

我們已經了解了I2C協議的基本內容,接下來我們需要考慮如何實現這一協議。實現了這一協議也就完成通過GPIO模擬I2C的驅動。

2.1、對象定義

我們們依然采用基于對象的操作來實現。所以在使用對象之前,我們需要得到對象。接下來我們就考慮GPIO模擬I2C的對象問題。

2.1.1、對象的抽象

一般的,作為一個對象肯定包括屬性和操作。所以我們考慮GPIO模擬I2C的對象也要從這兩方面來進行。

首先來考慮GPIO模擬I2C對象的屬性。作為屬性應該是必要的且能標識對象特點的參數。我們模擬的I2C其實是主站,作為主站沒有地址,所以地址不需要作為屬性。但通訊速度卻是主站需要控制的,所以我們將速度設置為GPIO模擬I2C的一個屬性。除此之外,作為主站沒有必須要記錄的參數了。

還需要考慮GPIO模擬I2C對象的操作。既然是使用GPIO模擬I2C,那么I2C的兩根總線SCLSDA都需要主站操作GPIO來實現,所以控制SCL和控制SDA的行為都是對象的操作。除了控制總線我們還需要從總線讀取數據,所以從SDA讀取數據也是對象的一個操作。還有如延時等操作與具體的平臺關系很大,我們也將其作為操作以便在具體的平臺初始化。

根據上述的分析,我們可以抽象得到GPIO模擬I2C的對象類型如下:

typedef struct SimuI2CObject{uint32_t period;????????????????????????????//確定速度為大于0K小于等于400K的整數,默認為100Kvoid (*SetSCLPin)(SimuI2CPinValue op);??????//設置SCL引腳void (*SetSDAPin)(SimuI2CPinValue op);??????//設置SDA引腳uint8_t (*ReadSDAPin)(void);????????????????//讀取SDA引腳位void (*Delayus)(volatile uint32_t period);??//速度延時函數 }SimuI2CObjectType;

2.1.2、對象的初始化

我們已經得到了GPIO模擬I2C的對象,但對象必須要初始化之后才可以操作,所以這里就需要考慮如何對對象進行初始化。一般來說,初始化函數需要處理幾個方面的問題。一是檢查輸入參數是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據此我們設計GPIO模擬I2C對象的初始化函數如下:

/* GPIO模擬I2C通訊初始化 */ void SimuI2CInitialization(SimuI2CObjectType *simuI2CInstance,uint32_t speed,SimuI2CSetPin setSCL,SimuI2CSetPin setSDA,SimuI2CReadSDAPin readSDA,SimuI2CDelayus delayus) {if((simuI2CInstance==NULL)||(setSCL==NULL)||(setSDA==NULL)||(readSDA==NULL)||(delayus==NULL)){return;}simuI2CInstance->SetSCLPin=setSCL;simuI2CInstance->SetSDAPin=setSDA;simuI2CInstance->ReadSDAPin=readSDA;simuI2CInstance->Delayus=delayus;/*初始化速度,默認100K*/if((speed>0)&&(speed<=400)){simuI2CInstance->period=500/speed;}else{simuI2CInstance->period=5;}/*拉高總線,使處于空閑狀態*/simuI2CInstance->SetSDAPin(Set);simuI2CInstance->SetSCLPin(Set); }

2.2、對象操作

我們已經定義了對象類型,也實現了對象的初始化函數,接下來我們就需要考慮封裝對象的操作了。根據前面我們對I2C協議的了解,需要實現的操作主要有:向從站寫數據、從從站讀數據、先向從站寫而后接著讀數據以及基于這三種模式的組合操作。

2.2.1、向從站寫數據操作

向從站寫數據包括向從站寫命令、地址以及設定數據等。如向一個或多個存儲地址寫數據,需要先寫存儲起始地址再寫需要保存的數據。所有的數據都是從主站發往從站,包括啟動通訊、下發數據、停止通訊這一過程。具體的實現如下:

/* 通過模擬I2C向從站寫數據 */ SimuI2CStatus WriteDataBySimuI2C(SimuI2CObjectType *simuI2CInstance,uint8_t deviceAddress,uint8_t *wData,uint16_t wSize) {//啟動通訊SimuI2CStart(simuI2CInstance);//發送從站地址(寫)SendByteBySimuI2C(simuI2CInstance,deviceAddress);if(SimuI2CWaitAck(simuI2CInstance,5000)){return I2C_ERROR;}while(wSize--){SendByteBySimuI2C(simuI2CInstance,*wData);if(SimuI2CWaitAck(simuI2CInstance,5000)){return I2C_ERROR;}wData++;simuI2CInstance->Delayus(10);}SimuI2CStop(simuI2CInstance);return I2C_OK; }

2.2.2、自從站讀數據操作

讀從站數據操作其實就是先向從站發送站地址(讀),然后接收數據。一般存儲器不會使用到這種模式,而對于向一些設備獲取數據會有這種模式,如MS5803壓力觸感器。其過程是先啟動通訊,再從主站發送包含讀的從站地址,然后主站接收自從站返回的數據,然后停止通訊。具體的實現過程如下:

/* 通過模擬I2C自從站讀數據 */ SimuI2CStatus ReadDataBySimuI2C(SimuI2CObjectType *simuI2CInstance,uint8_t deviceAddress,uint8_t *rData, uint16_t rSize) {//啟動通訊SimuI2CStart(simuI2CInstance);//發送從站地址(讀)SendByteBySimuI2C(simuI2CInstance,deviceAddress+1);if(SimuI2CWaitAck(simuI2CInstance,5000)){return I2C_ERROR;}simuI2CInstance->Delayus(1000);while(rSize--){*rData=RecieveByteBySimuI2C(simuI2CInstance);rData++;if(rSize==0){IIC_NAck(simuI2CInstance);}else{IIC_Ack(simuI2CInstance);simuI2CInstance->Delayus(1000);}}//結束通訊SimuI2CStop(simuI2CInstance);return I2C_OK; }

2.2.3、先寫后讀組合操作

對于組合操作則是寫數據并讀數據連續進行。這就像從某一存儲地址讀數據一樣,先發送要讀的其實地址,然后接收讀出來的數據。其一般過程是:先啟動通訊,然后寫數據,接著重啟通訊,然后讀數據,最后停止通訊。具體的實現過程如下:

/* 通過模擬I2C實現對從站先寫數據緊接讀數據組合操作 */ SimuI2CStatus WriteReadDataBySimuI2C(SimuI2CObjectType *simuI2CInstance,uint8_t deviceAddress, uint8_t *wData,uint16_t wSize,uint8_t *rData, uint16_t rSize) {//啟動通訊SimuI2CStart(simuI2CInstance);//發送從站地址(寫)SendByteBySimuI2C(simuI2CInstance,deviceAddress);if(SimuI2CWaitAck(simuI2CInstance,5000)){return I2C_ERROR;}while(wSize--){SendByteBySimuI2C(simuI2CInstance,*wData);if(SimuI2CWaitAck(simuI2CInstance,5000)){return I2C_ERROR;}wData++;simuI2CInstance->Delayus(10);}//再啟動SimuI2CStart(simuI2CInstance);//發送從站地址(讀)SendByteBySimuI2C(simuI2CInstance,deviceAddress+1);if(SimuI2CWaitAck(simuI2CInstance,5000)){return I2C_ERROR;}while(rSize--){*rData=RecieveByteBySimuI2C(simuI2CInstance);rData++;if(rSize==0){IIC_NAck(simuI2CInstance);}else{IIC_Ack(simuI2CInstance);}}//結束通訊SimuI2CStop(simuI2CInstance);return I2C_OK; }

3、驅動的使用

前面已經設計并實現了GPIO模擬I2C通訊的驅動,下面我們還需要使用此驅動設計一個簡單的應用以驗證驅動設計的是否合理。

3.1、聲明并初始化對象

在應用一個對象前,我們需要先得到這個對象。前面我們已經抽象了GPIO模擬I2C通訊的對象類型,這里我們將使用此對象類型聲明一個對象變量。具體形式如下:

SimuI2CObjectType simuI2C;

聲明了這個對象變量并不能立即使用,我們還需要使用驅動中定義的初始化函數對這個變量進行初始化。這個初始化函數所需要的輸入參數如下:

SimuI2CObjectType *simuI2CInstance

uint32_t speed

SimuI2CSetPin setSCL

SimuI2CSetPin setSDA

SimuI2CReadSDAPin readSDA

SimuI2CDelayus delayus

對于這些參數,對象變量我們已經定義了。而通訊速度根據實際情況選擇就好了,最大不超過500K,默認是100K。主要的是我們需要定義幾個函數,并將函數指針作為參數。這幾個函數的類型如下:

typedef void (*SimuI2CSetPin)(SimuI2CPinValue op);??????? //設置SDA引腳

typedef uint8_t (*SimuI2CReadSDAPin)(void);????????????????? //讀取SDA引腳位

typedef void (*SimuI2CDelayus)(volatile uint32_t period);??? //速度延時函數

對于這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬件平臺有關系。具體函數定義如下:

//設置SCL引腳 static void SetSCLPin(SimuI2CPinValue op) {if(op==Set){HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);} }//設置SDA引腳 static void SetSDAPin(SimuI2CPinValue op) {if(op==Set){HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);} }//讀取SDA引腳位 static uint8_t ReadSDAPin(void) {return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7); }

對于延時函數我們可以采用各種方法實現。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數。于是我們可以調用初始化函數如下:

SimuI2CInitialization(&simuI2C100SetSCLPinSetSDAPinReadSDAPinHAL_Delay);

這里我們將其設為100I2C通訊接口。

3.2、基于對象進行操作

我們定義了對象變量并使用初始化函數給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數據。我們在驅動中已經封裝了讀從站、寫從站以及讀寫混合操作,接下來我們使用這一驅動開發我們的應用實例。

這里我們考慮使用驅動讀寫一個I2C接口的存儲器,我們向某一個地址寫入數據和讀出數據,我們假定存儲器較小地址是8位的。

//從Memery中讀取數據 void ReadDataFromMem(uint8_t deviceAddress, uint8_t memAdd,uint8_t *rData, uint16_t rSize) {????WriteReadDataBySimuI2C(&simuI2C,deviceAddress,&memAdd,1,rData,rSize); }//向Memery中寫數據 void WriteDataToMem(uint8_t deviceAddress,uint8_t memAdd,uint8_t *wData,uint16_t wSize) {uint8_t data[10];uint16_t size=0;data[size++]=memAdd;for(int i=0;i<wSize;i++){data[size++]=wData[i];}WriteDataBySimuI2C(&simuI2C,deviceAddress,wData,size); }

在這一例中,我們實現了對8位地址的存儲器的數據寫入和讀出操作,根據封裝的驅動函數很容易實現。

4、應用總結

我們使用GPIO模擬的I2C協議在STM32平臺上與多個設備進行通訊,如SHT20溫濕度傳感器、TSEV01CL55紅外溫度傳感器、MLX90614紅外溫度傳感器等,等到的結果非常好,即使在長達1米的通訊線路上都沒有問題。

使用本驅動是需要注意一點,因為在I2C總線中SDA是雙向的,所以在模擬式需要將模擬SDA的引腳配置為開漏模式,否則就需要控制其方向。

說到I2C總線有幾個相關的總線不能不提,系統管理總線SMBus、電源系統管理總線PMBus以及TWI Bus。這些總線與I2C總線有很多的共同點,在通訊速率一致的情況下是可以通用的。

完整的源代碼可在GitHub下載:https://github.com/foxclever/ExPeriphDriver

歡迎關注:

總結

以上是生活随笔為你收集整理的外设驱动库开发笔记8:GPIO模拟I2C驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲天堂欧美 | 国产激情视频一区二区三区 | 香蕉久久网 | 超碰精品在线观看 | 欧美精品免费在线 | 日本一区二区三区免费看 | 日本www网站 | 日韩精品一区二区三区中文在线 | 日韩一级免费毛片 | 午夜精品久久久久久久99 | 国产精品爽爽 | 中文字幕第22页 | 成年免费视频黄网站在线观看 | 亚洲美女在线播放 | 国产女人呻吟高潮抽搐声 | 日韩成人无码影院 | 色视频免费看 | 日日夜夜免费视频 | 国产精品久久久久久久免费 | 夜夜爽夜夜叫夜夜高潮漏水 | 久久久久久久久网站 | 黄色高清视频在线观看 | 亚洲欧美国产一区二区三区 | 久久精品免费网站 | 好色艳妇小说 | 91pron在线 | 裸尼姑熟蜜桃 | 欧美成人精品一区 | 最近2019中文字幕大全第二页 | 快播视频在线观看 | 国产精品久久av无码一区二区 | 性欧美久久久 | 亚洲国产精品区 | 好吊色青青草 | 免费黄色短片 | 国内9l自拍 | 欧美怡红院视频一区二区三区 | 黑丝美女一区二区 | 51精品国产人成在线观看 | 欧美黑人精品一区二区不卡 | 欧美第五页 | 国产天堂在线 | 亚洲女人天堂成人av在线 | 国产精品高潮呻吟久久久久久 | 狠狠艹 | 打屁股调教视频 | 欧美a级在线 | 熟女少妇内射日韩亚洲 | av大西瓜 | 亚洲精品无码不卡在线播he | 中文字幕欲求不满 | 黄色另类小说 | 日本亲子乱子伦xxxx50路 | 日韩精品欧美 | 亚洲第一视频在线观看 | 国产精品77777 | 人妻激情文学 | 天堂网2020| 最近最新mv字幕观看 | 中文字幕+乱码+中文字幕明步 | 日本夫妻性生活视频 | 成人久久久精品乱码一区二区三区 | 日韩欧美在线观看视频 | 深夜福利视频网站 | 久久久亚洲成人 | 影音先锋资源av | 欧美色综合天天久久综合精品 | 国产美女免费无遮挡 | 99精品视频在线免费观看 | 久久久丁香 | 性感少妇av| 香蕉视频链接 | 欧洲在线观看 | 性欧美又大又长又硬 | av高清在线 | 免费av网址在线观看 | 国产精品久久久久久久久久久久久 | 日本久久一级片 | 黄页视频在线免费观看 | 亚洲色成人网站www永久四虎 | 久久久久久久久久网站 | 欧美日韩六区 | 精品熟女一区 | 摸大乳喷奶水www视频 | 国产又粗又猛又爽又黄的视频一 | 99国产揄拍国产精品 | 爱爱综合网| 韩国美女啪啪 | 一级特黄录像免费看 | va视频在线| 国产一级理论 | 欧美xxxx888| 6699av| 韩国黄色片网站 | 无码aⅴ精品一区二区三区 精品久久在线 | 丝袜美腿一区二区三区 | 亚洲熟妇一区二区 | 国产无精乱码一区二区三区 | 久草99 |