周立功教授新書《面向AMetal框架與接口的編程(上)》,對AMetal框架進行了詳細介紹,通過閱讀這本書,你可以學(xué)到高度復(fù)用的軟件設(shè)計原則和面向接口編程的開發(fā)思想,聚焦自己的“核心域”,改變自己的編程思維,實現(xiàn)企業(yè)和個人的共同進步。經(jīng)周立功教授授權(quán),即日起,致遠電子公眾號將對該書內(nèi)容進行連載,愿共勉之。
第五章為深入淺出AMetal,本文內(nèi)容為5.5 SPI 總線和5.6 I2C 總線。
5.5 SPI 總線
>>> 5.5.1 初始化
在使用SPI 通用接口前,必須先完成SPI 的初始化,以獲取標(biāo)準(zhǔn)的SPI 實例句柄。LPC82x支持SPI 功能的外設(shè)有SPI0 和SPI1,為方便用戶使用,AMetal 提供了與各外設(shè)對應(yīng)的實例初始化函數(shù),詳見表5.10。
表5.10 SPI 實例初始化函數(shù)(am_lpc82x_inst_init.h)
這些函數(shù)的返回值均為am_spi_handle_t 類型的SPI 實例句柄,該句柄將作為SPI 通用接口中handle 參數(shù)的實參。類型am_spi_handle_t(am_spi.h)定義如下:
因為函數(shù)返回的SPI 實例句柄僅作為參數(shù)傳遞給SPI 通用接口,不需要對該句柄做其它任何操作,因此完全不需要了解該類型。注意,若函數(shù)返回的實例句柄的值為NULL,則表明初始化失敗,不能使用該實例句柄。
如需使用SPI0,則直接調(diào)用SPI0 實例初始化函數(shù),即可獲取對應(yīng)的實例句柄:
>>>5.5.2 接口函數(shù)
MCU 的SPI 主要用于主從機的通信,AMetal 提供了8 個接口函數(shù),詳見表5.11。
表5.11 SPI 標(biāo)準(zhǔn)接口函數(shù)
1. 從機實例初始化
對于用戶來說,使用SPI 往往是直接操作一個從機器件,MCU 作為SPI 主機,為了與從機器件通信,需要知道從機器件的相關(guān)信息,比如,SPI 模式、SPI 速率、數(shù)據(jù)位寬等。這就需要定義一個與從機器件對應(yīng)的實例(從機實例),并使用相關(guān)信息完成對從機實例的初始化。其函數(shù)原型為:
p_dev 是指向SPI 從機實例描述符的指針,am_spi_device_t 在am_spi.h 文件中定義:
該類型用于定義從機實例,用戶無需知道其定義的具體內(nèi)容,只需要使用該類型定義一個從機實例。即:
mode 指定使用的模式,SPI 協(xié)議定義了4 種模式,詳見表5.12。各種模式的主要區(qū)別在于空閑時鐘極性(CPOL)和時鐘相位選擇(CPHA)的不同。CPOL 和CPHA 均有兩種選擇,因此兩兩組合可以構(gòu)成4 種不同的模式,即模式0~3。當(dāng)CPOL 為0 時,表示時鐘空閑時,時鐘線為低電平,反之,空閑時為高電平;當(dāng)CPHA 為0 時,表示數(shù)據(jù)在第1 個時鐘邊沿采樣,反之,則表示數(shù)據(jù)在第2 個時鐘邊沿采樣。
表5.12 SPI 常用模式標(biāo)志
cs_pin 和pfunc_cs 均與片選引腳相關(guān)。pfunc_cs 是指向自定義片選控制函數(shù)的指針,若pfunc_cs 的值為NULL,驅(qū)動將自動控制由cs_pin 指定的引腳實現(xiàn)片選控制;若pfunc_cs 的值不為NULL,指向了有效的自定義片選控制函數(shù),則cs_pin 不再被使用,片選控制將完全由應(yīng)用實現(xiàn)。當(dāng)需要片選引腳有效時,驅(qū)動將自動調(diào)用pfunc_cs指向的函數(shù),并傳遞state 的值為1。當(dāng)需要片選引腳無效時,也會調(diào)用pfunc_cs 指向的函數(shù),并傳遞state的值為0。一般情況下,片選引腳自動控制即可,即設(shè)置pfunc_cs 的值為NULL,cs_pin 為片選引腳,如PIO0_13。使用范例詳見程序清單5.60。
程序清單5.60 am_spi_mkdev()范例程序
2. 設(shè)置從機實例
設(shè)置SPI 從機實例時,會檢查MCU 的SPI 主機是否支持從機實例的相關(guān)參數(shù)和模式。如果不能支持,則設(shè)置失敗,說明該從機不能使用。其函數(shù)原型為:
其中的p_dev 是指向SPI 從機實例描述符的指針,如果返回AM_OK,說明設(shè)置成功;如果返回-AM_ENOTSUP,說明設(shè)置失敗,不支持的位寬、模式等,詳見程序清單5.61。
程序清單5.61 am_spi_setup()范例程序
3. 傳輸初始化
在AMetal 中,將收發(fā)一次數(shù)據(jù)的過程抽象為一個“傳輸”的概念,要完成一次數(shù)據(jù)傳輸,首先就需要初始化一個傳輸結(jié)構(gòu)體,指定該次數(shù)據(jù)傳輸?shù)南嚓P(guān)信息。其函數(shù)原型為:
其中,p_trans 為指向SPI 傳輸結(jié)構(gòu)體的指針,am_spi_transfer_t 類型是在am_spi.h 中定義的。即:
在實際使用時,只需要定義一個該類型的傳輸結(jié)構(gòu)體即可。比如:
表5.13 傳輸特殊控制標(biāo)志
因為SPI 是全雙工通信協(xié)議,所以單次傳輸過程中同時包含了數(shù)據(jù)的發(fā)送和接收。函數(shù)的參數(shù)中,p_txbuf 指定了發(fā)送數(shù)據(jù)的緩沖區(qū),p_rxbuf 指定了接收數(shù)據(jù)的緩沖區(qū),nbytes 指定了傳輸?shù)淖止?jié)數(shù)。特別地,有時候可能只希望單向傳輸數(shù)據(jù),若只發(fā)送數(shù)據(jù),則可以設(shè)置p_rxbuf 為NULL;若只接收數(shù)據(jù),則可以設(shè)置p_txbuf 為NULL。
當(dāng)傳輸正常進行時,片選會置為有效狀態(tài),cs_change 的值將影響片選何時被置為無效狀態(tài)。若cs_change 的值為0,表明不影響片選,此時,僅當(dāng)該次傳輸是消息(多次傳輸組成一個消息,消息的概念后文會介紹)的最后一次傳輸時,片選才會被置為無效狀態(tài)。若cs_change 的值為1,表明影響片選,此時,若該次傳輸不是消息的最后一次傳輸,則在本次傳輸結(jié)束后會立即將片選設(shè)置為無效狀態(tài),若該次傳輸是消息的最后一次傳輸,則不會立即設(shè)置片選無效,而是保持有效直到下一個消息的第一次傳輸開始,詳見程序清單5.62。
程序清單5.62 am_spi_mktrans()范例程序
4. 消息初始化
一般來說,與實際的SPI 器件通信時,往往采用的是“命令”+“數(shù)據(jù)”的格式,這就需要兩次傳輸:一次傳輸命令,一次傳輸數(shù)據(jù)。為此,AMetal 提出了“消息”的概念,一個消息的處理即為一次有實際意義的SPI 通信,其間可能包含一次或多次傳輸。
一次消息處理中可能包含很多次的傳輸,耗時可能較長,為避免阻塞,消息的處理采用異步方式。這就要求指定一個完成回調(diào)函數(shù),當(dāng)消息處理完畢時,自動調(diào)用回調(diào)函數(shù)以通知用戶消息處理完畢。回調(diào)函數(shù)的指定在初始化函數(shù)中完成,初始化函數(shù)的原型為:
其中的p_msg 為指向SPI 消息結(jié)構(gòu)體的指針,am_spi_message_t 類型是在am_spi.h 中定義的。即:
實際使用時,僅需使用該類型定義一個消息結(jié)構(gòu)體。即:
pfn_callback 指向的是消息處理完成回調(diào)函數(shù),當(dāng)消息處理完畢時,將調(diào)用指針指向的函數(shù)。其類型am_pfnvoid_t 在am_types.h 中定義的。即:
由此可見,函數(shù)指針指向的是參數(shù)為void *類型的無返回值函數(shù)。驅(qū)動調(diào)用回調(diào)函數(shù)時,傳遞給該回調(diào)函數(shù)的void*類型的參數(shù)即為p_arg 的設(shè)定值,詳見程序清單5.63。
程序清單5.63 am_spi_msg_init()范例程序
5. 在消息中添加傳輸
一次消息處理中包含單次或多次的傳輸,在消息處理前,需要將消息和相關(guān)的傳輸關(guān)聯(lián)起來。該函數(shù)用于添加一個傳輸至消息中,其函數(shù)原型為:
其中,p_msg 指向am_spi_msg_init()初始化的消息,p_trans 指向am_spi_mktrans()初始化的傳輸。可以多次使用該函數(shù)以便向一個消息中添加多個傳輸,由于每次都將傳輸添加在消息的尾部,因此先添加的傳輸先處理,后添加的傳輸后處理,詳見程序清單5.64。
程序清單5.64 am_spi_trans_add_tail()范例程序
6. 啟動SPI 消息處理
該函數(shù)用于啟動消息的處理,其函數(shù)原型為:
其中,p_dev 為指向SPI 從機實例描述符的指針,用于指定本次消息處理中收發(fā)數(shù)據(jù)的從機對象;p_msg 為指向本次需要處理的SPI 消息結(jié)構(gòu)體的指針。如果返回AM_OK,說明啟動成功,當(dāng)消息中所有的傳輸依次處理完畢時,將調(diào)用初始化消息時指定的處理完畢回調(diào)函數(shù);如果返回-AM_EINVAL,說明因參數(shù)錯誤啟動失敗,詳見程序清單5.65。
程序清單5.65 am_spi_msg_start ()范例程序
在這里,定義了一個初始值為0 的變量complete_flag,在初始化消息時,將它的地址作為回調(diào)函數(shù)的參數(shù),因此在回調(diào)函數(shù)中,p_arg 就是指向complete_flag 的指針,可以通過該指針將complete_flag 的值修改為1,如果檢測complete_flag 的值為1,表明消息處理完成。
7. 先寫后讀
SPI 傳輸和SPI 消息實現(xiàn)數(shù)據(jù)的發(fā)送和接收使得SPI 的使用非常靈活,可以支持豐富的SPI 從機器件。但正因為其靈活性,使得接口較多,使用起來較為繁瑣。對于絕大部分SPI從機器件,并不需要如此靈活,只需要實現(xiàn)簡單的數(shù)據(jù)發(fā)送和接收就可以了,基于此,AMetal提供了兩種十分常用的情形:寫入一段數(shù)據(jù)后讀取一段數(shù)據(jù)(先寫后讀);寫入一段數(shù)據(jù)后再寫入一段數(shù)據(jù)(連續(xù)兩次寫)。
先寫后讀即是主機先發(fā)送數(shù)據(jù)至從機(寫),再自從機接收數(shù)據(jù)(讀)。注意,該函數(shù)會等待數(shù)據(jù)傳輸完成后才會返回,因此該函數(shù)是阻塞式的,不應(yīng)在中斷環(huán)境中調(diào)用。其函數(shù)原型為:
如果返回AM_OK,說明數(shù)據(jù)寫和讀成功完成;如果返回-AM_EINVAL,說明由于參數(shù)錯誤導(dǎo)致數(shù)據(jù)寫和讀失敗;如果返回-AM_EIO,說明在數(shù)據(jù)寫或讀的過程中發(fā)生錯誤,詳見程序清單5.66。
程序清單5.66 am_spi_write_then_read()范例程序
8. 連續(xù)兩次寫
連續(xù)兩次寫即是主機先發(fā)送緩沖區(qū)0 的數(shù)據(jù)至從機(寫),再發(fā)送緩沖區(qū)1 的數(shù)據(jù)至從機(寫)。如果只需要發(fā)送一次數(shù)據(jù),可以將第二次發(fā)送的數(shù)據(jù)緩沖區(qū)設(shè)置為NULL,并設(shè)置發(fā)送長度n_tx1 為0。值得注意的是,該函數(shù)同樣是阻塞式的,會等待兩次數(shù)據(jù)發(fā)送完成后才會返回,不應(yīng)在中斷環(huán)境中調(diào)用。其函數(shù)原型為:
如果返回AM_OK,說明消息處理成功;如果返回-AM_EINVAL,說明參數(shù)錯誤導(dǎo)致數(shù)據(jù)發(fā)送失敗;如果返回-AM_EIO,說明在發(fā)送數(shù)據(jù)的過程中發(fā)生錯誤,詳見程序清單5.67。
程序清單5.67 am_spi_write_then_write()范例程序
>>> 5.5.3 SPI 擴展接口
LPC824 與74HC595 的硬件連接詳見表5.14,當(dāng)74HC595 作為SPI 從機時,數(shù)據(jù)僅需從MCU主機傳送至SPI 從機,無需讀取數(shù)據(jù)。對于MCU主機,數(shù)據(jù)是單向傳輸,只有數(shù)據(jù)輸出而沒有輸入,因此無需使用MISO。顯然,擴充74HC595僅占用MCU 的SCK、MOSI 與SSEL 引腳,即可實現(xiàn)8 位數(shù)據(jù)的輸出。
表5.14 74HC595 硬件連接
在使用SPI 驅(qū)動74HC595 時,必須先調(diào)用am_spi_mkdev()初始化與74HC595 對應(yīng)的SPI從機實例。為此需要獲取到數(shù)據(jù)寬度、SPI 模式、最高時鐘頻率和片選引腳等信息。即:
-
數(shù)據(jù)寬度:74HC595 只有8 個并行輸出口,因此每次傳輸?shù)臄?shù)據(jù)寬度為8 位。
-
SPI 模式:8 位數(shù)據(jù)是在CP 時鐘信號上升沿作用下依次送入74HC595 的,因此在空閑時對時鐘沒有要求。如果選擇空閑時鐘極性為低電平(CPOL=0),則必須在第一個時鐘邊沿(上升沿)采樣數(shù)據(jù)(CPHA=0),即模式0。反之,如果選擇空閑時鐘極性為高電平(CPOL=1),則必須在第二個時鐘邊沿(上升沿)采樣數(shù)據(jù)(CPHA=1),即模式3。因此選擇模式0 和模式3 均可,后續(xù)的程序選擇模式3 作為范例。
-
最高時鐘頻率:雖然74HC595 最高時鐘頻率高達100MHz,但MCU 最高主頻只有30MHz,因此最高時鐘頻率設(shè)置為一個相對合理的范圍,比如,3000000Hz(3MHz)。
-
片選引腳:片選引腳為PIO0_13。
有了這些信息,即可配置與74HC595 對應(yīng)的SPI 從機實例,詳見程序清單5.68。
程序清單5.68 初始化與74HC595 對應(yīng)的從機實例范例程序
接下來,可以使用消息的方式或者am_spi_write_then_write()和am_spi_write_then_read()進行數(shù)據(jù)的發(fā)送與接收。由于使用消息的方式進行數(shù)據(jù)發(fā)送時參數(shù)較多,因此暫不使用消息的方式。因為MCU 不需要從74HC595 中讀取數(shù)據(jù),所以直接使用am_spi_write_then_write()進行數(shù)據(jù)發(fā)送,詳見程序清單5.69。
程序清單5.69 驅(qū)動74HC595 輸出的范例程序
為了方便后續(xù)使用,不妨封裝74HC595 的驅(qū)動程序,這樣就可以將74HC595 當(dāng)作8 位I/O 口擴展接口來使用了。顯然,需要為74HC595 編寫初始化接口和數(shù)據(jù)輸出接口,其分別用于初始化與74HC595 對應(yīng)的SPI 從機實例和用于輸出指定的8 位數(shù),函數(shù)的聲明和實現(xiàn)詳見程序清單5.70 所示的hc595.h 和程序清單5.71 所示的hc595.c。
程序清單5.70 hc595.h 接口文件
程序清單5.71 hc595.c 實現(xiàn)文件
基于74HC595 的數(shù)據(jù)發(fā)送函數(shù),可以實現(xiàn)使用74HC595 驅(qū)動數(shù)碼管顯示,以節(jié)省引腳。同I/O 驅(qū)動數(shù)碼管一致,使用軟件定時器實現(xiàn)數(shù)碼管的自動掃描,詳見程序清單5.72。
程序清單5.72 新增使用軟件定時器相關(guān)函數(shù)
同樣地,將新的代碼添加到digitron1.c 中,其相應(yīng)的函數(shù)接口添加到程序清單4.47 所示的digitron1.h 中,詳見程序清單5.73。
程序清單5.73 digitron1.h 文件內(nèi)容
顯然,程序與之前的代碼基本相同,僅僅是回調(diào)函數(shù)調(diào)用的掃描函數(shù)變化了,測試程序詳見程序清單5.74。
程序清單5.74 測試軟件定時器自動掃描
由于回調(diào)函數(shù)運行在中斷環(huán)境,而am_spi_write_then_write()要等到數(shù)據(jù)發(fā)送完畢后才會返回,因此是阻塞式的,所以調(diào)用該函數(shù)實現(xiàn)的hc595_send_data()不能在中斷環(huán)境中使用。為了實現(xiàn)非阻塞式的(異步)數(shù)據(jù)發(fā)送函數(shù),可以使用SPI 消息的方式實現(xiàn)發(fā)送數(shù)據(jù)。為了區(qū)分與之前的hc595_send_data(),將其命名為hc595_send_data_async(),詳見程序清單5.75。
程序清單5.75 hc595_send_data_async()函數(shù)范例
在發(fā)送數(shù)據(jù)時,要先將數(shù)據(jù)data 保存g_tx_buf 中。因為使用SPI 消息的方式發(fā)送數(shù)據(jù)時,函數(shù)是異步的,會立即返回,函數(shù)返回后,因data 是局部變量,其地址空間就被釋放了。驅(qū)動獲取需要發(fā)送的數(shù)據(jù)時,是在緩沖區(qū)表明的地址中取數(shù)據(jù),因此必須保證緩沖區(qū)在整個數(shù)據(jù)傳輸過程中都是有效的。這里使用了一個全局變量來保存數(shù)據(jù),使得緩沖區(qū)一直有效。為什么使用am_spi_write_then_write()函數(shù)不需要這樣做呢?因為這個函數(shù)是同步的,會等到數(shù)據(jù)發(fā)送完畢后才返回,在整個數(shù)據(jù)傳輸過程中,data 的地址是有效的,不會被釋放。
這樣的異步傳輸函數(shù)可行嗎?如果使用者以較長的時間間隔來調(diào)用該函數(shù),每次調(diào)用前,上一個數(shù)據(jù)傳輸都已經(jīng)正確完成,則可以正常進行數(shù)據(jù)發(fā)送,不會出現(xiàn)問題。但是如果時間間隔很短,比如,連續(xù)2 次調(diào)用了該函數(shù)分別發(fā)送兩個數(shù)據(jù),將導(dǎo)致上一個transfer 被覆蓋,造成一種嚴(yán)重錯誤。可以類似SPI 消息一樣增加一個回調(diào)函數(shù),當(dāng)數(shù)據(jù)發(fā)送完成后,調(diào)用回調(diào)函數(shù)通知用戶數(shù)據(jù)發(fā)送完畢。由于消息本身就有這一特性,因此只需要直接將用戶傳遞的回調(diào)函數(shù)作為SPI 消息初始化的回調(diào)函數(shù)參數(shù)即可,詳見程序清單5.76。
程序清單5.76 修改hc595_send_data_async()函數(shù)
為了便于后續(xù)使用,則將該函數(shù)的聲明存放到程序清單5.70 所示的hc595.h 中,詳見程序清單5.77。
程序清單5.77 hc595.h 文件內(nèi)容
實現(xiàn)該異步數(shù)據(jù)發(fā)送函數(shù)后,即可實現(xiàn)在中斷環(huán)境中發(fā)送數(shù)據(jù)。顯然,使用軟件定時器實現(xiàn)數(shù)碼管自動掃描需要修改digitron_hc595_disp_scan(),使其調(diào)用hc595_send_data_async()來實現(xiàn)掃描。不妨使用一個標(biāo)志位,當(dāng)標(biāo)志位置1 時,說明傳輸完成,詳見程序清單5.78。
程序清單5.78 修改digitron_hc595_disp_scan()函數(shù)(1)
程序使用g_flag 變量來標(biāo)志消影段碼是否傳輸結(jié)束,初看起來并沒有什么問題,這是一種通用的編程方法。但的確犯了一個很嚴(yán)重的錯誤,由于該函數(shù)直接使用了阻塞式while()循環(huán)等待語句,雖然hc595_send_data_async()是異步的,但加上等待語句后,又將掃描函數(shù)
變成阻塞式的了,因此該掃描函數(shù)還是無法在中斷環(huán)境中使用。
掃描一次數(shù)碼管,首先需要傳送消影段碼(0xFF),接著確定相應(yīng)的位選,然后傳送顯示段碼,即會在極短的時間內(nèi)調(diào)用2 次段碼傳送函數(shù)(消影段碼和顯示的段碼)。顯然,消影段碼沒有傳送完畢不能傳送顯示段碼,由于消影段碼傳送完畢后會調(diào)用回調(diào)函數(shù),為何不將后續(xù)代碼放到消影段碼傳送完成的回調(diào)函數(shù)中執(zhí)行呢?詳見程序清單5.79。
程序清單5.79 修改digitron_hc595_disp_scan()函數(shù)(2)
程序只是將之前的掃描函數(shù)分成了兩部分,將消影段碼后的內(nèi)容放到了回調(diào)函數(shù)中實現(xiàn),解決了等待消影段碼傳送完畢的問題。那么后續(xù)發(fā)送正常顯示的段碼,還需要等待其結(jié)束嗎?其實在正常顯示的段碼傳送完成后,并不需要再做其它操作,因此可以不用設(shè)置回調(diào)函數(shù)。如果不利用回調(diào)函數(shù)判斷其是否傳送完畢,那再次掃描時,是否會因上次消息處理還未完成
而產(chǎn)生錯誤呢?下次掃描是在5ms 之后,由于SPI 傳輸速率很快,3MHz 的速率傳輸8 位數(shù)據(jù)只需要幾微秒,因此5ms 的時間足以使其傳輸完畢,因此能夠確保正常顯示的段碼傳送在下一次傳輸數(shù)據(jù)前成功完成。修改后的使用軟件定時器實現(xiàn)自動掃描的函數(shù),由于接口并沒有改變,只是修改了實現(xiàn),因此可以直接使用程序清單5.74 的范例程序來進行測試。
5.6 I2C 總線
絕大部分情況下,MCU 都作為I2C 主機與I2C 從機器件通信,因此這里僅介紹AMetal中與I2C 主機相關(guān)的接口函數(shù)。
>>> 5.6.1 初始化
在使用I2C 通用接口傳輸數(shù)據(jù)前,必須先完成I2C 的初始化,便于獲取I2C 實例句柄。在LPC824 中,支持I2C 功能的外設(shè)有:I2C0、II2C1、I2C2 和I2C3,各I2C 外設(shè)都提供了對應(yīng)的實例初始化函數(shù),詳見表5.15。
表5.15 I2C 實例初始化函數(shù)(am_lpc82x_inst_init.h)
這些函數(shù)返回值均為am_i2c_handle_t 類型的I2C 實例句柄,該句柄將作為I2C 通用接口中handle 參數(shù)的實參。類型am_i2c_handle_t(am_i2c.h)定義如下:
因為函數(shù)返回的I2C 實例句柄僅作為參數(shù)傳遞給I2C 通用接口,不需要對該句柄作其它任何操作,因此完全不需要對該類型作任何了解。注意,如果函數(shù)返回的實例句柄的值為NULL,表明初始化失敗,該實例句柄不能被使用。
如果使用I2C0,則直接調(diào)用I2C0 實例初始化函,即可獲取對應(yīng)的實例句柄:
>>> 5.6.2 接口函數(shù)
在AMetal 中,MCU 作為I2C主機與I2C從機器件通信的相關(guān)接口函數(shù)詳見表5.16。
表5.16 I2C標(biāo)準(zhǔn)接口函數(shù)
1. 從機實例初始化
對于用戶來講,使用I2C的目的就是直接操作一個從機器件,比如,LM75、E2PROM等。MCU 作為I2C主機與從機器件通信,需要知道從機器件的相關(guān)信息,比如,I2C從機地址等。這就需要定義一個與從機器件對應(yīng)的實例,即從機實例,并使用相關(guān)信息完成對從機實例的初始化。從機實例初始化函數(shù)的原型為:
其中,p_dev 為指向am_i2c_device_t 類型(am_i2c.h)I2C 從機實例的指針,該類型定義如下:
使用時無需知道該類型定義的具體內(nèi)容,僅需使用該類型完成一個I2C 從機實例的定義:
其中,dev 為用戶自定義的從機實例,其地址作為p_dev 的實參傳遞。dev_flags 為從機實例的相關(guān)屬性標(biāo)志,可分為3 大類:從機地址的位數(shù)、是否忽略無應(yīng)答和器件內(nèi)子地址(通常又稱之為“寄存器地址”)的字節(jié)數(shù)。具體可用屬性標(biāo)志詳見表5.17,可使用“|”操作連接多個屬性標(biāo)志。
表5.17 從機設(shè)備屬性
使用范例詳見程序清單5.80。
程序清單5.80 am_i2c_mkdev()范例程序
2. 寫操作
向I2C 從機實例指定的子地址中寫入數(shù)據(jù)的函數(shù)原型為:
如果返回值為AM_OK,表明寫入數(shù)據(jù)成功;如果返回值為其它值,表明寫入數(shù)據(jù)失敗。范例程序詳見程序清單5.81。
程序清單5.81 am_i2c_write()使用范例
3. 讀操作
從I2C從機實例指定的子地址中讀出數(shù)據(jù)的函數(shù)原型為:
如果返回值為AM_OK,表明讀取數(shù)據(jù)成功;如果返回值為其它值,表明讀取數(shù)據(jù)失敗,其相應(yīng)的范例程序詳見程序清單5.82。
程序清單5.82 am_i2c_read()使用范例
>>> 5.6.3 I2C擴展接口
在使用am_i2c_read()函數(shù)前,需要先使用am_i2c_mkdev()初始化與LM75B 對應(yīng)的I2C從機實例,便于LPC824 讀取溫度值。初始化從機實例時,還需要知道兩個重要的信息:器件從機地址和實例屬性。
LM75B 的從機地址為7 位,1001xxx,其中地址位0~2 分別與硬件連接的A0~A2 一一對應(yīng)。由于A0~A2 均與地連接,因此xxx 的值均為0,LM75B 的從機地址為0x48。
實例屬性可分為從機地址屬性、應(yīng)答屬性和器件內(nèi)子地址屬性,LM75B 的從機地址為7 位,其對應(yīng)的屬性標(biāo)志為AM_I2C_ADDR_7BIT。如果從機實例不能應(yīng)答,則設(shè)置AM_I2C_IGNORE_NAK 標(biāo)志。一般來說,標(biāo)準(zhǔn)的I2C從機實例可產(chǎn)生應(yīng)答信號,除非特殊說明,否則都不需要該標(biāo)志。
LM75B 共計有4 個寄存器,詳見表5.18。由于寄存器的地址都為8 位,因此器件內(nèi)子地址為一個字節(jié),對應(yīng)的屬性標(biāo)志為:AM_I2C_SUBADDR_1BYTE。由于只有一個字節(jié),所以沒有高字節(jié)與低字節(jié)之分, 也就不需要AM_I2C_SUBADDR_MSB_FIRST 或AM_I2C_SUBADDR_LSB_FIRST 標(biāo)志。
表5.18 寄存器功能
使用am_i2c_mkdev()初始化一個LM75 從機實例的示例代碼詳見程序清單5.83。
程序清單5.83 初始化一個與LM75 對應(yīng)的I2C 從機實例
初始化從機實例后,即可使用am_i2c_read()讀取溫度值,由表5.18 可知,溫度值存于地址為0x00 的寄存器中,包含了兩個字節(jié)的溫度值,且是只讀的。因此,可以直接使用am_i2c_read()讀取子地址為0x00 的兩字節(jié)內(nèi)容,即溫度值,使用范例詳見程序清單5.84。
程序清單5.84 讀取溫度值
讀取的這兩字節(jié)數(shù)據(jù)表示的溫度值是多少呢?這兩個字節(jié)具體表示的溫度值的含義可從芯片的數(shù)據(jù)手冊獲取。溫度是以雙字節(jié)16 位二進制補碼方式表示的,分別保存在字節(jié)0和字節(jié)1 中,首先讀出的是字節(jié)0 的數(shù)據(jù)。字節(jié)0 中保存了溫度的整數(shù)部分,字節(jié)1 中保存溫度的小數(shù)部分,僅高3 位有效,因此溫度的分辨率為1/23 = 0.125℃。
如果將字節(jié)0 和字節(jié)1 合并為一個16 位有符號整數(shù)的話,則這個16 位有符號整數(shù)便是實際溫度的256(28)倍。如果系統(tǒng)支持浮點數(shù),則使用以下公式即可獲得當(dāng)前溫度值:
當(dāng)前溫度值(浮點數(shù)變量)= (字節(jié)0 的值×28 + 字節(jié)1 的值)/ 256.0
在沒有硬件浮點運算單元的MCU 中,這樣的公式在計算時效率是非常低的。在實際使用過程中,一般也并不需要得出浮點數(shù)的溫度值,僅僅在使用時稍加處理即可。比如,對于數(shù)碼管顯示溫度值,只需要分別顯示溫度值的整數(shù)部分(使用整數(shù)表示)和小數(shù)部分(使用整數(shù)表示)即可,并不需要計算出浮點數(shù)。LM75 的接口函數(shù)聲明詳見程序清單5.85。
程序清單5.85 LM75 接口(lm75.h)
其中,lm75_read()的作用是讀取LM75 溫度值,其返回值(16 位有符號數(shù))為實際溫度的256 倍,其相應(yīng)的實現(xiàn)(lm75.c)詳見程序清單5.86。
程序清單5.86 LM75 接口的實現(xiàn)(lm75.c)
在公眾號后臺回復(fù)關(guān)鍵字【編程】,即可在線閱讀《面向AMetal框架與接口的編程(上)》和《程序設(shè)計與數(shù)據(jù)結(jié)構(gòu)》兩本書。
書籍的淘寶購買鏈接如下,可復(fù)制到瀏覽器打開:
【廣州致遠電子官方企業(yè)店】,復(fù)制這條信息¥Ebic03xkccD¥后打開手機淘寶
公眾號介紹
致遠電子官方微信公眾號,一個匯聚500名工程師的研發(fā)測試分享平臺,為您提供電子行業(yè)領(lǐng)先的產(chǎn)品技術(shù)與解決方案。
-
IIC總線
+關(guān)注
關(guān)注
1文章
66瀏覽量
20297 -
ametal_spi
+關(guān)注
關(guān)注
0文章
1瀏覽量
1469
原文標(biāo)題:周立功:深入淺出AMetal—— SPI 總線和 IIC 總線
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論