在嵌入式系統中,主控MCU往往集成了多種片上外設,例如,GPIO、PWM、I2C、SPI、UART、ADC等等。通過它們可以使MCU輕松的與“外部世界”相互連接、相互通信,從而與“外部世界”交互。在AWorks中,對這些外設進行了抽象,為同一類外設提供了相同的使用接口。對于用戶來講,無論使用什么MCU,只要該MCU具有相應的外設,則均可以使用相同的外設接口進行操作,因而使用某一外設的應用程序可以輕松的跨平臺(移到另外一個硬件平臺)復用。同時,由于直接為用戶提供了外設的使用接口,因此,用戶不再需要了解底層的寄存器操作,由此可以將用戶從閱讀寄存器手冊、編寫底層驅動中解脫出來,使用戶更加專注于產品本身的“核心域”,提升產品的競爭力。
7.1 GPIO
在使用一個I/O口前,必須正確的配置I/O的功能和模式。同時,由于一個I/O口往往可用于多種功能,但同一時刻只能用于某一確定的功能,為了避免沖突,在將一個引腳用作某一功能前,應該向系統申請,使用完畢后再釋放。
當將I/O口用作GPIO(General Purpose Input Output)功能時,GPIO作為一種通用輸入/輸出接口,有兩種常用的使用方式:一種是用作普通的輸入/輸出接口;一種是用作中斷輸入接口,即當指定的輸入狀態事件發生(比如:下降沿)時,觸發用戶自定義回調函數。
7.1.1 I/O配置
通常情況下,一個引腳往往可以用作多種功能,例如,在i.MX280中,PIO3_0可以用作下面4種功能:
-
應用串口0的接收引腳;
-
I2C0的時鐘引腳;
-
調試串口的CTS引腳;
-
通用GPIO。
同時,GPIO往往還具有多種模式,比如:上拉、下拉、開漏、推挽等。為了正確使用一個IO口,必須先將其配置為正確的功能和模式。AWorks提供了I/O配置接口,其原型為:
其中,pin為引腳編號,flags為配置的功能和模式標志,返回值為標準的錯誤號,返回AW_OK時表示配置成功,否則,表示配置失敗,配置失敗可能是由于部分功能和模式不支持造成的。
引腳編號pin用于指定需要配置的引腳,在GPIO標準接口層中,所有函數的第一個參數均為pin,用于指定具體操作的引腳,具體的引腳編號在{chip}_pin.h文件中定義(chip代表芯片名,例如,對于i.mx28x系列MCU,chip即為imx28x,對應文件名為imx28x_pin.h),宏名的格式為PIOx_y,比如:PIO3_0。
flags為配置標志,由“通用功能|通用模式|平臺功能|平臺模式”(‘|’就是C語言中的按位或)組成。
1. 通用功能和模式
通用功能和模式是AWorks抽象的GPIO最通用的功能和模式,它們在aw_gpio.h文件中使用宏的形式進行了定義,宏名格式為AW_GPIO_*。通用功能相關宏定義與含義詳見表7.1,通用模式相關宏定義與含義詳見表7.2。
表7.1 引腳通用功能
表7.2 引腳通用模式
通用功能和模式相關的宏定義在標準接口層中,不會隨芯片的改變而改變,使用通用功能和模式的應用程序是與具體芯片是無關的,可以跨平臺復用。使用通用功能和模式配置引腳的范例程序詳見程序清單7.1。
程序清單7.1 使用通用功能和模式配置引腳的范例程序
2. 平臺功能和模式
平臺功能和模式與具體芯片相關,會隨著芯片的不同而不同,主要包括引腳的復用功能和一些特殊的模式,平臺功能和模式相關的宏定義在{chip}_pin.h文件中,宏名的格式為PIOx_y_*,比如:PIO3_0_AUART0_RX,即PIO3_0的串口0接收引腳。
如需查找某一引腳(比如:PIO3_0)相關的平臺功能和平臺模式,可以打開{chip}_pin.h文件,找到以PIO或引腳編號PIO3_0為前綴的所有宏定義。若一個宏定義以引腳編號為前綴,則表明該宏僅可用于引腳編號對應的引腳,例如,PIO3_0_AUART0_RX僅可用于PIO3_0。若一個宏定義沒有以具體引腳編號為前綴,僅以PIO作為前綴,則表明該宏對應的功能或模式可用于所有引腳。例如,PIO3_0相關的平臺功能詳見表7.4,相關的平臺模式定義詳見表7.3。
表7.3 引腳平臺模式
表7.4 PIO3_0平臺功能
在表7.3中,所有平臺模式都以PIO作為前綴,而非PIO3_0作為前綴。表明這些平臺模式不是PIO3_0引腳所特有的,而是所有引腳都可能支持的模式,即這些模式對應的宏可以在配置任意引腳時使用。使用平臺功能和模式配置引腳的范例程序詳見程序清單7.2。
程序清單7.2 使用通用功能和模式配置引腳的范例程序
特別地,平臺功能和模式可以和通用功能和模式組合使用。例如,在配置I2C引腳時,可以開啟內部上拉,范例程序詳見程序清單7.3。
程序清單7.3 通用模式和平臺模式混合使用
7.1.2 I/O的申請和釋放
如表7.4所示,PIO3_0可以被用作4種功能。雖然PIO3_0的功能眾多,但是,在同一時刻,其只能被用作某一確定功能,并不能同時使用多種功能。
隨著系統復雜度的上升,用戶往往很難保證某一引腳只被用作一種功能,稍有不慎,就可能將某一引腳同時配置為多種功能,而實際上,由于引腳同一時刻只能被用作一種功能,因此,部分使用某種功能的程序將不能正常工作。這種情況下,用戶往往很難發現工作異常的原因。
例如,PIO3_0已經被用作I2C0的時鐘引腳,結果由于程序員的不小心,又將其配置為串口0的接收引腳,此時,若不加以保護,則引腳會被配置為串口0的接收引腳,這將導致I2C0工作不正常。
為了保證引腳功能的互斥使用,AWorks提供了一種申請機制,在將引腳用作某一功能前,必須向系統申請,若該引腳處于空閑狀態,還未被申請過,則本次申請成功,同時標記該引腳已被使用。若在申請時,該引腳已被申請,則本次申請失敗。當一個引腳使用完畢時,應該釋放該引腳,以便系統將該引腳分配給下一個申請者。
在實際應用中,往往在設計硬件電路時,一個引腳只會被用作某一確定的功能,不會將一個引腳同時用作多個外設功能引腳,因此,軟件設計正確無誤的情況下,通常不會出現申請失敗的情況。當申請失敗時,往往是由于軟件開發人員的錯誤導致的,這就需要軟件開發人員引起足夠的重視,出現申請失敗的情況時,必須立即解決,找到某一引腳被非法占用的位置。
引腳申請機制包括兩個接口,相關接口的原型詳見表7.5。
表7.5 引腳申請機制相關接口(aw_gpio.h)
1. 申請引腳
當需要使用一組引腳時,應該向系統申請,申請的函數原型為:
其中,p_name為申請者的名字,p_pins指向引腳列表,num為本次申請的引腳個數。返回值為標準的錯誤號,返回AW_OK時表示申請成功,可以正常使用相關的引腳,否則,表示申請失敗,相關引腳已經被占用,無法使用。
p_name為申請者的名字,其指向一個字符串,例如,"LED"、"UART" 、"SSP0"、"I2C0"等,名字僅作標記,便于引腳的跟蹤,可以任意設定。
p_pins為引腳列表,例如,要申請PIO3_2和PIO3_3這兩個引腳用作串口功能,則引腳列表可以定義為:
num表示本次申請的引腳個數,其值應該與p_pins指向的列表中的引腳個數相等,比如:2。申請PIO3_2和PIO3_3這兩個引腳用作串口功能的范例程序詳見程序清單7.4。
程序清單7.4 引腳申請范例程序
只有當引腳申請成功后,才能配置相應的引腳用作串口功能。
特別地,即使將一個IO口用作通用的GPIO口,也需要先申請,例如,要將PIO2_6用作通用I/O口,用以控制LED0。則同樣需要在將其配置為通用I/O功能前向系統申請,范例程序程序清單7.4。
關于需要申請引腳的時機,一個簡單的原則就是:在調用aw_gpio_pin_cfg()前,都應該確保已經向系統申請到該引腳的使用權。
2. 釋放引腳
當引腳使用完畢,暫時不再使用時,應該釋放引腳,清除每個引腳的使用記錄。便于其它模塊申請使用。其函數原型為:
其中, p_pins指向引腳列表,num為本次釋放的引腳個數。返回值為標準的錯誤號,返回AW_OK時表示釋放成功,否則,表示釋放失敗,可能是由于參數錯誤導致的。
p_pins為引腳列表,其應該與申請引腳時的引腳列表一致,例如,要釋放之前申請的PIO3_2和PIO3_3這兩個引腳,則引腳列表定義為:
num表示本次釋放的引腳個數,其值應該與p_pins指向的列表中的引腳個數相等,比如:2。釋放PIO3_2和PIO3_3這兩個引腳的范例程序詳見程序清單7.5。
程序清單7.5 釋放引腳范例程序
在釋放引腳前,可以將I/O口配置為通用GPIO功能,以使其不再作為串口的功能引腳。特別注意,這一配置操作只能在釋放前完成,釋放后將失去引腳的控制權,不可再對其進行配置操作。
7.1.3 普通I/O接口
當將一個引腳配置為通用I/O接口時(輸入或輸出),則可以通過相關的I/O接口控制其輸出狀態或獲取其輸入狀態。相關接口的原型詳見表7.6。
表7.6 GPIO通用接口(aw_gpio.h)
1. 獲取引腳電平
無論當前引腳處于輸出模式還是輸入模式,都可以通過該接口獲取當前的引腳電平狀態。其函數原型為:
其中的pin為引腳編號。返回值為標準的錯誤號,若返回值小于0,則表示獲取失敗,失敗的原因可能是用于引腳不存在。若返回值為非負數,則表明獲取成功,其值為0時,表明當前引腳的電平為低電平,其值大于0時,表明當前引腳的電平為高電平。范例程序詳見程序清單7.6。
程序清單7.6 獲取引腳電平范例程序
2. 設置引腳電平
當引腳處于輸出模式時,可以通過該接口設置引腳的輸出電平,其函數原型為:
其中,pin為引腳編號。value為設置的值,當value為0時,輸出低電平,否則,輸出高電平。返回值為標準的錯誤號,返回AW_OK時表示設置成功,否則,表示設置失敗。
例如,可以控制GPIO的輸出電平,達到控制LED狀態的目的。在EPC-AW280上板載了一個運行指示燈,標識為RUN,其對應的原理圖詳見圖7.1。
圖7.1 板載LED電路
其中,發光二極管的陰極與MCU的PIO2_6相連,當I/O輸出低電平時,由于LED陽極加了3.3V電壓(高電平1),因而形成了電位差,所以有電流流動,則LED發光二極管導通,即LED發光;當I/O輸出高電平1時,由于無法形成電位差,則LED二極管不導通,即LED熄滅。
基于此,可以使PIO2_6輸出低電平,以點亮LED,范例程序詳見程序清單7.7。
程序清單7.7 設置引腳電平范例程序
程序中,首先將PIO2_6配置為了輸出模式,并將初始值設定為高電平,以便初始化完畢后,LED默認處于熄滅狀態。
同理,點亮LED后,可以控制PIO2_6輸出高電平,以熄滅LED。如以一定的頻率交替使PIO2_6輸出高電平和低電平,則可以看到LED閃爍,范例程序程序清單7.8。
程序清單7.8 交替輸出高低電平的范例程序
這里通過GPIO的輸出控制LED,僅為介紹GPIO接口的使用方法。實際中,AWorks已經定義了通用的LED接口,在應用中操作LED時,均建議使用通用的LED接口。
3. 翻轉引腳電平
該接口用于翻轉 GPIO 引腳的輸出電平,如果GPIO當前輸出的是低電平,當調用該函數后,GPIO將輸出高電平,反之則輸出變為低電平。其函數原型為:
其中,pin為引腳編號。返回值為標準的錯誤號,返回AW_OK時表示翻轉成功,否則,表示翻轉失敗。在程序清單7.8中,通過交替輸出高電平和低電平實現了LED閃爍。而交替輸出高低電平本質上就是不斷翻轉GPIO的輸出電平。基于此,若以一定的頻率翻轉GPIO的輸出電平,同樣可以實現LED閃爍,范例程序詳見程序清單7.9。
程序清單7.9 翻轉引腳電平的范例程序
7.1.4 中斷I/O接口
由對普通I/O接口的介紹可知,若需獲取某一引腳的狀態,可以通過aw_gpio_get()接口得到引腳的當前輸入狀態。若用戶需要監控GPIO某一狀態事件(比如:GPIO由高電平變為低電平),即當出現某一狀態時,才執行相應的操作。若還是使用普通I/O接口,則必須不斷的調用aw_gpio_get()接口,直到讀取到引腳電平為0。顯然,由于使用這種方式需要不斷的輪詢,因而是非常消耗CPU的。為此,AWorks提供了中斷I/O接口,其可以使GPIO工作在中斷狀態,實時監控引腳的輸入狀態,只有當用戶期望的狀態發生時,才通知用戶,用戶進而執行相關的處理,當期望的狀態沒有發生時,則用戶完全不用關心引腳的狀態,無需對引腳進行不斷的輪詢。
AWorks為I/O中斷相關的操作定義了一套觸發接口,用戶期望的狀態稱之為觸發條件,當GPIO的輸入狀態滿足觸發條件時,將自動調用引腳觸發回調函數,以通知用戶作相關的處理。相關接口原型詳見表7.7。
表7.7 GPIO觸發相關接口函數
1. 連接引腳觸發回調函數
在使用引腳的觸發功能前,應該首先連接一個回調函數到觸發引腳,當相應引腳的觸發事件產生時,則會調用本函數連接的觸發回調函數。其函數原型為:
其中,pin為引腳編號,pfunc_callback為指向回調函數的指針,其指向的函數即為本次連接的回調函數,p_arg為傳遞給回調函數的用戶參數。返回值為標準的錯誤號,返回AW_OK時表示連接成功,否則,表示連接失敗,失敗可能是由于該引腳不支持觸發模式。
pfunc_callback為指向回調函數的指針,其類型為aw_pfuncvoid_t,該類型在aw_types.h文件中定義如下:
由此可見,回調函數的類型是無返回值,具有一個void *類型參數的函數。當觸發事件發生時,將自動調用pfunc_callback指向的函數,并將連接觸發回調函數時設定的p_arg作為回調函數的參數。例如,要將引腳PIO3_6用作觸發功能,則首先需要連接觸發回調函數,范例程序詳見程序清單7.10。
程序清單7.10 連接引腳觸發回調函數范例程序
2. 斷開引腳觸發回調函數
與aw_gpio_trigger_connect()函數的功能相反,當一個引腳不再需要用作觸發功能時,可以斷開引腳與回調函數的連接。或者當需要將一個引腳的回調函數重新連接到另外一個函數時,則應該先斷開當前連接的回調函數,再重新連接到新的回調函數。其函數原型為:
其中,pin為引腳編號,pfunc_callback 為指向回調函數的指針,其值應該與連接回調函數時設定的回調函數一致,p_arg為回調函數的參數,其值同樣應該與連接回調函數時設定的p_arg一致。返回值為標準的錯誤號,返回AW_OK時表示斷開連接成功,否則,表示斷開連接失敗,失敗可能是由于參數錯誤造成的。使用范例詳見程序清單7.11。
程序清單7.11 斷開引腳觸發回調函數范例程序
3. 配置引腳觸發條件
連接觸發回調函數后,需要配置引腳觸發的條件,其函數原型如下:
其中,pin為引腳編號,flags用于指定觸發條件。返回值為標準的錯誤號,返回AW_OK時表示配置成功,否則,表示配置失敗,配置失敗可能是由于該引腳不支持配置的觸發條件。
觸發條件決定了引腳觸發的時機,所有可選的觸發條件詳見表7.8。
表7.8 GPIO觸發條件配置宏
實際中,并不是每一個引腳都支持表中所有的觸發條件,當配置觸發條件時,應檢測返回值,確保相應引腳支持所配置的觸發條件。特別地,部分引腳可能不支持觸發模式,配置任何觸發條件都會失敗。
例如,配置PIO3_6為觸發模式,觸發條件為下降沿觸發,范例程序詳見程序清單7.12。
程序清單7.12 配置觸發條件的范例程序
4. 打開引腳觸發
當正確連接回調函數并設置相應的觸發條件后,可以打開引腳觸發,使引腳觸發開始工作。其函數原型為:
其中,pin為引腳編號,返回值為標準的錯誤號,返回AW_OK時表示打開成功,否則,表示打開失敗,打開失敗可能是由于未正確連接回調函數或設置觸發條件。
綜合連接引腳觸發回調函數和配置引腳觸發條件的接口,可以實現一個完整的引腳觸發范例程序,詳見程序清單7.13。
程序清單7.13 打開引腳觸發范例程序
程序中,由于需要使用aw_gpio_pin_cfg()將引腳配置為輸入模式,因此,在配置引腳前,同樣先向系統申請了引腳的控制權。特別地,在aw_gpio_trigger_cfg()前,無需再次申請。
程序運行后,若PIO3_6引腳出現下降沿(可以通過導線將PIO3_6與GND輕觸幾次),則可以觀察到LED的狀態發生變化。實際中,若采用輕觸GND的方式產生下降沿,由于輕觸時會有抖動,可能輕觸時將在短時間內產生多個下降沿,此時,可能觀察到LED無規律的翻轉。
注意,必須先連接回調函數,再配置引腳的觸發條件,這個順序不能顛倒,即不能顛倒程序清單7.13中第18行和第19行的順序。
特別地,當將引腳用作觸發功能時,往往需要將引腳配置為輸入模式。程序清單7.13的第14行程序將引腳配置為了輸入模式,并開啟了上拉,使得PIO3_6在外部懸空狀態時,仍能處于確定的高電平狀態。
5. 關閉引腳觸發
當暫時不使用引腳觸發功能時,可以關閉引腳觸發,引腳觸發將停止工作,此時,即使滿足配置的觸發條件,也不會觸發調用引腳相應的回調函數。其函數原型為:
其中,pin為引腳編號,返回值為標準的錯誤號,返回AW_OK時表示關閉成功,否則,表示關閉失敗。使用范例詳見程序清單7.14。
程序清單7.14 關閉引腳觸發范例程序
當引腳觸發被關閉后,若需引腳觸發重新開始工作,可以使用aw_gpio_trigger_on()再次打開引腳觸發功能。
7.2 PWM
7.2.1 PWM簡介
大小和方向隨時間發生周期性變化的電流稱為交流,交流中最基本的波形稱為正弦波,除此之外的波形稱為非正弦波。計算機、電視機、雷達等裝置中使用的信號稱為脈沖波、鋸齒波等,其電壓和電流波形都是非正弦交流的一種。
PWM(Pulse Width Modulation)就是脈沖寬度調制的意思,一種脈沖編碼技術,即可以按照信號電平改變脈沖寬度。而脈沖寬度調制波的周期也是固定的,用占空比(高電平/周期,有效電平在整個信號周期中的時間比率,范圍為0~100%)來表示編碼數值。PWM可以用于對模擬信號電平進行數字編碼,也可以通過高電平(或低電平)在整個周期中的時間來控制輸出的能量,從而控制電機轉速或LED亮度。
PWM信號是由計數器和比較器產生的,比較器中設定了一個閾值,計數器以一定的頻率自加。當計數器的值小于閾值時,則輸出一種電平狀態,比如,高電平。當計數器的值大于閾值,則輸出另一種電平狀態,比如,低電平。當計數器溢出清0時,又回到最初的電平狀態,即I/O引腳發生了周期性的翻轉而形成PWM波形,詳見圖7.2。
圖7.2 PWM波形圖
當計數器的值小于閾值時,則輸出高電平;當計數器的值大于閾值時,則輸出低電平。閾值為45,計數器的值最大為100。PWM波形有三個關鍵點:起始點①,此時計數器的值為0;計數器值達到閾值②,I/O狀態發生翻轉;計數器達到最大值③,I/O狀態發生翻轉,計數器的值回到0重新開始計數。
7.2.2 PWM接口
AWorks提供了接口函數,用于控制PWM輸出,接口原型詳見表7.9。
表7.9 PWM接口函數(aw_pwm.h)
1. 配置PWM通道
配置一個PWM通道的周期時間和高電平時間,其函數原型為:
其中,pid為PWM通道的id號,duty_ns為脈寬時間(單位:ns),period_ns為周期時間(單位:納秒)。返回值為標準的錯誤號,返回AW_OK時表示配置成功,否則,表示配置失敗。
在AWorks中,一個系統往往可以輸出多個通道的PWM,各個通道通過pid區分,一個系統實際可以輸出PWM的通道數與具體硬件平臺相關。例如,在i.MX28x系統中,可以輸出8路PWM。則PWM通道的編號為:0 ~ 7,各通道對應的默認I/O口詳見表7.10。
表7.10 各通道對應的I/O口
注:實際中,各個PWM通道對應的I/O口是可以配置的,具體配置方法詳見SDK配套資料中的《用戶手冊.pdf》。
輸出PWM的頻率由周期時間period_ns決定,例如,需要輸出1KHz的PWM,周期時間為1ms,則period_ns的值為1000000。
輸出PWM的脈寬由duty_ns決定,它和周期值決定了輸出PWM的占空比,占空比即為:duty_ns / period_ns。duty_ns的值不能超過period_ns。配置PWM通道0輸出PWM的頻率為1KHz,占空比為50%的范例程序詳見程序清單7.15。
程序清單7.15 配置PWM通道的范例程序
2. 使能PWM輸出
PWM通道配置完成后,并不會輸出PWM。只有使能通道輸出后,才能使相應通道開始輸出PWM,其函數原型為:
其中,pid為PWM通道的id號。返回值為標準的錯誤號,返回AW_OK時表示使能成功,PWM開始輸出,否則,表示使能失敗,PWM不能正常輸出,失敗原因可能是通道號設置有誤。使能PWM通道0輸出的范例程序詳見程序清單7.16。
程序清單7.16 使能PWM輸出范例程序
3. 禁能PWM輸出
若需關閉PWM通道的輸出,則可以禁能相應PWM通道,其函數原型為:
其中,pid為PWM通道的id號。返回值為標準的錯誤號,返回AW_OK時表示禁能成功,PWM停止輸出,否則,表示禁能失敗。禁能PWM通道0輸出的范例程序詳見程序清單7.17。
程序清單7.17 禁能PWM輸出范例程序
EPC-AW280上板載了一個無源蜂鳴器,其對應的原理圖詳見圖7.3,蜂鳴器的控制引腳與GPIO3_29連接,當GPIO3_29輸出高電平時,則三極管導通,向蜂鳴器供電;當GPIO3_29輸出低電平時,則三極管截止,停止向蜂鳴器供電。對于無源蜂鳴器,只要以一定的頻率控制蜂鳴器的“通電”和“斷電”,即可使蜂鳴器產生機械振動音,聲音的頻率與控制信號的頻率相同,只要頻率在人耳的聽覺范圍內,即可聽到蜂鳴器發出的聲音。
圖7.3 板載蜂鳴器電路
基于此,可以通過GPIO3_29輸出一定頻率的PWM,使蜂鳴器發聲。由表7.10可知,GPIO3_29對應的PWM通道為4。因此,控制PWM通道4輸出PWM即可聽到蜂鳴器發聲。范例程序詳見程序清單7.18。
程序清單7.18 使用PWM輸出驅動蜂鳴器發聲范例程序
運行程序,可以聽到蜂鳴器發出的聲音。由于程序中一直輸出PWM,因此蜂鳴器會一直鳴叫,若僅需蜂鳴器每秒“嘀”一聲,則可以在不需要發聲時禁能PWM輸出。范例程序詳見程序清單7.19。
程序清單7.19 使用PWM控制蜂鳴器每秒“嘀”一聲的范例程序
這里通過控制PWM的輸出驅動蜂鳴器發聲,僅為介紹PWM接口的使用方法。實際中,AWorks已經定義了通用的蜂鳴器接口,在應用中操作蜂鳴器時,均建議直接使用通用的蜂鳴器接口。
7.3 SPI總線
7.3.1 SPI總線簡介
SPI(Serial Peripheral Interface)是一種全雙工同步串行通信接口,常用于短距離高速通信,其數據傳輸速率通常可達到幾M甚至幾十M。SPI通信采用主/從結構,主/從雙方通信時,需要使用到4根信號線:SCLK、MOSI、MISO、CS。其典型的連接示意圖詳見圖7.4。
圖7.4 SPI連接示意圖——單從機
-
SCLK:時鐘信號,由主設備產生;
-
MOSI:主機數據輸出,從機數據輸入;
-
MISO:從機數據輸出,主機數據輸入;
-
CS:片選信號,由主設備控制。
數據傳輸是由主機發起的,主機在串行數據傳輸前驅動CS信號,使之變為有效狀態(通常情況下,有效狀態為低電平),接著,在SCLK上輸出時鐘信號,在時鐘信號的同步下,每個時鐘傳輸一位數據,主機數據通過MOSI傳輸至從機,從機數據通過MISO傳輸至主機,數據傳輸完畢后,釋放CS信號,使之變為無效狀態,一次數據傳輸完成。
一個主機可以連接多個從機,多個從機共用SCLK 、MOSI、MISO三根信號線,每個從機的片選信號CS是獨立的,因此,若主機需要連接多個從機,就需要多個片選控制引腳。連接示意圖詳見圖7.5。當一個主機連接多個從機時,同一時刻最多只能使一個片選信號有效,以選擇一個確定的從機作為數據通信的目標對象。也就是說,在某一時刻,最多只能激活尋址一個從機,以使各從機之間相互獨立的使用,互不干擾。
圖7.5 SPI連接示意圖——多從機
注意,單個通信網絡中,可以有多個從機,但有且只能有一個主機。
除了需要了解上述SPI的基本概念外,讀者還應該理解SPI的傳輸模式,以便在操作SPI從機器件時,可以正確的設置SPI主機的傳輸模式。
SPI數據傳輸是在片選信號有效時,數據位在時鐘信號的同步下,每個時鐘傳輸一位數據。根據時鐘極性和時鐘相位的不同,將SPI分為了4種傳輸模式,詳見表7.11。
表7.11 SPI模式
-
時鐘極性(CPOL)
時鐘極性表示了SPI空閑時的時鐘極性,可以為高電平(CPOL=1)或低電平(CPOL=0)。
-
時鐘相位
時鐘相位決定了數據采樣的時機,若CPHA=0,則表示數據在時鐘的第一個邊沿采樣;若CPHA=1,則表示數據在時鐘的第二個邊沿采樣。
CPHA=0時,對應的模式0(CPOL=0)和模式2(CPOL=1)的示意圖詳見圖7.6。
圖7.6 SPI模式0和模式2示意圖
在SPI模式0(CPOL=0,CPHA=0)中,時鐘空閑電平為低電平,在傳輸數據時,每一位數據在第一個邊沿(即上升沿)采樣。
在SPI模式2(CPOL=1,CPHA=0)中,時鐘空閑電平為高電平,在傳輸數據時,每一位數據在第一個邊沿(即下降沿)采樣。
同理,CPHA=1時,對應的模式1(CPOL=0)和模式3(CPOL=1)的示意圖詳見圖7.7。
圖7.7 SPI模式1和模式3示意圖
在SPI模式1(CPOL=0,CPHA=1)下,時鐘空閑電平為低電平,在傳輸數據時,每一位數據在第二個邊沿(即下降沿)采樣。
在SPI模式3(CPOL=1,CPHA=1)下,時鐘空閑電平為高電平,在傳輸數據時,每一位數據在第二個邊沿(即上升沿)采樣。
7.3.2 SPI總線接口
絕大部分情況下,MCU都作為SPI主機與SPI從機器件通信,因此這里僅介紹AWorks中將MCU作為SPI主機的相關接口,接口原型詳見表7.12。
表7.12 SPI標準接口函數
1. 定義SPI從機器件實例
對于用戶來講,使用SPI總線的目的往往是用于操作一個SPI從機器件,比如,HC595、SPI FLASH等。MCU作為SPI主機與從機器件通信,需要知道從機器件的相關信息,比如: SPI模式、SPI速率、數據位寬等。在AWorks中,定義了統一的SPI從機器件類型:aw_spi_device_t,用于包含從機器件相關的信息,以便主機正確的與之通信。該類型的具體定義用戶無需關心,在使用SPI操作一個從機器件前,必須先使用該類型定義一個與從機器件對應的器件實例,例如:
其中,spi_dev為用戶自定義的從機實例,其地址可作為接口函數中p_dev的實參傳遞。
2. 初始化SPI從機器件實例
當完成SPI從機器件實例的定義后,還需要完成其初始化,指定從機器件相關的信息,初始化函數的原型為:
其中,p_dev為指向SPI從機器件實例的指針,busid為SPI總線編號,bits_per_word為數據位寬,mode為使用的SPI模式,max_speed_hz為從設備支持的最高時鐘頻率,cs_pin為從機設備的片選引腳,pfunc_cs為自定義的片選引腳控制函數。
在AWorks中,一個系統往往具有多條SPI總線,各總線之間通過busid區分,一個系統實際支持的SPI總線條數與具體硬件平臺相關。例如,在i.MX28x系統中,最高可以支持5條SPI總線,對應的總線編號即為:0 ~ 4。busid參數即用于指定使用那條SPI總線與從機器件進行通信。
表7.13 SPI模式標志
bits_per_word指定了數據傳輸的位寬,一般來講,數據均為8位,即每個數據為一個字節。
mode為從機使用的SPI模式(模式0 ~ 模式3,對應的宏定義詳見表7.13),從機器件一般支持固定的一種或幾種SPI模式,這里的mode用于指定使用何種SPI模式與從機器件通信。
max_speed_hz為從機器件支持的最大速率(SCLK最高時鐘頻率),往往在從機器件對應的數據手冊上有定義。通常情況下,從機器件支持的最大速率都是很高的,有時可能會超過MCU中SPI主機能夠輸出的最大時鐘頻率,此時,將直接使用MCU中SPI主機能夠輸出的最大時鐘頻率。
cs_pin和pfunc_cs均與片選引腳相關。pfunc_cs是指向自定義片選引腳控制函數的指針,若pfunc_cs的值為NULL,驅動將自動控制由cs_pin指定的引腳實現片選控制;若pfunc_cs的值不為NULL,指向了有效的自定義片選控制函數,則cs_pin不再被使用,片選控制將完全由pfunc_cs指向的函數實現。當需要片選引腳有效時(SPI數據傳輸前),系統將自動調用pfunc_cs指向的函數,并傳遞state的值為1。當需要片選引腳無效時(SPI數據傳輸結束后),也會調用pfunc_cs指向的函數,并傳遞state的值為0。一般情況下,片選引腳自動控制即可,即設置pfunc_cs的值為NULL,cs_pin的值設置為片選引腳,比如:PIO2_19。
例如,要使用SPI0(SPI總線的busid為0)操作74HC595。則首先應該定義并初始化一個與74HC595對應的從機器件。這就還需要知道幾點重要的信息:數據位寬、模式、最高速率和片選引腳。
要獲取這些信息,就必須查看芯片相關的數據手冊和相應的硬件電路。74HC595是一種常用的串行輸入/并行輸出轉換芯片,其引腳分布圖詳見圖7.8。
圖7.8 74HC595管腳圖
74HC595內部有兩個8位寄存器:一個移位寄存器和一個數據鎖存寄存器。移位寄存器的8位數據使用Q0’ ~ Q7’表示,其中僅Q7’位對應的電平通過Q7’引腳輸出,其余位未使用引腳輸出。數據鎖存寄存器的8位數據使用Q0 ~ Q7表示,并使用Q0 ~ Q7引腳將8位數據輸出。
移位寄存器在時鐘信號CP的作用下,每個上升沿將D引腳電平移至移位寄存器的最低位,其余位依次向高位移動,移位寄存器的值發生改變,Q7’引腳的輸出隨著值的改變而改變,但此時,數據鎖存寄存器中的值保持不變,即Q0 ~ Q7的輸出保持不變。由此可見,由于移位作用,原移位寄存器中的最高位Q7’將被完全移除,數據丟失。若希望數據不丟失,即并行輸出的數據超過8位,則可以將多個74HC595串接,將Q7’引腳連接至下一個74HC595的數據輸入端D。這樣,每次移位時,原先的Q7’將移動至下一個74HC595中,這也是為什么單獨將Q7’通過引腳引出的原因,通過多個74HC595級聯,可以將并行輸出擴展為16位、24位、32位等。
數據鎖存寄存器的值可以通過STR引腳輸入上升沿信號更新,當STR引腳輸入上升沿信號時,數據鎖存寄存器中的值將更新為移位寄存器中的值,即Q0 ~ Q7的值更新為Q0’~Q7’的值。這樣的設計可以保證同時改變所有并行輸出,將8位數據一次性地在Q0 ~ Q7端輸出,如果不使用數據鎖存寄存器,而是直接將移位寄存器的值輸出,則輸出將受到移位過程的影響,即每次移位,數據輸出都可能發生變化。
除基本的CP時鐘信號、D數據輸入信號、STR鎖存信號、Q0 ~ Q7輸出信號、Q7’輸出信號外,還有和兩個控制信號,為鎖存寄存器的輸出使能信號,當
74HC595傳輸數據的過程為:D端為數據輸入口,在時鐘CP的作用下每次傳輸一位數據至移位寄存器中,如需傳輸8位數據,則應在CP端輸入8個時鐘信號,傳輸結束后,需要在STR上產生一個上升沿信號,以便將移位寄存器中的數據輸出。由此可見,其數據傳輸的方式和SPI傳輸數據的方式極為相似,均是在時鐘信號的同步下,每個時鐘傳輸一位數據,特殊的,74HC595在傳輸結束后需要在STR上輸入一個上升沿鎖存信號。實際上,在SPI傳輸中,傳輸數據前,主機會將片選信號拉低,傳輸結束后,主機會將片選信號拉高,顯然,若將STR作為從機片選信號由主機控制,則在數據傳輸結束后,主機會將片選信號拉高,同樣可以達到在STR上產生上升沿的效果。
基于此,可以將74HC595看作一個SPI從機器件,SPI主機的SCLK時鐘信號與CP相連,MOSI作為主出從入,與D相連,CS作為片選信號,與STR相連。此外,74HC595作為一個串/轉并芯片,只能輸出數據,不能輸入數據,即SPI主機只需向74HC595發送數據,不需要從74HC595接收數據,因此,無需使用SPI的MISO引腳。
MiniPort-595模塊采用74HC595擴展8路I/O,可以直接驅動LED顯示模塊,其等效電路詳見圖7.9。由此可見,模塊僅有3路信號輸入,但可并行輸出8位數據。電路中,將 直接接地,固定為低電平,使能了數據鎖存寄存器的輸出。將 直接與電源連接,固定為高電平,即不使用其復位功能,使其不影響移位寄存器的值。
圖7.9 74HC595電路圖
實際中,當MiniPort-595與i.MX28x的擴展板連接時,MOSI、SCLK與i.MX28x的SPI0連接,片選信號CS與PIO2_19連接。
在初始化與74HC595對應的SPI從機器件實例時,還需要獲取到數據寬度、SPI模式、最高時鐘頻率等信息。即:
-
數據寬度
74HC595只有8個并行輸出口,內部寄存器也均為8位,因此,單次傳輸的數據為8位,SPI傳輸時的數據寬度即為8。
-
SPI模式
數據在CP時鐘信號的上升沿被采樣送入74HC595的移位寄存器,對空閑時鐘電平沒有要求,根據SPI幾種模式的定義可知(詳見圖7.6和圖7.7),模式0和模式3均是在時鐘上升沿采樣數據,因此,使用模式0和模式3均可,后續的程序選擇模式3作為范例。
-
最高時鐘頻率
雖然74HC595支持的最高時鐘頻率可達100MHz,但實際中,受到MCU處理速度的限制以及MCU輸出引腳翻轉頻率的限制,往往并不能達到該值。因此,最高時鐘頻率可以先設置在一個相對合理的范圍,比如,3000000Hz(3MHz)。后續若3MHz不滿足應用需求,可以在此基礎上根據需要調大調小即可。
基于以上信息,可以定義并初始化一個與74HC595對應的SPI從機器件實例,范例詳見程序清單7.20。
程序清單7.20 初始化從機器件實例的范例程序
3. 設置SPI從機器件實例
設置SPI從機實例時,會檢查MCU的SPI主機是否支持從機實例的相關參數和模式。如果不能支持,則設置失敗,說明該從機不能使用。其函數原型為:
其中,p_dev是指向SPI從機器件實例的指針。返回值為標準的錯誤號,返回AW_OK時表示設置成功,否則,表示設置失敗,存在不支持的位寬、速率或模式等。
例如,在完成74HC595的初始化后,再通過該接口設置一次74HC595器件實例,范例程序詳見程序清單7.21。
程序清單7.21 設置SPI從機器件實例的范例程序
4. 先寫后讀
當設定好從機實例后,即可與從機器件進行數據交互。雖然SPI協議是可以全雙工通信的,即數據的發送和接收同時進行,但絕大部分情況下,并不會同時發送數據或接收數據,往往數據時單向進行的,即發送的時候不接收數據,或接收的時候不發送數據。
基于常見的SPI從機器件的操作方法,AWorks定義了兩種情形下的數據通信:先寫入一段數據至從機,再讀取一段數據;先寫入一段數據至從機,再寫入一段數據。之所以均是先寫入一段數據,是因為絕大多數數從機器件在操作前,首先都要發送一段命令數據至從機,以指定接下來的具體操作(讀數據或寫數據)。
先寫后讀即是主機先發送數據至從機(寫),再自從機接收數據(讀)。注意,該函數會等待數據傳輸完成后才會返回,因此該函數是阻塞式的,不應在中斷環境中調用。其函數原型為:
其中,p_dev指向從機器件實例,表示本次數據通信的目標對象,p_txbuf指向需要首先寫入從機的數據,n_tx為寫入數據的字節數。p_rxbuf指向數據發送完成后,接收數據的緩沖區,n_rx為接收數據的個數。返回值為標準的錯誤號,返回AW_OK時表示數據傳輸成功,否則,表示數據傳輸失敗。
注意,若只需要寫入數據,不需要讀取數據,則可以將p_rxbuf設置為NULL,n_rx設置為0,同理,若只需要讀取數據,不需要發送數據,則可以將p_txbuf設置為NULL,n_tx設置為0。例如,需要發送數據至74HC595,以并行輸出8位數據0x55,則范例程序詳見程序清單7.22。
程序清單7.22 先寫后讀范例程序(1)
為了更加直觀的觀察輸出數據是否正確,可以將MiniPort-LED與MiniPort-595連接,等效電路圖詳見圖7.10,由此可見,74HC595的輸出端為低電平時,對應LED點亮, 0x55對應的二進制為01010101,因此,若成功輸出,則可以觀察到LED1、LED3、LED5和LED7被點亮,LED0、LED2、LED4和LED6被熄滅。
圖7.10 MiniPort-595 + MiniPort-LED等效電路圖
由于74HC595使用簡單,只能寫入數據,因此,上述范例程序并沒有用到讀取數據。實際上,對于常見的SPI從機器件,其通信協議往往都是采用“命令”+“數據”的格式。如典型的SPI FLASH從機器件:MX25L1606,在對其操作時,均需要先寫1字節的命令,后續再是具體的數據,具體數據的含義隨著命令的不同而不同。例如,每個MX25L1606內部具有一個3字節的電子ID號(RDID),如需讀取該ID,則需要先發送單字節的命令0x9f。則使用先寫后讀接口讀取ID的關鍵語句詳見程序清單7.23。
程序清單7.23 先寫后讀范例程序(2)
更多命令以及RDID的含義詳見相應的數據手冊,這里僅用于說明對于這種常見的操作方法,如何正確的使用SPI接口。
5. 連續兩次寫
連續兩次寫,即主機先發送一段數據至從機(寫),發送結束后,再發送一段數據至從機(寫)。注意,該函數會等待數據傳輸完成后才會返回,因此該函數是阻塞式的,不應在中斷環境中調用。其函數原型為:
其中,p_dev指向從機器件實例,表示本次數據通信的目標對象,p_txbuf0指向需要首先寫入從機的數據,n_tx0為寫入數據的字節數。p_txbuf0指向數據發送完成后,開始發送下一段數據,p_txbuf1指向需要再次寫入從機的數據,n_tx1為寫入數據的字節數。返回值為標準的錯誤號,返回AW_OK時表示數據傳輸成功,否則,表示數據傳輸失敗。
注意,若只需要寫入一段數據,則可以將p_txbuf1設置為NULL,n_tx1設置為0。
同樣可以使用該接口發送數據至74HC595,例如,使用MiniPort-595控制MiniPort-LED,使LED0 ~ LED7依次循環點亮,實現簡易的流水燈效果,范例程序詳見程序清單7.24。
程序清單7.24 執行連續兩次寫的范例程序(1)
程序中,由于LED是低電平點亮,因此,通過74HC595輸出數據時,需要將data取反,以便使數據中與需要點亮的LED相應位的值為0。
由于74HC595只能寫入單字節數據,因此,上述范例程序并沒有使用到第二次的數據寫入,第二次寫入的數據緩沖區被設定為了NULL。為了更全面的說明該接口的用法,同樣以MX25L1606為例,如果要在SPI FLASH存儲器中存入一段數據,則應先發送命令0x02和存儲數據的起始地址(3字節,24位),接著再發送實際的待存儲的數據。寫入數據至指定地址的關鍵語句詳見程序清單7.25。
程序清單7.25 執行連續兩次寫的范例程序(2)
實際中,寫入數據前,應確保相應區域被擦除,擦除同樣有相關的命令,更多命令以及數據存儲的注意事項詳見相應的數據手冊,這里僅用于說明連續兩次寫接口的使用方法。
-
mcu
+關注
關注
146文章
17148瀏覽量
351197 -
嵌入式
+關注
關注
5082文章
19126瀏覽量
305198
原文標題:AWorks軟件篇 — 通用外設接口(GPIO、PWM、SPI)
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論