-基于TI CC254X OSAL的分析
當工具鏈配置完成后,SourceInsight向你展示一份源碼工程,不借助百度和開發文檔,能否在一兩個小時內理解源碼的組成框架和接口,進行快速開發?
上一篇《[如何快速理解一個全新的嵌入式操作系統](http://blog.csdn.net/yueqian_scut/article/details/48781937)》我們已經分析了如何快速理解OSAL的任務調度和任務間通信(其實OSAL只是酷似多任務操作系統的單任務系統),再理解好OASL的消息產生和處理過程,我們就能夠進行快速開發了。
>一、消息的來源
嵌入式系統的消息包括兩種,一是系統消息,包括低電、熱插拔等,由系統進程去處理;二是用戶消息,包括Timer、按鍵、串口、繪圖等消息,由各應用進程進行處理。對于TI CC254x的OSAL,我們理解好Timer、key、UART就已足夠。
>二、HAL
OSAL向用戶提供HAL硬件抽象層,對CC254x的所支持的硬件模塊進行了封裝抽象,如timer、key、UART、LED、LCD、flash、ADC等等,并向用戶提供硬件模塊的操作接口。
CC254x是低功耗藍牙集成芯片,用戶的產品和電路設計一般都會參考官方的典型電路,而OSAL為官方針對典型電路所設計的系統庫和接口,用戶的代碼編程就只需要按照其提供的HAL接口進行調用就可以實現功能,而不需要通過GPIO級別的驅動編程來實現模塊的功能。
針對CC254x的編程是應用編程,關注的是HAL接口,可以認為是基于硬件功能的API接口,而且電路的外設的功能也相對固定,如LED、LCD、UART所使用的pin腳都是相對固定的,因此調用簡單的HAL層接口即可實現功能;而驅動編程則是針對SOC片上資源來進行開發,需要根據SOC的datasheet明確的物理地址資源進行訪問和控制,并向用戶提供API接口。從兩者的區別可以理解HAL硬件抽象層的含義。當然,透傳理解SOC datasheet也有助于HAL接口編程。
>三、Timer
對于Timer定時器接口,一般是設置定時器、編寫定時器時間到達時的回調函數。而定時器模塊的初始化函數一般由系統初始化完成。
OSAL對于Timer的HAL層代碼對用戶并不透明,我們可以理解Timer的HAL層是設置Timer模塊的硬件相關操作,并且實現了Timer中斷時的服務處理過程。OSAL的Timer的封裝接口實際上是在HAL層的基礎進行再次封裝。其主要提供的接口如下:
uint8osal_start_timerEx( uint8 taskID, uint16 event_id, uint32 timeout_value )
taskID標識哪個目標任務來處理這個定時到達消息,即當定時完成時,定時器中斷服務器函數會往這個目標任務的taskEvents[taskID]寫入event_id這個消息;event_id可以由用戶自定義;timeout_value是定時時間,1毫秒為單位。
可見這個定時接口并沒有設置定時完成時的回調函數,而是在定時完成時向這個目標任務發送一個事件。而在目標任務的執行過程中要檢測這個事件并執行相應的處理。
>四、UART
?????? UARTHAL層代碼對用戶是透明的,對于用戶編程來說,最重要的就是串口的初始化(波特率)、串口輸入、串口輸出。
**1.串口初始化**
voidNPI_InitTransport( npiCBack_t npiCBack )
串口的初始化并沒有帶taskId這個參數,可見其是一個全局的系統級的接口。串口的使用一般是使用中斷串口輸入,非中斷直接串口輸出。該函數里面設置波特率是115200.
npiCBack是串口HAL提供給用戶的一個回調函數,即在串口中斷時會調用該回調函數。而串口中斷有以下幾種事件:

一般我們都會在該回調函數中實現串口接收,如實現串口透傳模式。回調接口聲明如下:
typedefvoid (*npiCBack_t) ( uint8 port, uint8 event )
?????? port是UART0或者UART1,而event即是串口中斷事件。
**2.串口輸入**
uint16NPI_ReadTransport( uint8 *buf, uint16 len )
**3.串口輸出**
uint16NPI_WriteTransport( uint8 *buf, uint16 len )
從這些接口來看其前綴是NPI,真實的意義是Network Processor Interface (NPI),表示所謂的網絡傳輸層。其實只是更高一層的數據輸入輸出罷了。NPI的底層可以是UART、SPI或者USB等等。我們這里默認是使用UART。
>五、按鍵消息來源和處理
**1. 代碼理解前的思考**
1)按鍵消息按理是跟應用相關的,因此其必然是跟某個taskId綁定。在這種簡單的嵌入式系統中,一般是由一個稱為UI的任務來統一處理按鍵的消息。
2)按照上一篇文章和上一節Timer的分析,OSAL的設計是將事件event_id發往目標task,即設置taskEvents[tasked]。我們可以想象在按鍵中斷(或者按鍵輪詢)時檢測到按鍵會往目標task發一個按鍵事件。但是,我們再細想,發一個KEY事件夠了嗎?很明顯taskEvents的元素才是16bit,每個bit表示一個事件,最多只能代表16種事件,就算這16事件都用來表示不同的按鍵,也顯得不夠。因為系統可能有更多的按鍵啊,如果這樣設計擴展性就太差了。事實上,它只是發了一個KEY_CHANGE事件,而鍵值是以MSG消息的形式發到系統的消息隊列,而該消息也會帶上目標taskId的標識。
3)以上兩點是OSAL的KEY處理機制。對于用戶快速開發,則需要知曉如何增加一個按鍵,或者改變一個按鍵對應的GPIO;處理按鍵的過程在哪里實現?
帶著以上問題,我們從頭到尾跟蹤一次KEY處理的過程。OSAL對KEY的處理機制有點繞,但封裝得挺有意思的。
對Key處理機制真正的理解過程應該是倒序的,即從按鍵的處理一步一步往前推,在現場教學時,對著代碼反跟蹤能夠更加體現本文的方法論。為了表述更加有條理性,這里就從頭到尾正序闡述。
**2. 初始化**
?????? 1)main->HalDriverInit->HalKeyInit

2)main->InitBoard( OB_READY )
OnboardKeyIntEnable= HAL_KEY_INTERRUPT_ENABLE;
HalKeyConfig(OnboardKeyIntEnable, OnBoard_KeyCallback);

相關的代碼在hal_keys.c和hal_keys.h,若要增加按鍵或者修改按鍵設置即修改這里。
OnBoard_KeyCallback是按鍵中斷的回調函數。我們在下一步再展開其實現過程,現在跟蹤在哪里會調用這個回調。我們從中斷的源頭開始跟蹤。
**3. 中斷的執行過程**

**4.HAL層任務的處理過程**

pHalKeyProcessFunction即是之前在HalKeyConfig接口中設置的OnBoard_KeyCallback,繼續跟蹤這個函數的實現:

這個 registeredKeysTaskID是什么,就是處理按鍵消息的任務Id。在哪里被初始化呢?
**5.按鍵處理任務的初始化**
main-> osal_init_system-> osalInitTasks-> SimpleBLEPeripheral_Init
-> RegisterForKeys( simpleBLEPeripheral_TaskID )
即在這個函數里面將simpleBLEPeripheral_TaskID賦值給registeredKeysTaskID,即SimpleBLEPeripheral對應的任務來處理這個消息。
**6.按鍵的處理**

用戶添加的按鍵處理即在simpleBLEPeripheral_HandleKeys函數中。
請一步步地印證第一點,代碼理解前的思考。
節日快樂!
更多原創嵌入式Linux,IOT、藍牙wifi開發技術分享請關注微信公眾號:嵌入式企鵝圈

- 前言
- 物聯網架構演進和微信智能設備平臺開發
- 基于微信硬件公眾平臺的智能控制開發流程
- Protocol buffer序列化及其在微信藍牙協議中的應用
- 網絡架構、云平臺和微信公眾平臺開發接入
- 如何快速理解一個全新的嵌入式操作系統
- 如何快速理解一個全新的嵌入式操作系統(續)
- 一張圖讀懂基于微信硬件平臺的物聯網架構
- 揭開智能配置上網(微信Airkiss)的神秘面紗
- 物聯網核心協議—消息推送技術演進
- 藍牙防丟器原理、實現與Android BLE接口編程
- 以藍牙開發的視覺解讀微信Airsync協議
- 全球最低功耗藍牙單芯片(DA14580)系統架構和應用開發框架分析
- 從零開始搭建微信硬件開發環境全過程——1小時掌握微信硬件開發流程