iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等
第七部分 LInux系统移植
在进行系统移植之前建议根据下面链接对应的视频来进行设备树的初步学习,了解一些基本的知识:
设备树的由来以及基本概念 → https://www.bilibili.com/video/BV1Vy4y1B7ta?p=24
设备树基本语法 → 设备树基本语法_哔哩哔哩_bilibili
官方源码说明,官方提供的系统源码存放路径为"iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\01_官方源码"的en.SOURCES-stm32mp1-openstlinux-5-4-dunfell-mp1-20-06-24.tar.xz。
将源码拷贝到ubuntu上并解压,解压完成进入对应的源码目录如下图所示:
默认会存在五个源码目录,而我们所用到的只有三个源码,分别为tf-a的源码,u-boot源码以及linux内核源码,如下图所示:
为了方便起见我们将之后移植所要用到的源码已经提取了出来,存放的路径为"iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\01_官方源码",如下图所示:
第二十章 Trusted Firmware-A 移植
第二十一章 U-Boot移植
在"第十一章 编译U-Boot"已经对U-Boot进行了编译以及介绍,且对编译出的文件作用做了简单的说明,下面我们对官方提供的源码来进行移植,以此来适配我们自己的开发板。
官方提供的系统源码存放路径为"iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\01_官方源码"下的u-boot-stm32mp-2020.01-r0文件夹。
移植好的系统源码存放路径为"iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\02_移植好的源码\u-boot"。
21.1 源码的导入以及打补丁
将官方提供的系统源码文件夹拷贝到ubuntu上,如下图所示
使用命令"cd u-boot-stm32mp-2020.01-r0/",进入源码文件夹如下图所示:
然后使用命令"tar -vxf u-boot-stm32mp-2020.01-r0.tar.gz",对源码的压缩文件进行解压,如下图所示:
解压完成之后,使用命令" cd u-boot-stm32mp-2020.01"进入tf-a-stm32mp-2.2.r1源码文件如下图所示:
然后我们使用一下命令进行打补丁,打补丁完成如下图所示:
for p in `ls -1 ../*.patch`;do patch -p1 < $p;done
21.2 编译U-Boot源码
21.2.1 增设自己的平台
使用命令"cd u-boot-stm32mp-2020.01",进入uboot目录,如下图所示:
使用以下命令
cp configs/stm32mp15_trusted_defconfig configs/stm32mp15_itop_trusted_defconfig
创建自己的的默认配置文件,如下图所示:
然后我们使用命令"cd arch/arm/dts/"进入设备树文件存放目录,可以看到ST官方开发板的设备树文件,而我们的板子同样也是参照官方开发板来进行设计的,所以为了方便我们直接通过修改官方开发板的设备树文件来适配我们自己的开发板。
使用以下命令,将官方的设备树文件进行复制并修改文件名,如下图所示
cp stm32mp15xx-dkx.dtsi stm32mp15xx-itop.dtsi
cp stm32mp157a-dk1.dts stm32mp157a-itop.dts
cp stm32mp157a-dk1-u-boot.dtsi stm32mp157a-itop-u-boot.dtsi
使用命令"vim stm32mp157a-itop.dts"进入stm32mp157a-itop.dts文件,将头文件中的
#include "stm32mp15xx-dkx.dtsi"
修改为
#include "stm32mp15xx-itop.dtsi"
修改完成如下图所示:
并将model从STMicroelectronics STM32MP157A-DK1 Discovery Board修改为STMicroelectronics STM32MP157A-iTOP Discovery Board,修改完成如下图所示:
保存退出之后使用以下命令进入stm32mp15xx-itop.dtsi文件夹
vim stm32mp15xx-itop.dtsi
由于官方的DK1开发板内存默认为512MB而我们的内存为1G,所以我们将 memory属性值由
0xc0000000 0x20000000
修改为
0xc0000000 0x40000000
修改完成如下图所示:
保存退出之后,使用命令"vim stm32mp157a-itop-u-boot.dtsi"进入stm32mp157a-itop-u-boot.dtsi文件将
#include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi"
修改为
#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi"
修改完成如下图所示:
保存退出之后,继续再设备树目录下使用命令" vim Makefile"对编译配置文件进行修改,打开之后如下图所示:
使用查找命令查找"stm32mp157a-dk1.dtb",然后在下方添加我们自己的设备树文件
stm32mp157a-itop.dtb \
添加完成之后如下图所示:
保存退出,至此我们自己的平台就增加完成了。
21.2.2 修改Makefile文件
回到uboot源码目录下,如下图所示:
使用命令"vim Makefile "进入Makefile进行文件的修改,进入文件如下图所示:
然后使用查找命令查找"CROSS_COMPILE ?=",查找完成如下图所示:
我们在endif下添加以下内容
ARCH = arm
CROSS_COMPILE = arm-none-linux-gnueabihf-
所添加的为我们对应的架构和编译器,添加完成如下图所示:
保存退出。至此我们的Makefile文件就修改完成了。
21.2.3修改Makefile.sdk文件
我们返回上一级目录,可以看到对应的Makefile.sdk文件。如下图所示:
使用命令"vim Makefile.sdk"编辑进入该文件,如下图所示:
这个DEVICE_TREE为最后我们要编译的设备树文件,为了减少编译时间,我们将DEVICE_TREE这一行注释,然后单独加上我们自己的内容,如下:
DEVICE_TREE ?= stm32mp157a-itop
修改完成如下图所示:
保存退出。
21.2.4修改stm32mp1.c文件
首先我们回到uboot源码目录下,如下图所示:
然后我们使用以下命令
vim board/st/stm32mp1/stm32mp1.c
对stm32mp1.c文件进行修改,我们注释掉以下两行内容,注释完成如下图所示:
boot_mode = BOOT_FASTBOOT;
boot_mode = BOOT_STM32PROG;
本修改的原因是使编译出来的uboot镜像具备otg烧写的功能。
21.2.5 编译uboot文件
回到源码目录下使用命令"vim create.sh"创建create.sh文件,并添加以下内容
#!/bin/sh
make stm32mp15_itop_trusted_defconfig
cp .config ../build-trusted/
make distclean
make -f $PWD/../Makefile.sdk all UBOOT_CONFIGS=stm32mp15_itop_trusted_defconfig,trusted,u-boot.stm32
添加完成如下图所示:
保存退出之后使用命令"chmod 777 create.sh ",给予文件可执行权限,如下图所示:
然后使用命令"./create.sh"来执行该脚本,编译完成如下图所示:
回到上一级目录下,可以看到多出了一个"build-trusted"文件夹,这个文件夹内存放的就是我们编译所得到的文件夹。
进入build-trusted文件夹,u.boot.stm32就是我们最终所要用到的烧写镜像。
这里需要注意的是,在使用STM32CubeProgram软件烧写的时候会烧写两个uboot如下图所示,虽然他们的名字不一样,但他们的来源都是上面编译出来的u-boot.stm32。
首先烧写进去的名为otg-uboot.stm32的用途为辅助STM32CubeProgram烧写,而第二个名为emmc_uboot.stm32或tf_uboot.stm32为我们最终emmc或者TF卡启动所要用到的uboot,这个uboot我们稍后会添加一些环境变量,对此进行一些修改,所以最终的uboot源码编译出的镜像会失去辅助STM32CubeProgram烧写的功能,但对于我们自身并没有影响,我们真正需要的只是最终烧写到EMMC或者TF卡的uboot。
21.2.6 烧写初始uboot镜像
注意由于官方的开发板默认没有配置EMMC,我们本小节使用TF卡进行烧写测试,如果目前手上没有TF卡,可以不进行本小节的测试,但对应的流程需要浏览一下。
将"iTOP-STM32MP157开发板光盘资料\02_开发板烧写工具\02_烧写文件模板"路径下的image文件夹拷贝到ubuntu虚拟机上,拷贝完成如下图所示:
然后我们使用命令" ls uboot/"命令查看对应的文件夹,可以看到uboot文件夹内的文件正是我们上一小节结束时所讲解的文件,如下图所示:
使用命令将编译出来文件使用以下命令拷贝到uboot文件夹内,对原有的文件进行替换如下图所示(每个人路径并不一样,在此需要注意):
cp /home/topeet/work/u-boot-stm32mp-2020.01-r0/build-trusted/u-boot.stm32 ./uboot/otg-uboot.stm32
cp /home/topeet/work/u-boot-stm32mp-2020.01-r0/build-trusted/u-boot.stm32 ./uboot/tf_u-boot.stm32
使用命令"stm32.sh"打开烧写软件,如下图所示:
烧写的tsv规则文件我们选择tfcard.tsv如下图所示:
将开发板的拨码开关设置为"0000",接好otg线之后,对开发板进行上电。在烧写软件之中选中对应的设备USB1,然后进行烧写(具体的烧写过程可以查看第八章STM32MP157烧写系统,在这里没有进行太多的讲解)。在烧写过程之中,也可以看到串口的打印信息,但是会卡在下图所在的地方,提示没有检测到设备,并不会进行后续的烧写
原因是我们在uboot设备树之中并没有进行TF热插拔引脚的修改,官方默认TF卡引脚和我们自己板子的设置引脚并不相同,在uboot源码下使用命令"vim arch/arm/dts/stm32mp15xx-itop.dtsi",打开我们对应的设备树文件,打开之后如下图所示:
使用查找命令查找"sdmmc",查找之后如下图所示:
sdmmc1对应的为TF卡,其中cd-gpios为热插拔检测引脚,在这里设置的为Pb7,由下图TF卡原理图可知我们开发板对应的引脚为PC0
所以我们将
cd-gpios = <&gpiob 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
修改为
cd-gpios = <&gpioc 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
修改完成如下图所示:
保存退出之后,按照步骤重新进行编译,编译完成之后再次替换烧写目录下的otg-uboot.stm32、tf_u-boot.stm32,这时候已经可以烧写成功了,烧写完成之后将拨码开关设置为"1010"TF卡启动,启动之后开发发板打印信息如下:
可以看到我们已经成功进入uboot的命令行了,对于以上存在报错的地方我们先不需要管,我们在uboot的命令行输入以下环境变量并保存,如下图所示:
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk0p5 rootwait rw'
setenv bootcmd 'ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-rgb-070.dtb;bootm c2000000 - c4000000'
saveenv
以上环境变量的作用是将内核与设备树加载进内存中对应的地方,并告诉内核挂载对应分区内的文件系统。保存完成之后,使用"reset"命令重启开发板。启动完成之后如下图所示,证明我们已经成功进入文件系统:
下面我们进行报错的修改。
21.3功能的修改与适配
21.3.1去掉ADC
官方参考板通过 ADC 检测开机电流,如果供电电流不足 3A 则启动失败,所以需要去掉这部分功能,否则就会报下图中显示的错误:
由于 u-boot 期间 ADC 主要功能是检测开机电流,这里直接去掉 ADC 功能即可。
在uboot源码目录下使用命令"make stm32mp15_itop_trusted_defconfig"生成我们开发板自己的.config,然后使用命令"make menuconfig",进入uboot的功能菜单界面,如下图所示:
将下面对应路径的adc相关设置取消勾选
路径一:
> Command line interface
> Device access commands
[ ] adc - Access Analog to Digital Converters info and data
路径二:
Device Drivers --->
[ ] Enable ADC drivers using Driver Model
保存退出之后,使用命令覆盖我们之前的配置如下图所示:
cp .config ./configs/stm32mp15_itop_trusted_defconfig
然后我们使用命令"./create.sh "进行编译,生成新的镜像。
根据上一小节相同的步骤,我们使用以下命令覆盖烧写文件的uboot文件夹内的对应文件,如下图所示(注意每个人的路径不一样,所以使用的命令也不尽相同,这里只覆盖了tf_u-boot.stm32原因是我们的otg_u-boot.stm32已经实现了他的otg烧写功能不需要再进行修改):
cp /home/topeet/work/u-boot-stm32mp-2020.01-r0/build-trusted/u-boot.stm32 tf_u-boot.stm32
再次烧写之后,发现ADC对应的问题已经没有了,对应的打印信息如下图所示:
剩下的报错只剩下了网络问题,在下一小节中我们将会对网络问提进行适配。
21.3.2 网卡适配
再一次启动开发板,我们可以看到uboot的打印报错信息位"invalid MAC address in OTP 00:00:00:00:00:00",该报错的意思为,MAC地址不正确,如下图所示:
然后我们在uboot命令行输入以下命令,如下图所示:
env set -f ethaddr 08:90:90:90:90:90
saveenv
reset
重启完成之后,可以看到并没有报错了,如下图所示:
然后我们使用"dhcp"命令来动态获取ip,但是最后会出现"Retry time exceeded; starting again"问题,如下图所示:
原因是我们在uboot的源码中没有对我们的AR8031网卡芯片进行配置,我们回到uboot源码目录下,如下图所示:
然后使用命令"make stm32mp15_itop_trusted_defconfig"生成我们开发板自己的.config,然后使用命令"make menuconfig",进入uboot的功能菜单界面,如下图所示:
对下面的路径功能进行勾选:
路径1:
> Device Drivers
> Ethernet PHY (physical media interface) support
[*] Atheros Ethernet PHYs support
[*] Fixed-Link PHY
路径2:
> Networking support
[*] Random ethaddr if unset
勾选完成之后,保存退出,使用命令覆盖我们之前的配置如下图所示:
cp .config ./configs/stm32mp15_itop_trusted_defconfig
修改完成默认配置之后我们还需要对网卡的源码进行内容的添加,首先我们使用以下命令进入phy.c文件:
vim drivers/net/phy/phy.c
进入文件之后使用查找命令找到"phy_probe"函数如下图所示:
在"return err;"内容上方添加以下内容:
u16 val;
/*----------------------------------------------*/
/* Ar803x phy SmartEEE feature cause link status generates glitch,
* which cause ethernet link down/up issue, so disable SmartEEE
*/
phy_write(phydev,MDIO_DEVAD_NONE, 0xd, 0x3);
phy_write(phydev,MDIO_DEVAD_NONE, 0xe, 0x805d);
phy_write(phydev,MDIO_DEVAD_NONE, 0xd, 0x4003);
val = phy_read(phydev,MDIO_DEVAD_NONE, 0xe);
phy_write(phydev,MDIO_DEVAD_NONE, 0xe, val & ~(1 << 8));
/* To enable AR8031 output a 125MHz clk from CLK_25M */
phy_write(phydev,MDIO_DEVAD_NONE, 0xd, 0x7);
phy_write(phydev,MDIO_DEVAD_NONE, 0xe, 0x8016);
phy_write(phydev,MDIO_DEVAD_NONE, 0xd, 0x4007);
val = phy_read(phydev,MDIO_DEVAD_NONE, 0xe);
val &= 0xffe3;
val |= 0x18;
phy_write(phydev,MDIO_DEVAD_NONE, 0xe, val);
phy_write(phydev,MDIO_DEVAD_NONE, 0x1d, 0x5);
val = phy_read(phydev,MDIO_DEVAD_NONE, 0x1e);
val |= 0x0100;
phy_write(phydev,MDIO_DEVAD_NONE, 0x1e, val);
//--------------------------------------
添加完成如下图所示:
保存退出。回到uboot源码目录下,然后我们使用以下命令:
vim include/configs/stm32mp1.h
进入stm32mp1.h文件之后,使用查找命令"CONFIG_EXTRA_ENV_SETTINGS",找到该内容之后如下图所示:
在该定义中,添加以下内容,这样就省去了我们启动之后对mac地址进行设置的步骤,添加完成如下图所示:
"ethaddr=08:90:90:90:90:90\0" \
保存退出,然后我们使用命令"./create.sh "进行编译,生成新的镜像。
根据上一小节相同的步骤,我们使用以下命令覆盖烧写文件的uboot文件夹内的对应文件,如下图所示(注意每个人的路径不一样,所以使用的命令也不尽相同):
cp /home/topeet/work/u-boot-stm32mp-2020.01-r0/build-trusted/u-boot.stm32 tf_u-boot.stm32
烧写成功之后,重启开发板,可以发现我们现在依然没有网卡的相关报错了,然后我们使用"dhcp"命令自动获取ip,可以看到我们的开发板目前已经能获取到对应的ip地址了,如下图所示:
然后我们使用命令
ping 192.168.1.1
可以看到我们的网关已经成功ping通了。至此我们的网卡就适配成功了。
21.3.3 EMMC适配
在源码目录下使用命令"cd arch/arm/dts/"进入设备树文件夹,然后使用命令"vim stm32mp15xx-itop.dtsi",对stm32mp15xx-itop.dtsi文件夹进行修改,如下图所示:
进图文件夹之后使用搜索命令"/sdmmc1"对sdmmc1进行搜索,搜索定位之后如下图所示:
在sdmmc1内容下方添加以下内容适配EMMC
&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;
no-sd;
no-sdio;
st,neg-edge;
bus-width = <8>;
vmmc-supply = <&v3v3>;
vqmmc-supply = <&vdd>;
mmc-ddr-3_3v;
status = "okay";
};
添加完成之后如下图所示:
保存退出之后,我们增加 emmc 映射,使用命令
vim stm32mp157a-itop-u-boot.dtsi
对 stm32mp157a-itop-u-boot.dtsi文件进行修改如下内容:
aliases {
i2c3 = &i2c4;
mmc0 = &sdmmc1;
usb0 = &usbotg_hs;
};
修改为(红色字体为增加内容):
aliases {
i2c3 = &i2c4;
mmc0 = &sdmmc1;
mmc1 = &sdmmc2;
usb0 = &usbotg_hs;
};
然后使用搜索命令搜索 sdmmc1 找到其对应节点,在其节点后添加 sdmmc2 节点的内容添加内容如下图所示:
&sdmmc2 {
u-boot,dm-spl;
}
添加完成如下图所示:
保存退出,回到源码目录下,使用命令"./create.sh"命令进行编译,编译完成之后使用如下命令将生成的uboot镜像覆盖烧写文件夹内uboot文件夹下的文件
cp /home/topeet/work/u-boot-stm32mp-2020.01-r0/build-trusted/u-boot.stm32 ./otg-uboot.stm32
cp /home/topeet/work/u-boot-stm32mp-2020.01-r0/build-trusted/u-boot.stm32 ./emmc_u-boot.stm32
这里要注意的是,因为我们添加的是emmc功能所以在这里我们替换的为emmc对应的uboot,又因为我们要烧写到emmc里面,软件需要识别到emmc对应的设备我们才能进行烧写,所以我们需要将otg烧写对应的uboot也要换掉。如下图所示:
然后我们进行镜像的烧写,这里要注意的是,这次我们选择的烧写脚本为emmc.tsv.
烧写完成之后,将拨码开关拨到"0100",然后启动开发板,在uboot的命令行输入以下内容
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk1p3 rootwait rw'
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-rgb-070.dtb;bootm c2000000 - c4000000'
saveenv
保存完成之后,使用重启命令"reset"重启开发板,启动之后如下如图所示:
从上图可以看到,我们已经成功进入文件系统。至此我们的EMMC就适配成功了。
这里要注意,在接下来的步骤中,我们将会对默认的环境变量进行修改,这样编译出来的uboot镜像将失去otg辅助烧写功能,所以otg对应的uboot(即otg-uboot.stm32)将不会再更改
21.3.4 TF卡环境变量的设置
21.3.4.1 LCD选择环境变量的添加
首先我们回到uboot的源码目录,如下图所示:
然后使用命令"vim common/board_r.c",对board_r.c的文件进行修改
打开文件之后如下图所示:
使用查找命令查找函数"initr_env",查找完成如下图所示:
我们在函数上方添加以下恒定义:
#define setenv env_set
#define getenv env_get
#define saveenv env_save
在uboot源码中所用到的函数和uboot命令行所用到的命令功能对应,但是名字不相同,所以我们在这里为了方便起见,使用恒定义使uboot命令行中的命令名字匹配uboot源码之中的函数。添加完成如下图所示:
然后我们添加以下内容到"load_addr = env_get_ulong("loadaddr", 16, load_addr);"下方
char *p = NULL;
p = getenv("lcdtype");
if (NULL == p)
{
printf("*** Warning use default panel:%s ***\n", "1024x600");
p = CONFIG_DISPLAY_LCD_TYPE;
}
printf("LCD type:%s\n", p);
if(!strcmp(p, "4.3"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-rgb-043.dtb;bootm c2000000 - c4000000");
}
else if(!strcmp(p, "5.0"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-rgb-050.dtb;bootm c2000000 - c4000000");
}
else if(!strcmp(p, "1024x600"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-rgb-070.dtb;bootm c2000000 - c4000000");
}
else if(!strcmp(p, "9.7"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-lvds-097.dtb;bootm c2000000 - c4000000");
}
else if(!strcmp(p, "10.1"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-lvds-101.dtb;bootm c2000000 - c4000000");
}
else if(!strcmp(p, "7.0"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop-lvds-070.dtb;bootm c2000000 - c4000000");
}
else if(!strcmp(p, "hdmi"))
{
setenv("bootcmd", "ext4load mmc 0:4 c2000000 uImage;ext4load mmc 0:4 c4000000 stm32mp157a-itop.dtb;bootm c2000000 - c4000000");
}
/*add end*/
以上内容,具体实现的功能为当不设置任何参数的时候,默认的环境变量为7寸RGB屏幕,当设置参数之后就会进行条件判断,加载不同的设备树到内存之中。添加完成如下图所示:
保存退出,在上面的内容中,我们可以看到第477行,我们的p是由CONFIG_DISPLAY_LCD_TYPE 赋值来的,我们在源码目录下使用命令
vim include/configs/stm32mp1.h
进入stm32mp1.h文件,然后添加以下关于CONFIG_DISPLAY_LCD_TYPE的定义,添加完成如下图所示:
#define CONFIG_DISPLAY_LCD_TYPE "1024x600"
保存退出,回到uboot源码目录下,我们定义了这个变量之后,还需要添加该变量的白名单,我们首先使用以下命令
vim scripts/config_whitelist.txt
进入config_whitelist.txt文件,如下图所示:
可以看到该文件中存储的都是各种定义的变量且都是按照英文顺序来进行排序的(这里的排序很严格,需要完全按照规则排序),使用查找命令对"CONFIG_DISPLAY_AER_xxxx"进行查找,查找完成如下图所示:
在该内容下方添加我们之前的LCD相关的定义,添加完成如下图所示:
CONFIG_DISPLAY_LCD_TYPE
至此我们的初步设置就完成了,虽然我们现在已经成功设置屏幕了,且能正常使用,但实际上参数并没有写进uboot中,为了在uboot第一次启动就将参数写入,我们回到uboot源码目录下,使用以下命令:
vim include/env_default.h
进入env_default.h文件中,如下图所示:
在#ifndef CONFIG_USE_DEFAULT_ENV_FILE定义下添加以下内容,添加完成之后如下图所示:
#ifdef CONFIG_DISPLAY_LCD_TYPE
"lcdtype=" CONFIG_DISPLAY_LCD_TYPE "\0"
#endif
保存退出。回到源码目录下,然后使用命令"./create.sh"进行源码的编译,编译完成之后,将新生成的uboot镜像文件对image烧写文件夹中tf_u-boot.stm32进行替换,然后进行烧写,这里要注意的是我们要将image烧写目录下的flashlayout/tfcard.tsv的uboot烧写规则设置为PD(默认就是PD,如果不是需要换成PD),否则会保留之前的环境变量。修改完成如下图所示:
烧写完成之后,在uboot启动界面会自动的对我们所设置的功能进行匹配,uboot的打印信息如下图所示,默认选择的为7寸RGB屏幕所设置的环境变量。且能够不用自行设置的加载进了内核。
当我们想要进行屏幕选择的话,只需要在uboot倒计时的过程中点击任意键进入uboot命令行,然后根据各屏幕与对应的uboot命令如下:
|---------|-------------------------|
| 屏幕 | 设置命令 |
| 4.3寸屏幕 | setenv lcdtype 4.3 |
| 5寸屏幕 | setenv lcdtype 5.0 |
| 9.7寸屏幕 | setenv lcdtype 9.7 |
| 10.1寸屏幕 | setenv lcdtype 10.1 |
| 塑胶壳7寸屏幕 | setenv lcdtype 7.0 |
| 金属框7寸屏幕 | setenv lcdtype 1024x600 |
| HDMI屏幕 | setenv lcdtype hdmi |
以设置7寸lvds屏为例,上电后在uboot倒计时按回车,进入命令行,输入"setenv lcdtype 7.0",然后输入"saveenv"保存,然后输入"reset"重启使环境变量生效,
下图为不进行设置的uboot默认启动打印信息:
21.3.4.2 文件系统挂载变量设置
虽然上面我们已经对内核和设备树的环境变量进行了设置,但是并没有进入文件系统,会在内核加载结束之后显示如下信息:
并卡在这里,原因是我们并没有挂载文件系统,所以才会报一下内容,为了解决这一问题,我们可以在进入uboot命令行输入以下内容
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk0p5 rootwait rw'
saveenv
然后进行重启可以进入文件系统,但这比较麻烦,为了方便起见我们直接在源码之中进行添加。回到源码目录下,如下图所示:
在源码目录下使用命令"vim common/board_r.c",对board_r.c文件进行修改。
打开文件之后如下图所示:
使用查找命令查找函数"initr_env",查找完成如下图所示:
我们再上一小节之中进行了修改,现在我们找到上次添加内容的最后(即add end),添加以下内容
char *p1 = NULL;
p1 = getenv("bootsystem");
if (NULL == p1)
{
printf("*** Warning use default panel:%s ***\n", "qt");
p1 = CONFIG_BOOT_SYSTEM;
}
printf("bootsystem:%s\n", p1);
if(!strcmp(p1, "qt"))
{
setenv("bootargs", "console=ttySTM0,115200 root=/dev/mmcblk0p5 rootwait rw");
}
else if(!strcmp(p1, "yocto"))
{
setenv("bootargs", "console=ttySTM0,115200 root=/dev/mmcblk0p6 rootwait rw");
}
添加完成如下图所示:
保存退出,在上面的内容中,我们可以看到第514行,我们的p是由CONFIG_DISPLAY_LCD_TYPE 赋值来的,我们在源码目录下使用命令
vim include/configs/stm32mp1.h
进入stm32mp1.h文件,然后添加以下关于CONFIG_BOOT_SYSTEM的定义,添加完成如下图所示:
#define CONFIG_BOOT_SYSTEM "qt"
保存退出,回到uboot源码目录下,我们定义了这个变量之后,还需要添加该变量的白名单,我们首先使用以下命令
vim scripts/config_whitelist.txt
进入config_whitelist.txt文件,如下图所示:
使用查找命令对"CONFIG_BOOT_RETRY_TIME"进行查找,查找完成如下图所示:
在该内容下方添加我们之前的系统选择相关的定义,添加完成如下图所示:
CONFIG_BOOT_SYSTEM
至此我们的初步设置就完成了,虽然我们现在已经成功设置系统选择参数了,且能正常使用,但实际上参数并没有写进uboot中,为了在uboot第一次启动就将参数写入,我们回到uboot源码目录下,使用以下命令:
vim include/env_default.h
进入env_default.h文件中,如下图所示:
在#ifndef CONFIG_USE_DEFAULT_ENV_FILE定义下添加以下内容,添加完成之后如下图所示:
#ifdef CONFIG_BOOT_SYSTEM
"bootsystem=" CONFIG_BOOT_SYSTEM "\0"
#endif
保存退出,之后回到源码目录,使用命令" ./create.sh"进行编译,编译完成之后将对应的镜像烧写到TF卡之中。
烧写完成之后,在uboot启动界面会自动的对我们所设置的功能进行匹配,uboot的打印信息如下图所示,默认选择的为qt系统所设置的环境变量。且能够不用自行设置的加载进入文件系统了。
当我们想要进行系统选择的话,只需要在uboot倒计时的过程中点击任意键进入uboot命令行,然后根据系统与对应的uboot命令如下:
|----------------------------------------------|-------------------------|
| 系统 | 设置命令 |
| 最小linux系统、Qt系统、buildroot系统、Ubuntu系统、Debian系统 | setenv bootsystem qt |
| Yocto系统 | setenv bootsystem yocto |
除了yocto系统的分区不同外,其余系统的分区都相同,所以我们大部分情况是不需要设置系统选择参数的,只有用yocot系统的时候才会进行选择。
以设yocto系统为例,上电后在uboot倒计时按回车,进入命令行,输入"setenv bootsystem yocto",然后输入"saveenv"保存,然后输入"reset"重启使环境变量生效
21.3.4.3 LCD适配
Uboot启动之后,会有一小段的时间,可以让我们可以显示一下我们公司的logo或者自己喜欢的图片,所以我们加入了LCD适配章节。
我们提供了六款屏幕,屏幕尺寸分别为10.1寸金属框、9.7寸塑胶壳、7寸塑胶壳、7寸金属框(我们也会称之1024x600金属框)、5寸金属框和4.3寸金属框。具体的展示可以去第二章进行具体的查看。每款屏幕的参数如下:
|-----------------|-----------|------------|
| 款式 | 参数 | 值 |
| 4.3寸金属框 | 水平显示区域 | 480(tCLK) |
| 4.3寸金属框 | HSPW(thp) | 41(tCLK) |
| 4.3寸金属框 | HBP(thb) | 4(tCLK) |
| 4.3寸金属框 | HFP(thf) | 8(tCLK) |
| 4.3寸金属框 | 垂直显示区域 | 272(th) |
| 4.3寸金属框 | VSPW(tvp) | 10(th) |
| 4.3寸金属框 | VBP(tvb) | 2(th) |
| 4.3寸金属框 | VFP(tvf) | 4(th) |
| 4.3寸金属框 | 像素时钟 | 9200(KHz) |
| 5寸金属框 | 水平显示区域 | 800(tCLK) |
| 5寸金属框 | HSPW(thp) | 32(tCLK) |
| 5寸金属框 | HBP(thb) | 80(tCLK) |
| 5寸金属框 | HFP(thf) | 48(tCLK) |
| 5寸金属框 | 垂直显示区域 | 480 (th) |
| 5寸金属框 | VSPW(tvp) | 5(th) |
| 5寸金属框 | VBP(tvb) | 14(th) |
| 5寸金属框 | VFP(tvf) | 3(th ) |
| 5寸金属框 | 像素时钟 | 20000(KHz) |
| 7寸金属框(1024x600) | 水平显示区域 | 1024(tCLK) |
| 7寸金属框(1024x600) | HSPW(thp) | 20(tCLK) |
| 7寸金属框(1024x600) | HBP(thb) | 80(tCLK) |
| 7寸金属框(1024x600) | HFP(thf) | 48(tCLK) |
| 7寸金属框(1024x600) | 垂直显示区域 | 600(th ) |
| 7寸金属框(1024x600) | VSPW(tvp) | 5(th ) |
| 7寸金属框(1024x600) | VBP(tvb) | 14(th ) |
| 7寸金属框(1024x600) | VFP(tvf) | 3(th ) |
| 7寸金属框(1024x600) | 像素时钟 | 70000(KHz) |
| 7寸塑胶壳 | 水平显示区域 | 800(tCLK) |
| 7寸塑胶壳 | HSPW(thp) | 32(tCLK) |
| 7寸塑胶壳 | HBP(thb) | 80(tCLK) |
| 7寸塑胶壳 | HFP(thf) | 48(tCLK) |
| 7寸塑胶壳 | 垂直显示区域 | 1280(th ) |
| 7寸塑胶壳 | VSPW(tvp) | 5(th ) |
| 7寸塑胶壳 | VBP(tvb) | 14(th ) |
| 7寸塑胶壳 | VFP(tvf) | 3(th ) |
| 7寸塑胶壳 | 像素时钟 | 58000(KHz) |
| 9.7寸塑胶壳 | 水平显示区域 | 1024(tCLK) |
| 9.7寸塑胶壳 | HSPW(thp) | 32(tCLK) |
| 9.7寸塑胶壳 | HBP(thb) | 80(tCLK) |
| 9.7寸塑胶壳 | HFP(thf) | 48(tCLK) |
| 9.7寸塑胶壳 | 垂直显示区域 | 768(th ) |
| 9.7寸塑胶壳 | VSPW(tvp) | 5(th ) |
| 9.7寸塑胶壳 | VBP(tvb) | 14(th ) |
| 9.7寸塑胶壳 | VFP(tvf) | 3(th ) |
| 9.7寸塑胶壳 | 像素时钟 | 70000(KHz) |
| 10.1寸塑胶壳 | 水平显示区域 | 1024(tCLK) |
| 10.1寸塑胶壳 | HSPW(thp) | 32(tCLK) |
| 10.1寸塑胶壳 | HBP(thb) | 80(tCLK) |
| 10.1寸塑胶壳 | HFP(thf) | 48(tCLK) |
| 10.1寸塑胶壳 | 垂直显示区域 | 600(th ) |
| 10.1寸塑胶壳 | VSPW(tvp) | 5(th ) |
| 10.1寸塑胶壳 | VBP(tvb) | 14(th ) |
| 10.1寸塑胶壳 | VFP(tvf) | 3(th ) |
| 10.1寸塑胶壳 | 像素时钟 | 51200(KHz) |
以上就是我们屏幕的参数,下面我们将进行每个屏幕的适配,首先回到我们的uboot源码目录下如下图所示:
然后我们使用以下命令
vim arch/arm/dts/stm32mp15xx-itop.dtsi
对stm32mp15xx-itop.dtsi文件进行修改,在修改之前我们首先分析分析一下原理图,其中 VSYS_LCD 是LCD的供电引脚,LCD电源使能有两种方案:
-
当 R76 为 0 欧姆电阻接上时 VSYS_LCD 直接系统电源VIN供电。上电时 LCD 就可以亮。
-
当去掉 R76 时,VSYS_LCD 由系统控制提供电源,Q6 NPN 三极管基极(CHG_EN)为低电平时, 三极管不导通,PMOS 管的 G 极与 S 极都为高电平,不导通。反之,当 CHG_EN 给高电平时,PMOS的 G 极为低点平,满足|Vg-Vs| > |Vtp| ,PMOS 导通,LCD供电使能。
而在这里我们的解决方法为第二种,给首先LCD_BL_CTRL为高电平,通过软件来使能LCD供电。通过原理图我们可以查找到LCD_BL_CTRL对应的引脚为PD13,如下图所示:
分析完成之后我们回到stm32mp15xx-itop.dtsi 文件,找到led节点如下图所示:
在led标签中添加我们的LCD供电内容,添加内容如下,添加完成如下图所示
lcdpower{
label = "lcdpower";
gpios = <&gpiod 13 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
然后我们找到ltdc节点,如下图所示:
我们回到设备树目录下,打开stm32mp15-pinctrl.dtsi文件对ltdc_pins_a、ltdc_pins_sleep_a和我们的原理图进行比较,发现引脚并不对应,而ltdc_pins_b和ltdc_pins_sleep_b刚好对应,所以在这里我们应该将
pinctrl-0 = <<dc_pins_a>;
pinctrl-1 = <<dc_pins_sleep_a>;
修改为
pinctrl-0 = <<dc_pins_b>;
pinctrl-1 = <<dc_pins_sleep_b>;
修改完成如下图所示:
保存退出之后,回到uboot源码目录下,然后我们使用以下命令
vim arch/arm/dts/stm32mp157a-itop.dts
对stm32mp157a-itop.dts进行内容的添加,来到文件的底部,我们首先对ltdc节点进行内容的追加,追加内容如下,添加完成如下图所示:
<dc {
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep1_out: endpoint@1 {
reg = <0>;
remote-endpoint = <&panel_in_rgb>;
};
};
};
添加完成之后,再回到我们的原理图,每一个LCD接口还会有着一个背光接口,相应的引脚名称为ADC119_DAC2,如下图所示:
我们只是列出了RGB接口的原理图,LVDS接口也是同样的引脚来控制背光的,同样的该背光也有两种控制方法,第一种为使该引脚作为LED设备,这样屏幕只有两种状态,亮和灭,第二种方式为使用PWM脉冲宽度调制,这样我们就可以控制LED背光的强弱。在这里我们使用第一种方式。
通过查看原理图可知,ADC119_DAC2对应的CPU引脚为PA5,如下图所示:
然后我们在根节点下添加背光节点,添加内容如下,添加完成如下图所示:
panel_backlight: panel-backlight {
compatible = "gpio-backlight";
gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;
default-on;
status = "okay";
};
随后在该节点下,添加以下内容,通过port 节点来接收 LTDC 接口的数据,添加完成之后如下图所示:
panel_rgb: panel-rgb {
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_ep1_out>;
};
};
};
至此我们的设备树就修改完毕了,但是我们还没有添加对应屏幕的参数和屏幕直接的选择,我们在21.3.4.1章节中添加了LCD选择环境变量,我们可以利用该小节的内容进行本小节的功能适配。
保存退出之后回到uboot源码目录下,使用以下命令对board_r.c进行内容的添加:
vim common/board_r.c
进入文件夹之后。来到我们的LCD环境变量设置部分,如下图所示:
在每个if判断中的setenv内容下面再添加对应的setenv("lcd_id", "0");,每个屏幕对应的lcd_id值如下:
|-------------|--------------|
| 屏幕 | 对应的lcd_id匹配值 |
| 4.3寸金属框 | 0 |
| 5寸金属框 | 1 |
| 1024x600金属框 | 2 |
| 7寸塑胶壳 | 3 |
| 9.7寸塑胶壳 | 4 |
| 10.1寸金属框 | 5 |
添加完成如下图所示:
在这里,uboot启动之后会根据我们所设置的lcdtype参数来获取对应的id,这个id值我们稍后会用到,以此来设置对应屏幕的物理参数。
保存退出之后,回到uboot源码目录下,使用以下命令,对stm32_ltdc.c 文件进行内容的添加
vim drivers/video/stm32/stm32_ltdc.c
进入文件之后,使用查找命令对"stm32_ltdc_probe"进行查找,定位到下图所示的地点:
我们在ret下添加以下定义,添加完成如下图所示:
int timings_id = (int)((*(env_get("lcd_id"))) - '0');
该定义是获取到了lcd_id的值,最后得到的是整型值,然后我们继续使用查找命令对"panel_get_display_timing(panel, &timings);"内容进行查找,查找完成之后如下图所示:
我们对以下内容进行注释:
ret = panel_get_display_timing(panel, &timings);
然后进行内容的替换为如下内容:
ret=topeet_panel_get_display_timing(timings_id, &timings);
替换完成如下图所示:
这里的topeet_panel_get_display_timing函数是用来选择对应屏幕的物理参数的,我们来到本文件的宏定义下,添加该函数的内容,内容如下:
static int topeet_panel_get_display_timing(int lcd_id,
struct display_timing *timings){
switch(lcd_id){
case topeet_rgb_043 :
memcpy(timings, &timing_4x3_480x272, sizeof(*timings));
break;
case topeet_rgb_050 :
memcpy(timings, &timing_5x0_800x480, sizeof(*timings));
break;
case topeet_rgb_070 :
memcpy(timings, &timing_7x0_1024x600, sizeof(*timings));
break;
case topeet_lvds_070 :
memcpy(timings, &timing_7x0_800x1280, sizeof(*timings));
break;
case topeet_lvds_097 :
memcpy(timings, &timing_9x7_1024x768, sizeof(*timings));
break;
case topeet_lvds_101 :
memcpy(timings, &timing_10x1_1024x600, sizeof(*timings));
break;
default :
break;
}
return 0;
}
可以看到该函数就是根据 lcd_id的值来确定对应的物理参数,对于每个板子的物理参数相关的定义我们稍后再添加,我们可以看到上图的switch case 结构,对于每个case的定义我们还未添加,再该函数上方我们使用enum关键字来添加对应的内容,添加内容如下:
enum lcd_select {
topeet_rgb_043,
topeet_rgb_050,
topeet_rgb_070,
topeet_lvds_070,
topeet_lvds_097,
topeet_lvds_101
};
这样就相当于每个变量从0-5赋值。随后我们在该内容下方添加每个屏幕的物理参数内容,添加内容如下
static const struct display_timing timing_4x3_480x272 = {
.pixelclock = {.min = 9200000, .typ = 9200000, .max = 9200000,},
.hactive = {.min = 480, .typ = 480, .max = 480,},
.hfront_porch = {.min = 8, .typ = 8, .max = 8,},
.hback_porch = {.min = 4, .typ = 4, .max = 4,},
.hsync_len = {.min = 41, .typ = 41, .max = 41,},
.vactive = {.min = 272, .typ = 272, .max = 272,},
.vfront_porch = {.min = 4, .typ = 4, .max = 4,},
.vback_porch = {.min = 2, .typ = 2, .max = 2,},
.vsync_len = {.min = 10, .typ = 10, .max = 10,},
};
static const struct display_timing timing_5x0_800x480 = {
.pixelclock = {.min = 20000000, .typ = 20000000, .max = 20000000,},
.hactive = {.min = 800, .typ = 800, .max = 800,},
.hfront_porch = {.min = 48, .typ = 48, .max = 48,},
.hback_porch = {.min = 80, .typ = 80, .max = 80,},
.hsync_len = {.min = 32, .typ = 32, .max = 32,},
.vactive = {.min = 480, .typ = 480, .max = 480,},
.vfront_porch = {.min = 3, .typ = 3, .max = 3,},
.vback_porch = {.min = 14, .typ = 14, .max = 14,},
.vsync_len = {.min = 5, .typ = 5, .max = 5,},
};
static const struct display_timing timing_7x0_1024x600 = {
.pixelclock = {.min = 70000000, .typ = 70000000, .max = 70000000,},
.hactive = {.min = 1024, .typ = 1024, .max = 1024,},
.hfront_porch = {.min = 48, .typ = 48, .max = 48,},
.hback_porch = {.min = 80, .typ = 80, .max = 80,},
.hsync_len = {.min = 20, .typ = 20, .max = 20,},
.vactive = {.min = 600, .typ = 600, .max = 600,},
.vfront_porch = {.min = 3, .typ = 3, .max = 3,},
.vback_porch = {.min = 14, .typ = 14, .max = 14,},
.vsync_len = {.min = 5, .typ = 5, .max = 5,},
};
static const struct display_timing timing_7x0_800x1280 = {
.pixelclock = {.min = 53000000, .typ = 53000000, .max = 53000000,},
.hactive = {.min = 800, .typ = 800, .max = 800,},
.hfront_porch = {.min = 40, .typ = 40, .max = 40,},
.hback_porch = {.min = 220, .typ = 220, .max = 220,},
.hsync_len = {.min = 60, .typ = 60, .max = 60,},
.vactive = {.min = 1280, .typ = 1280, .max = 1280,},
.vfront_porch = {.min = 7, .typ = 7, .max = 7,},
.vback_porch = {.min = 21, .typ = 21, .max = 21,},
.vsync_len = {.min = 10, .typ = 10, .max = 10,},
};
static const struct display_timing timing_9x7_1024x768 = {
.pixelclock = {.min = 70000000, .typ = 70000000, .max = 70000000,},
.hactive = {.min = 1024, .typ = 1024, .max = 1024,},
.hfront_porch = {.min = 48, .typ = 48, .max = 48,},
.hback_porch = {.min = 80, .typ = 80, .max = 80,},
.hsync_len = {.min = 32, .typ = 32, .max = 32,},
.vactive = {.min = 768, .typ = 768, .max = 768,},
.vfront_porch = {.min = 3, .typ = 3, .max = 3,},
.vback_porch = {.min = 14, .typ = 14, .max = 14,},
.vsync_len = {.min = 5, .typ = 5, .max = 5,},
};
static const struct display_timing timing_10x1_1024x600 = {
.pixelclock = {.min = 51200000, .typ = 51200000, .max = 51200000,},
.hactive = {.min = 1024, .typ = 1024, .max = 1024,},
.hfront_porch = {.min = 48, .typ = 48, .max = 48,},
.hback_porch = {.min = 80, .typ = 80, .max = 80,},
.hsync_len = {.min = 32, .typ = 32, .max = 32,},
.vactive = {.min = 600, .typ = 600, .max = 600,},
.vfront_porch = {.min = 3, .typ = 3, .max = 3,},
.vback_porch = {.min = 14, .typ = 14, .max = 14,},
.vsync_len = {.min = 5, .typ = 5, .max = 5,},
};
以上内容就是我们每个屏幕对应的物理参数。至此我们对于每个屏幕参数的设置就完成了。但是我们要是想让logo自动加载,需要修改环境变量,而且需要将logo对应的bmp文件添加到内核所在的bootfs.ext4文件,迅为电子提供的每个屏幕对应的logo存放位置为"iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\12_logo文件"。为了方便,可以直接烧写我们默认提供的内核镜像中。
我们保存退出之后,回到uboot源码目录下,使用命令继续对board_r.c内容进行添加
vim common/board_r.c
来到我们添加内容的地方,如下图所示:
在每个bootcmd环境变量中添加自己对应的加载bmp文件的内容,每个屏幕对应的添加内容如下:
|-------------|--------------------------------------------------------------|
| 屏幕 | 对应的lcd_id匹配值 |
| 4.3寸金属框 | ext4load mmc 0:4 c4300000 480x272.bmp;bmp display c4300000; |
| 5寸金属框 | ext4load mmc 0:4 c4300000 800x481.bmp;bmp display c4300000; |
| 1024x600金属框 | ext4load mmc 0:4 c4300000 1024x600.bmp;bmp display c4300000; |
| 7寸塑胶壳 | ext4load mmc 0:4 c4300000 800x1280.bmp;bmp display c4300000; |
| 9.7寸塑胶壳 | ext4load mmc 0:4 c4300000 1024x768.bmp;bmp display c4300000; |
| 10.1寸金属框 | ext4load mmc 0:4 c4300000 1024x600.bmp;bmp display c4300000; |
添加完成如下图所示:
保存退出,回到uboot源码目录下,使用命令"./create.sh"来进行源码的编译,编译完成之后,将编译出来的镜像进行烧写,进入uboot之后如下图所示:
根据8.7 设置屏幕参数章节设置对应的屏幕参数,然后输入reset命令重启之后会发现uboot加载之后会显示我们对应的logo。
至此,我们的lcd就适配成功了。
21.3.5 EMMC环境变量的设置
需要注意的是EMMC和TF卡的配置相同,只是最后镜像选择的对应分区不同,所以我们需要对源码进行拷贝,这是出于更容易理解的角度将TF卡和EMMC的源码分开了,其实为了方便起见可以在源码中再增加一个环境变量以此来进行TF卡和EMMC的选择,有兴趣的各位可以对此进行一下修改,在这里我们采用的是第一种方法,将TF卡和EMMC的源码分开。
我们回到uboot源码目录的上一级目录下,如下图所示:
使用命令以下命令
cp -r u-boot-stm32mp-2020.01 emmc_u-boot-stm32mp-2020.01
mv u-boot-stm32mp-2020.01 tf_u-boot-stm32mp-2020.01
对uboot源码进行拷贝和修改名称,修改完成如下图所示
我们在上一小节中已经对TF卡源码修改完成,下面我们进入到emmc对应uboot的源码目录下,即emmc_u-boot-stm32mp-2020.01文件夹下,如下图所示:
然后使用命令"vim common/board_r.c",对board_r.c的文件进行修改 打开文件之后如下图所示:
然后我们将之前添加的"load_addr = env_get_ulong("loadaddr", 16, load_addr);"下方的内容进行修改,修改完成如下
char *p = NULL;
p = getenv("lcdtype");
if (NULL == p)
{
printf("*** Warning use default panel:%s ***\n", "1024x600");
p = CONFIG_DISPLAY_LCD_TYPE;
}
printf("LCD type:%s\n", p);
if(!strcmp(p, "4.3"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-rgb-043.dtb;ext4load mmc 1:2 c4300000 480x272.bmp;bmp display c4300000;bootm c2000000 - c4000000");
setenv("lcd_id", "0");
}
else if(!strcmp(p, "5.0"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-rgb-050.dtb;ext4load mmc 1:2 c4300000 800x481.bmp;bmp display c4300000;bootm c2000000 - c4000000");
setenv("lcd_id", "1");
}
else if(!strcmp(p, "1024x600"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-rgb-070.dtb;ext4load mmc 1:2 c4300000 1024x600.bmp;bmp display c4300000;bootm c2000000 - c4000000");
setenv("lcd_id", "2");
}
else if(!strcmp(p, "9.7"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-lvds-097.dtb;ext4load mmc 1:2 c4300000 1024x768.bmp;bmp display c4300000;bootm c2000000 - c4000000");
setenv("lcd_id", "4");
}
else if(!strcmp(p, "10.1"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-lvds-101.dtb;ext4load mmc 1:2 c4300000 1024x600.bmp;bmp display c4300000;bootm c2000000 - c4000000");
setenv("lcd_id", "5");
}
else if(!strcmp(p, "7.0"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop-lvds-070.dtb;ext4load mmc 1:2 c4300000 800x1280.bmp;bmp display c4300000;bootm c2000000 - c4000000");
setenv("lcd_id", "3");
}
else if(!strcmp(p, "hdmi"))
{
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop.dtb;bootm c2000000 - c4000000");
}
/*add end*/
以上内容,具体实现的功能为当不设置任何参数的时候,默认的环境变量为7寸RGB屏幕,当设置参数之后就会进行条件判断,加载不同的设备树到内存之中。添加完成如下图所示:
继续修改以下内容
char *p1 = NULL;
p1 = getenv("bootsystem");
if (NULL == p1)
{
printf("*** Warning use default panel:%s ***\n", "qt");
p1 = CONFIG_BOOT_SYSTEM;
}
printf("bootsystem:%s\n", p1);
if(!strcmp(p1, "qt"))
{
setenv("bootargs", "console=ttySTM0,115200 root=/dev/mmcblk1p3 rootwait rw");
}
else if(!strcmp(p1, "yocto"))
{
setenv("bootargs", "console=ttySTM0,115200 root=/dev/mmcblk1p4 rootwait rw");
}
修改完成如下图所示:
保存退出,在上面的内容中,我们可以看到第475行和514行,我们的p和p1是由CONFIG_DISPLAY_LCD_TYPE和CONFIG_BOOT_SYSTEM 赋值来的(其实以下的内容我们在上一节中做过了,而emmc源码是直接复制的tf卡的,但为了能更清晰的还原操作,所以我们并没有删除下列步骤),我们在源码目录下使用命令
vim include/configs/stm32mp1.h
进入stm32mp1.h文件,然后添加以下关于CONFIG_DISPLAY_LCD_TYPE的定义,添加完成如下图所示:
#define CONFIG_DISPLAY_LCD_TYPE "1024x600"
#define CONFIG_BOOT_SYSTEM "qt"
保存退出,回到uboot源码目录下,我们定义了这个变量之后,还需要添加该变量的白名单,我们首先使用以下命令
vim scripts/config_whitelist.txt
进入config_whitelist.txt文件,如下图所示:
可以看到该文件中存储的都是各种定义的变量且都是按照英文顺序来进行排序的(这里的排序很严格,需要完全按照规则排序),使用查找命令对"CONFIG_DISPLAY_AER_xxxx"进行查找,查找完成如下图所示:
在该内容下方添加我们之前的LCD相关的定义,添加完成如下图所示:
CONFIG_DISPLAY_LCD_TYPE
然后使用查找命令对"CONFIG_BOOT_RETRY_TIME"进行查找,查找完成如下图所示:
在该内容下方添加我们之前的系统选择相关的定义,添加完成如下图所示:
CONFIG_BOOT_SYSTEM
至此我们的初步设置就完成了,虽然我们现在已经成功设置屏幕和选择系统了,且能正常使用,但实际上参数并没有写进uboot中,为了在uboot第一次启动就将参数写入,我们回到uboot源码目录下,使用以下命令:
vim include/env_default.h
进入env_default.h文件中,如下图所示:
在#ifndef CONFIG_USE_DEFAULT_ENV_FILE定义下添加以下内容,添加完成之后如下图所示:
#ifdef CONFIG_DISPLAY_LCD_TYPE
"lcdtype=" CONFIG_DISPLAY_LCD_TYPE "\0"
#endif
#ifdef CONFIG_BOOT_SYSTEM
"bootsystem=" CONFIG_BOOT_SYSTEM "\0"
#endif
保存退出。回到源码目录下,然后使用命令"./create.sh"进行源码的编译,编译完成之后,将新生成的uboot镜像文件对image烧写文件夹中emmc_u-boot.stm32进行替换,然后进行烧写,这里要注意的是我们要将image烧写目录下的flashlayout/emmc.tsv的uboot烧写规则设置为PD(默认就是PD,如果不是需要换成PD),否则会保留之前的环境变量。修改完成如下图所示:
烧写完成之后,在uboot启动界面会自动的对我们所设置的功能进行匹配,uboot的打印信息如下图所示,默认选择的为7寸RGB屏幕所设置的环境变量。且能够不用自行设置的加载进了内核。
当我们想要进行屏幕选择的话,只需要在uboot倒计时的过程中点击任意键进入uboot命令行,然后根据各屏幕与对应的uboot命令如下:
|---------|-------------------------|
| 屏幕 | 设置命令 |
| 4.3寸屏幕 | setenv lcdtype 4.3 |
| 5寸屏幕 | setenv lcdtype 5.0 |
| 9.7寸屏幕 | setenv lcdtype 9.7 |
| 10.1寸屏幕 | setenv lcdtype 10.1 |
| 塑胶壳7寸屏幕 | setenv lcdtype 7.0 |
| 金属框7寸屏幕 | setenv lcdtype 1024x600 |
| HDMI屏幕 | setenv lcdtype hdmi |
以设置7寸lvds屏为例,上电后在uboot倒计时按回车,进入命令行,输入"setenv lcdtype 7.0",然后输入"saveenv"保存,然后输入"reset"重启使环境变量生效,
当我们想要进行系统选择的话,只需要在uboot倒计时的过程中点击任意键进入uboot命令行,然后根据系统与对应的uboot命令如下:
|----------------------------------------------|-------------------------|
| 系统 | 设置命令 |
| 最小linux系统、Qt系统、buildroot系统、Ubuntu系统、Debian系统 | setenv bootsystem qt |
| Yocto系统 | setenv bootsystem yocto |
除了yocto系统的分区不同外,其余系统的分区都相同,所以我们大部分情况是不需要设置系统选择参数的,只有用yocot系统的时候才会进行选择。
以设yocto系统为例,上电后在uboot倒计时按回车,进入命令行,输入"setenv bootsystem yocto",然后输入"saveenv"保存,然后输入"reset"重启使环境变量生效
至此我们的uboot移植就结束了。我们接下来将进行linux内核的移植。