超详细移植Ubuntu到stm32mp157

1.准备工作

根文件系统下载解压

官网地址:Index of /

Ubuntu 的移植非常简单,不需要编译任何东西,因为 Ubuntu 官方已经将根文件系统制作好了,只需简单配置

Ubuntu 官方提供的 base 根文件系统,使其在我们的开发板上跑起来即可。

->首先

->其次

->最后

这里选择低于18.04的根文件系统都可以,理论上适配最优解是18.04 我选择的是16.04.6

Ubuntu 针对不同的 CPU 架构提供相应的 ubuntu base 根文件系统,有 amd64(64位X86)、 armhf、 i386(32 位 X86) , powerpc、 ppc64el 等系统的。 MP157 是 CortexA 内核的 CPU,并且有 硬件Q 浮 点运算单元,因此选择 armhf 版本。

下载后放一份到Ubuntu

去ubuntu解压然后复制到opt新建的xyrootfs目录中

mkdir xyrootfs

sudo tar -vzxf ubuntu-base-16.04.6-base-armhf.tar.gz -C xyrootfs/

sudo vi /etc/exports //配置NFS文件配置

正式移植之前需要一个辅助工具,对根文件系统进行一个基本的搭建使其文件结构清晰,我的一个老师梁老师说过,毕竟linux一切皆文件

sudo apt-get install qemu-user-static

//apt下载不了的检查一下软件源,后续在开发板上跑起来的Ubuntu要下载同理

stm32mp157平台是32位控制器,因此选择以下指令:

sudo cp /usr/bin/qemu-arm-static ./xyrootfs/usr/bin/ sudo cp /etc/resolv.conf ./xyrootfs/etc/resolv.conf  sudo vim ./xyrootfs/etc/resolv.conf

//谷歌的公开DNS

nameserver 8.8.8.8

64bit的也附上:

sudo cp /usr/bin/qemu-aarch64-static ./rootfs/usr/bin/ sudo cp /etc/resolv.conf ./rootfs/etc/resolv.conf

sudo vim ./rootfs/etc/resolv.conf

nameserver 8.8.8.8

2.移植过程

根文件系统要运行起来,还需要虚拟文件系统,也就是说现在需要模拟根文件系统的运行环境。

接下来将上面制作的根文件系统挂载到主机上,需要挂载 proc、 sys、 dev、 dev/pts 等文件系统,最   后使用 chroot 将主机的根文件系统切换到我们前面制作的根文件系统中。这里我们通过脚本文件来完成 挂载和卸载操作

新建一个mk.sh

chmod 777 mk.sh

脚本内容:

#!/bin/bash
# 指定脚本的解释器为 Bash

mnt ()
# 定义一个名为 mnt 的函数,用来挂载所需的文件系统

{
    echo "MOUNTING"
    # 输出 "MOUNTING",表明正在执行挂载操作

    sudo mount -t proc /proc ${2}proc
    # 挂载 proc 文件系统,将目标文件系统路径的 `/proc` 目录挂载到当前文件系统的 `${2}proc`
    目录
    sudo mount -t sysfs /sys ${2}sys
    # 挂载 sysfs 文件系统,将目标文件系统路径的 `/sys` 目录挂载到当前文件系统的 `${2}sys`
    目录
    sudo mount -o bind /dev ${2}dev
    # 挂载当前系统的 `/dev` 目录到目标文件系统的 `${2}dev` 目录, `bind` 表示硬链接方式挂载                                            
    sudo mount -o bind /dev/pts ${2}dev/pts
    # 同理,挂载当前系统的 `/dev/pts` 目录到目标文件系统的 `${2}dev/pts` 目录

    sudo chroot ${2}
    # 切换到目标文件系统 `${2}` 目录下,执行 chroot 操作,改变根目录到指定路径 
}

umnt ()
# 定义一个名为 umnt 的函数,用来卸载之前挂载的文件系统

{
    echo "UNMOUNTING"
    # 输出 "UNMOUNTING",表明正在执行卸载操作

    sudo umount ${2}proc
    # 卸载目标文件系统的 `${2}proc` 目录 sudo umount ${2}sys
    # 卸载目标文件系统的 `${2}sys` 目录 sudo umount ${2}dev/pts
    # 卸载目标文件系统的 `${2}dev/pts` 目录 sudo umount ${2}dev
    # 卸载目标文件系统的 `${2}dev` 目录 
}

if [ "$1" = "-m" ] && [ -n "$2" ]; 
then
    # 判断第一个参数是否为 "-m"(挂载操作)且第二个参数不为空
    mnt $1 $2
    # 调用 mnt 函数,传递 `$1`(-m)和 `$2`( 目标文件系统路径)作为参数 
    echo "mnt -m pwd"
    # 输出 "mnt -m pwd",提示挂载操作完成

elif [ "$1" = "-u" ] && [ -n "$2" ]; 
then
    # 判断第一个参数是否为 "-u"(卸载操作)且第二个参数不为空

    umnt $1 $2
    # 调用 umnt 函数,传递 `$1`(-u)和 `$2`( 目标文件系统路径)作为参数 
    echo "mnt -u pwd"
    # 输出 "mnt -u pwd",提示卸载操作完成

else
    # 如果参数不符合要求,执行 else 分支

    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    # 输出参数缺失的提示信息
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    # 提示第一个参数应为 "-m"(挂载)或 "-u"(卸载) echo "2'nd parameter is the rootfs path"
    # 提示第二个参数是根文件系统路径 echo ""
    echo "For example: ./mk.sh -m ./rootfs/"
    # 提供一个示例命令
    echo ""
    echo "1st parameter : ${1}"
    # 输出第一个参数的值
    echo "2nd parameter : ${2}"
    # 输出第二个参数的值 
fi

执行语句挂载:

sudo ./mk.sh -m ./xyrootfs/

注意脚本存放路径在xyrootfs外面

sudo ./mk.sh -m ./xyrootfs/

sudo ./mk.sh -u ./xyrootfs/

挂载成功后,已切换到上面制作的rootfs根文件系统中。

安装常用的命令和软件

由于 ubuntu base 是一个最小根文件系统,很多命令和软件都没有,因此我们需要先安装一下常用的命令和软件,输入如下命令

apt update
apt install sudo
apt install vim
apt install kmod net-tools ethtool ifupdown language-pack-en-base rsyslog htop iputils-ping

 

用户配置:

1.设置一下 root 用户的密码,设置简单一点, root 用户密码也设置为“root”,相当于用户名和密码一 样,命令如下:

passwd root

2.输入“passwd root”以后会让你输入 root 用户密码,输入两次。 添加一个普通用户xieya:

useradd -s '/bin/bash' -m -G adm,sudo xieya

3.给用户 xieya 设置密码:

passwd xieya

输入“passwdsdr”以后会让你输入用户密码,输入两次。

主机配置

因为现在我们挂载的根文件系统也是一个主机,当然也需要设置些基础信息

echo “sdr” > /etc/hostname

echo “127.0.0.1 localhost” >> /etc/hosts echo “127.0.0.1 sdr” >> /etc/hosts

终端设置

因为我们的开发板是使用串口作为终端的,所以需要设置一下

ubuntu 根文件系统在开发板上启动以后我们通常也希望串口终端正常工作,首先确定自己所使用的串   口设备文件,比如正点原子的 STM32MP157 开发板使用的UART4对应的串口设备文件为tySTM0,我们 需要添加一个名

为getty@ttySTM0.service 的链接,链接到getty@.service 服务上,输入如下命令:

ln -s /lib/systemd/system/getty@.service

/etc/systemd/system/getty.target.wants/getty@ttySTM0.service

注意:ttySTM32为串口节点,具体根据自己的板子设置

如果是18.04的Ubuntu.base有可能会提示

/bin/ls: cannot access '/lib/systemd/system/getty@.service': No such file or directory

解决办法:

apt install systemd

正确现象:

ls /lib/systemd/system/getty@.service

提示/lib/systemd/system/getty@.service

安装图形化界面

图形程序安装,安装的桌面是xubuntu-desktop:

apt install xubuntu-desktop

取决于网速可能会有点久

中间可能会弹出来紫色界面让选择键盘界面布局,可参考该帖子解决:

Linux系统UI界面_please select the layout matching the keyboard for-CSDN博客

fromshare=blogdetail&sharetype=blogdetail&sharerId=126789586&sharerefer=PC&share source=m0_68220839&sharefrom=from_link

取决于网速该过程可能会有点久 执行命令之后会报错:

Errors were encountered while processing:

blueman

E: Sub-process /usr/bin/dpkg returned an error code (1)

这是正常现象,不用管。

这样基本配置我们就设置好了,我们退出这个根文件系统,然后取消挂载。设置好以后就可以退出根文 件系统了,输入如下命令退出:

exit

./mk.sh -u ./rootfs/

至此, ubuntu base 根文件系统就已经制作好了,接下来就是挂载到开发板上去测试

3.开发板验证

uboot

1.首先准备

一份uImage和stm32mp157a-fsmp1a.dtb

2.进入开发板uboot

开机狂按enter   然后配置开发板

env set -f ethaddr 00:80:E1:42:60:10//物理地址不固定

env set serverip 192.168.80.136//你的Ubuntu服务器ip

env set gatewayip 192.168.80.1//你的局域网网关

env set ipaddr 192.168.80.3//你的开发板ip自定义,确保和Ubuntu服务器同一网段即可
env set -f bootcmd tftp 0xc2000000 uImage;tftp 0xc4000000 stm32mp157a-fsmp1a.dtb;bootm 0xc2000000 - 0xc4000000
//使用bootm指令通过tftp服务,将内核写入起始为0xc2000000的开发板地址,设备树同理

//nfs 挂载测试根文件系统已经制作完成了,接下来就是测试,先用 nfs 挂载根文件系统,在 uboot 里面设置 bootargs 环境变量的值如下:

env set -f bootargs root=/dev/nfs nfsroot=192.168.80.136:/opt/xyrootfs intr rsize=1024 wsize=1024 rootwait rw earlyprintk ip=192.168.80.3 console=ttySTM0,115200

在保证内核正确的前提下,能进入根文件系统,即为成功。

串口登录开发板出现例如这样的界面:

wqc login: root Password:

Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.14.0-xilinx armv7l)

* Documentation:  Official Ubuntu Documentation

* Management:     Landscape

* Support:        Ubuntu Pro | Ubuntu

The programs included with the Ubuntu system are free software;

the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.

root@xieya:~#

因为此时LCD屏幕未移植,还无法查看图形化界面

4.移植LCD(屏幕型号st7701)

内核配置

在内核根目录下,执行:

make ARCH=arm menuconfig

//层级关系

Device Drivers  —>

Graphics support —>  — 图形配置

选中配置如图:

开机logo

[*] Bootup logo  —>

[*]   Standard black and white Linux logo

[*]   Standard 16-color Linux logo

[*]   Standard 224-color Linux logo

设备树

1. 添加pwm2内容

修改stm32mp15xx-fsmp1x.dtsi,在stm32mp15xx-fsmp1x.dtsi文件末尾添加如下内容: (如已添加请忽略)

&timers2 {

/* spare dmas for other usage */

/delete-property/dmas;

/delete-property/dma-names;

status = “okay”;

pwm2: pwm {

pinctrl-0 = <&pwm2_pins_b>;

pinctrl-1 = <&pwm2_sleep_pins_b>;

pinctrl-names = “default”, “sleep”;

#pwm-cells = <2>;

status = “okay”;

};

timer@2 {

status = “okay”;

};

};

把status设置为okay。

设置此节点不用dma。

pwm2 是我们为pwm设置的一个别名。

设置 PWM 所使用的IO配置。

此参数是用来规定pwms属性的参数。比如:#pwm-cells =<2>,表示pwms属性 有2个参数,如下所示:

pwms= <&pwm2 1 5000000>

其中pwm2表示使用PWM2,后面两个是参数,其中1表示使用PWM2的通道2(通道从1开始),5000000表示为 200Hz

2. 添加背光内容

修改stm32mp15xx-fsmp1x.dtsi,在根节点中添加如下内容:

panel_backlight: panel-backlight {

compatible = “pwm-backlight”;

pwms = <&pwm2 0 5000000>;

brightness-levels = <0 4 8 16 32 64 128 255>;

default-brightness-level = <6>;

status = “okay”;

};

设置背光使用pwm2的第二个通道,PWM频率为200Hz。

设置背8级背光(0~7),分别为0、4、8、16、32、64、128、255对应占空比为0%、1.57%、3.13%、 6.27%、12.55%、25.1%、50.19%、100%

设置默认背光等级为7,也就是100%的亮度

//参考内核PWM文档

(添加请忽略)

3. 修改stm32mp157a-fsmp1a.dts

&ltdc {

status = “okay”;

port {

#address-cells = <1>;

#size-cells = <0>;

ltdc_ep1_out: endpoint@1 {
 reg = <1>;

remote-endpoint = <&dsi_in>; };

};

};

在port下添加了一个子节点为ltdc_ep1_out

remote-endpoint属性是用来告诉ltdc节点输出到那里

添加dsi内容:

&dsi {

#address-cells = <1>;

#size-cells = <0>; status = “okay”;

ports {

#address-cells = <1>;

#size-cells = <0>; port@0 {

reg = <0>;

dsi_in: endpoint {

remote-endpoint = <&ltdc_ep1_out>; };

};

port@1 {

reg = <1>;

dsi_out: endpoint {

remote-endpoint = <&dsi_panel_in>; };

};

};

panel_dsi: panel-dsi@0 {

compatible = “sitronix,st7701”;

reg = <0>;

reset-gpios = <&gpiog 9 GPIO_ACTIVE_HIGH>; backlight = <&panel_backlight>;

power-supply = <&v3v3>;

status = “okay”;

port {

dsi_panel_in: endpoint {

remote-endpoint = <&dsi_out>; };

};

};

};

代码移植

和厂商沟通发现这个屏幕和demo板的有所不同,所以要修改原代码文件:

驱动代码路径如下:

/home/crl/crl/linux-5.4.31/drivers/gpu/drm/panel

vim panel-sitronix-st7701.c

之前的给他注释掉

初始化序列:
/**
* ST7701_SPEC_V1.2 is unable to provide enough information above this
* specific command sequence, so grab the same from vendor BSP driver.
*/
ST7701_DSI(st7701,0xe0, 0x00, 0x00, 0x02);
ST7701_DSI(st7701,0xe1, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20);
ST7701_DSI(st7701,0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
ST7701_DSI(st7701,0xe3, 0x00, 0x00, 0x33, 0x00);
ST7701_DSI(st7701,0xe4, 0x22, 0x00);ST7701_DSI(st7701,0xe5, 0x07, 0x34, 0xa0, 0xa0, 0x05, 0x34, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
ST7701_DSI(st7701,0xe6, 0x00, 0x00, 0x33, 0x00);
ST7701_DSI(st7701,0xe7, 0x22, 0x00);
ST7701_DSI(st7701,0xe8, 0x06, 0x34, 0xa0, 0xa0, 0x04, 0x34, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
ST7701_DSI(st7701,0xeb, 0x02, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00);
ST7701_DSI(st7701,0xec, 0x02, 0x00);
ST7701_DSI(st7701,0xed, 0xaa, 0x54, 0x0b, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xb0, 0x45, 0xaa);
/* disable Command2 */

模式:

Video模式分三种子模式:

1.Non-burst Mode Sync pulses: 在这种模式下,DSI基于各种不同的同步数据包来做数据同步。这种数据包括:重构,时间校准等。更具体的请参考DSI协议标准。

2.Non-burst Mode Sync event: 这种模式和第一种模式很像,但是这种模式不会发重构和时间校准的数据包,它们只发送一种叫做”Sync event”的包。

3.Burst mode: 在horizontal 的时序是一样的情况下DSI会把连接的速度提升到Panel支持的最大速

度。在这种模式下发送RGB数据包的时间被压缩,以留出更多的时间用来传送其他的数据。

//.flags = MIPI_DSI_MODE_VIDEO,

.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM,

.vsync_end  = 854 + 18 + 18,

.vtotal     = 854 + 18 + 18 + 11,

回头检查

选中完毕即可上电启动开发板,进行屏幕测试

至此, LCD屏幕移植完成。接下来需要移植USB驱动,使得可以直接在开发板上插上鼠标键盘就能像电脑一样进行操作。

排查问题的思路很关键

panel-sitronix-st7701

然后去内核的buildroot找这个屏幕

默认没有选中给他选中,之后make -j8就会把它一起编译到内核了

检查阶段:

新思路:锁定范围后ai介入解释设备树节点代码

如果是拷贝的别人现成的内核,那么设备树中已经包含有这个定时器节点了,不用再加

背光节点也有了不用再加,以后工作了回头翻阅笔记记得注意这点 所以我们真正要添加的设备树节点是ltdc

编译常用语句

make -j4 ARCH=arm uImage vmlinux LOADADDR=0xC2000040

make arch=arm dtbs

16> 拷贝文件到对应的目录

cp -raf arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb /tftpboot/ cp -raf arch/arm/boot/uImage /tftpboot/

compatible匹配不成功导致内核死机 所以要改如下:

发现核心层和设备控制层都没问题,只能是驱动问题了,接下来去找panel.c

5.USB驱动部分

设备树

USB子系统是一个标准和复杂的接口,所以驱动基本我们不用写,都是内核里有现成的,我们只需要在设备树 提供对应的设备节点即可。

USBH是只能主机模式,要编写USB HOST就用USBH控制器即可。

ST官方的STM3MP157开发板已经配置好了USBH的节点信息,所以我们直接参考此节点即可,这样开发板就能 够使用USB Host模式

1. USBH 控制器节点信息

打开“stm32mp151.dtsi”文件,找到 USBH 两个控制器的节点信息,名字分别为“usbh_ohci”和 “usbh_ehci”。如下示例代码所示:

2. 配置 PHY 控制器

我们先去了解一下 PHY 控制器, 一些通用配置,打开 stm32mp151.dtsi 文件,找的如下内 容所示:

usbphyc: usbphyc@5a006000 {
    #address-cells = <1>;
    #size-cells = <0>;
    #clock-cells = <0>;
    compatible = "st,stm32mp1-usbphyc";
    reg = <0x5a006000 0x1000>;
    clocks = <&rcc USBPHY_K>;
    resets = <&rcc USBPHY_R>;
    vdda1v1-supply = <&reg11>;
    vdda1v8-supply = <&reg18>;
    status = "disabled";
    usbphyc_port0: usb-phy@0 {
        #phy-cells = <0>;
        reg = <0>;
    };

    usbphyc_port1: usb-phy@1 {
        #phy-cells = <1>;
        reg = <1>;
    };
};

usbphyc 节点就是 STM32MP1 的 USB PHY。我已经知道 PHY 控制器有两个端口,刚好 usbphyc 节点里有两个子节点名字分别为:usbphyc_port0 和 usbphyc_port1,这个两个子节点就是 PHY 控制器的两个端口,其中 usbphyc_port0 只能分配给 USB Host。注意:“#phy-cells”属性和“#gpio-cells”属性作用是一样的,如果#phy-cells 为 1,表示一个 cell,此cell 表示端口做 USBH 的 PHY 端口还是 OTG 的 PHY 端口,0 表示做 OTG 的 PHY 端口,1 表示做 USBH 的 PHY 端口。

打开“stm32mp15xx-dkx.dtsi”文件找到“usb_phy_tuning”节点,把此节点的内容拷贝到 stm32mp157a- fsmp157.dts 的根目录下,拷贝内容如下所示:

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; };

usb_phy_tuning 此节点负责调整PHY的配置,对于此节点的属性内容感兴趣的可以去看 Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml文件

接 着 我 还 是 在 stm32mp157a-fsmp157.dts 文件中使能 usbphyc 以及向 usbphyc_port0 节点追加的 内容,要修改的如下所示:

&usbphyc {

        status = “okay”; 

};

&usbphyc_port0 {

phy-supply = <&v3v3>;

        st,phy-tuning =<&usb_phy_tuning>; 

};

第2行,这里我们把usbphyc的status属性修改为“okay”,使能usbphyc。

第6行,给usbphyc_port0节点追加phy-supply属性,添加一个电源管理属性。 第7行,把我们要修改的PHY配置,添加到usbphyc_port0节点里。

最后我们在 stm32mp157d-atk.dts 文件,使能 usbh_ehci 和指定 PHY 端口:

&usbh_ehci {

phys = <&usbphyc_port0>;

        status = “okay”; 

};

这里的内容很简单,就是修改status属性为“okay”和指定使用usbphyc_port0端口

内核配置

1.USB鼠标键盘驱动使能

USB 鼠标键盘属于 HID 设备,内核已经集成了相应的驱动,ST 官方提供的 linux 内核默认

已经使能了 USB 鼠标键盘驱动,但是我们还要学习一下如何手动使能这些驱动。输入“make menuconfig”,打开 linux 内核配置界面,首先打开 HID 驱动,按照如下路径到相应的配置项目:

-> Device Drivers -> HID support

-> HID bus support

-> <*> Generic HID driver //使能通用 HID 驱动

-> Device Drivers -> HID support

-> USB HID support

//去掉该项,用自己的驱动程序来测试

-> < > USB HID transport layer //USB 键盘鼠标等 HID 设备驱动

到这里已经可以重启验证,先输入密码登录,由于芯片性能羸弱需要等待一会,进入ubuntu桌面系统成功!

2.U盘实验

ST 提供的Linux内核默认也已经使能了U盘驱动,因此我们可以直接插上去使用。但是我们还是需要学习一下 如何手动配置Linux内核,使能U盘驱动

-> Device Drivers

-> SCSI device support

-> <*> SCSI disk support //选中此选项

-> Device Drivers -> USB support

-> USB Gadget Support

-> USB Gadget functions configurable through configfs -> [*] Mass storage //选中

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
话说今事的头像 - 宋马
评论 抢沙发

请登录后发表评论

    暂无评论内容