1. 嵌入式系統開發為什麼要採用交叉編譯的方式
由於嵌入式系統資源匱乏,一般不能像PC一樣安裝本地編譯器和調試器,不能在本地編寫、編譯和調試自身運行的程序,而需藉助其它系統如PC來完成這些工作,這樣的系統通常被稱為宿主機。宿主機通常是linux系統,並安裝交叉編譯器、調試器等工具;宿主機也可以是Windows系統,安裝嵌入式Linux集成開發環境。在宿主機上編寫和編譯代碼,通過串口、網口或者硬體調試器將程序下載到目標系統裡面運行。所謂的交叉編譯,就是在宿主機平台上使用某種特定的交叉編譯器,為某種與宿主機不同平台的目標系統編譯程序,得到的程序在目標系統上運行而非在宿主機本地運行。這里的平台包含兩層含義:一是核心處理器的架構,二是所運行的系統,這樣,交叉編譯有3種情形:(1)目標系統與宿主機處理器相同,運行不同的系統;(2)目標系統與宿主機處理器不同,運行相同的系統;(3)目標系統與宿主機處理器不同,運行不同的系統。實際上,在PC機上進行非Linux的嵌入式開發,哪怕使用IDE集成環境如Keil、ADS、Realview,都是交叉編譯和調試的過程,只是IDE工具隱藏了細節,沒有明確提出這個概念而已。
2. 關於android NDK開發中application.mk文件的疑惑
介紹:
Android SDK是一個允許Android應用開發人員使用C或C++源文件編譯並嵌入到本機源代碼中的應用程序包的一組工具。
重要說明:
Android NDK只能用於android 1.5以上版本
1.Android NDK的目的:
Android虛擬機允許你的應用程序源代碼通過JNI調用在本地實現的源代碼,簡單的說,這就意味著:
你的應用程序將聲明一個或多個用』native』關鍵字的方法用來指明它們是通過本地代碼實現的
例如:native byte[] loadFile(String filePath)
你必須提供包含實現這些方法的共享庫(就是.so),將共享庫打包到你的應用程序包apk中,這些庫文件必須根據標準的Unix約定來命名為 lib<something>.so,並且是需要包含一個標準的JNI的介面,例如
libFileLoader.so
你的應用程序必須明確的裝載這些庫文件(.so文件),比如,在程序的開始裝載它,只需要簡單的添加幾句源代碼:
java代碼:
static {
System.loadLibrary(「FileLoader」);
}
注意:這里你不必再將前綴lib和後綴.so寫入。
Android NDK對於Android SDK只是個組件,它可以幫你:
生成的JNI兼容的共享庫可以在大於Android1.5平台的ARM CPU上運行
將生成的共享庫拷貝到合適的程序工程路徑的位置上,以保證它們自動的添加到你的apk包中(並且簽名的)
在以後的版本中,我們將提供來幫助你的源代碼通過遠程gdb連接和盡可能多的源代碼的信息。
而且,Android NDK還提供:
一組交叉編譯鏈(編譯器、鏈接器等)來生成可以在Linux,OS X和Windows(用Cygwin)運行的二進制文件
一組與由Android平台提供的穩定的本地API列表的頭文件
它們在docs/STABLE-APIS.html中有說明
重要提示:
記住,在以後的更新和發布平台中,Android系統鏡像中的大多數本地系統庫並不是一成不變的,而是可以徹底改變,甚至刪除的
一個編譯系統(build system)可以允許開發者寫一個非常短的編譯文件(build files)去描述哪個源代碼需要編譯,並且怎樣編譯。編譯系統可以解決所有的toolchain/platform/CPU/ABI細節的問題。並且,較晚的NDK版本中還添加了更多的可以不用改變開發者的編譯文件的情況下的toolchains,platforms,系統介面。
2.Android NDK的缺點
NDK並不是一個可以編寫通用的源代碼並且可以在Android設備上運行的方法,你的應用程序還是需要使用JAVA程序,適當的處理系統事件來避免「應用程序沒有反應」的對話框或者處理Android應用程序的生命周期
注意:可以適當的在源代碼中寫一個復雜的應用程序,用於啟動/停止一個小型的「應用程序包」
強烈建議很好地理解的 JNI,因為許多操作在這種環境要求的開發人員,都採取具體的行動,不一定在常典型的本機代碼。這些措施包括:
不能通過指針直接訪問VM的對象。比如:你不能安全的得到一個指向String對象的16位char數組的循環遍歷
需要顯示引用管理本機代碼時候要保持處理JNI調用之間的VM對象
NDK在Android平台僅僅提供了有限的本地API和庫文件的支持的系統頭文件,然而一個標準的Android系統鏡像包括許多本地共享庫,這些都應該被考慮在更新和發行版本的可以徹底改變的實現細節
如果Android系統庫沒有明確的被NDK明確的支持,然後應用程序不應該依賴於它提供的,或者打破了將來在各種設備上的無線系統更新
選定的系統庫將逐漸被添加到穩定的NDK API中。
3.NDK開發實踐
下面將給出一個怎樣用Android NDK開發本地代碼的粗略的概述
(1) 把本地代碼放在$PROJECT/jni/…下,比如將hello.c放到apps/hello/jni/目錄下
(2) 在你的NDK編譯系統中在$PROJECT/jni/Android.mk來描述你的源代碼
(3) 可選:在$PROJECT/jni/Application.mk到你的編譯系統中來詳細描述你的項目,盡管你開始的話不一定需要它,但是它允許你使用更多的CPU或者覆蓋編譯器/鏈接器的標記
(4) 從你的項目的目錄開始通過運行」$NDK/ndk-build」來編譯你的代碼,或者從子目錄開始
(5) 最後一步可以,萬一成功,剝離共享庫的應用層序需要你的應用程序的項目根目錄。然後你通過通常的方法來生成最終的apk。
3. 嵌入式學什麼
一、嵌入式系統含義簡介
嵌入式系統是以應用為中心,以現代計算機技術為基礎,能夠根據用戶需求(功能、可靠性、成本、體積、功耗、環境等)靈活裁剪軟硬體模塊的專用計算機系統。它是由硬體和軟體組成,其軟體內容只包括軟體運行環境及其操作系統,硬體內容包括信號處理器、存儲器、通信模塊等在內的多方面的內容。比於一般的計算機處理系統而言,嵌入式系統存在較大的差異性, 它不能實現大容量的存儲功能,因為沒有與之相匹配的大容量介質,大部分採用的存儲介質有E-PROM、EEPROM DENG等, 軟體部分以API編程介面作為開發平台的核心。?
二、嵌入式系統學什麼內容
1、基本電路知識:嵌入式硬體也是需要許多電路搭建起來的,學習嵌入式之前必須對電路基本知識有一定基礎。了解常用的基本器件,基本儀器使用,具有一定的電路分析能力。這樣你才能看得懂嵌入式系統的硬體,才能為後續開發奠定基礎。
2、基本語言知識:嵌入式驅動程序編寫需要用到C語言,因此在學習嵌入式之前還必須熟練C語言基本語法,並能編寫些普通程序代碼。在學習C語言時養成規范的編程習慣,這將對以後的程序准確性有很大影響。
3、單片機:基於單片機自己設計並繪制電路圖,自己焊接或者生產PCB板,設計小型的電子系統。首先使用51單片機學習編寫流水燈、按鍵掃描、數碼管、液晶顯示、AD/DA采樣等簡單程序。有了一定基礎後可以設計尋線小車,溫度採集、時鍾顯示等嵌入式系統。之後可以使用430單片機、STM32以及Cortex-M3處理器作為學習嵌入式操作系統前的過渡階段,可自行選擇學習。
4、ARM9/ARM11裸機學習:裸機程序編寫,即不帶操作系統的程序編寫,其作用和上面430單片機的作用相似,目地就是為了熟悉ARM架構,對ARM寄存器有深入的了解,這將對以後的驅動程序編寫帶來很大的方便。
5、Linux系統:嵌入式系統學習特別注意又特別難的地方就是Linux系統移植,對於系統的移植、系統的裁剪是學習的難點。搭建嵌入式操作系統的開發環境,即交叉編譯環境也比較麻煩,因此在學習過程中一定要一步一步動手實踐操作。學習了Linux系統移植,就可以編寫底層驅動程序了,通過交叉編譯環境將驅動程序編譯並下載到目標板上,並且編寫一段小測試程序驗證驅動的正確性。如在Linux下實現流水燈,實現按鍵功能。
4. 64位系統上源碼編譯32位libcurl庫
有時候需要交叉編譯libcurl,比如目標機器是32位系統的,但是本地機器是64位系統的,而且由於某些原因,我們無法在32位系統上直接編譯,所以需要用到交叉編譯
libcurl是依賴openssl的,所以先編譯openssl的32位庫 完整編譯選項配置如下:
詳細選項含義如下:預先已經export CC的版本 配置-m32指定編譯32位的庫 配置–prefix指定openssl的安裝目錄 配置–openssldir指定openssl的頭文件目錄 配置shared關鍵字指定編譯時生成動態庫(libssl.so/libcrypto.so及其相關軟連接)然後再make && make install即可
有時候有的系統是默認安裝了32位zlib庫的,那麼就可以跳過這一步,但是有的系統需要自己下載編譯zlib-32位庫 完整編譯選項配置如下:直接修改CMakeLists.txt文件,增加以下兩行 set(CMAKE_C_FLAGS 「-m32」) set(CMAKE_CXX_FLAGS 「-m32」) 詳細選項含義如下:配置CMAKE_C_FLAGS指定編譯32位庫環境 配置CMAKE_CXX_FLAGS指定編譯32位庫環境然後再mkdir build && cd build && cmake .. && make && make install即可
最後就是編譯libcurl 完整編譯選項配置如下:
詳細選項含義如下:配置PKG_CONFIG_PATH指定啟動openssl選項(啟動這個選項,就會默認鏈接lssl,lcrypto,lz三個庫) 配置CFLAGS指定編譯32位庫環境 配置CPPFLAGS指定鏈接的庫的頭文件 配置LDFLAGS指定鏈接的庫的路徑然後再make && make install即可
當編譯第三方庫的時候,如果有CMakeLists.txt,直接用CMakeLists.txt編譯就很方便;如果只有configure,那麼需要先了解編譯選項執行./configure –help來查看當前支持的編譯選項然後根據提示配置一下我們需要指定的選項,比如自己指定的openssl的版本的庫和頭文件路徑名,比如CC的版本,比如安裝路徑等等 (當然,如果不需要額外配置這些東西的話,直接走默認配置的話,那麼直接執行./config或者./configure就行)然後在生成Makefile之後,再make && make install即可
5. 交叉編譯幾種常見的報錯
(1)交叉編譯器
在主機上用來編譯其它類型機器上可執行代碼的編譯器就叫交叉編譯器,我們進行嵌入式linux的開發主機大部分都是X86,而我們的嵌入式系統的處理器有可能是ARM/MIPS等非X86處理器,這時候就必須使用ARM/MIPS的交叉編譯器才能編譯出在這些處理器上能夠執行的代碼。這里我們使用的是ARM最新的EABI編譯器。
交叉編譯器在編譯的時候,對於浮點運行會預設硬浮點運算FPA(float point architecture),而沒有FPA的CPU,比如三星的2440等,會使用FPE(float point emulation即軟浮點),這樣在速度上就會受到極大限制。使用EABI(embeded application binary interface)則可以對此改善處理。
(2)不修改MAKEFILE來建立編譯環境
將arm-2008q3.tar.bz2拷貝到ubuntu系統的某個目錄,解壓後。使用VI編輯/etc/bash.bashrc,在文件最後加入環境變數設置(註:加bin的含義是交叉編譯器工具目錄):
保存後,用source運行一次該文件,就可以了。
(3)gcc: error trying to exec 'cc1': execvp: No such file or directory 的解決
今天在編譯開發板環境時,明明設置好編譯器的環境變數了,編譯時就是會出現:gcc: error trying to exec 'cc1': execvp: No such file or directory 錯誤提示。後來發現一個方法可以解決,輸入:whereis gcc,就可以了發現好幾個gcc,包括/usr/bin/gcc,所以我就把PATH路徑設過去,就OK了。
(4)Clock skew detected. Your build may be incomplete
如果你裝了Windows Linux雙系統,系統時間很可能出問題,從而造成文件修改時間比系統時間晚,兩種辦法:
1,應該是你的PC的系統時鍾錯誤,在BIOS中修改正確。
2,使用touch命令將所有文件的時間戳修改為你系統的當前時間。解決方法:find ./-name "*" -exec touch {} \;