嵌入式linux学习笔记(2)
筆記目錄
- 學(xué)習(xí)目標(biāo)
- 學(xué)習(xí)內(nèi)容
- 一、VI 編輯器的設(shè)置
- 1、設(shè)置 TAB 鍵為 4 字節(jié)
- 2、VIM 編輯器顯示行號
- 3.VI/VIM 編輯器使用空格代替了 TAB 鍵
- 二、存儲
- 1、ROM
- 2、RAM
- 3、FLASH
- 三、Makefile語法
- 重要提醒
- 1、Makefile作用
- 2、Makefile 規(guī)則格式
- 3、gcc命令
- 4、Makefile 變量
- 四、U-Boot
- 1、U-Boot 簡介
- 2、U-Boot編譯
- 3、U-Boot 一些命令
- 4、uboot啟動linux測試
- 從emmc啟動
- 從網(wǎng)絡(luò)啟動
- 在ubuntu上搭建tftp服務(wù)器
- 配置網(wǎng)絡(luò)
- 5、U-Boot 頂層makefile部分
- 6、U-Boot 圖形化配置
- 7、uboot移植
- 五、linux內(nèi)核
- 1、linux內(nèi)核編譯
- 2、重要的文件夾
- 六、根文件系統(tǒng)
- 1、根文件系統(tǒng)介紹
- 2、根文件系統(tǒng)的目錄
- 3、BusyBox
- 編譯 busybox
- busybox編譯
- 向根文件系統(tǒng)添加 lib 庫
- 七、Linux 驅(qū)動開發(fā)
- 1.字符設(shè)備驅(qū)動
- 八、設(shè)備樹
- 學(xué)習(xí)時間
- 學(xué)習(xí)產(chǎn)出
學(xué)習(xí)目標(biāo)
學(xué)習(xí)linux開發(fā)
學(xué)習(xí)內(nèi)容
一、VI 編輯器的設(shè)置
vi 打開文件/etc/vim/vimrc
1、設(shè)置 TAB 鍵為 4 字節(jié)
VI 編輯器默認(rèn) TAB 鍵為 8 空格,為了使代碼更好看我們改成 4 空格。
在此文件最后面輸入如下代碼:set ts=4
2、VIM 編輯器顯示行號
在此文件最后面輸入如下代碼:set nu
3.VI/VIM 編輯器使用空格代替了 TAB 鍵
在此文件最后面輸入如下代碼:set noexpandtab
二、存儲
1、ROM
ROM是只讀內(nèi)存,其特性是一旦儲存資料就無法再將之改變或刪除,存儲的資料不會因為電源關(guān)閉而消失。
2、RAM
RAM是隨機存儲,掉電不會保存數(shù)據(jù)。
??SRAM(靜態(tài)隨機訪問存儲器)不需要刷新電路即能保存它內(nèi)部存儲的數(shù)據(jù)
??DRAM (動態(tài)隨機訪問存儲器)只能將數(shù)據(jù)保持很短的時間。為了保持?jǐn)?shù)據(jù),DRAM使用電容存儲,所以 必須隔一段時間刷新(refresh)一次,如果存儲單元沒有被刷新,存儲的信息就會丟失。
??SDRAM(同步動態(tài)隨機訪問存儲器)同步是指 Memory工作需要同步時鐘,內(nèi)部的命令的發(fā)送與數(shù)據(jù)的傳輸都以它為基準(zhǔn)。傳統(tǒng)的DRAM在兩個讀周期之間需要等待一段時間,用于充電操作。而SDRAM一個模組有兩個bank,在對一個bank充電時,可以操作另一個bank,實現(xiàn)流水線。SDRAM的發(fā)展已經(jīng)經(jīng)歷了五代:分別是SDR SDRAM、 DDR SDRAM、 DDR2 SDRAM、 DDR3 SDRAM、 DDR4 SDRAM。
3、FLASH
FLASH 存儲器又稱閃存,它結(jié)合了ROM和RAM的長處,不僅具備電子可擦除可編程(EEPROM)的性能,還不會斷電丟失數(shù)據(jù)同時可以快速讀取數(shù)據(jù)(NVRAM 的優(yōu)勢)。
??NOR Flash和NAND Flash區(qū)別
????NOR的讀速度比NAND稍快一些。
????NAND的寫入速度比NOR快很多。
????NAND的4ms擦除速度遠(yuǎn)比NOR的5s快。
????大多數(shù)寫入操作需要先進(jìn)行擦除操作。
????NAND的擦除單元更小,相應(yīng)的擦除電路更少。
EMMC=NAND閃存+閃存控制芯片+標(biāo)準(zhǔn)接口封裝(對廠家而言簡化了電路設(shè)計,降低了成本。)
DDR屬于SDRAM
NANO FLASH屬于flash
使用emmc的好處是,除了得到大容量的空間(這一點,只用NAND FLASH多堆疊也可以做到),還有就是emmc可以管理NAND (壞塊處理,ECC,FFS)等。
三、Makefile語法
重要提醒
Makefile 里面是由一系列的規(guī)則組成的。
Makefile在編寫時不能使用空格只能用TAB鍵,否則會報錯。
Makefile:12: *** 遺漏分隔符 (null)。 停止。
1、Makefile作用
make 的執(zhí)行過程,make 工具就是在 Makefile 中一層一層的查找依賴關(guān)系,并執(zhí)行
相應(yīng)的命令。編譯出最終的可執(zhí)行文件。
Makefile 的好處就是“自動化編譯”,一旦寫好了 Makefile文件,以后只需要一個 make 命令即可完成整個工程的編譯,極大的提高了開發(fā)效率。
2、Makefile 規(guī)則格式
?目標(biāo)… : 依賴文件集合…
??命令 1
??命令 2
??…
3、gcc命令
gcc [選項] [文件名字]
?主要選項如下:
??-c:只編譯不鏈接為可執(zhí)行文件,編譯器將輸入的.c 文件編譯為.o 的目標(biāo)文件。
??-o:<輸出文件名> 用來指定編譯結(jié)束以后的輸出文件名,如果使用這個選項的話 GCC 默
認(rèn)編譯出來的可執(zhí)行文件名字為 a.out。
??-g:添加調(diào)試信息,如果要使用調(diào)試工具(如 GDB)的話就必須加入此選項,此選項指示編
譯的時候生成調(diào)試所需的符號信息。
??-O:對程序進(jìn)行優(yōu)化編譯,如果使用此選項的話整個源代碼在編譯、鏈接的的時候都會進(jìn)
行優(yōu)化,這樣產(chǎn)生的可執(zhí)行文件執(zhí)行效率就高。
??-O2:比-O 更幅度更大的優(yōu)化,生成的可執(zhí)行效率更高,但是整個編譯過程會很慢。
簡單的例程:
main: main.o input.o calcu.o #需要main.o input.o等文件生成maingcc -o main #編譯的最終目標(biāo)是生成一個可執(zhí)行文件main main.o: main.c #需要的main.o文件由main.c文件生成gcc -c main.c #編譯main.c生成main.o input.o: input.cgcc -c input.c calcu.o: calcu.cgcc -c calcu.cclean: #執(zhí)行make clean清理文件rm *.orm main??在第一次編譯的時候由于 main 還不存在,因此第一條規(guī)則會執(zhí)行,第一條規(guī)則依賴于文件 main.o、 input.o 和 calcu.o這個三個.o 文件,這三個.o 文件目前還都沒有,因此必須先更新這三個文件。make 會查找以這三個.o 文件為目標(biāo)的規(guī)則并執(zhí)行。以 main.o 為例,發(fā)現(xiàn)更新 main.o 的是第二條規(guī)則,因此會執(zhí)行第二條規(guī)則,第二條規(guī)則里面的命令為“gcc –c main.c”,這行命令很熟悉了吧,就是不鏈接編譯 main.c,生成 main.o,其它兩個.o 文件同理。最后一個規(guī)則目標(biāo)是 clean,它沒有依賴文件,因此會默認(rèn)為依賴文件都是最新的,所以其對應(yīng)的命令不會執(zhí)行,當(dāng)我們想要執(zhí)行 clean 的話可以直接使用命令“make clean”,執(zhí)行以后就會刪除當(dāng)前目錄下所有的.o 文件以及 main。
4、Makefile 變量
Makefile 中變量的引用方法是"$(變量名)"
注釋"#"
| 變量追加 | “+=” |
四、U-Boot
1、U-Boot 簡介
Linux 系統(tǒng)要啟動就必須需要一個 bootloader 程序,也就說芯片上電以后先運行一段bootloader 程序。這段 bootloader 程序會先初始化 DDR 等外設(shè),然后將 Linux 內(nèi)核從 flash(NAND,NOR FLASH,SD,MMC 等)拷貝到 DDR 中,最后啟動 Linux 內(nèi)核。
2、U-Boot編譯
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- (加空格) mx6ull_14x14_ddr512_emmc_defconfig make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12ARCH=arm 設(shè)置目標(biāo)為 arm 架構(gòu),CROSS_COMPILE 指定所使用的交叉編譯器。
第一條命令相當(dāng)于“make distclean”,目的是清除工程,一般在第一次編譯的時候最好清理一下工程。
第二條指令相當(dāng)于“make mx6ull_14x14_ddr512_emmc_defconfig”,用于配置 uboot,配置文件為mx6ull_14x14_ddr512_emmc_defconfig。(14x14代表芯片的封裝大小)
最后一條指令相當(dāng)于 “make -j12”也就是使用 12 核來編譯 uboot。
在頂層的makefile輸入
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
可以make后面不用加
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
3、U-Boot 一些命令
uboot 命令中的數(shù)字都是十六進(jìn)制的!不是十進(jìn)制的!
進(jìn)入 uboot 的命令行模式
| help/? | 幫助 | help/?命令名 |
| bdinfo | 查看板子信息 | DRAM 的起始地址和大小、啟動參數(shù)保存起始地址、波特率、sp(堆棧指針)起始地址等信息 |
| printenv | 輸出環(huán)境變量信息 | |
| version | 查看 uboot 的版本號 | |
| setenv | 設(shè)置環(huán)境變量 | 字符串中有空格需要使用單引號‘’將其括起來 |
| saveenv | 保存修改的環(huán)境變量 | 刪除一個環(huán)境變量只要給這個環(huán)境變量賦空值 |
| md | 顯示內(nèi)存值 | md[.b, .w, .l] address [# of objects]命令中的[.b .w .l]對應(yīng) byte、word 和 long,也就是分別以 1 個字節(jié)、2 個字節(jié)、4 個字節(jié)來顯示內(nèi)存值。address 就是要查看的內(nèi)存起始地址,[# of objects]表示要查看的數(shù)據(jù)長度,這個數(shù)據(jù)長度單位不是字節(jié),而是跟你所選擇的顯示格式有關(guān)。(md.b 80000000 14) |
| nm | 修改指定地址的內(nèi)存值 | nm [.b, .w, .l] address(nm.l 80000000) |
| mm | 修改指定地址內(nèi)存值的 | 使用 mm 修改內(nèi)存值的時候地址會自增,而使用命令 nm 的話地址不會自增 |
| mw | 用于使用一個指定的數(shù)據(jù)填充一段內(nèi)存 | mw [.b, .w, .l] address value [count]mw 命令同樣可以以.b、.w 和.l 來指定操作格式, address 表示要填充的內(nèi)存起始地址, value為要填充的數(shù)據(jù), count 是填充的長度。 |
| cp | 數(shù)據(jù)拷貝命令 | cp [.b, .w, .l] source target count cp 命令同樣可以以.b、.w 和.l 來指定操作格式,source 為源地址,target 為目的地址,count為拷貝的長度。 |
| cmp | 比較兩段內(nèi)存的數(shù)據(jù)是否相等 | cmp [.b, .w, .l] addr1 addr2 count 其中cmp 命令同樣可以以.b、.w 和.l 來指定操作格式,addr1 為第一段內(nèi)存首地址,addr2 為第二段內(nèi)存首地址, count 為要比較的長度。 |
| ipaddr | 開發(fā)板 ip 地址 | 使用 dhcp 命令來從路由器獲取 IP 地址 |
| ethaddr | 開發(fā)板的 MAC 地址 | 一定要設(shè)置 |
| gatewayip | 網(wǎng)關(guān)地址 | |
| netmask | 子網(wǎng)掩碼 | |
| serverip | 服務(wù)器 IP 地址,也就是 Ubuntu 主機 IP 地址 | 用于調(diào)試代碼 |
| ping | 測試開發(fā)板的網(wǎng)絡(luò)能否使用 | |
| dhcp | 用于從路由器獲取 IP 地址 | |
| nfs | 計算機之間通過網(wǎng)絡(luò)來分享資源 | nfs [loadAddress] [[hostIPaddr:]bootfilename]其中l(wèi)oadAddress 是要保存的 DRAM 地址,[[hostIPaddr:]bootfilename]是要下載的文件地址。分析 |
4、uboot啟動linux測試
從emmc啟動
emmc三個分區(qū),第一個分區(qū)存放uboot,第二個分區(qū)格式化成FAT文件系統(tǒng)在里面存放.dtb和zimage文件,第三個分區(qū)放根文件系統(tǒng)。
首先查看emmc里面是否有系統(tǒng),linux鏡像zimage和.dtb文件。先將當(dāng)前設(shè)備切換到emmc
mmc dev 1 //切換到EMMC fatls mmc 1:1 //查看EMMC分區(qū)1里面的文件 fatload mmc 1:1 80800000 zImage //將zimage下載到DDR的0x80800000 fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb //將dtb下載到DDR的0x83000000 bootz 80800000 - 83000000 //啟動內(nèi)核如果內(nèi)核啟動成功,說明uboot支持emmc啟動,驗證成功。
從網(wǎng)絡(luò)啟動
tftp服務(wù)器
需要確定ubuntu的~/linux/tftpboot文件夾下有.dtb和zimage文件
在ubuntu上搭建tftp服務(wù)器
tftp 命令的作用和 nfs 命令一樣,都是用于通過網(wǎng)絡(luò)下載東西到 DRAM 中,只是 tftp 命令使用的 TFTP 協(xié)議,Ubuntu 主機作為 TFTP 服務(wù)器。因此需要在 Ubuntu 上搭建 TFTP 服務(wù)器,需要安裝 tftp-hpa 和 tftpd-hpa,命令如下:
sudo apt-get install tftp-hpa tftpd-hpa sudo apt-get install xinetd和 NFS 一樣,TFTP 也需要一個文件夾來存放文件,在用戶目錄下新建一個目錄,命令如下:
mkdir /home/wyd/linux/tftpboot chmod 777 /home/wyd/linux/tftpboot最后配置 tftp,安裝完成以后新建文件/etc/xinetd.d/tftp,如果沒有/etc/xinetd.d 目錄的話自行創(chuàng)建sudo vi /etc/xinetd.d/tftp,然后在里面輸入如下內(nèi)容:
service tftp {socket_type=dgramprotocol=udpwait=yesuser=rootserver=/usr/sbin/in.tftpdserver_args=-s /home/wyd/linux/tftpboot/disable=noper_source=11cps=100 2flags=IPv4}sudo vi /etc/default/tftpd-hpa,然后在里面輸入如下內(nèi)容:
# /etc/default/tftpd-hpa TFTP_USERNAME="tftp" TFTP_DIRECTORY="/home/wyd/linux/tftpboot" TFTP_ADDRESS=":69" TFTP_OPTIONS="-1 -c -s"最后輸入如下命令, 重啟 tftp 服務(wù)器:
sudo service tftpd-hpa restart
將 zImage 鏡像等文件拷貝到 tftpboot 文件夾中,并且給予 zImage 相應(yīng)的權(quán)限
進(jìn)入 uboot 的命令行模式
tftp 80800000 zImage //將 tftpboot 文件夾里面的 zImage 文件下載到開發(fā)板 DRAM 的 0X80800000 地址處 tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb //將 tftpboot 文件夾里面的 dtb 文件下載到開發(fā)板 DRAM 的 83000000 地址處 bootz 80800000 - 83000000 //啟動內(nèi)核設(shè)置 bootargs 和 bootcmd 這兩個環(huán)境變量,設(shè)置如下:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw' setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000' saveenv一開始是通過 tftp 下載 zImage 和 imx6ull-alientek-emmc.dtb 這兩個文件
配置網(wǎng)絡(luò)
setenv ipaddr 192.168.1.10 //開發(fā)板 IP 地址 setenv ethaddr 00:04:9f:04:d2:35 //開發(fā)板網(wǎng)卡 MAC 地址 setenv gatewayip 192.168.1.1 //開發(fā)板默認(rèn)網(wǎng)關(guān) setenv netmask 255.255.255.0//開發(fā)板子網(wǎng)掩碼 setenv serverip 192.168.1.6 //服務(wù)器地址,也就是 Ubuntu 地址(如果不是靜態(tài)地址可能需要長期更改) saveenv //保存環(huán)境變量ping測試
=> ping 192.168.1.6Using FEC1 devicehost 192.168.1.6 is alive5、U-Boot 頂層makefile部分
#版本號 VERSION = 2016 #主版本號 PATCHLEVEL = 03 #修補版本號 SUBLEVEL = #次版本號 EXTRAVERSION = #附加信息 NAME = #名字#隱含規(guī)則 #隱含規(guī)則則是內(nèi)建在make 中,為make 提供了重建某一類目標(biāo)文件(.o 等)的通用方法,同時這些隱含規(guī)則所用到的變量也就是所謂的隱含變量。 #隱含規(guī)則的好處是在Makefile 中不需要明確給出重建某一個目標(biāo)的命令,甚至可以不需要規(guī)則。make會為你自動搜尋匹配的隱含規(guī)則鏈。 #隱含規(guī)則的代價之一就是低效,系統(tǒng)必須搜索可能的隱含規(guī)則鏈。同時隱含規(guī)則也有可能應(yīng)用了不是你想要的規(guī)則而引入很難debug的錯誤。 #變量SHELL與MAKEFLAGS一樣,默認(rèn)情況(沒有用“unexport”聲明)下在整個make的執(zhí)行過程中被自動的傳遞給所有的子make。 #“+=”來給變量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用內(nèi)置的隱含規(guī)則和變量定義,“--include-dir”指明搜索路徑,”$(CURDIR)”表示當(dāng)前目錄 MAKEFLAGS += -rR --include-dir=$(CURDIR) #CURDIR是make的內(nèi)嵌變量,自動設(shè)置為當(dāng)前目錄#export將變量傳遞到子make過程,unexport禁止將變量傳遞到子make過程。 #在locale環(huán)境中,有一組變量,代表國際化環(huán)境中的不同設(shè)置,"C"是系統(tǒng)默認(rèn)的locale: #LC_ALL是一個宏,如果該值設(shè)置了,則該值會覆蓋所有LC_*的設(shè)置值。注意,LANG的值不受該宏影響。 #LC_COLLATE定義該環(huán)境的排序和比較規(guī)則 #LC_NUMERIC非貨幣的數(shù)字顯示格式 unexport LC_ALL LC_COLLATE=C LC_NUMERIC=C export LC_COLLATE LC_NUMERIC# Avoid interference with shell env settings #根據(jù)注釋可以看到為了避免當(dāng)前shell環(huán)境變量對編譯的影響,去除grep的配置選項GREP_OPTIONS; unexport GREP_OPTIONS#輸入make -j12 V=1 打印詳細(xì)信息 #判斷V的代碼是不是來自于命令行 #origin 用于告訴你變量是哪來的 ifeq ("$(origin V)", "command line")KBUILD_VERBOSE = $(V) #如果是,KBUILD_VERBOSE=1 endif ifndef KBUILD_VERBOSEKBUILD_VERBOSE = 0 endififeq ($(KBUILD_VERBOSE),1) #如果 KBUILD_VERBOSE 為 1quiet = #quiet和 Q 都為空Q = elsequiet=quiet_Q = @ #加 @ 命令不顯示在終端 endif# If the user is running make -s (silent mode), suppress echoing of # commands#make -s 靜默輸出#判斷當(dāng)前正在使用的編譯器版本號是否為 4.x#filter 是個過濾函數(shù),函數(shù)格式如下: #$(filter <pattern...>,<text>) #filter 函數(shù)表示以 pattern 模式過濾 text 字符串中的單詞,僅保留符合模式 pattern 的單詞, #可以有多個模式。函數(shù)返回值就是符合 pattern 的字符串。因此$(filter 4.%,$(MAKE_VERSION)) #的 含 義 就 是 在 字 符 串 “ MAKE_VERSION ” 中 找 出 符 合 “ 4.% ” 的 字 符 (% 為 通 配 符 ) , #MAKE_VERSION 是 make 工具的版本號, #ubuntu16.04 里面默認(rèn)自帶的 make 工具版本號為 4.1, ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 #firstword 是獲取首單詞,函數(shù)格式如下: #$(firstword <text>) #firstword 函數(shù)用于取出 text 字符串中的第一個單詞,函數(shù)的返回值就是獲取到的單詞。 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)quiet=silent_ endif else # make-3.8x ifneq ($(filter s% -s%,$(MAKEFLAGS)),)quiet=silent_ endif endif #導(dǎo)出 quiet Q KBUILD_VERBOSE export quiet Q KBUILD_VERBOSEifeq ($(KBUILD_SRC),)# OK, Make called in directory where kernel src resides # Do we want to locate output files in a separate directory? #make -O 指定輸出結(jié)果到某一個文件夾 ifeq ("$(origin O)", "command line")KBUILD_OUTPUT := $(O) endif# That's our default target when none is given on the command line PHONY := _all _all:# Cancel implicit rules on top Makefile $(CURDIR)/Makefile Makefile: ;ifneq ($(KBUILD_OUTPUT),) # Invoke a second make in the output directory, passing relevant variables # check that the output directory actually exists saved-output := $(KBUILD_OUTPUT) KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \&& /bin/pwd) $(if $(KBUILD_OUTPUT),, \$(error failed to create output directory "$(saved-output)"))PHONY += $(MAKECMDGOALS) sub-make$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make@:sub-make: FORCE$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))# Leave processing to above invocation of make skip-makefile := 1 endif # ifneq ($(KBUILD_OUTPUT),) endif # ifeq ($(KBUILD_SRC),)# We process the rest of the Makefile if this is the final invocation of make ifeq ($(skip-makefile),)# Do not print "Entering directory ...", # but we want to display it when entering to the output directory # so that IDEs/editors are able to understand relative filenames. MAKEFLAGS += --no-print-directory# Call a source code checker (by default, "sparse") as part of the # C compilation. # # Use 'make C=1' to enable checking of only re-compiled files. # Use 'make C=2' to enable checking of *all* source files, regardless # of whether they are re-compiled or not. # # See the file "Documentation/sparse.txt" for more details, including # where to get the "sparse" utility. #“make C=1”使能代碼檢查,檢查那些需要重新編譯的文件。 #“make C=2”用于檢查所有的源碼文件 ifeq ("$(origin C)", "command line")KBUILD_CHECKSRC = $(C) endif ifndef KBUILD_CHECKSRCKBUILD_CHECKSRC = 0 endif# Use make M=dir to specify directory of external module to build # Old syntax make ... SUBDIRS=$PWD is still supported # Setting the environment variable KBUILD_EXTMOD take precedence #編譯模塊 ifdef SUBDIRSKBUILD_EXTMOD ?= $(SUBDIRS) endififeq ("$(origin M)", "command line")KBUILD_EXTMOD := $(M) endif# If building an external module we do not care about the all: rule # but instead _all depend on modules PHONY += all ifeq ($(KBUILD_EXTMOD),) _all: all else _all: modules endififeq ($(KBUILD_SRC),)# building in the source treesrctree := . elseifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))# building in a subdirectory of the source treesrctree := ..elsesrctree := $(KBUILD_SRC)endif endif objtree := . src := $(srctree) obj := $(objtree)VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))#srctree 源碼路徑 export srctree objtree VPATH#打印變量便于調(diào)試 mytest:echo srctree=$(srctree)echo objtree=$(objtree) # Make sure CDPATH settings don't interfere unexport CDPATH######################################################################### #獲取主機架構(gòu)和系統(tǒng) HOSTARCH := $(shell uname -m | \sed -e s/i.86/x86/ \-e s/sun4u/sparc64/ \-e s/arm.*/arm/ \-e s/sa110/arm/ \-e s/ppc64/powerpc/ \-e s/ppc/powerpc/ \-e s/macppc/powerpc/\-e s/sh.*/sh/)HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \sed -e 's/\(cygwin\).*/cygwin/')export HOSTARCH HOSTOS########################################################################## set default to nothing for native builds #設(shè)置目標(biāo)架構(gòu)、交叉編譯器 ifeq ($(HOSTARCH),$(ARCH)) CROSS_COMPILE ?= endif################ ARCH ?= arm CROSS_COMPILE ?= arm-linux-gnueabihf- #################設(shè)置配置文件 KCONFIG_CONFIG ?= .config export KCONFIG_CONFIG# SHELL used by kbuild CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \else if [ -x /bin/bash ]; then echo /bin/bash; \else echo sh; fi ; fi)HOSTCC = cc HOSTCXX = c++ HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer HOSTCXXFLAGS = -O2ifeq ($(HOSTOS),cygwin) HOSTCFLAGS += -ansi endififeq ($(HOSTOS),darwin) # get major and minor product version (e.g. '10' and '6' for Snow Leopard) DARWIN_MAJOR_VERSION = $(shell sw_vers -productVersion | cut -f 1 -d '.') DARWIN_MINOR_VERSION = $(shell sw_vers -productVersion | cut -f 2 -d '.')os_x_before = $(shell if [ $(DARWIN_MAJOR_VERSION) -le $(1) -a \$(DARWIN_MINOR_VERSION) -le $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;)# Snow Leopards build environment has no longer restrictions as described above HOSTCC = $(call os_x_before, 10, 5, "cc", "gcc") HOSTCFLAGS += $(call os_x_before, 10, 4, "-traditional-cpp") HOSTLDFLAGS += $(call os_x_before, 10, 5, "-multiply_defined suppress")# since Lion (10.7) ASLR is on by default, but we use linker generated lists # in some host tools which is a problem then ... so disable ASLR for these # tools HOSTLDFLAGS += $(call os_x_before, 10, 7, "", "-Xlinker -no_pie") endif# Decide whether to build built-in, modular, or both. # Normally, just do built-in.KBUILD_MODULES := KBUILD_BUILTIN := 1# If we have only "make modules", don't compile built-in objects. # When we're building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to # make sure the checksums are up to date before we record them.ifeq ($(MAKECMDGOALS),modules)KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) endif# If we have "make <whatever> modules", compile modules # in addition to whatever we do anyway. # Just "make" or "make all" shall build modules as well# U-Boot does not need modules #ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) # KBUILD_MODULES := 1 #endif#ifeq ($(MAKECMDGOALS),) # KBUILD_MODULES := 1 #endifexport KBUILD_MODULES KBUILD_BUILTIN export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD# We need some generic definitions (do not try to remake the file). scripts/Kbuild.include: ; #用文件 scripts/Kbuild.include 這個文件 include scripts/Kbuild.include# Make variables (CC, etc...) #交叉編譯工具變量設(shè)置 AS = $(CROSS_COMPILE)as # Always use GNU ld ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),) LD = $(CROSS_COMPILE)ld.bfd else LD = $(CROSS_COMPILE)ld endif CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm LDR = $(CROSS_COMPILE)ldr STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump AWK = awk PERL = perl PYTHON = python DTC = dtc CHECK = sparseCHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \-Wbitwise -Wno-return-void -D__CHECK_ENDIAN__ $(CF)KBUILD_CPPFLAGS := -D__KERNEL__ -D__UBOOT__KBUILD_CFLAGS := -Wall -Wstrict-prototypes \-Wno-format-security \-fno-builtin -ffreestanding KBUILD_AFLAGS := -D__ASSEMBLY__# Read UBOOTRELEASE from include/config/uboot.release (if it exists) UBOOTRELEASE = $(shell cat include/config/uboot.release 2> /dev/null) UBOOTVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION #架構(gòu)(arm)cpu(arm7)板卡(mx6ullevk)供應(yīng)商(freescale)soc(mx6) cpu文件所處的目錄 板子配置信息所處目錄 #config.mk定義變量 export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC export CPP AR NM LDR STRIP OBJCOPY OBJDUMP export MAKE AWK PERL PYTHON export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGSexport KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS export KBUILD_CFLAGS KBUILD_AFLAGS# When compiling out-of-tree modules, put MODVERDIR in the module # tree rather than in the kernel tree. The kernel tree might # even be read-only. export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions# Files to ignore in find ... statementsexport RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \-name CVS -o -name .pc -o -name .hg -o -name .git \) \-prune -o export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \--exclude CVS --exclude .pc --exclude .hg --exclude .git6、U-Boot 圖形化配置
menuconfig是一套圖形化的配置工具,需要 ncurses 庫支持。ncurses 庫提供了一系列的 API 函數(shù)供調(diào)用者
sudo apt-get install build-essential
sudo apt-get install libncurses5-dev
在uboot源碼的根目錄下輸入命令打開圖形化窗口
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
如果要將某個功能編譯為模塊,那就按下“M”,此時“[ ]”就會變?yōu)椤?lt; M >。
設(shè)置完成后需要重新編譯
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
7、uboot移植
uboot的燒寫有兩種方法,一種是燒寫到SD卡,一種是燒寫到EMMC中(也需要用到SD卡)。
下載NXP的uboot(NXP有一個例程板卡,仿照那個板卡改自己的板子)
編譯NXP的uboot
需要將SD卡格式化成FAT32的格式。
(正點原子專門編寫了一個軟件來將編譯出來的.bin 文件燒寫到 SD 卡中,這個軟件叫做“imxdownload”,軟件放到了開發(fā)板光盤中,路徑為:開發(fā)板光盤->5、開發(fā)工具->2、Ubuntu 下裸機燒寫軟件->imxdownload,imxdownlaod 只能在 Ubuntu 下使用。)
將imxdownlaod拷貝到工程根目錄下。
確定要燒寫的 SD 卡(ls /dev/sd*(我的是/dev/sdb))
使用 imxdownload 軟件將 u-boot.bin燒寫到 SD 卡中。
五、linux內(nèi)核
1、linux內(nèi)核編譯
需要下載lzop庫,用于打包和加載zimage
sudo apt-get install lzop
編譯內(nèi)核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfigmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfigmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16編譯完成以后就會在 arch/arm/boot 這個目錄下生成一個叫做 zImage 的文件,zImage 就是我們要用的 Linux 鏡像文件。另外也會在 arch/arm/boot/dts 下生成很多.dtb 文件(imx6ull-alientek-emmc.dtb),這些.dtb 就是設(shè)備樹文件。
2、重要的文件夾
| arch | 架構(gòu)相關(guān)目錄名 |
| block | 塊設(shè)備相關(guān)目錄 |
| crypto | 加密相關(guān)目錄 |
| Documentation | 文檔相關(guān)目錄 |
| drivers | 驅(qū)動相關(guān)目錄 |
| firmeare | 固件相關(guān)目錄 |
| fs | 文件系統(tǒng)相關(guān)目錄 |
| include | 頭文件相關(guān)目錄 |
| init | 初始化相關(guān)目錄 |
| ipc | 進(jìn)程間通信相關(guān)目錄 |
| kernel | 內(nèi)核相關(guān)目錄 |
| lib | 庫相關(guān)目錄 |
| mm | 內(nèi)存管理相關(guān)目錄 |
| net | 網(wǎng)絡(luò)相關(guān)目錄 |
| samples | 例程相關(guān)目錄 |
| scripts | 腳本相關(guān)目錄 |
| security | 安全相關(guān)目錄 |
| sound | 音頻處理相關(guān)目錄 |
| tools | 工具相關(guān)目錄 |
| usr | 與 initramfs 相關(guān)的目錄,用于生成initramfs |
| virt | 提供虛擬機技術(shù)(KVM) |
linux頂層的makefile和uboot非常相似
六、根文件系統(tǒng)
1、根文件系統(tǒng)介紹
根文件系統(tǒng)首先是內(nèi)核啟動時所 mount(掛載)的第一個文件系統(tǒng),內(nèi)核代碼映像文件保存在根文件系統(tǒng)中,而系統(tǒng)引導(dǎo)啟動程序會在根文件系統(tǒng)掛載之后從中把一些基本的初始化腳本和服務(wù)等加載到內(nèi)存中去運行。
2、根文件系統(tǒng)的目錄
| /bin | 系統(tǒng)需要的可執(zhí)行文件(ls rm…) |
| /dev | 設(shè)備文 件(串口…) |
| /etc | 存放配置文件目錄 |
| /mnt | 臨時掛載目錄,可以將 SD 卡或者 U 盤掛載到/mnt/sd 或者/mnt/usb 目錄中 |
| /proc | 臨時掛載目錄,存儲系統(tǒng)運行信息文件 |
| /usr | 軟件資源目錄 |
| /var | 此目錄存放一些可以改變的數(shù)據(jù) |
| /sbin | 用戶存放一些可執(zhí)行文件 |
| /sys | 系統(tǒng)啟動以后此目錄作為 sysfs 文件系統(tǒng)的掛載點,sysfs 是一個類似于 proc 文件系統(tǒng)的特殊文件系統(tǒng),sysfs 也是基于 ram 的文件系統(tǒng),也就是說它也沒有實際的存儲設(shè)備。此目錄是系統(tǒng)設(shè)備管理的重要目錄,此目錄通過一定的組織結(jié)構(gòu)向用戶提供詳細(xì)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)信息。 |
| /opt | 可選的文件、軟件存放區(qū)(由用戶決定) |
3、BusyBox
BusyBox 是一個集成了大量的 Linux 命令和工具的軟件,像 ls、mv、ifconfig 等命令 BusyBox 都會提供。BusyBox 就是一個大的工具箱,這個工具箱里面集成了 Linux 的許多工具和命令。
編譯 busybox
busybox編譯
make //將編譯的結(jié)果放入/home/wyd/linux/mnt/rootfs文件夾下
make install CONFIG_PREFIX=/home/wyd/linux/mnt/rootfs
編譯完成以后會在 busybox 的所有工具和文件就會被安裝到 rootfs 目錄中, rootfs 目錄內(nèi)容有:bin linuxrc sbin usr。
Linux 內(nèi)核 init 進(jìn)程最后會查找用戶空間的 init 程序,找到以后就會運行這個用戶空間的 init 程序,從而切換到用戶態(tài)。如果 bootargs 設(shè)置 init=/linuxrc,那么 linuxrc 就是可以作為用戶空間的 init 程序,所以用戶態(tài)空間的 init 程序是 busybox 來生成的。busybox 的工作就完成了,但是此時的根文件系統(tǒng)還不能使用,還需要一些其他的文件。
向根文件系統(tǒng)添加 lib 庫
在 rootfs 中創(chuàng)建一個名為“l(fā)ib”的文件夾 mkdir lib
將交叉編譯器的庫文件放到根文件系統(tǒng)中。
cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
mkdir dev proc mnt sys tmp root
setenv bootargs ‘console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.250(服務(wù)器ip地址ubuntu):
/home/wyd/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.251(開發(fā)板):192.168.1.250(服務(wù)器ip地址ubuntu):192.168.1.1:
255.255.255.0::eth0:off’ //設(shè)置 bootargs
七、Linux 驅(qū)動開發(fā)
1.字符設(shè)備驅(qū)動
字符設(shè)備是 Linux 驅(qū)動中最基本的一類設(shè)備驅(qū)動,字符設(shè)備就是一個一個字節(jié),按照字節(jié)
流進(jìn)行讀寫操作的設(shè)備,讀寫數(shù)據(jù)是分先后順序的。比如我們最常見的點燈、按鍵、IIC、SPI,
LCD 等等都是字符設(shè)備,這些設(shè)備的驅(qū)動就叫做字符設(shè)備驅(qū)動。
Linux 驅(qū)動有兩種運行方式,第一種就是將驅(qū)動編譯進(jìn) Linux 內(nèi)核中,這樣當(dāng) Linux 內(nèi)核啟
動的時候就會自動運行驅(qū)動程序。第二種就是將驅(qū)動編譯成模塊(Linux 下模塊擴展名為.ko),在
Linux 內(nèi)核啟動以后使用“insmod”命令加載驅(qū)動模塊。
模塊有加載和卸載兩種操作,我們在編寫驅(qū)動的時候需要注冊這兩種操作函數(shù),模塊的加載和
卸載注冊函數(shù)如下:
printk在內(nèi)核源碼中用來記錄日志信息的函數(shù),只能在內(nèi)核源碼范圍內(nèi)使用。用法和printf非常相似
.ko文件是kernel object文件(內(nèi)核模塊),該文件的意義就是把內(nèi)核的一些功能移動到內(nèi)核外邊, 需要的時候插入內(nèi)核,不需要時卸載。
insmod xxx.ko 裝載驅(qū)動
modprobe xxx.ko 裝載驅(qū)動
cat /proc/device
lsmod 內(nèi)核中已經(jīng)加載的設(shè)備程序
rmmod 驅(qū)動設(shè)備名稱 卸載驅(qū)動
八、設(shè)備樹
設(shè)備樹(Device Tree),將這個詞分開就是“設(shè)備”和“樹”,描述設(shè)備樹的文件叫做 DTS(Device
Tree Source),這個 DTS 文件采用樹形結(jié)構(gòu)描述板級設(shè)備,也就是開發(fā)板上的設(shè)備信息,比如
CPU 數(shù)量、 內(nèi)存基地址、IIC 接口上接了哪些設(shè)備、SPI 接口上接了哪些設(shè)備等等。
DTS 是設(shè)備樹源碼文件,DTB 是將DTS 編譯以后得到的二進(jìn)制文件。
將.dts 編譯為.dtb需要用到 DTC 工具
編譯 DTS 文件的話只需要進(jìn)入到 Linux 源碼根目錄下,然后執(zhí)行如下命令:
make all或者:make dtbs
“make all”命令是編譯 Linux 源碼中的所有東西,包括 zImage,.ko 驅(qū)動模塊以及設(shè)備樹,如果只是編譯設(shè)備樹的話建議使用“make dtbs”命令。
DTS 語法
.dtsi 頭文件:設(shè)備樹的頭文件擴展名為.dtsi,一般.dtsi 文件用于描述 SOC 的內(nèi)部外設(shè)信息,比如 CPU 架構(gòu)、主頻、外設(shè)寄存器地址范圍,比如 UART、IIC 等等。
設(shè)備節(jié)點:設(shè)備樹是采用樹形結(jié)構(gòu)來描述板子上的設(shè)備信息的文件,每個設(shè)備都是一個節(jié)點,叫做設(shè)備節(jié)點,每個節(jié)點都通過一些屬性信息來描述節(jié)點信息,屬性就是鍵—值對。
節(jié)點標(biāo)簽(label) :節(jié)點名字(節(jié)點名字@首地址)
節(jié)點標(biāo)簽可以用來給節(jié)點追加信息。
例如:
每個節(jié)點都有不同屬性,不同的屬性又有不同的內(nèi)容,屬性都是鍵值對,值可為空或任意的字節(jié)流。設(shè)備樹源碼中常用的幾種數(shù)據(jù)形式如下所示:
1、字符串
compatible = “arm,cortex-a7”;
2、32 位無符號整數(shù)
reg = <0>;
也可以設(shè)置為一組值,比如:
reg = <0 0x123456 100>;
3、字符串列表
屬性值也可以為字符串列表,字符串和字符串之間采用“,”隔開,如下所示:
compatible = “fsl,imx6ull-gpmi-nand”, “fsl, imx6ul-gpmi-nand”;
compatible 屬性
compatible 屬性也叫做“兼容性”屬性,compatible 屬性的值是一個字符串列表,compatible 屬性用于將設(shè)備和驅(qū)動綁定起來。一般驅(qū)動程序文件都會有一個 OF 匹配表,此 OF 匹配表保存著一些 compatible 值,如果設(shè)備節(jié)點的compatible 屬性值和 OF 匹配表中的任何一個值相等,那么就表示設(shè)備可以用這個驅(qū)動。
model 屬性
model 屬性值也是一個字符串,一般 model 屬性描述設(shè)備模塊信息,比如名字之類的。
status 屬性
status 屬性看名字就知道是和設(shè)備狀態(tài)有關(guān)的,status 屬性值是字符串,字符串是設(shè)備的狀態(tài)信息。常用的是“okay”和“disabled”
“okay”表明設(shè)備是可操作的。
“disabled” 表明設(shè)備當(dāng)前是不可操作的,但是在未來可以變?yōu)榭刹僮鞯?比如熱插拔設(shè)備插入以后。至于 disabled 的具體含義還要看設(shè)備的綁定文檔。
#address-cells 和#size-cells 屬性
這兩個屬性的值都是無符號 32 位整形,#address-cells 和#size-cells 這兩個屬性可以用在任何擁有子節(jié)點的設(shè)備中,用于描述子節(jié)點的地址信息。 #address-cells 屬性值決定了子節(jié)點 reg 屬性中地址信息所占用的字長(32位)
#size-cells 屬性值決定了子節(jié)點 reg 屬性中長度信息所占的字長(32位)。
#address-cells 和#size-cells 表明了子節(jié)點應(yīng)該如何編寫 reg 屬性值,一般 reg 屬性都是和地址有關(guān)的內(nèi)容,和地址相關(guān)的信息有兩種:起始地址和地址長度。
reg 屬性
reg 屬性的值一般是(address,length),reg 屬性一般用于描述設(shè)備地址空間資源信息,一般都是某個外設(shè)的寄存器地址范圍信息。
reg 屬性的格式一為:reg = <address1 length1 address2 length2 address3 length3......>
每個“address length”組合表示一個地址范圍,其中 address 是起始地址,length 是地址長度,#address-cells 表明 address 這個數(shù)據(jù)所占用的字長,#size-cells 表明 length 這個數(shù)據(jù)所占用的字長。
開發(fā)板查看設(shè)備樹:/proc/device-tree
cd /lib/modules/4.1.15-gb78e551/
root@ATK-IMX6U:~# cp /mnt/gpioled.ko /lib/modules/4.1.15-gb78e551/
root@ATK-IMX6U:~# cp /mnt/ledApp /lib/modules/4.1.15-gb78e551/
cd /lib/modules/4.1.15-gb78e551/
depmod: ERROR: could not open directory /lib/modules/4.1.15-g871ccc8: No such file or directory
depmod: FATAL: could not search modules: No such file or directory
學(xué)習(xí)時間
2021.4-2021.10
學(xué)習(xí)產(chǎn)出
1、 csdn筆記 1篇
總結(jié)
以上是生活随笔為你收集整理的嵌入式linux学习笔记(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux LED驱动开发实验(直接操作
- 下一篇: Linux设备树