標籤:
這幾天一直在折騰nRF52832的硬體I2C,到了今天終於出現了成果,在此也印證了那句話:“耕耘就有收穫”
52832的硬體I2C雖然官方提供了demo,但是自己對I2C通訊理解的不夠深入,再一個52832的代碼也封裝的太深了,但是對介面函數沒有一個明確的解釋(也可以說是我英文太渣,別人寫了但是我沒看懂。。。),這樣對於首次接觸nRF產品的人就造成了一定的難度
根據我的開發過程,還是先說明一下I2C的一些相關知識,因為我是先調硬體I2C搞了半天不對頭,然後再開發類比I2C,類比的成功了再來調試的硬體TWI(也就是52832的硬體I2C,全稱估計是two wire interface)
I2C通訊需要兩條線:SDA,SCL。I2C通訊裝置有兩種角色:master和slave,一般使用者開發程式都是開發master端,然後去讀寫作為slave的外設,比如:eeprom,flash,sensor,display device。
在通訊過程中,有兩組特殊控制訊號:
start :scl為高電平時,sda由高電平變為低電平。
stop: scl為高電平時,sda由低電平變為高電平。
(注意在通訊過程中,SCL始終由master控制,這句話在做類比I2C的時候就顯得意義非凡了)
master做寫資料操作時,先是SCL和SDA都處於空閑狀態(兩者都是高電平),然後SDA由高變低(start訊號);變低後SCL拉低,這個時候SDA就可以變成想要的電平,高電平代表bit為1,低電平代表bit為0;電平穩定後拉高時鐘(拉高的目的是為了讓slave讀取資料,SCL為高時,SDA要保持不變,slave讀取SDA的電平);資料轉送完了後要結束:先拉高SCL,然後拉高SDA,然後拉低SDA,一個完整的stop訊號完成了。
讀資料操作時,start和stop這些時序一樣,但是主機要去解析slave傳來的資料(電平訊號), 拉低SCL,然後釋放SDA(即拉高SDA),一段延時之後拉高SCL再去讀取SDA電平訊號(既然是讀取電平,這裡必要設定為輸入引腳啦),如果是高電平則記下是一個H_bit,否則是L_bit,讀取到8位元據後如果還要繼續讀取則回複ACK,否則回複NCK。
ACK訊號是SCL拉低後給SDA一個低電平,然後拉高SCL;
ANK訊號是SCL拉低和給SDA一個高電平,然後拉高SCL;
下面以講解下master 和 slave傳輸時總體操作:
master向slave寫資料,一般slave端寫資料都要一個確定的寄存器地址,即你要往這個外設的哪個位置傳資料
以eeprom為例,先發送器件地址0xAE(8位元據,高7位是地址,LSB是資料轉送方向:0;0代表寫,1代表讀,可以當做out,in來理解這樣容易記住);
然後發送寄存器地址,然後發送資料;
時序上面可以是
start–slave_address_write–register_address–N*Send_data–stop
Send_data每發一個位元組,slave會回應一個“CK訊號”,如果是ACK則發送資料成功了,否則失敗
因為是連續的寫資料,因此中間可以沒有stop,start
讀資料操作,要先寫進一個寄存器地址,再傳遞一個讀命令
start–slave_address_write–register_address–start–slave_address_Read–N*Receive_data–stop
發送slave_address_Read前要先re_start,跟start訊號一樣
Receive_data 是接收資料,這時要去識別SDA電平並且解析資料,作出ACK回應,最後一個位元組接收完畢回複NCK訊號;然後stop。
下面說明nRF52832的硬體I2C代碼問題
nRF留出的API介面是
ret_code_t app_twi_perform(app_twi_t * \ p_app_twi,app_twi_transfer_t const * p_transfers,uint8_t number_of_transfers,void (* user_function)(void))
這個函數調用了app_twi_schedule函數,以此來匯入到隊列
ret_code_t app_twi_schedule(app_twi_t * p_app_twi, app_twi_transaction_t const * p_transaction)
想要調用app_twi_perform函數那麼得準備好參數
1、p_app_twi,這是在TWI傳輸隊列裡申請一個位置
英文原話是creating an instance of the TWI transaction manager.
2、p_transfers,這是包含了要傳輸的數組塊的一個數組
3、number_of_transfers,這個是你傳輸資料區塊的個數
4、user_function,一個使用者的回呼函數的函數指標,資料區塊傳輸完了API內部會調用這個user_function
解釋:上面說的資料區塊的意思就是一個完整的I2C操作需要用到的資訊:包含了slave地址,資料傳遞方向(讀或者寫),傳輸的資料data_buffer,資料長度length,有無結束標誌(意思就是這團資料轉送完了後是否結束通訊了)
在官方SDK裡面目錄
examples\peripheral\twi_master_using_app_twi裡開啟工程
首先初始化TWI
資料轉送
傳輸的內容
注意AT24C02_init_transfers是一個全域變數數組
也就是它的地址是在堆裡面的,不會自動釋放;這麼做的原因是這個數組的地址可能會被多次調用,而放在某個函數裡面會造成地址不同造成錯誤
demo裡面有解釋
// [these structures have to be "static" - they cannot be placed on stack // since the transaction is scheduled and these structures most likely // will be referred after this function returns] static app_twi_transfer_t const transfers[] = { AT24C02_READ(&AT24C02_first_page_addr,AT_buffer,5) };
注意這裡的AT_WRITE_NUMBER數組可以理解為一個資料緩衝區,可以通過改變這個數組的內容然後調用app_twi_perform來發資料出去(把const去掉)
讀資料類似,demo用的自訂傳輸函數
仿寫一個
nRF52832之硬體I2C