TI-RTOS 使用介紹
- TI-RTOS介紹
2012 年 12 月 17 日,北京訊日前,德州儀器 (TI) 宣布推出面向 MCU平臺、基于搶占式多執行緒內核的完整實時作業系統 TI-,加大對嵌入式處理軟體及工具產業環境的投入,軟體設計已變得更加便捷的今天,微控制器 (MCU) 開發人員可將更多的時間和精力集中在獨特應用開發上,TI 在為實時應用提供生產質量級作業系統 (OS) 方面擁有超過 20 年的豐富經驗,現已將其專業技術應用于各種 組件(包括普及型 SYS/? 實時內核與網路開發套件 NDKTCP/IP 協議堆疊),并將其集成,創建了完整的微控制器 ,該最新 OS 可顯著加速軟體開發,設計人員無需撰寫和維護諸如調度工具、協議堆疊以及低級驅動器等復雜的系統軟體程式,TI-RTOS 的市場獨特性在于,可在整個 TI 完整 MCU 產品系列中提供統一的嵌入式軟體平臺,幫助開發人員便捷地擴展設計,通過將原有應用移植至最新處理器來更新或添加功能,此外,該統一平臺還可為 TI 設計網路軟體產業環境的合作伙伴帶來優勢,為其提供一種無專利限制的廣泛應用型免費平臺,
TI MCU 副總裁 Scott Roller 指出:“有了高度集成的 MCU,嵌入式硬體開發現已變得更加便捷,但是,由于集成了更多外設、存盤器以及連接選項,軟體開發也已經變得更加復雜,因而我們推出了 TI-RTOS,現在開發人員可構建支持互聯網及 USB 連接的 MCU 設計,無需擔心軟體開發過于繁重耗時,”
TI-RTOS 的特性與優勢:
· 便捷的軟體開發:提供完整、成熟與穩定的嵌入式操作環境,可通過中間件與驅動器的全面啟動增加更多產品功能,這些組件包括:
確定性實時多任務內核 (SYS/);
TCP/IP 協議堆疊,包括網路應用;
USB、EMAC、MMC/SD 主機及器件協議堆疊以及類驅動器;
與 C RTS 檔案 I/O 功能全面集成的 FAT 兼容型檔案系統;
以太網、USB、、I2C 與 SD 器件驅動器;
雙核器件的低開銷內核間通信機制,
· 直接開始軟體開發,實作網路連接:提供已集成并經過測驗的組件,用戶無需拼湊代碼,也不會出現組件版本不匹配問題,可確保其應用在多執行緒環境下作業;
· 使用新功能便捷改進現有軟體基礎:添加新任務不會中斷重要系統功能的實時回應;
· 在雙核器件間移動函式,優化性能:在 ARM? 與 DSP 內核上使用相同的 TI-RTOS 內核;
· 接受高穩健檔案與示例來擴展設計,包括適用于多任務開發與集成的示例和 API,有助于評估 TI-RTOS 并獲得培訓;
· 支持片上存盤器限制:RTOS 基于支持小型封裝的模塊化架構,可便捷地移除應用不需要的軟體功能,此外,組件也可擴展,可進一步降低存盤器需求;
· 可在熟悉的環境中無縫開發:TI-RTOS 全面集成于 TI Composer Studio? 集成型開發環境 (IDE),提供電路板支持套件與開發套件,包括 TI MCU LaunchPads 等;
· 通過 TI 廣泛的設計網路軟體開發商網路獲得專用軟體:Interniche 和 Simma 等合作伙伴可提供更多配合 TI-RTOS 作業的通信協議堆疊;
· 無提前支付或運行時許可證費,提供免費支持:由 TI 提供直接支持的全面 C 語言源代碼, - TI-RTOS概述
TI-RTOS是CC2640R2F設備上低功耗藍牙專案的運行環境,TI-RTOS內核是傳統SYS/BIOS內核的定制版本,是一個具有驅動程式,同步和調度工具的實時搶占式多執行緒作業系統, - 執行緒模塊
TI-RTOS內核管理執行緒執行的四個不同的任務級別,執行緒模塊串列如下圖所示,按照優先級降序排列,
? 硬體中斷
? 軟體中斷
? 任務
? 后臺空閑功能的空閑任務

圖1. TI-RTOS執行執行緒
這一節將要介紹四個執行執行緒以及整個TI-RTOS中用于訊息傳遞和同步的各種結構,在大多數情況下,TI-RTOS函式在util.c(Util)中已被抽象為更高級別的函式,較低級的TI-RTOS函式在TI-RTOS Kernel API Guide中有描述,你可以在TI-RTOS內核用戶指南中查看,本檔案還定義了TI-RTOS包含的軟體包和模塊,
3.1硬體中斷(Hwi)
Hwi執行緒(也稱為中斷服務程式或ISR)是TI-RTOS應用程式中具有最高優先級的執行緒,Hwi執行緒用于執行有嚴格時間限制的關鍵任務,它們被觸發以回應在實時環境中發生的外部異步事件(中斷),Hwi執行緒總是運行至完成,但是如果有其他的Hwi中斷使能,它也可以暫時地被其他中斷觸發的Hwi執行緒搶占,這就是所謂的中斷嵌套,有關中斷嵌套,向導和功能的具體資訊,可以在CC26XX技術參考手冊中查看,
一般來說中斷服務程式運行時間較短,不影響硬實時系統的要求,另外,由于Hwis總是運行至完成,所以在在其背景關系中不會呼叫阻塞API,
中斷的TI-RTOS驅動程式將初始化分配的外設所需的中斷,有關詳細資訊,請參閱外設驅動,
注意:
外部資源(External Resources)提供了使用GPIO和Hwis的示例,雖然SDK包含一個外設驅動程式庫抽象了對硬體暫存器的訪問,但建議使用執行緒>安全的TI-RTOS驅動程式,如外設驅動中所述,
CC2640R2F的Hwi模塊還支持零延遲中斷,這些中斷不通過TI-RTOS Hwi調度程式,因此比標準中斷更靈敏,但是該功能禁止其中斷服務程式直接呼叫任何TI-RTOS內核API,ISR要保護自己的背景關系防止它干擾內核的調度程式,
為了能讓低功耗藍牙協議堆疊滿足RF嚴格的時序要求,所有應用程式定義的Hwis都要以最低優先級執行,TI向系統添加新的Hwis時,建議不要修改默認的Hwi優先級,為了不破壞低功耗藍牙協議堆疊中TI-RTOS的嚴格時序,應用程式中定義了臨界段代碼,執行臨界段代碼時中斷會被關閉,在臨界段代碼執行完畢之后才會重新啟用中斷,
3.2 軟體中斷(Swi)
軟體中斷執行緒(Swis)是在Hwi執行緒和任務執行緒之間提供的一個額外的優先級,與Hwis由硬體中斷觸發不同,Swis是通過在程式撰寫程序中呼叫某些Swi模塊的API來觸發中斷,由于時間限制,Swis中斷服務程式不能作為任務來運行,其截止時間不如硬體中斷服務程式那么嚴格,Swi也總是運行至完成,它允許Hwis將不太重要的中斷處理放到較低優先級的執行緒來處理,從而最小化CPU在中斷服務程式中花費的時間,Swis需要足夠的空間來保存每個Swi中斷優先級的背景關系,它的每個執行緒都使用單獨的堆疊,
與Hwis類似,Swis需要保持簡短,它不應該包含任何阻塞API的呼叫,這保證了諸如無線協議堆疊等高優先級任務能根據需要執行,在Swis中建議發布某些TI-RTOS同步物件,然后把具體處理放在任務背景關系中,圖2說明了這種用例,

圖2. 搶占情景
Swi背景關系中常常會由時鐘模塊呼叫,對于Swi服務函式,不呼叫阻塞API,保證較短的執行時間是很重要的,
3.3任務
任務執行緒的優先級高于空閑任務執行緒,低于軟體中斷,任務與軟體中斷的不同之處在于,任務可以在執行期間等待(阻塞),直到有必要的資源可用,每個任務執行緒都有一個獨立的堆疊,TI-RTOS提供了可用于任務間通信和同步的多種機制,這些包括信號量,事件,訊息佇列和郵箱,
有關詳細資訊,可以在本文后面的任務中查看,
3.4空閑任務
空閑任務執行緒在TI-RTOS應用程式中優先級最低,它會執行一個空閑回圈,在主函式回傳之后,TI-RTOS應用程式會呼叫每個TI-RTOS模塊的啟動程式,然后進入空閑回圈,每個執行緒在被再次呼叫之前都必須等待所有其他執行緒執行完成,空閑回圈在沒有更高優先級的任務需要執行的時候會一直執行,只有沒有嚴格截止期限的函式才能在空閑回圈中執行,
對于CC2640R2F設備,空閑任務支持電源策略管理器設定為允許的最低功率節省模式,
4. 內核配置
TI-RTOS應用程式可以使用工程中的.cfg檔案來配置TI-RTOS內核,在IAR和CCS工程中,組態檔在應用程式專案作業區中的TOOLS檔案夾下,
該配置通過選擇性地包括或使用可用于內核的RTSC模塊來實作,.cfg中通過呼叫xdc.useModule()函式來設定TI-RTOS內核用戶指南中定義的各種選項來啟用一個模塊,
注意:
BLE5-Stack中的專案(如simple_peripheral)通常會包含一個app_ble.cfg組態檔,
可以在.cfg檔案中配置的一些選項(但不限于這些):
? 啟動選項
? Hwi,Swi和任務優先級的數量
? 例外和錯誤處理
? 系統刻度的持續時間(TI-RTOS內核中最基本的時間單位),
? 定義應用程式的入口點和中斷向量
? TI-RTOS堆和堆疊(不要與其他堆管理器混淆!)
? 包括預編譯的內核和TI-RTOS驅動程式庫
? 系統配置(for System_printf())
一旦.cfg檔案發生變動時,您需要重新運行XDCTools的configuro工具,在IAR和CCS提供的示例中這一步作為預構建步驟已經為您提供,
注意:
.cfg的名稱并不重要,但是專案只能包含一個.cfg檔案,
對于CC2640R2F,TI-RTOS內核存盤在ROM中,通常為了節省Flash的訪問足跡,.cfg也會包含在內核的ROM模塊中,如清單1所示,
清單1. 如何把TI-RTOS內核包含到ROM中
/ * ================ ROM configuration================ * /
/ *
*To use BIOS in flash, comment out the code block below.
* /
if (typeof NO_ROM == 'undefined' ||(typeof NO_ROM != 'undefined' && NO_ROM == 0 ))
{
var ROM = xdc.useModule('ti.sysbios.rom.ROM');
if(program.cpu.deviceName .match(/CC26/)){
ROM.romName = ROM.CC2640R2F;
}
else if(Program.cpu.deviceName.match(/CC13/)){
ROM.romName = ROM.CC1350;
}
}
ROM中的TI-RTOS內核針對性能進行了優化,如果您的應用程式需要額外的工具(通常用于除錯),則必須將TI-RTOS內核包含在Flash中,這將增加Flash消耗,下面顯示的是在ROM中使用TI-RTOS內核的簡要串列,
? BIOS.assertsEnabled必須設定為false
? BIOS.logsEnabled必須設定為false
? BIOS.taskEnabled必須設定為true
? BIOS.swiEnabled必須設定為true
? BIOS.runtimeCreatesEnabled 必須設定為true
? BIOS必須使用該ti.sysbios.gates.GateMutex模塊
? Clock.tickSource必須設定為Clock.TickSource_TIMER
? Semaphore.supportsPriority一定是false
? Swi,Task和Hwi hooks不容許
? Swi,Task和Hwi name instances不容許
? 任務堆疊檢查被禁用
? Hwi.disablePriority必須設定為0x20
? Hwi.dispatcherAutoNestingSupport必須設定為true
? 默認的Heap instance必須設定為ti.sysbios.heaps.HeapMem管理者
有關上述串列外的其他檔案,可以在TI-RTOS內核用戶指南中查看,
5. 創造與構建
大多數TI-RTOS模塊通常都有_create()和_construct()APIs用來初始化最初的例程,這兩個API之間運行時的主要差異是記憶體分配和錯誤處理,
在初始化之前,創建API會從默認的TI-RTOS堆執行記憶體分配,因此,應用程式必須在繼續之前檢查有效句柄的回傳值,
清單2. 創建一個信號量
Semaphore_Handle sem;
Semaphore_Params semParams;
Semaphore_Params_init(&semParams);
sem = Semaphore_create(0,&semParams,NULL);/*Memory allocated in here*/
if (sem == NULL) /* Check if the handle is valid */
{
System_abort("Semaphore could not be created");
}
構造API提供了一個用于存盤實體變數的資料結構,由于記憶體已被預先分配給實體,構建后不再需要進行錯誤檢查,
清單3. 構造一個信號量
Semaphore_Handle SEM ;
Semaphore_Params semParams ;
Semaphore_Struct structSem; /* Memory allocated at build time */
Semaphore_Params_init(&semParams);
Semaphore_construct(&structSem, 0, &semParams);
/* It's optional to store the handle */
sem = Semaphore_handle(&structSem);
-
執行緒同步
TI-RTOS內核提供了幾個諸如信號量,事件和佇列用于同步任務的模塊,以下部分討論這些常見的TI-RTOS同步模塊,
6.1信號量
信號量通常用于TI-RTOS應用中的任務同步和互斥,圖3.顯示了信號量的功能,信號量可以分為計數信號量或二進制信號量,程式通過Semaphore_post()來釋放信號量,計數信號量會記錄跟蹤信號量發布的次數,當一組資源在任務之間共享時,信號量很有用,在使用這些資源之前,任務會呼叫Semaphore_pend()來查看資源是否可用,只有共享資源被釋放出來之后處于等待狀態的任務得到該資源才能執行,二進制信號量只能有兩種狀態:可用(count = 1)和不可用(count = 0),二進制信號量可用于在任務之間共享一個資源,或者用于基本信令機制,可以多次發布信號量,二進制信號不跟蹤計數, 他們只跟蹤信號量是否已經發布,

圖3. 信號量功能
初始化信號量
以下代碼描述了如何在TI-RTOS中初始化信號量,信號量可以創建和構造,如本文上面的創建與構造中所述,
有關如何創建信號量,請參見清單2,
有關如何構造信號量,請參見清單3,
等待信號量
Semaphore_pend()是一個阻塞函式呼叫,它只能在任務中呼叫,當任務呼叫此阻塞函式后將會等待信號量的釋放(post),這時就緒的低優先級任務可以執行,呼叫Semaphore_pend()如果計數器為0,任務將阻塞,否則計數器會遞減1,任務執行,在另一個執行緒呼叫Semaphore_post()釋放信號量或者提供的系統滴答時鐘超時之前,任務都會保持阻塞狀態,通過讀取其回傳值Semaphore_pend()可以區分信號量是否發布或超時,
清單4. 等待一個信號量
bool isSuccessful;
uint32_t timeout = 1000 * (1000/Clock_tickPeriod);
/* Pend (approximately) up to 1 second */
isSuccessful = Semaphore_pend(sem, timeoutInTicks);
if (isSuccessful)
{
System_printf("Semaphore was posted");
}
else
{
System_printf("Semaphore timed out");
}
注意:
默認的TI-RTOS系統滴答時鐘周期為1毫秒,在CC26xx和CC13xx設備中通過設定.cfg檔案中的Clock.tickPeriod = 10,將此默認值重新配
置為10微秒,
給定一個10微秒的系統滴答時鐘,timeout在清單4中約為1秒,
發布信號量
信號量的發布是通過呼叫Semaphore_post()完成的,在此發布的信號量上掛起的任務將從阻塞狀態轉換到就緒狀態,如果此時正好沒有更高優先級的執行緒準備運行,得到該信號量的任務將會運行,如果信號量上沒有掛起任務,呼叫Semaphore_post()將時信號量計數器加1,二進制信號量的最大計數為1,
清單5. 發布信號量
1 Semaphore_post (sem );
6.2事件
信號量提供了執行緒之間的基本同步,有些情況下,信號量本身就足夠了解什么行程需要觸發,然而有些同步的特定原因也需要跨執行緒傳遞,為了實作這一點,可以使用TI-RTOS 事件(Event)模塊,事件類似于信號量,每個事件實際上都包含一個信號量,使用事件的附加優點在于可以以執行緒安全的方式向任務通知特定事件,
初始化事件
創建和構建事件同樣遵循本文上面的創建與構建中說明的準則,如清單6所示,是一個關于如何構造Event實體的例子,
清單6. 構造事件
Event_Handle event;
Event_Params eventParams;
Event_Struct structEvent; /* Memory allocated at build time */
Event_Params_init(&eventParams);
Event_construct(&structEvent, 0, &eventParams);
/* It's optional to store the handle */
event = Event_handle(&structEvent);
事件等待
類似于Semaphore_pend(),任務執行緒會在呼叫Event_pend()時阻塞, 直到事件通過一個Event_post()發布或者等待超時,清單7展示了一個任務等待下面顯示的3個示例事件ID中的任意一個發布的代碼片段, BIOS_WAIT_FOREVER引數設定表示不會等待超時,只要時間沒發布會永遠等待下去,因此, Event_pend()將在回傳的位掩碼值中發布一個或多個事件,
Event_pend()回傳的每個事件會以執行緒安全的方式在事件實體中自動清除,因此,對于發布的事件只需保留本地副本,有關使用Event_pend()的詳細介紹 ,可以在TI-RTOS內核用戶指南中查看,
清單7. 事件掛起
#define START_ADVERTISING_EVT Event_Id_00
#define START_CONN_UPDATE_EVT Event_Id_01
#define CONN_PARAM_TIMEOUT_EVT Event_Id_02
void TaskFxn(..)
{
/* Local copy of events that have been posted */
uint32_t events;
while(1)
{
/* Wait for an event to be posted */
events = Event_pend(event,
Event_Id_NONE,
START_ADVERTISING_EVT |
START_CONN_UPDATE_EVT |
CONN_PARAM_TIMEOUT_EVT,
BIOS_WAIT_FOREVER);
if (events & START_ADVERTISING_EVT)
{
/* Process this event */
}
if (events & START_CONN_UPDATE_EVT)
{
/* Process this event */
}
if (events & CONN_PARAM_TIMEOUT_EVT)
{
/* Process this event */
}
}
}
事件發布
事件可以從一些TI-RTOS內核任務中發布(Post),通過呼叫Event_post()就可以發布事件物體和事件ID,清單8.顯示了高優先級執行緒(如Swi)如何發布特定事件,
清單8. 發布事件
#define START_ADVERTISING_EVT Event_Id_00
#define START_CONN_UPDATE_EVT Event_Id_01
#define CONN_PARAM_TIMEOUT_EVT Event_Id_02
void SwiFxn(UArg arg)
{
Event_post(event, START_ADVERTISING_EVT);
}
6.3佇列
TI-RTOS佇列是一個基于先入先出(FIFO),執行緒安全的單向訊息傳遞模塊,佇列通常用于高優先級執行緒將訊息傳遞給較低優先級的執行緒以進行延遲處理;因此允許低優先級任務阻塞直到需要運行,
在圖24中,佇列被配置為從任務A到任務B的單向通信,任務A將訊息放入到佇列中,任務B從佇列獲取訊息,

圖4. 佇列訊息傳遞程序
在BLE5-Stack中,TI-RTOS佇列功能在util.c中已經被抽象為介面函式,在TI-RTOS內核用戶指南的佇列模塊檔案中可以查看有關的基礎功能 ,util.c中的函式結合佇列模塊中的佇列與事件模塊中的事件來實作執行緒之間訊息傳遞,
在CC2640R2F軟體中,ICall使用來自各自模塊的佇列和事件在應用程式和協議堆疊任務之間傳遞訊息,高優先級任務,Hwi或Swi將訊息發布到佇列中傳遞給應用程式任務,然后,當沒有其他高優先級執行緒運行時,應用程式任務將在其背景關系中處理此訊息,
util模塊包含一組抽象的TI-RTOS佇列功能,如下所示:
? Util_constructQueue()創建一個佇列,
? Util_enqueueMsg()將專案放入佇列,
? Util_dequeueMsg()從佇列中獲取專案,
功能實體
圖5和圖6說明了佇列如何將按鍵訊息從Hwi排入佇列(到Swi中的板卡按鍵模塊),然后在任務背景關系中發布后處理,這個實體來自于BLE5-Stack中的simple_central工程,

圖5. 訊息加入佇列的時序圖
在中斷使能的情況下,引腳中斷可能會在Hwi背景關系中異步發生 ,為了使中斷盡可能短,與中斷相關的作業推遲到任務來處理,在BLE5-Stack中的simple_central示例中,引腳中斷通過板卡按鍵模塊進行抽象,該模塊通過Swi注冊回呼通知函式 ,在這種情況下,SimpleBLECentral_keyChangeHandler是注冊的回呼函式,
圖5中的步驟1:展示了當鍵按下的時候回呼SimpleBLECentral_keyChangeHandler,該事件被放入應用程式的佇列中等待處理,
清單9. 定義
SimpleBLECentral_keyChangeHandler()
void SimpleBLECentral_keyChangeHandler(uint8 keys)
{
SimpleBLECentral_enqueueMsg(SBC_KEY_CHANGE_EVT, keys, NULL);
}
圖5中的步驟2:顯示了按鍵訊息是如何被壓入simple_central任務的佇列中的,首先通過ICall_malloc()分配記憶體,以便訊息可以放入佇列中,一旦訊息添加進佇列,Util_enqueueMsg()將發布一個UTIL_QUEUE_EVENT_ID事件來通知應用程式進行處理,
清單10. 定義SimpleBLECentral_enqueueMsg()
static uint8_t SimpleBLECentral_enqueueMsg(uint8_t event, uint8_t state, uint8_t *pData)
{
sbcEvt_t *pMsg = ICall_malloc(sizeof(sbcEvt_t));
// Create dynamic pointer to message.
if (pMsg)
{
pMsg->hdr.event = event;
pMsg->hdr.state = state;
pMsg->pData = pData;
// Enqueue the message.
return Util_enqueueMsg(appMsgQueue, sem, (uint8_t *)pMsg);
}
return FALSE;
}

圖6. 訊息出隊的時序圖
圖6中的步驟3:simple_central應用程式一直在檢查是否有訊息被放置在佇列中需要進行處理,當UTIL_QUEUE_EVENT_ID事件發布之后,它也就被解除阻塞,開始處理訊息,
清單11. 處理應用程式訊息
// If TI-RTOS queue is not empty, process app message
while (!Queue_empty(appMsgQueue))
{
sbcEvt_t *pMsg = (sbcEvt_t *)Util_dequeueMsg(appMsgQueue);
if (pMsg)
{
// Process message
SimpleBLECentral_processAppMsg(pMsg);
// Free the space from the message
ICall_free(pMsg);
}
}
圖6中的步驟4:simple_central應用取出佇列中的訊息并對其進行處理,
清單12. 處理按鍵中斷訊息
static void SimpleBLECentral_processAppMsg(sbcEvt_t *pMsg)
{
switch (pMsg->hdr.event)
{
case SBC_KEY_CHANGE_EVT:
SimpleBLECentral_handleKeys(0, pMsg->hdr.state);
break;
//...
}
}
圖6中的步驟5:simple_central應用程式處理完訊息后可以釋放在步驟2中所分配的記憶體,
7. 任務
TI-RTOS任務(或稱為執行緒)就是一段簡單的程式,通常是一段死回圈,實際上,將處理器從一個任務切換到另一個任務有助于實作并發,每個任務總是處于以下運行狀態之一:
? 運行:任務當前正在運行
? 就緒:任務準備好等待執行
? 阻塞:任務被暫停執行
? 終止:任務終止執行
? 無效:任務處于無效串列中(還不受TI-RTOS管理)
有且只有一個任務總是在CPU中運行,當然它有可能只是空閑任務( 見圖21.),當前運行的任務可以被某些喚醒的高優先級任務以及其他模塊(如Semaphores)的功能阻止執行,當前任務也可以自行終止執行,在任一情況下,處理器都切換到準備運行的最高優先級任務,有關這些功能的更多資訊,請參見TI-RTOS內核用戶指南中TI.sysbios.knl軟體包中的任務模塊,
每個任務都會分配相應的優先級,多個任務可以具有相同的優先級,任務是從最高優先級向最低優先級執行的; 相同優先級的任務按照到達順序進行執行,當前運行的任務的優先級永遠不會低于任何就緒任務的優先級,當有更高優先級的任務就緒時,正在運行的任務才會被搶占并重新安排執行,
在simple_peripheral應用中,低功耗藍牙協議堆疊任務被給予最高優先級(5),應用任務被給予最低優先級(1),
初始化任務
初始化任務時,會給它分配獨立的運行堆疊,用于存盤區域變數以及進一步嵌套函式呼叫,在單個程式中執行的所有任務共享一組通用的全域變數,根據C語言標準規范中的函式訪問范圍進行訪問,分配的運行堆疊就是任務的背景關系,以下是正在構建的應用程式任務的示例,
清單13. TI-RTOS任務
#include <xdc/std.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
/* Task's stack */
uint8_t sbcTaskStack[TASK_STACK_SIZE];
/* Task object (to be constructed) */
Task_Struct task0;
/* Task function */
void taskFunction(UArg arg0, UArg arg1)
{
/* Local variables. Variables here go onto task stack!! */
/* Run one-time code when task starts */
while (1) /* Run loop forever (unless terminated) */
{
/*
* Block on a signal or for a duration. Examples:
* ``Sempahore_pend()``
* ``Event_pend()``
* ``Task_sleep()``
*
* "Process data"
*/
}
}
int main() {
Task_Params taskParams;
// Configure task
Task_Params_init(&taskParams);
taskParams.stack = sbcTaskStack;
taskParams.stackSize = TASK_STACK_SIZE;
taskParams.priority = TASK_PRIORITY;
Task_construct(&task0, taskFunction, &taskParams, NULL);
BIOS_start();
}
在啟動TI-RTOS內核的調度程式BIOS_start()之前,在main()中需要完成所有任務的創建,任務在調度程式啟動后按其分配的優先級執行,
TI建議盡量使用現有的任務應用程式進行特定應用的處理,在工程中添加額外的任務時,必須在TI-RTOS組態檔(.cfg)中定義的TI-RTOS優先級范圍內為該任務分配優先級,
提示:
盡量減少任務優先級別數量,以在TI-RTOS組態檔(.cfg)中節省出額外的RAM:
Clock.tickPeriod = 6;
不要添加具有等于或高于低功耗藍牙協議堆疊任務優先級的任務,有關系統任務層次結構的詳細資訊,可以在標準工程任務層次結構中查看,
確保任務的堆疊大小至少為512位元組的預定義記憶體,必須分配足夠大的堆疊空間來保證程式的正常運行以及任務搶占背景關系的存盤,任務搶占背景關系是當一個任務由于中斷產生或者被較高優先級任務搶占而被保存的背景關系,使用IDE的TI-RTOS分析工具,可以分析任務以確定任務堆疊使用情況的峰值,
注意:
這里用術語講了如何去構建任務,在TI-RTOS中實際是如何構建任務的你可以在本文前面的創建與構造中查看,
任務功能
當任務被初始化時,任務函式的函式指標會傳遞給Task_construct函式,當任務第一次有機會運行時,這個函式就是由TI-RTOS管理運行的函式了,清單13.顯示了此任務函式的一般拓撲關系,
就典型的使用情況而言,任務大部分時間都通過呼叫稱為_pend()的API(例如Semaphore_pend())而處于阻塞狀態,通常,高優先級執行緒(例如Hwis或Swis)使用_post()API(例如Semaphore_post())來解除某些任務的阻塞,
8.時鐘
時鐘實體是可以在一定數量的系統時鐘節拍之后運行的函式,時鐘實體可以是單次或周期性的,這些實體可以配置為在創建后立即開始或者在一段延遲后啟動,并可以隨時停止,所有的時鐘實體當它們在Swi的背景關系中時鐘溢位就會被執行,以下示例顯示了TI-RTOS組態檔(.cfg)中設定的TI-RTOS時鐘周期的最小解析度,
注意:
默認的TI-RTOS內核刻度周期為1毫秒,對于CC2640R2F器件,在TI-RTOS組態檔(.cfg)中重新配置:
Clock.tickPeriod = 10 ;
每個系統時鐘節拍會啟動一個時鐘物件來為節拍計數,然后再和一系列的時鐘進行比較以確定相關函式是否該運行,對于需要更高解析度的定時器,TI建議使用16位硬體定時器通道或傳感器控制器來做,有關這些功能的更多資訊,請參見TI-RTOS內核用戶指南的 TI.sysbios.knl軟體包中的Clock模塊,
您可以直接在應用程式中使用內核的Clock API,Util模塊中提供了一組抽象的TI-RTOS時鐘功能,如下所示:
? Util_constructClock()創建一個Clock物件,
? Util_startClock()啟動一個現有的Clock物件,
? Util_restartClock()停止,重新啟動一個現有的Clock物件,
? Util_isActive()檢查Clock物件是否正在運行,
? Util_stopClock()停止一個現有的Clock物件,
? Util_rescheduleClock()重新配置一個現有的Clock物件,
功能實體
以下示例來自BLE5-Stack中的simple_peripheral專案,

圖7. 觸發時鐘物件
步驟1:在圖7中,通過Util_constructClock()來構建時鐘物件,在示例進入連接狀態后,它將通過Util_startClock()啟動Clock物件,
清單14. 在simple_peripheral中的構建periodicClockClock物件
// Clock instances for internal periodic events.
static Clock_Struct periodicClock;
// Create one-shot clocks for internal periodic events.
Util_constructClock(&periodicClock, SimpleBLEPeripheral_clockHandler,
SBP_PERIODIC_EVT_PERIOD, 0, false, SBP_PERIODIC_EVT);
步驟2:在圖7中,Clock物件的計時器到期后,它將在Swi背景關系中執行SimpleBLEPeripheral_clockHandler(),由于此呼叫不能被阻止并能阻止所有的任務,所以呼叫simple_peripheral中的Event_post(SBP_PERIODIC_EVT)來進行事件發布以盡可能保證其處理程序足夠短暫,
清單15. 定義
SimpleBLEPeripheral_clockHandler()
static void SimpleBLEPeripheral_clockHandler(UArg arg)
{
/* arg is passed in from Clock_construct() */
Event_post(events, arg);
}
注意:
時鐘功能不能呼叫阻塞內核的API或TI-RTOS驅動程式的API!時鐘功能的處理時間過長將影響分配給無線協議堆疊的高優先級任務中的實時約束!
步驟3:在圖7中,在Event_post(SBP_PERIODIC_EVT)發布了事件之后simple_peripheral任務被解除阻塞,然后繼續呼叫SimpleBLEPeripheral_performPeriodicTask()函式,最后如果要重新啟動此功能的定期執行,需要重新啟動periodicClock時鐘物件,
清單16.維護SBP_PERIODIC_EVT事件
if (events & SBP_PERIODIC_EVT)
{
// Perform periodic application task
SimpleBLEPeripheral_performPeriodicTask();
Util_startClock(&periodicClock);
}
9.驅動
TI-RTOS提供了一套可以添加到應用程式的CC26xx外設驅動程式,驅動程式為應用程式提供了與CC26xx板載外設介面以及外部設備通信的機制,這些驅動程式在DriverLib中抽象了對暫存器的訪問,
有關BLE5-Stack中每個TI-RTOS驅動程式的重要檔案及有關具體位置,請參閱BLE5-Stack release notes,本節僅概述了驅動程式如何適應軟體生態系統,有關可用的功能和驅動程式API的說明,請參閱TI-RTOS API Reference,
9.1添加驅動程式
某些驅動程式會作為源檔案被添加到工程專案作業區中Drivers檔案夾下的相應檔案夾中,如圖8所示,

圖8. 驅動程式檔案夾
驅動的源檔案可以在
T
I
R
T
O
S
D
R
I
V
E
R
S
B
A
S
E
TI_RTOS_DRIVERS_BASE
TIR?TOSD?RIVERSB?ASE\ti\drivers的相應檔案夾中找到,(
T
I
R
T
O
S
D
R
I
V
E
R
S
B
A
S
E
TI_RTOS_DRIVERS_BASE
TIR?TOSD?RIVERSB?ASE指的是安裝位置,可以在IAR Tools\Configure Custom Argument Variables選單中查看,對于CCS,相應的路徑變數在Project Options\ Resource\Linked Resources選項卡中定義)
例如,ECC和TRNG驅動程式是BLE5-Stack而不是TI-RTOS的一部分,它們分別位于
<SDK_INSTALL_DIR>\source\ti\ble5stack\common\cc26xx\ecc和
<SDK_INSTALL_DIR>\source\ti\ble5stack\hal\src\target_common中,
要向工程中添加驅動程式,需要將相應驅動程式的頭檔案包含在參考驅動程式API的應用程式檔案中,
例如,要添加用于讀取或控制輸出I/O引腳的PIN驅動程式,請添加以下內容:
#include <ti/drivers/pin/PINCC26XX.h>
還需要將以下TI-RTOS驅動程式檔案添加到Drivers\PIN檔案夾下的專案中:
? PINCC26XX.c
? PINCC26XX.h
? PIN.h
9.2板檔案
板檔案為將通過設定相應的引數將固定的驅動配置修改為特定的板配置,例如配置PIN驅動程式的GPIO表或者定義將哪些引腳分配3給I2C,SPI或UART,
有關板檔案的更多資訊,以及如何在TI EMs和LPs或本地硬體埠之間切換,請參見TI提供的板檔案部分部分,
9.3可用驅動程式
本節給大家介紹一些可用的驅動程式,并提供一些基本示例演示如何將驅動程式添加到simple_peripheral專案中,有關每個驅動程式的詳細資訊,可在TI-RTOS API Reference中查看,
9.4引腳
PIN驅動程式用于控制GPIO的I/O引腳或著連接在上面的硬體外圍設備,如TI提供的板檔案部分所述,引腳在main()中必須首先初始化為安全狀態(在板檔案中配置),此初始化后,任何模塊都可以使用PIN驅動程式配置一組引腳供使用,以下是在simple_peripheral任務中將一個引腳配置成作為中斷使用,另一個配置成作為輸出使用的示例,在中斷發生時交換它們的角色,IOID_x引腳號對應于CC26XX技術參考手冊中參考的DIO引腳號 ,下表列出了使用的引腳及其在CC2640R2F LaunchPad上的映射,這些已在板檔案中定義了,
信號名稱 針號 CC2640R2F LaunchPad映射
CC2640R2_LAUNCHXL_PIN_RLED IOID_6 DIO6(紅色)
CC2640R2_LAUNCHXL_PIN_BTN1 IOID_13 DIO13(BTN_1)
simple_peripheral.c代碼需要做以下修改,
- 包括PIN驅動程式檔案:
#include <ti / drivers / pin / PINCC26xx.h> - 宣告引腳配置表和引腳狀態并申明simple_peripheral任務使用的變數:
清單17. 引腳配置表
static PIN_Config SBP_configTable[] =
{
CC2640R2_LAUNCHXL_PIN_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
CC2640R2_LAUNCHXL_PIN_BTN1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_BOTHEDGES | PIN_HYSTERESIS,
PIN_TERMINATE
};
static PIN_State sbpPins;
static PIN_Handle hSbpPins;
static uint8_t LED_value = 0;
- 宣告在Hwi背景關系中執行ISR:
清單18. 宣告ISR
static void buttonHwiFxn(PIN_Handle hPin, PIN_Id pinId)
{
SimpleBLEPeripheral_enqueueMsg(SBP_BTN_EVT, 0, NULL);
}
- 在SimpleBLEPeripheral_processAppMsg中,添加一個case來處理上面的事件,并定義事件:
清單19. ISR事件處理
#define SBP_BTN_EVT
static void SimpleBLEPeripheral_processAppMsg(sbcEvt_t *pMsg)
{
switch (pMsg->hdr.event)
{
case SBP_BTN_EVT:
//toggle red LED
if (LED_value)
{
PIN_setOutputValue(hSbpPins, CC2640R2_LAUNCHXL_PIN_RLED , LED_value--);
}
else
{
PIN_setOutputValue(hSbpPins, CC2640R2_LAUNCHXL_PIN_RLED, LED_value++);
}
break;
//...
}
}
- 打開引腳使用,并在simple_peripheral_init()中配置中斷:
清單20. 打開引腳并配置中斷
// Open pin structure for use
hSbpPins = PIN_open(&sbpPins, SBP_configTable);
// Register ISR
PIN_registerIntCb(hSbpPins, buttonHwiFxn);
// Configure interrupt
PIN_setConfig(hSbpPins, PIN_BM_IRQ, CC2640R2_LAUNCHXL_PIN_BTN1 | PIN_IRQ_NEGEDGE);
// Enable wakeup
PIN_setConfig(hSbpPins, PINCC26XX_BM_WaKEUP, CC2640R2_LAUNCHXL_PIN_BTN1|PINCC26XX_WAKEUP_NEGEDGE);
- 編譯
- 下載
- 運行
注意:
按下CC2640R2F LaunchPad上的BTN-1按鈕可切換紅色LED,這里沒有做去抖動處理,
9.5 GPIO
GPIO模塊允許您通過簡單易用的API來管理通用I/O引腳,GPIO引腳的行為通常是靜態配置的,但也可以在運行時進行重新配置,
由于其簡單性,GPIO驅動程式不遵循其他TI-RTOS驅動程式型別,其中驅動程式的API具有單獨的特定于設備的實作,在GPIOxxx_Config結構中,這種差異最為明顯,它不需要您指定特定的函式表或物件,而其他驅動程式在各自的xxx_config中則要指定函式表和物件,你可以在驅動程式的組態檔中查看,例如:CC2640R2_LAUNCHXL.c,
以下是一個如何配置GPIO引腳,并利用注冊中斷回呼函式來控制LED燈的打開關閉的示例, - 在simple_peripheral.c中包含GPIO驅動程式檔案:
#include <ti / drivers / GPIO.h>
在Board.c中做一下添加: - 添加一組GPIO_PinConfig元素,用于定義應用程式使用的每個引腳的初始配置,引腳型別(即INPUT/OUTPUT),初始狀態(即OUTPUT_HIGH或LOW),中斷特性(RISING/FALLING邊沿等)以及器件特定引腳標識,以下是對于CC26XX設備GPIO_PinConfig陣列的特定配置示例:
清單21. 設定GPIO引腳配置陣列
//
// Array of Pin configurations
// NOTE: The order of the pin configurations must coincide with what was
// defined in CC2640R2_LAUNCH.h
// NOTE: Pins not used for interrupts should be placed at the end of the
// array. Callback entries can be omitted from callbacks array to
// reduce memory usage.
//
GPIO_PinConfig gpioPinConfigs[] = {
// Input pins
GPIOCC26XX_DIO_13 | GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_RISING, // Button 0
GPIOCC26XX_DIO_14 | GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_RISING, // Button 1
// Output pins
GPIOCC26XX_DIO_07 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW, // Green LED
GPIOCC26XX_DIO_06 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW, // Red LED
};
- 添加一組GPIO_CallbackFxn元素,用于存盤配置有中斷的GPIO引腳的回呼函式指標,這些陣列元素的索引對應于GPIO_PinConfig陣列中定義的引腳,這些函式指標可以通過參考陣列元素中的回呼函式名來靜態定義,也可以通過動態地將陣列元素設定為NULL并在運行時使用GPIO_setCallback()來插入回呼條目,不用于中斷的引腳可以從回呼陣列中省略,以減少記憶體使用(如果它們位于GPIO_PinConfig陣列的末尾),回呼函式語法應符合以下條件:
void (* GPIO_CallbackFxn )(unsigned int index );
index引數與傳遞給GPIO_setCallback()的索引相同,這允許通過使用索引來識別哪個GPIO發生了中斷,可以將相同的回呼函式用于多個GPIO中斷,以下是CC26XX設備的GPIO_CallbackFxn陣列的特定示例:
清單22. 設定GPIO回呼函式陣列
//
// Array of callback function pointers
// NOTE: The order of the pin configurations must coincide with what was
// defined in CC2640R2_LAUNCH.h
// NOTE: Pins not used for interrupts can be omitted from callbacks array to
// reduce memory usage (if placed at end of gpioPinConfigs array).
//
GPIO_CallbackFxn gpioCallbackFunctions[] = {
NULL, // Button 0
NULL, // Button 1
};
- 將前面提到的兩個陣列以及每個陣列的元素數量添加到GPIOCC26XX_Config結構體中用于驅動程式介面呼叫,此處還指定了所有能產生中斷的引腳的中斷優先級,中斷優先級的值是是跟設備有關的,同一個引腳的中斷優先級對于不同設備是不同的,在將此引數設定為非默認值之前,應熟悉設備中使用的中斷控制器,優先級的默認值用于表示應使用最低可能的優先級,以下是初始化GPIOCC26XX_Config結構的示例:
清單23. 設定GPIO配置結構
const GPIOCC26XX_Config GPIOCC26XX_config = {
.pinConfigs = (GPIO_PinConfig *)gpioPinConfigs,
.callbacks = (GPIO_CallbackFxn *)gpioCallbackFunctions,
.numberOfPinConfigs = sizeof(gpioPinConfigs)/sizeof(GPIO_PinConfig),
.numberOfCallbacks = sizeof(gpioCallbackFunctions)/sizeof(GPIO_CallbackFxn),
.intPriority = (~0)
};
以下是需要在simple_peripheral.c中添加的內容:
5. 按鍵回呼函式:
清單24. 設定按鍵的回呼函式
//
// ======== gpioButtonFxn0 ========
// Callback function for the GPIO interrupt on CC2640R2_LAUNCHXL_PIN_BTN1.
//
void gpioButtonFxn0(unsigned int index)
{
// Toggle the LED
GPIO_toggle(CC2640R2_LAUNCHXL_PIN_BTN1);
}
6. 初始化和使用GPIO(將其添加到simple_peripheral_init()):
清單25. 初始化和使用的GPIO
// Call GPIO driver init function
GPIO_init();
// Turn on user LED
GPIO_write(CC2640R2_LAUNCHXL_PIN_RLED, Board_GPIO_LED_ON);
// install Button callback
GPIO_setCallback(CC2640R2_LAUNCHXL_PIN_BTN1, gpioButtonFxn0);
// Enable interrupts
GPIO_enableInt(CC2640R2_LAUNCHXL_PIN_BTN1);
- 編譯
- 下載
- 運行
9.6其他驅動程式
TI-RTOS附帶的其他驅動程式有:UART,SPI,加密(AES),I2C,PDM,Power,RF和UDMA,由于協議堆疊使用了電源,RF和UDMA,因此在使用它們的時候,必須特別小心,與其他驅動程式一樣,這些驅動程式也在檔案中被詳細記錄,BLE5-Stack中提供了示例,
10.功耗管理
所有功耗管理功能由外設驅動程式和低功耗藍牙協議堆疊進行處理,可以通過包含或移除POWER_SAVING前處理器定義的符號來啟用或禁用此功能,當POWER_SAVING被使能時,設備根據低功耗藍牙事件,外設事件,應用計時器等的要求進入和退出休眠狀態,當POWER_SAVING未定義時,設備保持喚醒,有關修改前處理器定義的符號的步驟,請參閱用CCS開發中的訪問前處理器符號或者用IAR開發中的訪問前處理器符號,
有關電源管理功能的更多資訊,包括API和自定義UART驅動程式的示例,可以在安裝的TI-RTOS中包含的CC26xx的TI-RTOS電源管理中找到,只有使用自定義驅動程式時,才需要這些API,
另請參閱Measuring Bluetooth Smart Power Consumption (SWRA478),以分析系統功耗和電池壽命,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/183490.html
標籤:python
