輪詢與中斷的通訊處理比較|解決I2C裝置通訊穩定性的問題

本文以增強I2C通訊的穩定性出發,介紹輪詢與中斷的差異,並給不熟悉通訊的讀者說明何謂I2C Bus及其工作原理。同時給進階的讀者以實際波形分析通訊失敗時的實際狀況,並將中斷服務(ISR)視覺化觀察中斷發生的時機以及時序,以利除錯。{alertInfo}

目錄

    前言

        這次的應用是要讓F280025C作為接收端,透過I2C回應其他裝置要求的資料,並傳送相關狀態給主機。為了達到裝置的高穩定度,想使用中斷的方式處理I2C Request,而非輪詢。

    在通訊時使用中斷一直是停留在我腦中的一個設計方式,在這之前都還沒有機會實際應用,因為以前的專案簡單的輪詢就可以達成效果就一直沒有去精進程式。這篇文章會記錄輪詢與中斷在實際應用上會遇到的問題以及視覺化MCU在處理程式時佔用的時間,讓設計者能夠一目了然的知道問題出在哪。

    可以參考我在TI E2E論壇所提問的兩篇文章:

    1. TMS320F280025C: I2C interrupt ARDYINT, AASINT clarification and TX FIFO question - C2000 microcontrollers forum - C2000™︎ microcontrollers - TI E2E support forums
    2. TMS320F280025C: I2C bus busy, unknow reason to occupy the bus - C2000 microcontrollers forum - C2000™︎ microcontrollers - TI E2E support forums


    I2C通訊協議介紹

    I2C是被廣泛應用在板間電路或短距離通訊的協議,他與SPI相比除了使用的資料線更少之外,更可以串聯最多1023組接收端(Slave, 10-bit address),讓他擁有許多衍伸通訊協議,如PMBus、SMBus等等。

    要在C2000系列MCU使用I2C功能,需要先將GPIO設定成I2C模式,同時若是主控端(Master)則需要設定時脈、位址格式、資料位元數以及資料長度。若是將C2000,如 F280025C作為接收端(Slave),則是需要設定接收端自己的位址(I2COAR)。

    由於I2C在工作上有四種模式,擔任控制端時,可以為控制傳輸模式(Master Transmitter)、控制接收模式(Master Receiver),擔任接收端時也可以擔任接收傳輸模式(Slave Transmitter)與接收傳輸模式(Slave Reviever)。以這次的例子,F280025C會需要扮演Slave Reciever與Slave Transmiter的角色,Mater則由其他裝置扮演。I2C的常見連如下圖1所示。

    I2C最重要的核心精神在於他的資料線與時脈線是全部並聯再一起,以資料的角度看就是全部AND在一起。也就是說只要其中一個裝置把匯流排下拉至Low,整條線的訊號就都是Low。

    而I2C的時脈訊號SCL只能由Master提供,但任一個Slave都可以將SCL下拉為Low告訴Master等等待回應,這個功能叫做Time stretching 時脈延展。這時候SCL訊號被拉到地,Master內部接收資料的位移暫存器也不會動作,讓Master得以等待Slave處理完成後釋放SCL線,Master的SCL訊號才可以被傳送到Slave繼續通訊。

    圖1. I2C 匯流排連接圖

        除此之外,新一點的I2C標準還允許多組主控端(Multi-Master),但這並不在本文的討論範圍,有興趣的讀者可以自行搜尋相關知識。

    F280025C I2C工作原理說明

    圖2. F280025C I2C模組方塊圖

    在F280025C中,I2C模組方塊圖如圖2所示。本文暫且只討論MCU作為Slave時的工作原理。
    當SCL收到時脈訊號後,CPU會在SCL為LOW時讀取SDA線的狀態,並將此位元(bit)放入I2CRSR中,再放置到I2CDRR。若FIFO開啟,則會將I2CDRR資料放置FIFO中,並依照I2CFFSTS暫存器的FIFO準位設定,產生FIFO Full中斷。利用FIFO Full中斷我們可以將FIFO內部資料進行處理,執行對應動作。

    而作為Slave要傳送資料時,要注意的是SCL只能由Master提供,如果Master沒有支援Time stretch功能,可能會讓Master不知道要等Slave處理,導致SDA線為空並解析出錯誤資料。因此在收到指令後,MCU要快速處理I2C指令,並將要傳送的資料放到I2C TXFIFO中。當I2C TXFIFO放滿資料後(機制如同RXFFSTS),在I2C模組收到SCL時脈時便會自動將暫存器中的資料轉移到SDA線上給Master讀取。

    而0025的I2C中斷被OR在一起成兩個中斷,分別是I2C Interrupt與I2C FIFO Interrupt。
    I2C Interrupt可以在依照來源細分成ARBLINT, NACKINT,ARDYINT,RRDYINT,XRDYINT,SCDINT,AASINT七個,而I2C FIFO則是TX FIFO與RX FIFO兩個。
    前者主要用於判斷I2C匯流排上的資料狀態,包含Master呼叫自己(Bus上傳送對應自己的Address), Bus Busy,Stop Condition,ACK Condition等等。後者則是針對FIFO狀態進行中斷通知,當RX FIFO滿了就叫MCU把FIFO中的資料解碼/產生對應資料,TX FIFO不為空就叫MCU把FIFO內的東西傳出去。

    詳細的實作可以參考TI C2000 Library的I2C相關範例: I2c_ex6_eeprom_interrupt.c

    圖3. F280025C I2C 中斷與中斷向量表

    輪詢與中斷的主要區別

    輪詢(Polling)一種概念簡單且編寫容易的通訊法,當輪詢應用在通訊上時只需要檢查通訊暫存器有沒有新的資料,若有就處理,沒有就繼續執行其他任務。這種方式在編寫上很線性,但是會有兩個大缺點,第一是當CPU再做其他事情的時候,不一定有足夠的資源去處理通訊訊息,會導致通訊不穩定;第二則是當通訊訊息發生異常(如暫存器溢位或有超時設定)時,CPU沒辦法及時處理通訊事件,造成通訊不穩定。

    因此,需要改用另一種較穩定,但撰寫起來不線性化的中斷服務(Interrupt Service Routine (wikipedia)寫法。中斷的概念是當CPU在處理固定程序時,若有中斷發生會優先處理中斷內容,處理完畢再跳回原本的程式繼續執行。不同的中斷副程式都會有不同的優先權(priority),這對應到CPU要處理的優先順序,這個資料被記錄在中段向量服務表(Interrupt vector Table(wikipedia))

    舉個生活上的例子來說,當你在滑手機的時候,同學叫你跟他一起玩遊戲,還有媽媽叫你去掃地兩件事情同時發生時,肯定是媽媽的要求優先。這個例子中,滑手機對應到的是 CPU固定處理程序,同學叫你看手機對應到低優先權的中斷副程式,而媽媽叫你去掃地則是對應到最高優先權的中斷程式。

    那把這個概念套用到通訊處理中,就是當我用I2C介面向從端MCU發送訊息時,CPU會先放下手邊的工作優先處理I2C佔存器中的訊息,確保每次通訊都可以即時回應並傳輸資料。


    中斷服務程式時序分析

    在撰寫中斷服務時遇到最大的問題是沒辦法一眼看出何時進入中斷,何時跳出中斷。因為在中斷服務中不一定能扣透過中斷點將程式暫停在指定位置。因此可以使用一個小技巧:
    使用GPIO點亮/熄滅特定腳位,再透過示波器來分析中斷服務程式的進入時間。

    上述提到中斷服務會有各自不同的優先順序,當兩個中斷同時生時,優先權較高的中段會優先執行。我們已經將I2C通訊程式移到主程式之外,使用中斷觸發避免掉輪詢造成的不穩定,但依然會被更高優先權的中斷搶佔(Preempted (wikipedia)),這時抓出是哪個中斷搶佔了I2C程式除了從先前提到的中斷向量表中列出比I2C優先的中斷之外,同時也要搭配GPIO將懷疑的中斷再示波器上顯示出來。
    下面的例子就是I2C傳輸時遇到被其他中斷搶佔的情形,導致I2C Bus在傳輸時時好時壞,非常不穩定,這會導致通訊端系統錯誤,嚴重時會讓整條I2C Bus在運轉I2C有限狀態機(Finite State Machine)時卡在無窮迴圈之中。

    以下是實際遇到通訊阻礙時的中斷服務與I2C Bus波型,其中:
    ISR1 為I2C中斷服務 (I2CFIFO ISR)
    ISR2 為更高優先權之中斷服務 (Timer0, Timer1 etc.)


    從中可以看到當ISR2發生時,ISR1是被擠到後面去處理的,因此在資料傳輸過程中與ISR2產生碰撞,使得本該處理這串資料的ISR1被拖到後面去執行,變成SDA上Slave沒有回傳任何資料導致通訊不穩定(註1)


    移除掉ISR2後的中斷服務與I2C Bus波型

    既然ISR2會影響I2C通訊的穩定度,那可以嘗試將ISR2暫時移除來驗證我們的推測。
    當ISR2被移除後,中斷服務只剩下ISR1,也就是I2C程式不會有人跟他搶資源,那就可以盡情地使用CPU資源而不需要顧慮其他事情。
    從波型圖上來看可以看到當ISR沒有被干擾後,傳輸就一直保持暢通且順利。

    結論

    綜合這次經驗,在設計Embedded Firmware的同時除了要考量到main loop所運行的內容是否很重要之外,大部分與時間相關或有即時性回復的程式都會被放在中斷中執行。這也是實時作業系統(RTOS, Real Time Operation Syustem)的基礎。
    在RTOS中,作業系統將各個服務(子程式)變成一的軟體可調整的中斷服務,在透過中斷優先權調整每個服務能分配的資源,以及處理/排序各個服務運行的時間,確保每一個任務都可以在指定時間內處理完成。

    (註1): 在I2C介紹中有提到Time Stretch功能,在這個範例中若Master端支援Time stertch 其實就不用搞這麼複雜,因為ISR1還是會執行,只是時間稍微落後一點點。
    Time stretch最主要的功能就是要應付這種狀況,當Slave端來不及處理I2C通訊時,會暫停時脈訊號等待Slave處理完成。所以當ISR1執行完,ISR2也執行完後,I2C 就可以恢復原有通訊狀態繼續執行,不會造成不隱定。

    Post a Comment

    留個言吧

    較新的 較舊