一、ST 官方 U-boot 编译测试
1. ST 官方 uboot 源码打补丁
1.1 获得 ST 官方的 uboot 源码
https://www.cnblogs.com/toutiegongzhu/p/17578847.html 这里可以看如何下载uboot源码。解压完后输入以下命令:
cd stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/u-boot-stm32mp-2020.01-r0/
解压 u-boot-stm32mp-2020.01-r0.tar.gz ,命令如下:
tar -vxf u-boot-stm32mp-2020.01-r0.tar.gz
1.2 打补丁
输入以下命令:
cd u-boot-stm32mp-2020.01/ //进入 uboot 源码目录
for p in `ls -1 ../*.patch`;do patch -p1 < $p;done //打补丁
# 解释一下打补丁这行代码。
ls -1 ../*.patch:使用 ls 命令列出指定目录上层目录中所有以 .patch 结尾的文件,并使用 -1 参数确保每个文件名只占一行。
for p in ...; do ...; done:对第一步列出的每个补丁文件执行循环操作。
patch -p1 < $p:对当前循环遍历到的补丁文件应用 patch 命令。-p1 参数用于在应用补丁之前去除文件路径中的前缀目录层级,保持与当前目录下的源码结构一致。< $p 用于将补丁文件作为输入传递给 patch 命令。
# 所以这行代码的作用是:循环遍历所有补丁文件,并依次应用到当前目录下的源码中
创建 my_uboot 来保存移植的uboot源码,输入以下命令:
cd ~
cd linux/atk-mpl/uboot/
mkdir my_uboot
cd ~
cd linux/atk-mpl/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/u-boot-stm32mp-2020.01-r0/u-boot-stm32mp-2020.01/
cp * /home/alientek/linux/atk-mpl/uboot/my_uboot/ -rf
# * 代表通配符,表示匹配当前目录下的所有文件
# -r:递归复制子目录
# -f:强制覆盖已存在的文件
2. 编译 ST 官方 uboot 源码
2.1 修改Makefile
打开Makefile文件,增加以下代码:
uboot的移植就是: 编译 EVK 开发板对应的 uboot,编译完成以后将得到的 uboot 可执行文件烧写到正点原子的 STM32MP1 开发板中,看看能不能运行,不能的话就要修改 uboot 相应的文件。命令如下:
make stm32mp15_trusted_defconfig # 使用 make 工具来生成一个名为 stm32mp15_trusted_defconfig 的配置文件
make DEVICE_TREE=stm32mp157d-ev1 all -j8 # DEVICE_TREE=stm32mp157d-ev1 参数指定了使用的设备树文件(Device Tree File),这是描述硬件配置的文件
# -j8 参数表示使用 8 个并行任务进行编译,可以加快构建过程
编译完成后就可以获得uboot可执行文件:
u-boot.bin 包含了设备树(dtb),也就是将 uboot 镜像和设备树打包在了一起。其中 u-boot.stm32 是在 u-boot.bin前面添加了 256 字节头部信息的可执行文件,是要烧写到开发板里面的。
2.2 烧写测试
为什么不能用上一小节编译出来的u-boot.stm32,是因为必须要保证这个uboot是工作正常的,我们刚刚才编译出来的uboot肯定有问题,所以使用正点原子提供的uboot。下载到DDR是正点原子的,下载到EMMC是我们自己编译出来的。
将上一小节编译出来的u-boot.stm32 重命名为 my-u-boot.stm32。然后将 my-u-boot.stm32 放在 images 目录下,并且还要修改Flashlayouts文件,修改烧写到EMMC里面的uboot名字。
首先拨片换到USB才检测得到USB。
烧写完成后,换成EMMC,再重启。我们发现重启以后会一直进行重启,并发出警报声。
从上图可以看出:
1、uboot是能运行的,就是说ST官方的EVK开发板的uboot可以直接在正点原子 STM32Mp157上面运行,但是运行会出错。
2、uboot版本为2020.01,编译日期为2023.8.2,就是刚刚自己编译了uboot。
3、
,因为ST 的EVK开发板使用了电源管理芯片 STPMIC1A,所以uboot运行的时候会初始化PMIC芯片,但这个板子是没有这个芯片的,所以会报 STPMIC的错误。
一步步的修改 uboot,至到其正常工作,也就是所谓的 uboot 移植。
二、 在 U-boot 中添加开发板
1. 创建默认配置文件
首先进入这个目录
其实开发板是参考了官方的 EVK 开发板,所以直接参考官方的板子即可,输入以下命令:
cd configs //进入 uboot 的 configs 目录
cp stm32mp15_trusted_defconfig stm32mp15_atk_trusted_defconfig // 拷贝并重命名
此时这里的 stm32mp15_atk_trusted_defconfig 文件就是开发板使用的默认配置文件。
2. 创建默认设备树
直接复制ST官方的开发板的设备树,命令如下:
cd ..
cd arch/arm/dts/
cp stm32mp157d-ed1.dts stm32mp157d-atk.dts // 拷贝并重命名.dts
cp stm32mp15xx-edx.dtsi stm32mp157d-atk.dtsi // 拷贝并重命名.dtsi
cp stm32mp157a-ed1-u-boot.dtsi stm32mp157d-atk-u-boot.dtsi // 拷贝并重命名.dtsi
打开 stm32mp157d-atk.dts ,需要修改头文件引用:
#include "stm32mp157.dtsi"
#include "stm32mp15xd.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxaa-pinctrl.dtsi"
#include "stm32mp157-m4-srm.dtsi"
#include "stm32mp157-m4-srm-pinctrl.dtsi"
// 原本是 #include "stm32mp15xx-edx.dtsi"
// 修改为:
stm32mp157d-atk.dtsi
// 因为需要修改成自己的头文件
3. 修改电源管理设置
打开 stm32mp157d-atk-u-boot.dtsi ,代码如下:
/ SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright : STMicroelectronics 2018
*/
#include <dt-bindings/clock/stm32mp1-clksrc.h>
#include "stm32mp15-u-boot.dtsi"
#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi"
/ {
aliases {
i2c3 = &i2c4;
mmc0 = &sdmmc1;
mmc1 = &sdmmc2;
};
config {
u-boot,boot-led = "heartbeat";
u-boot,error-led = "error";
u-boot,mmc-env-partition = "ssbl";
// st,fastboot-gpios = <&gpioa 13 GPIO_ACTIVE_LOW>; 删除
// st,stm32prog-gpios = <&gpioa 14 GPIO_ACTIVE_LOW>; 删除
};
led {
/* red {
label = "error";
gpios = <&gpioa 13 GPIO_ACTIVE_LOW>; 删除
default-state = "off";
status = "okay";
};*/
};
};
#ifndef CONFIG_STM32MP1_TRUSTED
&clk_hse {
st,digbypass;
};
&i2c4 {
u-boot,dm-pre-reloc;
};
&i2c4_pins_a {
u-boot,dm-pre-reloc;
pins {
u-boot,dm-pre-reloc;
};
};
/* &pmic {
u-boot,dm-pre-reloc; 删除
}; */
接着修改 stm32mp157d-atk.dtsi :
// adc整段删除
&adc {
/* ANA0, ANA1 are dedicated pins and don't need pinctrl: only in6. */
pinctrl-0 = <&adc1_in6_pins_a>;
pinctrl-names = "default";
vdd-supply = <&vdd>;
vdda-supply = <&vdda>;
vref-supply = <&vdda>;
status = "disabled";
adc1: adc@0 {
st,adc-channels = <0 1 6>;
/* 16.5 ck_cycles sampling time */
st,min-sample-time-nsecs = <400>;
status = "okay";
};
};
&cpu0{
cpu-supply = <&vddcore>;
};
&crc1 {
status = "okay";
};
// dac整段删除
&dac {
pinctrl-names = "default";
pinctrl-0 = <&dac_ch1_pins_a &dac_ch2_pins_a>;
vref-supply = <&vdda>;
status = "disabled";
dac1: dac@1 {
status = "okay";
};
dac2: dac@2 {
status = "okay";
};
};
&dma1 {
sram = <&dma_pool>;
};
&dma2 {
sram = <&dma_pool>;
};
&dts {
status = "okay";
};
&hash1 {
status = "okay";
};
// i2c4整段删除
&i2c4 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c4_pins_a>;
pinctrl-1 = <&i2c4_pins_sleep_a>;
i2c-scl-rising-time-ns = <185>;
i2c-scl-falling-time-ns = <20>;
clock-frequency = <400000>;
status = "okay";
/* spare dmas for other usage */
/delete-property/dmas;
/delete-property/dma-names;
pmic: stpmic@33 {
compatible = "st,stpmic1";
reg = <0x33>;
interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>;
interrupt-controller;
#interrupt-cells = <2>;
status = "okay";
regulators {
compatible = "st,stpmic1-regulators";
buck1-supply = <&vin>;
buck2-supply = <&vin>;
buck3-supply = <&vin>;
buck4-supply = <&vin>;
ldo1-supply = <&v3v3>;
ldo2-supply = <&v3v3>;
ldo3-supply = <&vdd_ddr>;
ldo4-supply = <&vin>;
ldo5-supply = <&v3v3>;
ldo6-supply = <&v3v3>;
vref_ddr-supply = <&vin>;
boost-supply = <&vin>;
pwr_sw1-supply = <&bst_out>;
pwr_sw2-supply = <&bst_out>;
vddcore: buck1 {
regulator-name = "vddcore";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
regulator-initial-mode = <0>;
regulator-over-current-protection;
};
......
vbus_sw: pwr_sw2 {
regulator-name = "vbus_sw";
interrupts = <IT_OCP_SWOUT 0>;
regulator-active-discharge = <1>;
};
};
onkey {
compatible = "st,stpmic1-onkey";
interrupts = <IT_PONKEY_F 0>, <IT_PONKEY_R 0>;
interrupt-names = "onkey-falling", "onkey-rising";
power-off-time-sec = <10>;
status = "okay";
};
watchdog {
compatible = "st,stpmic1-wdt";
status = "disabled";
};
};
};
继续修改,将下面的代码都删除:
led {
compatible = "gpio-leds";
blue {
label = "heartbeat";
gpios = <&gpiod 9 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
};
sd_switch: regulator-sd_switch {
compatible = "regulator-gpio";
regulator-name = "sd_switch";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-type = "voltage";
regulator-always-on;
gpios = <&gpiof 14 GPIO_ACTIVE_HIGH>;
gpios-states = <0>;
states = <1800000 0x1 2900000 0x0>;
};
最后需要向 stm32mp157d-atk.dtsi 文件的根节点‘/’下添加自己的电源管理配置,将下面的代码添加到下方代码中间处:
retram: retram@38000000 {
compatible = "shared-dma-pool";
reg = <0x38000000 0x10000>;
no-map;
};
};
// 此处添加代码
vddcore: regulator-vddcore {
compatible = "regulator-fixed";
regulator-name = "vddcore";
regulator-min-microvolt = <1200000>; // 最小电压设定为 1.2V
regulator-max-microvolt = <1350000>; // 最大电压设定为 1.35V
regulator-always-on; // 始终开启,不能被关闭
regulator-boot-on; // 引导时开启
};
v3v3: regulator-3p3v {
compatible = "regulator-fixed";
regulator-name = "v3v3";
regulator-min-microvolt = <3300000>; // 最小电压设定为 3.3V
regulator-max-microvolt = <3300000>; // 最大电压设定为 3.3V
regulator-always-on; // 始终开启,不能被关闭
regulator-boot-on; // 引导时开启
};
v1v8_audio: regulator-v1v8-audio {
compatible = "regulator-fixed";
regulator-name = "v1v8_audio";
regulator-min-microvolt = <1800000>; // 最小电压设定为 1.8V
regulator-max-microvolt = <1800000>; // 最大电压设定为 1.8V
regulator-always-on; // 始终开启,不能被关闭
regulator-boot-on; // 引导时开启
};
vdd: regulator-vdd {
compatible = "regulator-fixed";
regulator-name = "vdd";
regulator-min-microvolt = <3300000>; // 最小电压设定为 3.3V
regulator-max-microvolt = <3300000>; // 最大电压设定为 3.3V
regulator-always-on; // 始终开启,不能被关闭
regulator-boot-on; // 引导时开启
};
vdd_usb: regulator-vdd-usb {
compatible = "regulator-fixed";
regulator-name = "vdd_usb";
regulator-min-microvolt = <3300000>; // 最小电压设定为 3.3V
regulator-max-microvolt = <3300000>; // 最大电压设定为 3.3V
regulator-always-on; // 始终开启,不能被关闭
regulator-boot-on; // 引导时开启
};
vin: vin {
compatible = "regulator-fixed";
regulator-name = "vin";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
};
};
4. 修改 TF 卡和 EMMC 配置
继续修改 stm32mp157d-atk.dtsi 找到 sdmmc1 和 sdmmc2 这两个节点,将这两个节点改为如下所示内容:
&sdmmc1 {
/* 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>; /* 休眠引脚控制器 */
st,neg-edge; /* 设置负边沿触发 */
broken-cd; /* CD引脚已损坏 */
bus-width = <4>; /* 总线宽度为4 */
vmmc-supply = <&v3v3>; /* VMMC供电引脚 */
status = "okay"; /* 设备状态正常 */
};
&sdmmc2 {
/* SDMMC2配置 */
pinctrl-names = "default", "opendrain", "sleep"; /* 引脚控制器命名 */
pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>; /* 默认引脚控制器 */
pinctrl-1 = <&sdmmc2_b4_od_pins_a &sdmmc2_d47_pins_a>; /* 开漏引脚控制器 */
pinctrl-2 = <&sdmmc2_b4_sleep_pins_a &sdmmc2_d47_sleep_pins_a>; /* 休眠引脚控制器 */
non-removable; /* 不可移动设备 */
st,neg-edge; /* 设置负边沿触发 */
bus-width = <8>; /* 总线宽度为8 */
vmmc-supply = <&v3v3>; /* VMMC供电引脚 */
keep-power-in-suspend; /* 在挂起状态下保持电源 */
status = "okay"; /* 设备状态正常 */
};
5. 编译uboot
编译之前先在 stm32mp157d-atk.dtsi 文件中找到 usbotg_hs节点,此节点默认内容如下所示:
// 定义USB_OTG电源配置
&usbotg_hs {
vbus-supply = <&vbus_otg>;
};
ST 官方开发板的 USB_OTG 电源也是通过 PMIC 配置的,所以 USB_OTG 电源配置也要修改,但在此之前需要先测试一下uboot能不能运行,把上方的代码给注释掉。
打开 arch/arm/dts/Makefile 文件,找到“dtb-$(CONFIG_STM32MP15x)”配置项,然后在此配置项中加入“stm32mp157d-atk.dtb”:
第 847 行里就是告诉“Makefile”把我们刚新建的 stm32mp157d-atk.bts 编译成对应的 dtb 文件。以前编译 uboot 都是自己输入一条一条命令编译,我们可以创建一个 shell 脚本,将所有的编译命令都写到这个 shell 脚本里面,然后每次的时候只需要执行一下这个 shell脚本即可。在 uboot 源码根目录下新建一个名为 stm32mp157d_alientek.sh 的 shell 脚本,在这个 shell 脚本里面输入如下内容:
#!/bin/bash
make distclean # 清除之前的构建文件
make stm32mp15_atk_trusted_defconfig # 使用 stm32mp15_atk_trusted_defconfig 进行配置
make DEVICE_TREE=stm32mp157d-atk all -j12 # 使用 stm32mp157d-atk 设备树进行构建,使用12个并发任务
给予 stm32mp157d_alientek.sh 可执行权限,然后运行脚本来完成编译,命令如下:
chmod 777 stm32mp157d_alientek.sh # 给予可执行权限
./stm32mp157d_alientek.sh # 运行脚本编译 uboot
把 u-boot.stm32 重命名为 my-u-boot.stm32 ,跟上次一样的方法用 STM32CubeProgrammer 烧写进板子里,并用 MobaXterm 去查看板子信息:
uboot 启动成功,并且进入了命令行模式,并且命令行也可以正常操作,那么是不是说明 uboot 已经移植成功了呢?不一定, uboot 有没有移植成功取决于当前uboot 能不能满足我们的要求,如果不满足就说明还没移植成功。
当前 uboot 的网络、 USB_OTG 也不能正常工作,所以我们还需要接着修改。
6. 网络驱动修改
6.1 修改网络设备树
从这我们可以看出,并没有找到网络,说明uboot的网络驱动有问题。原因是当前设备树没有网络相关节点,只需要将网络相关的节点加入设备树即可。
打开 stm32mp157d-atk.dtsi 文件,将如下所示的 ethernet0 节点加添加到最后面:
ðernet0 {
status = "okay";
pinctrl-0 = <ðernet0_rgmii_pins_a>; // 配置引脚控制器0为rgmii_pins_a
pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>; // 配置引脚控制器1为rgmii_pins_sleep_a
pinctrl-names = "default", "sleep"; // 引脚控制器的命名,用于区分默认和睡眠状态
phy-mode = "rgmii-id"; // 物理层模式设置为rgmii-id(双主机模式)
max-speed = <1000>; // 最大速度设置为1000Mbps
phy-handle = <&phy0>; // 物理设备的句柄引用
mdio0 {
#address-cells = <1>; // MDIO地址单元的数量(在这种情况下为1)
#size-cells = <0>; // MDIO大小单元的数量(在这种情况下为0)
compatible = "snps,dwmac-mdio"; // 兼容性标识,指定MDIO兼容性
phy0: ethernet-phy@0 { // 第一个以太网物理设备,地址为0
reg = <0>; // 注册地址设置为0
};
};
};
6.2 修改网络驱动
用正点原子提供的修改后的 phy.c 文件替换掉 uboot 下的/drivers/net/phy/phy.c 文件即可,设备树无需做任何修改,直接编译。
设置网络相关命令:
setenv ipaddr 192.168.1.106 # 开发板 IP 地址
setenv ethaddr b8:ae:1d:01:01:00 # 开发板网卡 MAC 地址
setenv gatewayip 192.168.1.1 # 开发板默认网关
setenv netmask 255.255.255.0 # 开发板子网掩码
setenv serverip 192.168.1.105 # 服务器地址,也就是 Ubuntu 地址
saveenv
测试一下:
ping 192.168.1.105
7. 修改 USB OTG 设备树
7.1 添加 usb_phy_tuning 子节点
打开 stm32mp157d-atk.dtsi 这个文件,在根节点“/”下添加名为“usb_phy_tuning”的子节点,节点内容如下:
usb_phy_tuning: usb-phy-tuning {
st,hs-dc-level = <2>;
st,fs-rftime-tuning;
st,hs-rftime-reduction;
st,hs-current-trim = <15>;
st,hs-impedance-trim = <1>;
st,squelch-level = <3>;
st,hs-rx-offset = <2>;
st,no-lsfs-sc;
};
7.2 添加 STUSB1600 I2C 子节点
开发板上的 USB OTG 接口类型为 Type-C,使用 STUSB1600 芯片来实现此接口功能, STUSB1600 有一个 I2C 接口,此 I2C 接口用来配置芯片,因此我们还需要在设备树中添加 STUSB1600 相关的 I2C 节点内容。将如下内容添加到 stm32mp157d-atk.dtsi 的最后面:
&i2c1 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c1_pins_a>;
pinctrl-1 = <&i2c1_pins_sleep_a>;
i2c-scl-rising-time-ns = <100>;
i2c-scl-falling-time-ns = <7>;
status = "okay";
/delete-property/dmas;
/delete-property/dma-names;
stusb1600@28 {
compatible = "st,stusb1600";
reg = <0x28>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpiog>;
pinctrl-names = "default";
pinctrl-0 = <&stusb1600_pins_a>;
status = "okay";
vdd-supply = <&vin>;
connector {
compatible = "usb-c-connector";
label = "USB-C";
power-role = "dual";
power-opmode = "default";
port {
con_usbotg_hs_ep: endpoint {
remote-endpoint = <&usbotg_hs_ep>;
};
};
};
};
};
7.3 添加 usb 接口相关节点
继续向 stm32mp157d-atk.dtsi 添加 USB 接口代码,内容如下:
&usbh_ehci {
phys = <&usbphyc_port0>;
status = "okay";
};
&usbotg_hs {
phys = <&usbphyc_port1 0>;
phy-names = "usb2-phy";
usb-role-switch;
status = "okay";
port {
usbotg_hs_ep: endpoint {
remote-endpoint = <&con_usbotg_hs_ep>;
};
};
};
&usbphyc {
status = "okay";
};
7.4 在 stm32mp157d-atk-u-boot.dtsi 中添加 usbotg_hs 节点
&usbotg_hs {
u-boot,force-b-session-valid;
hnp-srp-disable;
/* TEMP: force peripheral for USB OTG */
dr_mode = "peripheral";
};
重新编译 uboot 并烧写,然后使用 ums 命令测试,看看能不能将 EMMC 模拟成 U 盘,挂载到电脑上,命令如下:
ums 0 mmc 1
8. 使能 boot 和 bootd 命令
ST 官方 uboot 默认并没有使能 boot 和 bootd 这两个命令,这两个命令的实现源文件为cmd/bootm.c, bootm.c 代码如下:
/*******************************************************************/
/* bootd - boot default image */
/*******************************************************************/
#if defined(CONFIG_CMD_BOOTD) // 要使能boot和bootd,必须定义CONFIG_CMD_BOOTD
int do_bootd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
return run_command(env_get("bootcmd"), flag);
}
U_BOOT_CMD(
boot, 1, 1, do_bootd,
"boot default, i.e., run 'bootcmd'",
""
);
/* keep old command name "bootd" for backward compatibility */
U_BOOT_CMD(
bootd, 1, 1, do_bootd,
"boot default, i.e., run 'bootcmd'",
""
);
#endif
打开 include/configs/stm32mp1.h,然后在后面添加如下宏定义:
重新编译并烧写,并输入命令:
? boot
// 或
? bootd
9. LCD 驱动修改
uboot 也是支持 LCD 显示的,主要是设置屏幕背光、屏幕时序参数这些,这些直接在设备树里面修改即可。打开 stm32mp157d-atk.dts 文件,在里面添加 LCD 相关节点信息,首先在根节点“/”下添加 panel_backlight 和 panel_rgb 这两个节点,节点内容如下:
/* panel_backlight 为 LCD 的背光控制节点,主要指定 LCD 背光 IO 所使用的引脚,正点原子的 STM32MP157 开发板 LCD 背光引脚为 PD13*/
panel_backlight: panel-backlight {
compatible = "gpio-backlight";
gpios = <&gpiod 13 GPIO_ACTIVE_HIGH>;
default-on;
status = "okay";
};
panel_rgb: panel-rgb { // panel_rgb 为 RGB LCD 节点,指定了 LTDC 接口所使用的 IO、屏幕时序参数等
compatible = "simple-panel";
pinctrl-names = "default", "sleep";
pinctrl-0 = <<dc_pins_b>;
pinctrl-1 = <<dc_pins_sleep_b>;
backlight = <&panel_backlight>;
status = "okay";
port {
panel_in_rgb: endpoint {
remote-endpoint = <<dc_ep0_out>;
};
};
display-timings {
native-mode = <&timing0>; /* 时序信息 */
timing0: timing0 { /* 7 寸 1024*600 分辨率 */
clock-frequency = <51200000>; /* LCD 像素时钟,单位 Hz */
hactive = <1024>; /* LCD X 轴像素个数 */
vactive = <600>; /* LCD Y 轴像素个数 */
hfront-porch = <160>; /* LCD hfp 参数 */
hback-porch = <140>; /* LCD hbp 参数 */
hsync-len = <20>; /* LCD hspw 参数 */
vback-porch = <20>; /* LCD vbp 参数 */
vfront-porch = <12>; /* LCD vfp 参数 */
vsync-len = <3>; /* LCD vspw 参数 */
};
};
};
并且在这个文件下继续添加 ltdc 节点内容:
<dc {
status = "okay";
pinctrl-names = "default";
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&panel_in_rgb>;
};
};
};
三、U-boot测试
3.1 自烧写测试
修改 FlashLayout 如下图所示:
用我们自己编译的 uboot 来烧写自身。检测方法很简单,看能不能正常烧写。
3.2 EMMC 启动 Linux
从EMMC启动也就是将编译出来的Linux镜像文件uImage和.dtb设备树文件保存在EMMC中, uboot 从 EMMC 中读取这两个文件并启动,这个是我们产品最终的启动方式。首先 EMMC里面要先存放 Linux 镜像文件 uImage 和.dtb 设备树,但是我们前面新建的 FlashLayout 文件并没有烧写 uImage 和.dtb,所以我们需要修改一下 FlashLayout 文件,加入 uImage 和.dtb 烧写命令。由于我们还没有移植 Linux 系统,所以 uImage 和.dtb 就先使用正点原子出厂系统提供的。
atk-image-bootfs.ext4 是 ext4 格式的打包文件,因为 STM32CubeProgrammer软件要求将 uImage 和.dtb 打包在一起,格式为 ext4。 把 atk-image-bootfs.ext4 复制到 image 文件夹下,并修改 FlashLayout:
修改完成重新烧录并重启, 使用 ext4ls 命令查看一下 EMMC 的分区 2 里面有没有 uImage 和.dtb 文件,命令如下:
ext4ls mmc 1:2
这里我们只需要用到 uImage 和 stm32mp157d-atk.dtb 这两个文件,设置bootcmd 环境变量从 EMMC 里面读取系统文件,然后再启动,命令如下:
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157datk.dtb;bootm c2000000 - c4000000'
saveenv
boot
# 挨个来解释
# ext4load mmc 1:2 c2000000 uImage:从MMC设备的第1个分区(mmc 1)的第2个文件系统分区(分区号2)中加载uImage文件到内存地址c2000000
# ext4load mmc 1:2 c4000000 stm32mp157datk.dtb:从MMC设备的第1个分区(mmc 1)的第2个文件系统分区(分区号2)中加载stm32mp157datk.dtb设备树文件到内存地址c4000000
# bootm c2000000 - c4000000:使用加载的内核映像和设备树进行引导,内核位于地址c2000000,设备树位于地址c4000000
只要出现了 Booting Linux on physical CPU 0x0 即引导 Linux 内核成功。
3.3 从网络启动 Linux 系统 (测试)
从网络启动 Linux 系统的唯一目的就是为了调试。如果每次修改 Linux 系统文件或者 Linux 下的某个驱动,以后都要烧写到 EMMC 去测试,太麻烦。 我们可以设置 Linux 从网络启动,也就是将 Linux 镜像文件和根文件系统都放到 Ubuntu 下某个指定的文件夹中,这样每次重新编译 Linux 内核或者某个 Linux 驱动以后只需要使用 cp 命令将其拷贝到这个指定的文件夹中即可,这样就不用需要频繁的烧写EMMC。
可以通过 nfs 或者 tftp 从 Ubuntu 中下载 uImage 和设备树文件,根文件系统的话也可以通过 nfs 挂载,但这里我们使用 tftp 从 Ubuntu 中下载 uImage和设备树文件,前提是要将 uImage 和设备树文件放到 Ubuntu 下的 tftp 目录中。
首先使用 FileZilla 把文件 传到 tftpboot 目录下:
之后用命令把这两个文件给予权限:
再在uboot里面输入以下命令:
setenv bootcmd 'tftp c2000000 uImage;tftp c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
saveenv
boot
四、bootcmd 和 bootargs 环境变量
4.1 bootcmd 环境变量
bootcmd 保存着 uboot 默认命令, uboot 倒计时结束以后就会执行 bootcmd 中的命令。这些命令一般都是用来启动 Linux 内核的,比如读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 Linux 内核。可以在 uboot 启动以后进入命令行设置 bootcmd 环境变量的值。如果 EMMC 或者 NAND 中没有保存 bootcmd 的值,那么 uboot 就会使用默认的值,板子第一次运行 uboot 的时候都会使用默认值来设置 bootcmd 环境变量。
bootcmd 的默认值就是 CONFIG_BOOTCOMMAND,bootargs 的默认值就是 CONFIG_BOOTARGS。我们可以直接在 stm32mp1.h 文件中通过设置宏CONFIG_BOOTCOMMAND 来设置 bootcmd 的默认值。
4.2 bootargs 环境变量
bootargs 环境变量是用于设置 Linux 内核启动参数的变量。它定义了在引导 Linux 内核时传递给内核的命令行参数。 如下面 bootargs 环境变量值:
console=ttySTM0,115200 root=/dev/mmcblk2p3 rootwait rw
① console
console 用来设置 linux 终端(或者叫控制台),也就是通过什么设备来和 Linux 进行交互,是串口还是 LCD 屏幕?如果是串口的话应该是串口几等等。一般设置串口作为 Linux 终端,这样我们就可以在电脑上通过 MobaXterm 来和 linux 交互了。这里设置 console 为 ttySTM0,因为linux 启动以后 STM32MP1 的串口 4 在 linux 下的设备文件就是/dev/ttySTM0,在 Linux 下,一切皆文件。
ttySTM0 后面有个“,115200”,这是设置串口的波特率, console=ttySTM0,115200 综合起来就是设置 ttySTM0(也就是串口 4)作为 Linux 的终端,并且串口波特率设置为 115200。
② root
root 用来设置根文件系统的位置, root=/dev/mmcblk2p3 用于指明根文件系统存放在 mmcblk2 设备的分区3中。 其 中/dev/mmcblkx(x=0~n) 表 示 mmc 设 备 , 而/dev/mmcblkxpy(x=0~n,y=1~n)表示 mmc 设备 x 的分区 y。在 STM32MP1 开发板中/dev/mmcblk2表示 EMMC,而/dev/mmcblk2p3 表示 EMMC 的分区 3。
root 后面有“rootwait rw”, rootwait 表示等待 mmc 设备初始化完成以后再挂载,否则的话mmc 设备还没初始化完成就挂载根文件系统会出错的。 rw 表示根文件系统是可以读写的,不加rw 的话可能无法在根文件系统中进行写操作,只能进行读操作。
③ rootfstype
此选项一般配合 root 一起使用, rootfstype 用于指定根文件系统类型,如果根文件系统为ext 格式的话此选项可有可无。如果根文件系统是 yaffs、 jffs 或 ubifs 的话就需要设置此选项,指定根文件系统的类型。