CC254x--OSAL
OSAL運行原理
藍牙協議棧PROFILE、所有的應用程序、驅動等都是圍繞著OSAL組織運行的。OSAL(Operating System Abstraction Layer)操作系統抽象層,它不是一個真正的操作系統(它沒有 Context Switch 上下文切換功能),但它巧妙地組織各任務,支持任務優先級,任務之間可以通過事件和消息來通信,為任務提供軟定時器和動態內存分配。要避免的陷阱是,應用任務的單個函數運行時間不能太長 (如操作大批量數據的 Flash 寫),否則它無法及時調度高優先級的 LL(Link Layer)任務而導致藍牙通信中斷。
OSAL 為每一個任務函數分配了一個 16 位的EVENT 事件,每一位代表一個事件,其中最高位代表的事件為 SYS_EVENT_MSG,這個事件被 OSAL 系統保留,其他的 15 位可以由用戶定義,OSAL 在主循環里運行每次都會檢查每個任務函數的是否有事件發生(事件置位),如果有事件發生,將通過 taskid 來調用發生事件的任務函數,并將發生的事件傳遞到該函數中去,由任務函數處理對應的事件。
為什么要將返回值設置到tasksEvents中,就是因為其本質是一個單任務循環,如果某個子任務時間執行過長,會影響更高優先級的任務的響應變慢,影響整體性能。因此如果一個任務執行比較長,宜進行分割,在返回值那里設置繼續處理事件,然后返回大循環,看看有沒有更高優先級的任務需要執行。
事件和任務對應關系
事件和任務的事件處理函數是如何關聯起來的呢?
建立一個事件表,保存各個任務對應的事件。【uint16 *tasksEvents;】。tasksEvents為指向一個內存分配的事件數組的指針。比如:tasksEvents[0]為第0個任務的事件變量(short int)。
建立另一個函數表,保存各個任務事件處理函數的地址。【const pTaskEventHandlerFn tasksArr[] =】將會根據tasksArr[x]去執行對應的回調函數。
然后將這兩張表建立某種對應關系,【void osalInitTasks( void ),初始化task_id和tasksArr[]的函數指針對應關系】【uint8 osal_set_event( uint8 task_id, uint16 event_flag ),設置事件發生】,藍牙協議棧也會調用并設置任務,具體的實現已經被封裝起來了。
當某一事件發生時則查找函數表找到對應的事件處理函數即可。【events = (tasksArr[idx])( idx, events );,調用idx變好的函數指針回調函數】
OSAL是一種事件驅動的輪詢式操作系統。事件驅動是指發生事件后采取相應的事件處理方法,輪詢指的是不斷地查詢是否有事件發生。
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
在系統初始化時,將所有任務的事件初始化為0。通過taskEvents[idx]是否為0來判斷是否有事件發生【if (tasksEvents[idx])】。如果有事件發生,則查找函數對應的事件處理函數對事件進行處理【events = (tasksArr[idx])( idx, events );】。
事件表使用數組來實現,數組的每一項對應任務的事件,每一位表示一個事件;函數表使用函數指針數據來實現,數組的每一項是一個函數指針,指向了事件處理函數。
OSAL提供的API
總體而言,大致可以分為10個方面:1.消息管理
2.任務同步
3.時間管理
4.中斷管理
5.任務管理
6.內存管理
7.電源管理
8.非易失性閃存管理
9.時鐘管理
10.其他常用
事件
OSAL為每個任務函數分配了一個16位的事件變量,每一位代表一個事件。最高位0x8000保留為系統事件SYS_ENENT_MSG。其余的15位留給用戶自定義需要的事件。通常事件由定時器啟動,比如2s后我要點亮LED1,這就需要發送一個點亮LED1的事件,然后等待,當2s后接收到點亮LED1事件的時候調用HAL層開關LED1的函數開啟LED1。
消息
MAG是比EVENT事件更具體并且可以攜帶數據的一種通信方式。而且MSG的標記是按數值,而不是按位。比如0x02和0x03是兩個不同的消息,但是對于事件0x03則是0x01和0x02事件的組合。MSG收發使用osal_mag_send()和osal_msg_receive();當調用osal_msg_send()發送一個MSG的同時會在EVENT列表中觸發一個message ready event。為了降低消息傳遞的開支,通常傳遞指向消息的指針。
消息與事件的區別
講解消息隊列之前需要講解一下消息與事件的區別。
事件是驅動任務去執行某些操作的條件,當系統中產生了一個事件,OSAL 將這個事件傳遞給相應的任務后,任務才能執行一個相應的操作(調用事件處理函數去處理)。
通常某些事件發生時,又伴隨著一些附加信息的產生,例如:主機 GATT 接收到數據后,會產生 GATT_MSG_EVENT 消息,但是任務的事件處理函數在處理這個事件的時候,還需要得到所收到的數據。
因此,這就需要將事件和數據封裝成一個消息,將消息發送到消息隊列osal_msg_send,然后在事件處理函數中就可以使用 osal_msg_receive 從消息隊列中得到該消息。這里需要說明一點,消息一般用于不同任務函數之間的數據傳遞,因為不同的任務具有各自的堆棧空間。在同一任務中使用全局函數或者用戶事件完全能夠勝任。當然消息也可以在同一個任務中傳遞數據。
EVENT用于同一任務函數傳遞命令,而MSG則用于不同的任務函數傳遞命令數據。
這里需要說明一點,消息一般用于不同任務函數之間的數據傳遞,因為不同的任務具有各自的堆棧空間。在同一任務中使用全局函數或者用戶事件完全能夠勝任。當然消息也可以在同一個任務中傳遞數據。
事件處理
消息處理
OSAL 維護了一個消息隊列,每一個消息都會被放到這個消息隊列中去,當任務接收到事件后,可以從消息隊列中獲取屬于自己的消息,然后調用消息處理函數進行相應的處理即可。
Osal_msg_allocate()為消息分配緩存空間,分配之后,可以填充消息,然后通過osal_msg_send( ) 將消息發送出去,然后任務函數中通過 osal_msg_reveive()函數接收屬于 自己的消息,并處理,最后調用osal_msg_deallocate() 函數銷毀由Osal_msg_allocate()分配的內存空間。
下面是按鍵的消息處理過程,按鍵的消息最終將會發送到第一次注冊的任務中去。
這里需要說明一點,消息一般用于不同任務函數之間的數據傳遞,因為不同的任務具有各自的堆棧空間。在同一任務中使用全局函數或者用戶事件完全能夠勝任。當然消息也可以在同一個任務中傳遞數據。
事實上,它只是發了一個KEY_CHANGE事件,而鍵值是以MSG消息的形式發到系統的消息隊列,而該消息也會帶上目標taskId的標識。
如下代碼可以從消息隊列中得到一個消息:pMsg = osal_msg_receive( registeredKeysTaskID))
時間管理
時間管理API用于開啟和關閉定時器,定時時間一般為毫秒級定時。
osal_start_reload_timer()和osal_start_timerEx()功能一樣,但是本接口還多了一個功能:就是定時時間到后相應事件被執行,并重新加載定時器,也就是又重新設置了定時器,繼續進行定時工作,除非調“osal_stop_timerEx()”接口,否則一直循環定時操作。
總結
以上是生活随笔為你收集整理的CC254x--OSAL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 代码段编辑器SnippetEditor
- 下一篇: 漏洞挖掘之信息收集