一、編譯 ST 的 Linux 系統
1. 壓縮源碼
??首先先下載 ST 官方源碼,之前章節已經下載過了,直接輸入以下命令:
cd linux/atk-mpl/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/
??然后壓縮? linux-5.4.31.tar.xz? 源碼包壓縮包:
tar -vxf linux-5.4.31.tar.xz
2. 給內核打補丁
輸入以下命令:
cd linux-5.4.31/
for p in `ls -1 ../*.patch`; do patch -p1 < $p; done //打補丁# ls -1 ../*.patch:通過執行 ls 命令列出上一級目錄(..)中以 .patch 結尾的所有文件,并使用 -1 選項確保每個文件顯示為一行。這將生成一個包含所有補丁文件名的列表。
# for p in ...; do ...; done:通過循環結構遍歷列表中的每個元素,并在循環體內執行相應的操作。這里對列表中的每個元素都被賦值給變量 p。
# patch -p1 < $p:對于每個補丁文件 $p,執行 patch 命令來應用補丁文件到當前目錄。-p1 選項告訴 patch 命令去除補丁文件中的前綴路徑(通常是一個目錄),從而使補丁適用于當前目錄。
# 循環遍歷上一級目錄中的所有以 .patch 結尾的文件,并使用 patch 命令將這些補丁文件應用到當前目錄中,其中補丁文件的前綴路徑被去除
3. 生成默認配置文件
??ST 原廠 Linux 內核需要先生成默認配置文件,并且對其進行打補丁,進入 Linux 內核源碼根目錄下,然后執行如下命令:?
make ARCH=arm multi_v7_defconfig "fragment*.config"# multi_v7_defconfig:指定要使用的配置文件。
# "fragment*.config":用于指定附加的配置片段文件。這里的 "fragment*.config" 是通配符表達式,指定了以 fragment 開頭且以 .config 結尾的文件名模式。
??.config 文件非常重要, Linux 內核的所有配置項最終都保存在.config 文件里面,最終編譯Linux 內核的時候需要讀取.config 里面的配置項!此時我們只是生成了.config,并沒有將?fragment config 補丁文件打進去,執行如下命令:?
for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done
yes '' | make ARCH=arm oldconfig# scripts/kconfig/merge_config.sh -m -r .config $f:執行 merge_config.sh 腳本,將文件 $f 的配置合并到當前目錄的 .config 文件中。-m 選項表示使用模塊化配置,-r 選項表示要保留已經存在的配置。
# yes '':yes 命令會重復地輸出指定的字符串(在本例中是空字符串 ''),直到被終止
# ARM 架構上使用舊的配置 .config 文件執行 make oldconfig,并在自動回答用戶提示時使用空字符串來完成配置過程。這樣可以自動化配置過程,減少了手動輸入配置的需要
??至此, Linux 源碼根目錄下的.config 文件就已經保存了所有的配置項,所以只需要復制一份.config 作為我們的默認配置文件即可,復制命令如下:?
cp .config ./arch/arm/configs/stm32mp1_atk_defconfig
??Linux 內核全部打完補丁, linux-5.4.31 目錄就是我們要移植的 Linux 源碼。我們新建一個名為“my_linux”的目錄來保存我們要移植的 linux 源碼,然后將打完補丁的 linux 源碼 linux-5.4.31 拷貝到“my_linux”目錄下,命令如下:?
# 首先在/linux/atk-mpl創建 linux 目錄,然后再linux目錄下創建my_linux子目錄
cd ~
cd linux/atk-mpl/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/
cp linux-5.4.31 /linux/atk-mpl/linux/my_linux/ -rf //拷貝
二、編譯 ST 官方 Linux 源碼?
1. 修改Makefile
??其實跟uboot修改頂層 Makefile 一樣,為了減少輸入參數,就添加以下代碼到頂層 Makefile 中。
??創建一個名為“stm32mp157d_atk.sh”的編譯腳本,腳本內容如下:?
#!/bin/sh
make distclean
make stm32mp1_atk_defconfig
make menuconfig
make uImage dtbs LOADADDR=0XC2000040 -j16
??給 stm32mp157d_atk.sh? 執行權限:
chmod 777 stm32mp157d_atk.sh // 給予可執行權限
./stm32mp157d_atk.sh // 運行編譯腳本
??編譯完成以后的到 uImage 鏡像文件和設備樹,其中 STM32MP157 系列的設備樹有很多。
2. 修改網絡驅動
??文件均來自正點原子的包里,下載地址:STM32MP157開發板 — 正點原子資料下載中心 1.0.0 文檔
??將 motorcomm.c 和 motorcomm_phy.h 分別拷貝到 Linux 源碼下的 drivers/net/phy 和include/linux 目錄下。拷貝完成以后修改 drivers/net/phy/Makefile 文件,加上下面這句:?
obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o# 如果定義了配置選項 CONFIG_MOTORCOMM_PHY 并且其值為 true,那么目標 motorcomm.o 將被添加到目標列表中,進而進行編譯。
??還需要修改 drivers/net/phy/Kconfig 文件,添加以下代碼:
config MOTORCOMM_PHYtristate "Motorcomm PHYs"---help---Supports the YT8010, YT8510, YT8511, YT8512 PHYs./* config MOTORCOMM_PHY:定義了一個名為 MOTORCOMM_PHY 的配置選項。這個配置選項可用于在編譯內核時啟用或禁用與 Motorcomm PHY 相關的功能。tristate "Motorcomm PHYs":配置選項的類型被設置為 tristate,即可以選擇三個狀態:y(編譯進內核), m(編譯為模塊)和 n(禁用)。
"Motorcomm PHYs" 是配置選項的顯示名稱,將顯示在配置界面上。---help---:用來提供配置選項的幫助說明。該行以下的內容將被視為對配置選項的詳細描述。Supports the YT8010, YT8510, YT8511, YT8512 PHYs.:是對配置選項的詳細描述,說明該選項的作用是支持 YT8010、YT8510、YT8511 和 YT8512 PHY 模塊。
我們可以在 Linux 內核構建過程中選擇是否編譯或加載與 Motorcomm PHY 相關的功能模塊*/
???在終端輸入 make menuconfig,進入以下路徑:
-> Device Drivers-> Network device support (NETDEVICES [=y])-> PHY Device support and infrastructure (PHYLIB [=y])-> <*> Motorcomm PHYs //將 YT8511 驅動編譯進內核
3. 啟動測試
??需要兩個文件: uImage 和 stm32mp157d-ed1.dtb?
??首先將 /home/alientek/linux/tftpboot 文件刪除,然后把這兩個文件復制到tftp目錄下并給予執行權限,命令如下:
cp stm32mp157d-ed1.dtb /home/alientek/linux/tftpboot/
cd ..
cp uImage /home/alientek/linux/tftpboot/
cd ~
cd linux/tftpboot/
chmod 777 stm32mp157d-ed1.dtb
chmod 777 uImage
??在 uboot 中輸入:
tftp c2000000 uImage
tftp c4000000 stm32mp157d_ed1.dtb
bootm c2000000 - c4000000
三、在 Linux 中添加自己的開發板
1. 添加開發板對應的默認配置文件?
??首先添加開發板對應的默認配置文件,這里輸入以下命令:
cd linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/configs
find stm32mp1_atk_defconfig
??就可以找到? stm32mp1_atk_defconfig? 這個文件了。
2. 添加開發板對應的設備樹?
?① 新建設備樹文件
???輸入以下命令:
cd ~
cd linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts/
cp stm32mp15xx-edx.dtsi stm32mp157d-atk.dtsi
cp stm32mp157d-ed1.dts stm32mp157d-atk.dts
??修改? stm32mp157d-atk.dts? 文件:
?② 修改 stm32mp157d-atk.dtsi 文件?
???跟uboot移植類似,PMIC 配置不需要,并在設備樹里面添加電源節點信息。
#include "stm32mp157-m4-srm.dtsi"
#include "stm32mp157-m4-srm-pinctrl.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/mfd/st,stpmic1.h>/ {memory@c0000000 {device_type = "memory";reg = <0xC0000000 0x40000000>;};reserved-memory {#address-cells = <1>;#size-cells = <1>;ranges;mcuram2: mcuram2@10000000 {compatible = "shared-dma-pool";reg = <0x10000000 0x40000>;no-map;};vdev0vring0: vdev0vring0@10040000 {compatible = "shared-dma-pool";reg = <0x10040000 0x1000>;no-map;};vdev0vring1: vdev0vring1@10041000 {compatible = "shared-dma-pool";reg = <0x10041000 0x1000>;no-map;};vdev0buffer: vdev0buffer@10042000 {compatible = "shared-dma-pool";reg = <0x10042000 0x4000>;no-map;};mcuram: mcuram@30000000 {compatible = "shared-dma-pool";reg = <0x30000000 0x40000>;no-map;};retram: retram@38000000 {compatible = "shared-dma-pool";reg = <0x38000000 0x10000>;no-map;};};vddcore: buck1 {compatible = "regulator-fixed";regulator-name = "vddcore";regulator-min-microvolt = <1200000>;regulator-max-microvolt = <1350000>;regulator-always-on;regulator-boot-on;};v3v3: regulator-3p3v {compatible = "regulator-fixed";regulator-name = "v3v3";regulator-min-microvolt = <3300000>;regulator-max-microvolt = <3300000>;regulator-always-on;regulator-boot-on;};
};&cpu0 {cpu-supply = <&vddcore>;
};&crc1 {status = "okay";
};&dma1 {sram = <&dma_pool>;
};&dma2 {sram = <&dma_pool>;
};&dts {status = "okay";
};ðernet0 {status = "okay";pinctrl-0 = <ðernet0_rgmii_pins_a>;pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>;pinctrl-names = "default", "sleep";phy-mode = "rgmii-id";max-speed = <1000>;phy-handle = <&phy0>;mdio0 {#address-cells = <1>;#size-cells = <0>;compatible = "snps,dwmac-mdio";phy0: ethernet-phy@0 {reg = <0>;};};
};&hash1 {status = "okay";
};&ipcc {status = "okay";
};&iwdg2 {timeout-sec = <32>;status = "okay";
};&rng1 {status = "okay";
};&rtc {status = "okay";
};&sdmmc1 {pinctrl-names = "default", "opendrain", "sleep";pinctrl-0 = <&sdmmc1_b4_pins_a>;pinctrl-1 = <&sdmmc1_b4_od_pins_a>;pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;broken-cd;st,neg-edge;bus-width = <4>;vmmc-supply = <&v3v3>;status = "okay";
};&sdmmc2 {pinctrl-names = "default", "opendrain", "sleep";pinctrl-0 = <&sdmmc2_b4_pins_a>;pinctrl-1 = <&sdmmc2_b4_od_pins_a>;pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>;non-removable;st,neg-edge;bus-width = <8>;vmmc-supply = <&v3v3>;keep-power-in-suspend;status = "okay";
};&sram {dma_pool: dma_pool@0 {reg = <0x50000 0x10000>;pool;};
};&uart4 {pinctrl-names = "default", "sleep", "idle";pinctrl-0 = <&uart4_pins_a>;pinctrl-1 = <&uart4_sleep_pins_a>;pinctrl-2 = <&uart4_idle_pins_a>;pinctrl-3 = <&uart4_pins_a>;/delete-property/dmas;/delete-property/dma-names;status = "okay";
};
??這里是替換整個文件的代碼,直接復制進去即可。
?③ 編譯 stm32mp157d-atk.dts 設備樹
??打開 arch/arm/boot/dts/Makefile,到“ dtb-$(CONFIG_ARCH_STM32)”配置項,在此配置項中加入“stm32mp157d-atk.dtb”, 表示編譯的時候也將 stm32mp157datk.dts 編譯為 stm32mp157d-atk.dtb?。
3. 關閉內核模塊驗證
??后續做 Linux 驅動實驗的時候我們都是編譯驅動模塊,然后在系統里面加載,加載的時候系統會驗證模塊,有時候會驗證出錯。比如板子運行的系統和編譯驅動模塊所用的系統不一致的時候。為了方便開發,我們可以關閉內核模塊驗證。打開默認配置文件 stm32mp1_atk_defconfig,里面有如下所示配置項目:?
??我們也可以直接在 Linux 的圖形化配置界面上關閉掉內核模塊驗證,輸入如下命令打開 Linux 內核圖形化配置界面:?
# 在 /linux/atk-mpl/linux/my_linux/linux-5.4.31 該目錄下
make menuconfig# 配置路徑如下:
-> Enable loadable module support (MODULES [=y])->Module signature verification
??只要通過圖形化界面修改了 Linux 內核配置,最好及時將其保存到stm32mp1_atk_defconfig 文件。因為圖形化界面修改的配置只是暫時保存到.config 文件里面,一旦使用“make clean”清理工程,那么.config 文件就會被刪除掉,所有的配置也就丟失了!
4. 編譯測試
??進入該目錄 /linux/atk-mpl/linux/my_linux/linux-5.4.31,再編譯 stm32mp157d_atk.sh。二、3啟動測試類似,把文件拷貝到 tftp 服務器目錄下。在uboot下輸入以下命令。
tftp c2000000 uImage
tftp c4000000 stm32mp157d-atk.dtb
bootm c2000000 - c4000000
??有這樣的 log 信息就啟動成功。
四、燒寫系統鏡像到 EMMC ?
??我們現在都是通過 tftp 命令從網絡上下載測試的,實際產品開發中最終是要將系統燒寫到外部 Flash 中的,比如 EMMC。現在我們將 uIamge 和 stm32mp157d-atk.dtb 打包成 ext4 格式,然后通過 STM32CubeProgrammer 燒寫到 EMMC 里面,最終啟動 EMMC 里面的 Linux 系統。
1. 系統鏡像打包
??先在 linux/atk-mpl/linux/ 路徑下創建子目錄 bootfs,然后再把 /linux/tftpboot 路徑下的 stm32mp157d-atk.dtb 和 uImage 文件拷貝到 bootfs 里面。
① 新建 ext4 格式磁盤
??首先新建一個 ext4 格式的磁盤,然后掛載這個 ext4 格式的磁盤,將 stm32mp157d-atk.dtb 和 uImage 拷貝到這個 ext4 磁盤即可。?
cd bootfs
dd if=/dev/zero of=bootfs.ext4 bs=1M count=10
mkfs.ext4 -L bootfs bootfs.ext4 # 使用 mkfs.ext4 將 bootfs.ext4 磁盤格式化為 ext4 格式# 使用 dd 命令創建一個名為 bootfs.ext4 的磁盤, of 指定磁盤名字為“bootfs.ext4”;bs 指定磁盤輸入/輸出塊大小為 1MB; count 指定磁盤的塊數量為 10 個。
# 將會在當前目錄下創建一個大小為10MB的bootfs.ext4文件,并用零填充它。
② 將系統鏡像拷貝到 ext4 磁盤中
??首先創建一個目錄用來掛載前面制作制作出來的 bootfs.ext4,比如我這里創建目錄/mnt/bootfs,命令如下:?
cd /
sudo mkdir /mnt/bootfs
??使用 mount 命令將 bootfs.ext4 掛載到/mnt/bootfs 目錄下,命令如下:?
cd /home/alientek/linux/atk-mp1/linux/bootfs
sudo mount bootfs.ext4 /mnt/bootfs
??掛載成功以后就將?uImage 和 stm32mp157d-atk.dtb 拷貝到/mnt/bootfs 目錄下,命令如下:?
sudo cp uImage stm32mp157d-atk.dtb /mnt/bootfs/
??拷貝完成以后使用 umount 卸載/mnt/bootfs 即可,命令如下:?
sudo umount /mnt/bootfs
??這里我的理解是:先創建.ext4 磁盤,這個磁盤必須掛在到目錄下,把需要放在這個磁盤下的東西放在掛載的目錄里,最后再卸載該目錄下的掛載。
2. 燒寫到 EMMC?
??利用 FileZilla 將 bootfs.ext4 拷貝到 image 目錄下:
??修改 Flashlayout:
??先把撥片波到USB,然后燒寫,燒寫完成后,把撥片撥到 EMMC 然后 RESET ,在 uboot 輸入以下命令來驗證是否燒寫了? uImage 和stm32mp157d-atk.dtb?
ext4ls mmc 1:2
??設置 bootcmd 環境變量,從 EMMC 里面讀取系統鏡像和設備樹并啟動,命令如下:
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
saveenv
boot
??一般情況下,調試階段都是用網絡調試,也就是 tftp 或 nfs,當最終產品開發完成后,出廠的時候才燒寫到 EMMC 里面。
五、文件系統缺失錯誤 ??
??Linux 內核啟動以后是需要根文件系統的,根文件系統存在哪里是由 uboot 的 bootargs 環境變量指定的,它會傳遞給 Linux 內核作為命令行(command line)參數。? 沒有對應的根文件系統,必須要自己做根文件系統。 在沒有根文件系統的情況下, Linux 內核啟動的時候就會有下圖所示的錯誤:?
??也就是提示內核崩潰, VFS(虛擬文件系統)不能掛載根文件系統,因為根文件系統目錄不存在。解決方法就是制作根文件系統,并且設置 uboot 的 bootargs 環境變量,指定根文件系統所在的目錄。?