搜索
bottom↓
回复: 0

《STM32MP157嵌入式Linux驱动开发指南》第十七章 Linux内核移植

[复制链接]

出0入234汤圆

发表于 2021-6-24 17:03:56 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-6-24 17:03 编辑

1)实验平台:正点原子STM32MP157开发板
2)  章节摘自【正点原子】《STM32MP157嵌入式Linux驱动开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=629270721801
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyzmp157.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32MP157技术交流群:691905614
1.png

2.jpg


3.png



第十七章 Linux内核移植


前两章我们简单了解了一下Linux内核顶层Makefile和Linux内核的启动流程,本章我们就来学习一下如何将ST官方提供的Linux内核移植到正点原子的STM32MP157开发板上。通过本章的学习,我们将掌握如何将半导体厂商提供的Linux BSP包移植到我们自己的平台上。


17.1 Linux内核初次编译
17.1.1 编译正点原子出厂Linux源码
        1、第三方库安装
        编译内核之前需要先在ubuntu上安装lzop库,否则内核编译会失败!命令如下:
  1. sudo apt-get update                                //先更新在安装,防止安装的时候报错
  2. sudo apt-get install lzop                        
  3. sudo apt-get install libssl-dev
复制代码

        2、mkimage工具安装
        STM32MP1编译出来的Linux内核镜像文件为uImage,这是uboot所使用的内核镜像格式,通过在zImage镜像的前面添加0X40个字节的头部来得到uImage,这个需要mkimage工具来完成此工作。所以我们需要在Ubuntu下安装mkimage工具,输入如下命令:
  1. sudo apt-get install u-boot-tools
复制代码

        3、编译Linux系统
首先编译一下正点原子出厂的Linux源码,在Ubuntu下新建一个名为“alientek_linux”的目录存放正点原子出厂Linux源码,正点原子出厂Linux系统源码已经放到了开发板光盘中,路径为:开发板光盘1、程序源码1、正点原子Linux出厂系统源码linux-5.4.31-gb8d3ec3ac-v1.1.tar.bz2,将正点原子出厂linux系统源码拷贝到前面在Ubuntu下新建的“alientek_linux”目录下,拷贝完成以后使用如下命令解压缩:
  1. cd alientek_linux                                                        //进入到alientek_linux目录
  2. tar -vxjf linux-5.4.31-gb8d3ec3ac-v1.1.tar.bz2        //解压缩
复制代码

        解压完成以后的如图17.1.1.1所示:
第十七章916.png

图17.1.1.1 正点原子提供的Linux源码根目录

        在图17.1.1.1中Linux源码根目录下新建名为“stm32mp157d_atk.sh”的shell脚本,然后在这个shell脚本里面输入如下所示内容:
示例代码17.1.1.1 stm32mp157d_atk.sh文件内容
  1. 1 #!/bin/sh
  2. 2 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- distclean
  3. 3 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- stm32mp1_atk_defconfig
  4. 4 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig
  5. 5 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- uImage dtbs LOADADDR=0XC2000040 -j16
复制代码

        第2行,执行“make distclean”,清理工程,所以stm32mp157d_atk.sh每次都会清理一下工程。如果通过图形界面配置了Linux,但是还没保存新的配置文件,那么就要慎重使用stm32mp157d_atk.sh编译脚本了,因为它会把你的配置信息都删除掉!
        第3行,执行“make xxx_defconfig”,配置工程,这里使用的配置文件为stm32mp1_atk_defconfig
        第4行,执行“make menuconfig”,打开图形配置界面,对Linux进行配置,如果不想每次编译都打开图形配置界面的话可以将这一行删除掉。
        第5行,编译Linux内核,后面的“uImage”表示编译uImage格式的Linux内核,“dtbs”表示编译设备树,LOADADDR表示Linux内核在DDR中的加载地址为0XC2000040。
        stm32mp157d_atk.sh编译脚本每次执行都是先清理整个工程,然后在重新全编译,这个过程很费时间,如果电脑配置差的话要编译很久。除了第一次编译Linux源码,我们很少清理工程全编译的,后续驱动开发很少用“make distclean”来清理工程,都是直接“make uImage LOADADDR=0XC2000040”编译内核或者“make dtbs”编译设备树。
        使用chmod给予stm32mp157d_atk.sh可执行权限,然后运行此shell脚本,命令如下:
  1. chmod 777 stm32mp157d_atk.sh                //给予可执行权限
  2. ./ stm32mp157d_atk.sh                                //执行编译脚本
复制代码

        编译的时候会弹出Linux图形配置界面,如图17.1.1.2所示:
第十七章2112.png

图17.1.1.2 Linux图形配置界面

        Linux的图行界面配置和uboot是一样的,这里我们不需要做任何的配置,直接按两下ESC键退出,退出图形界面以后会自动开始编译Linux,等待编译完成。编译完成以后如图17.1.1.3所示:
第十七章2234.png

图17.1.1.3 Linux内核编译成功

        编译完成以后就会在arch/arm/boot这个目录下生成一个叫做uImage的文件,uImage就是我们要用的Linux镜像文件,如图17.1.1.4所示:
第十七章2339.png

图17.1.1.4 编译得到uImage

另外也会在arch/arm/boot/dts下生成很多.dtb文件,这些.dtb就是设备树文件,我们需要的是stm32mp157d-atk.dtb这个文件,如图17.1.1.5所示:
第十七章2454.png

图17.1.1.5 编译出来的dtb文件

        在编译脚本stm32mp157d_atk.sh里面,每条编译命令我们都指定ARCH和CROSS_COMPILE这两个变量的值,如果自己单独运行编译命令的话输入起来会很麻烦,我们可以直接在Linux内核源码的Makefile文件里面指定ARCH和CROSS_COMPILE这两个变量的值,打开Linux内核源码目录下的主Makefile,按照如图17.1.1.6所示设置ARCH和CROSS_COMPILE:
第十七章2682.png

图17.1.1.6 变量ARCH和CROSS_COMPILE

        在主Makefile中添加ARCH和CROSS_COMPILE以后,stm32mp157d_atk.sh就可以简化为:
示例代码17.1.1.2 简化后的stm32mp157d_atk.sh文件内容
  1. 1 #!/bin/sh
  2. 2 make distclean
  3. 3 make stm32mp1_atk_defconfig
  4. 4 make menuconfig
  5. 5 make uImage dtbs LOADADDR=0XC2000040 -j16
复制代码

        如果要单独编译uImage的话可以使用如下命令:
  1. make uImage LOADADDR=0XC2000040
复制代码

        单独编译设备树的话使用如下命令:
  1.         make dtbs
复制代码

        可以看出,Linux的编译过程基本和uboot一样,都要先执行“make xxx_defconfig”来配置一下,然后在执行“make uImage”进行编译吗,如果需要使用图形界面配置的话就执行“make menuconfig”。
17.1.2 运行测试
        上一小节我们已经成功编译了正点原子出厂Linux系统,得到了uImage和对应的stm32mp157d-atk.dtb设备树,本节我们就测试一下两个文件能不能正常启动,将uImage和stm32mp157d-atk.dtb这两个文件发送到Ubuntu的TFTP服务器目录下。启动开发板,uboot通过tftp命令从Ubuntu中uImage和stm32mp157d-atk.dtb并启动。为了不用每次手动输入命令,我们可以直接设置uboot中的bootcmd环境变量为:
  1. setenv bootcmd 'tftp c2000000 uImage;tftp c4000000 stm32mp157d-atk.dtb;bootm c2000000 - (有空格) c4000000'
  2. saveenv
复制代码

        启动以后Linux输出如图17.1.2.1所示信息:
第十七章3532.png

图17.1.2.1 Linux内核启动log信息

        从图17.1.2.1可以看出,Linux内核已经运行成功,并且当前Linux内核编译时间为2020年12月11日16:52:12。
        注意!如果出现如图17.1.2.2所示“end Kernel panic”错误为正常现象!

第十七章3674.png

图17.1.2.2 根文件系统缺失错误

        图17.1.2.2中的错误是由于根文件系统缺失导致的,也就是我们没有指定根文件系统,根文件系统的制作我们后面会详细讲解。
17.2 编译ST官方Linux系统
        上一节我们编译了正点原子提供的出厂系统,主要是学习一下Linux系统的编译流程,我们最终只要将ST原厂提供的Linux系统移植到正点原子的STM32MP157开发板上,因此我们肯定要先编译一下ST原厂的系统。
17.2.1 ST官方Linux源码打补丁
        1、获取到ST官方Linux源码
        首先肯定要获取到ST官方的Linux源码,这个已经在6.1.1小节获取到了,进入到对应的Linux源码目录,命令如下:
  1. cd /home/zuozhongkai/linux/atk-mp1/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0                                //进入ST官方Linux源码
复制代码

        ST官方Linux源码如图17.2.1.1所示:
第十七章4165.png

图17.2.1.1 ST官方Linux源码

        从图17.2.1.1可以看出,ST官方的Linux源码包有各种.patch补丁文件以及Linux源码压缩包,注意细心的朋友可能观察到了,图17.1.1.1中有4个以“fragment”开头的.config文件,这些文件是默认配置文件的补丁文件,用来生成默认配置文件,稍后会讲解怎么操作。
首先解压图17.2.1.1中“linux-5.4.31.tar.xz”这个真正的源码包压缩包,解压命令如下:
  1. tar -vxf linux-5.4.31.tar.xz
复制代码

        解压完成以后就会得到一个名为“linux-5.4.31”的Linux源码文件夹,如图17.2.1.2所示:
第十七章4473.png

图17.2.1.2 解压得到Linux源码

2、内核打补丁
        上面已经解压出来了Linux的源码文件,接下来就要对其打补丁,进入到上面解压出来的linux-5.4.31目录,然后执行相应的打补丁命令:
  1. cd linux-5.4.31/                                                        //进入Linux源码目录
  2. for p in `ls -1 ../*.patch`; do patch -p1 < $p; done        //打补丁
复制代码

打补丁结果如图17.2.1.3所示:
第十七章4691.png

图17.2.1.3 打补丁结果

        3、生成默认配置文件
        编译linux内核的时候也需要使用“make xxx_defconfig”来对其进行默认配置,比如编译正点原子出厂Linux系统的时候我们使用了“make stm32mp1_atk_defconfig”,其中stm32mp1_atk_defconfig就是正点原子开发板使用的默认配置文件。但是ST官原厂Linux内核需要先生成默认配置文件,并且对其进行打补丁,进入Linux内核源码根目录下,然后执行如下命令:
  1. cd linux-5.4.31/                                                                        //进入到linux内核
  2. make ARCH=arm multi_v7_defconfig "fragment*.config"        //生成默认配置文件
复制代码

        完成以后结果如图17.2.1.4所示:
第十七章5051.png

图17.2.1.4 生成.config文件

        上面我们只是在Linux源码根目录下生成了.config配置文件,如图17.2.1.5所示:
第十七章5123.png

图17.2.1.5 .config文件

        .config文件非常重要,Linux内核的所有配置项最终都保存在.config文件里面,最终编译Linux内核的时候需要读取.config里面的配置项!此时我们只是生成了.config,还并没有将图17.1.1.1中的这些fragment config补丁文件打进去,执行如入两条命令打补丁:
  1. for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done
  2. yes '' | make ARCH=arm oldconfig
复制代码

        第1条命令是将图17.1.2.1中的fragment config补丁打到.config文件里面。
        第2条命令是对oldconfig的所有选项都选择yes,这些选项最终都会写入到.config里面。注意,这条命令yes后面是两个‘'’,而不是一个“''”,不要输入错了!!!
        完成以后如图17.2.1.6所示:
第十七章5582.png

图17.2.1.6 配置完成的.config

        至此,Linux源码根目录下的.config文件就已经保存了所有的配置项,所以只需要复制一份.config作为我们的默认配置文件即可,复制命令如下:
  1. cp .config ./arch/arm/configs/stm32mp1_atk_defconfig
复制代码

        此时在Linux内核的/arch/arm/configs目录下存在一个名为stm32mp1_atk_defconfig的默认配置文件,如图17.2.1.7所示:
第十七章5818.png

图17.2.1.7默认配置文件

        至此,Linux内核全部打完补丁,linux-5.4.31目录就是我们要移植的Linux源码,但是图17.2.1.3中的linux-5.4.31目录路径有点长,不适合阅读和编译。所以我们新建一个名为“my_linux”的目录来保存我们要移植的linux源码,然后将打完补丁的linux源码linux-5.4.31拷贝到“my_linux”目录下,命令如下:
  1. cd /home/zuozhongkai/linux/atk-mp1/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0                                 
  2. cp linux-5.4.31 /home/zuozhongkai/linux/atk-mp1/linux/my_linux/ -rf         //拷贝
复制代码

        拷贝完成以后的my_linux目录如图17.2.1.8所示:
第十七章6263.png

图17.2.1.8 拷贝过来的linux源码

        最后就是创建vscode工程,方便我们移植和阅读。
17.2.2 编译ST官方Linux源码
        上一小节已经准备好了ST官方Linux源码,存放到了my_ulinux目录下,本小节我们来编译一下这个Linux源码。
        1、修改Makefile
        首先修改一下Linux源码的Makefile文件,也可以不修改,但是在编译的时候需要多输入一些参数,为了偷懒,还是修改一下。修改方法已经在17.1.1小节讲解过了,就是在Makefile文件里面添加ARCH和CROSS_COMPILE这两个变量的值,如图17.2.2.1所示:
第十七章6551.png

图17.2.2.1 设置ARCH和CROSS_COMPILE变量值

        ST官方Linux肯定适配了官方的STM32MP1 EVK开发板,我们就编译EVK开发板对应的Linux内核和设备树,编译完成以后在正点原子的STM32MP1开发板启动,看看能不能运行,不能的话就要修改相应的文件,这就是Linux的移植。创建一个名为“stm32mp157d_atk.sh”的编译脚本,脚本内容如下:
示例代码17.1.2.1 stm32mp157d_atk.sh文件内容
  1. 1 #!/bin/sh
  2. 2 make distclean
  3. 3 make stm32mp1_atk_defconfig
  4. 4 make menuconfig
  5. 5 make uImage dtbs LOADADDR=0XC2000040 -j16
复制代码

        给予stm32mp157d_atk.sh可执行文件,然后运行此编译脚本,命令如下:
  1. chmod 777 stm32mp157d_atk.sh                //给予可执行权限,仅第一次
  2. ./stm32mp157d_atk.sh                                //运行编译脚本
复制代码

        等待编译完成,编译完成以后的到uImage镜像文件和设备树,其中STM32MP157系列的设备树有很多,如图17.2.2.3所示:
第十七章7092.png

图17.2.2.3 编译得到的.dtb文件

        从图17.2.2.3可以看出,此时有很多.dtb文件,我们这里使用stm32mp157d-ed1.dtb这个文件。
17.2.3 启动测试
        上一小节我们已经编译出来了ST官方开发板对应的uImage和stm32mp157d-ed1.dtb设备树,本章我们直接在正点原子的STM32MP157开发板上运行,看看ST官方系统能不能运行下去。讲uImage和stm32mp157d-ed1.dtb发送到Ubuntu下的tftp服务器目录下,然后通过uboot的tftp命令下载并启动,Linux系统运行log信息如图17.2.3.1所示:
第十七章7385.png

图17.2.3.1 ST官方Linux系统运行结果

        从图17.2.3.1可以看出,ST官方开发板的Linux系统在正点原子STM32MP157开发板上启动成功,所以后续的移植就要简单的多(实际不需要移植,直接在ST官方开发板相应文件上修改即可,但是为了学习,我们还是学习一下如何在Linux源码里面添加我们自己的开发板),只需要参考ST官方开发板创建一个设备树即可。
17.3 在Linux中添加自己的开发板
17.3.1 添加开发板对应的默认配置文件
        首先就是添加开发板对应的默认配置文件,这里直接使用17.2.1小节制作出来的stm32mp1_atk_defconfig文件即可。
17.3.2 添加开发板对应的设备树
        1、新建相应的设备树文件
        上面我们使用ST官方开发板的设备树已经成功启动了Linux系统,但是我们在实际开发中肯定要添加一份自己开发板所对应的设备树。正点原子STM32MP157开发板是以ST官方的开发板为蓝本制作的,所以设备树也是参考ST官方开发板的。在arch/arm/boot/dts/目录下新建名为“stm32mp157d-atk.dtsi”的设备树头文件,然后将stm32mp15xx-edx.dtsi文件里面的内容全部复制到stm32mp157d-atk.dtsi里面。
        再新建一个名为“stm32mp157d-atk.dts”的文件,将stm32mp157c-ed1.dts文件里面的内容都拷贝到stm32mp157d-atk.dts里面,如图17.3.2.1所示:
第十七章8045.png

图17.3.2.1 stm32mp157d-atk.dts文件内容

        记得一定要将图17.3.2.1中14行原来的“stm32mp15xx-edx.dtsi”头文件改为“stm32mp15d-atk.dtsi”!
        2、修改stm32mp157d-atk.dtsi文件
        stm32mp157d-atk.dtsi里面的内容是直接复制的ST官方开发板的stm32mp15xx-edx.dtsi,所以里面的很多配置是针对ST官方开发板的,比如PMIC芯片,这些配置是不需要的,要删除掉。正点原子开发板没有用到集成PMIC芯片,因此还需要在设备树里面添加一些电源节点信息,修改完成以后的stm32mp157d-atk.dtsi文件内容如下所示:
示例代码17.3.2.1 stm32mp157d_atk.dtsi文件内容
  1. /*
  2. 1   * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
  3. 2   * Author: Ludovic Barre <<a href="mailto:ludovic.barre@st.com">ludovic.barre@st.com</a>> for ST.
  4. 3   */
  5. 4   #include "stm32mp157-m4-srm.dtsi"
  6. 5   #include "stm32mp157-m4-srm-pinctrl.dtsi"
  7. 6   #include <dt-bindings/gpio/gpio.h>
  8. 7   #include <dt-bindings/mfd/st,stpmic1.h>
  9. 8   
  10. 9   / {
  11. 10      memory@c0000000 {
  12. 11          device_type = "memory";
  13. 12          reg = <0xC0000000 0x40000000>;
  14. 13      };
  15. 14  
  16. 15      reserved-memory {
  17. 16          #address-cells = <1>;
  18. 17          #size-cells = <1>;
  19. 18          ranges;
  20. 19  
  21. 20          mcuram2: mcuram2@10000000 {
  22. 21              compatible = "shared-dma-pool";
  23. 22              reg = <0x10000000 0x40000>;
  24. 23              no-map;
  25. 24          };
  26. 25  
  27. 26          vdev0vring0: vdev0vring0@10040000 {
  28. 27              compatible = "shared-dma-pool";
  29. 28              reg = <0x10040000 0x1000>;
  30. 29              no-map;
  31. 30          };
  32. 31  
  33. 32          vdev0vring1: vdev0vring1@10041000 {
  34. 33              compatible = "shared-dma-pool";
  35. 34              reg = <0x10041000 0x1000>;
  36. 35              no-map;
  37. 36          };
  38. 37  
  39. 38          vdev0buffer: vdev0buffer@10042000 {
  40. 39              compatible = "shared-dma-pool";
  41. 40              reg = <0x10042000 0x4000>;
  42. 41              no-map;
  43. 42          };
  44. 43  
  45. 44          mcuram: mcuram@30000000 {
  46. 45              compatible = "shared-dma-pool";
  47. 46              reg = <0x30000000 0x40000>;
  48. 47              no-map;
  49. 48          };
  50. 49  
  51. 50          retram: retram@38000000 {
  52. 51              compatible = "shared-dma-pool";
  53. 52              reg = <0x38000000 0x10000>;
  54. 53              no-map;
  55. 54          };
  56. 55      };
  57. 56  
  58. 57      vddcore: buck1 {
  59. 58          compatible = "regulator-fixed";
  60. 59          regulator-name = "vddcore";
  61. 60          regulator-min-microvolt = <1200000>;
  62. 61          regulator-max-microvolt = <1350000>;
  63. 62          regulator-always-on;
  64. 63          regulator-boot-on;
  65. 64      };
  66. 65  
  67. 66      v3v3: regulator-3p3v {
  68. 67          compatible = "regulator-fixed";
  69. 68          regulator-name = "v3v3";
  70. 69          regulator-min-microvolt = <3300000>;
  71. 70          regulator-max-microvolt = <3300000>;
  72. 71          regulator-always-on;
  73. 72          regulator-boot-on;
  74. 73      };
  75. 74  };
  76. 75  
  77. 76  &cpu0{
  78. 77      cpu-supply = <&vddcore>;
  79. 78  };
  80. 79  
  81. 80  &crc1 {
  82. 81      status = "okay";
  83. 82  };
  84. 83  
  85. 84  &dma1 {
  86. 85      sram = <&dma_pool>;
  87. 86  };
  88. 87  
  89. 88  &dma2 {
  90. 89      sram = <&dma_pool>;
  91. 90  };
  92. 91  
  93. 92  &dts {
  94. 93      status = "okay";
  95. 94  };
  96. 95  
  97. 96  ðernet0 {
  98. 97      status = "okay";
  99. 98      pinctrl-0 = <ðernet0_rgmii_pins_a>;
  100. 99      pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>;
  101. 100     pinctrl-names = "default", "sleep";
  102. 101     phy-mode = "rgmii-id";
  103. 102     max-speed = <1000>;
  104. 103     phy-handle = <&phy0>;
  105. 104
  106. 105     mdio0 {
  107. 106         #address-cells = <1>;
  108. 107         #size-cells = <0>;
  109. 108         compatible = "snps,dwmac-mdio";
  110. 109         phy0: ethernet-phy@0 {
  111. 110             reg = <0>;
  112. 111         };
  113. 112     };
  114. 113 };
  115. 114
  116. 115 &hash1 {
  117. 116     status = "okay";
  118. 117 };
  119. 118
  120. 119 &ipcc {
  121. 120     status = "okay";
  122. 121 };
  123. 122
  124. 123 &iwdg2 {
  125. 124     timeout-sec = <32>;
  126. 125     status = "okay";
  127. 126 };
  128. 127
  129. 128 &rng1 {
  130. 129     status = "okay";
  131. 130 };
  132. 131
  133. 132 &rtc {
  134. 133     status = "okay";
  135. 134 };
  136. 135
  137. 136 &sdmmc1 {
  138. 137     pinctrl-names = "default", "opendrain", "sleep";
  139. 138     pinctrl-0 = <&sdmmc1_b4_pins_a>;
  140. 139     pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
  141. 140     pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;
  142. 141     broken-cd;
  143. 142     st,neg-edge;
  144. 143     bus-width = <4>;
  145. 144     vmmc-supply = <&v3v3>;
  146. 145     status = "okay";
  147. 146 };
  148. 147
  149. 148 &sdmmc2 {
  150. 149     pinctrl-names = "default", "opendrain", "sleep";
  151. 150     pinctrl-0 = <&sdmmc2_b4_pins_a>;
  152. 151     pinctrl-1 = <&sdmmc2_b4_od_pins_a>;
  153. 152     pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>;
  154. 153     non-removable;
  155. 154     st,neg-edge;
  156. 155     bus-width = <8>;
  157. 156     vmmc-supply = <&v3v3>;
  158. 157     keep-power-in-suspend;
  159. 158     status = "okay";
  160. 159 };
  161. 160
  162. 161 &sram {
  163. 162     dma_pool: dma_pool@0 {
  164. 163         reg = <0x50000 0x10000>;
  165. 164         pool;
  166. 165     };
  167. 166 };
  168. 167
  169. 168 &uart4 {
  170. 169     pinctrl-names = "default", "sleep", "idle";
  171. 170     pinctrl-0 = <&uart4_pins_a>;
  172. 171     pinctrl-1 = <&uart4_sleep_pins_a>;
  173. 172     pinctrl-2 = <&uart4_idle_pins_a>;
  174. 173     pinctrl-3 = <&uart4_pins_a>;
  175. 174     /delete-property/dmas;
  176. 175     /delete-property/dma-names;
  177. 176     status = "okay";
  178. 177 };
复制代码

3、编译stm32mp157d-atk.dts设备树
        最后我们肯定要编译stm32mp157d-atk.dts,得到对应的.dtb文件,打开arch/arm/boot/dts/Makefile,到“dtb-$(CONFIG_ARCH_STM32)”配置项,在此配置项中加入“stm32mp157d-atk.dtb”,完成以后如图17.3.2.2所示:
第十七章12989.png

图17.3.2.2 Makefile文件

        图17.3.2.2中第1011行添加了“stm32mp157d-atk.dtb”,表示编译的时候也将stm32mp157d-atk.dts编译为stm32mp157d-atk.dtb。
17.3.3 关闭内核模块验证
        后续做Linux驱动实验的时候我们都是编译驱动模块,然后在系统里面加载,加载的时候系统会验证模块,有时候会验证出错。比如板子运行的系统和编译驱动模块所用的系统不一致的时候,这样为我们的学习带来了很大的不便,为了方便开发,我们可以关闭内核模块验证。打开默认配置文件stm32mp1_atk_defconfig,里面有如下所示配置项目:
  1. CONFIG_MODULE_SIG=y
  2. CONFIG_MODULE_SIG_ALL=y
  3. CONFIG_MODULE_SIG_SHA256=y
  4. CONFIG_MODULE_SIG_HASH="sha256"
复制代码

        将上面4个配置项屏蔽掉,如图17.3.3.1所示:
第十七章13420.png

图17.3.3.1 关闭内核模块验证功能

        另外,我们也可以直接在Linux的图形化配置界面上关闭掉内核模块验证,输入如下命令打开Linux内核图形化配置界面:
  1. make menuconfig
复制代码

        配置路径如下:
  1. -> Enable loadable module support (MODULES [=y])   
  2.                 ->Module signature verification  
复制代码

        取消对“Module signature verification”选项的选中,如图17.3.3.2所示:
第十七章13672.png

图17.3.3.2 取消内核模块验证

        配置完成以后退出,打开.config文件,查看还有没有CONFIG_MODULE_SIG配置项,如图17.3.3.3所示:
第十七章13756.png

图17.3.3.3 .config文件

        从图17.3.3.3可以看出,COFIG_MODULE_SIG选项没有设置,同样的CONFIG_MODULE_SIG_ALL、CONFIG_MODULE_SIG_SHA256和CONFIG_MODULE_SIG_HASH也一起消失了。
        最后我们需要将配置项保存到stm32mp1_atk_defconfig里面,打开图形化配置界面,选择<Save>项目,将修改后的所有配置项保存到stm32mp1_atk_defconfig文件里面,如图17.3.3.4所示:
第十七章14011.png

图17.3.3.4 保存默认配置

        后续试验中,只要通过图形化界面修改了Linux内核配置,最好及时将其保存到stm32mp1_atk_defconfig文件。因为图形化界面修改的配置只是暂时保存到.config文件里面,一旦使用“make clean”清理工程,那么.config文件就会被删除掉,所有的配置也就丢失了!
17.3.4 关闭内核log信息时间戳
        大家可以仔细观察会发现Linux系统启动的时候每行信息前面都会打印出相应的时间,如图17.3.4.1所示:
第十七章14250.png

图17.3.4.1 log时间信息输出

        从图17.3.4.1可以看出,每行log信息前面都有时间信息,在调试的时候这个时间信息看起来比较“烦人”我们可以通过配置内核将其关闭,配置路径如下:
  1. -> Kernel hacking                                                  
  2.              -> printk and dmesg options   
  3.                         ->Show timing information on printks           //取消选中
复制代码

如图17.3.4.1所示:
第十七章14518.png

图17.3.4.1 取消printk时间戳

17.3.5 编译测试
        设备树修改好以后就可以编译了,因为我们只是修改了设备树,所以编译脚本直接用17.1.2小节创建的stm32mp157d_atk.sh编译脚本即可!
        编译完成以后在arch/arm/boot目录下生成uImage镜像,在arch/arm/boot/dts目录下生成stm32mp157d-atk.dtb文件,将这两个文件拷贝到tftp服务器目录下,然后在uboot中使用tftp命令下载并运行,命令如下:
  1. tftp c2000000 uImage
  2. tftp c4000000 stm32mp157d-atk.dtb
  3. bootm c2000000 - c4000000
复制代码

        当出现如图17.3.5.1所示的log信息以后就说明Linux启动运行成功:
第十七章14879.png

图17.3.5.1 Linux运行log信息

17.4 烧写系统镜像到EMMC里面
        上面已经测试过了Linux系统镜像和设备树,但是我们是通过tftp命令从网络上下载测试的,实际产品开发中最终是要将系统烧写到外部Flash中的,比如EMMC。本节我们来学习一下如何将uIamge和stm32mp157d-atk.dtb打包成ext4格式,然后通过STM32CubeProgrammer烧写到EMMC里面,最终启动EMMC里面的Linux系统。
17.4.1 系统镜像打包
        首先就是将uImage和stm32mp157d-atk.dtb打包在一起,格式为ext4格式。当然了,你也可以向这个ext4格式打包文件里面添加其他的内容,比如图片等,本节我们只用到uImage和stm32mp157d-atk.dtb。
        打包过程是在Ubuntu下完成的,首先新建一个名为“bootfs”的文件夹,然后将uImage和stm32mp157d-atk.dtb这两个文件放到bootfs文件夹下,如图17.4.1.1所示:
第十七章15338.png

图17.4.1.1 bootfs文件夹

        1、新建ext4格式磁盘
        首先新建一个ext4格式的磁盘,然后挂载这个ext4格式的磁盘,将stm32mp157d-atk.dtb和uImage拷贝到这个ext4磁盘即可。
进入到bootfs文件夹,然后输入如下命令创建ext4磁盘:
示例代码17.4.1.1 ext4磁盘创建命令
  1. 1 cd bootfs
  2. 2 dd if=/dev/zero of=bootfs.ext4 bs=1M count=10
  3. 3 mkfs.ext4 -L bootfs bootfs.ext4   
复制代码

        第1行,进入到图17.4.1.1中的bootfs目录。
        第2行,使用dd命令创建一个名为bootfs.ext4的磁盘,of指定磁盘名字为“bootfs.ext4”;bs指定磁盘输入/输出块大小为1MB;count指定磁盘的块数量为10个。因此bootfs.ext4只能存放不超过10MB的文件,如果要存放的文件总大小超过10MB,那么就要适当调整count参数的大小。
        第3行,使用mkfs.ext4将bootfs.ext4磁盘格式化为ext4格式。
        完成以后就会生成名为“bootfs.ext4”的磁盘,如图17.4.1.2所示:
第十七章15874.png

图17.4.1.2 bootfs.ext4磁盘

        2、将系统镜像拷贝到ext4磁盘中
        首先创建一个目录用来挂载前面制作制作出来的bootfs.ext4,比如我这里创建目录/mnt/bootfs,命令如下:
  1. sudo mkdir /mnt/bootfs
复制代码

接下来使用mount命令将bootfs.ext4挂载到/mnt/bootfs目录下,命令如下:
  1. cd /home/zuozhongkai/linux/atk-mp1/linux/bootfs
  2. sudo mount bootfs.ext4 /mnt/bootfs/
复制代码

        挂载成功以后就将图17.4.1.1中的uImage和stm32mp157d-atk.dtb拷贝到/mnt/bootfs目录下,命令如下:
  1. cd /home/zuozhongkai/linux/atk-mp1/linux/bootfs
  2. sudo cp uImage stm32mp157d-atk.dtb /mnt/bootfs/
复制代码

        拷贝完成以后使用umount卸载/mnt/bootfs即可,命令如下:
  1. sudo umount /mnt/bootfs
复制代码

        至此,uImage和stm32mp157d-atk.dtb就已经打包到了图17.4.1.2中的bootfs.ext4中,稍后使用STM32CubeProgrammer软件将其烧写到EMMC里面。烧写之前最好在Windows下打开bootfs.ext4看一下,看看是否已经将uImage和stm32mp157d-atk.dtb打包进去,如图17.4.1.3所示:
第十七章16544.png

图17.4.1.3 bootfs.ext4

17.4.2 烧写到EMMC
        接下来就是将上一小节打包好的ext4格式的bootfs.ext4烧写到开发板的EMMC里面,使用STM32CubeProgrammer软件完成此操作。将bootfs.ext4拷贝到以前创建的images目录下,如图17.4.2.1所示:
第十七章16703.png

图17.4.2.1 images目录

        修改flashlayout文件tf-a.tsv,在后面加入bootfs.ext4的烧写脚本,如图17.4.2.2所示:
第十七章16785.png

图17.4.2.2 修改后的flashlayout

        图17.4.2.2中第7行就是bootfs.ext4的烧写脚本,设置好以后就可以使用STM32CubeProgrammer烧写系统了,烧写完成以后设置拨码开关从EMMC启动,启动以后进入uboot的命令行,输入如下命令查看EMMC分区2里面是否正确烧写了uImage和stm32mp157d-atk.dtb:
  1. ext4ls mmc 1:2
复制代码

        结果如图17.4.2.3所示:
第十七章17001.png

图17.4.2.3 EMMC分区2

        从图17.4.2.3可以看出,此时EMMC的分区2存在uImage和stm32mp157d-atk.dtb这两个文件,设置bootcmd环境变量,从EMMC里面读取系统镜像和设备树并启动,命令如下:
  1. setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
  2. saveenv
  3. boot
复制代码

        如果烧写正常的话就会从EMMC的分区2中加载系统并运行,如图17.4.2.4所示:
第十七章17301.png

图17.4.2.4 EMMC启动系统

        可以看出,将系统镜像烧写到EMMC里面还是很繁琐的,因此一般都是在产品最终开发完成,出厂的时候才会烧写到EMMC里面。在调试阶段都不会烧写到EMMC里面,而是在uboot里面使用tftp或nfs命令通过网络下载系统镜像并运行,这样当我们修改了系统以后只需要将系统镜像复制到tftp或nfs服务器目录即可,极大的简化开发方式。
17.5 根文件系统缺失错误
Linux内核启动以后是需要根文件系统的,根文件系统存在哪里是由uboot的bootargs环境变量指定的,它会传递给Linux内核作为命令行(command line)参数。比如13.4.2小节设置root=/dev/mmcblk2p3,也就是说根文件系统存储在/dev/mmcblk2p3中,也就是EMMC的分区3中。如果我们不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?这个问题是很常见的,我们在实际的工作中开发一个产品,这个产品的第一版硬件出来以后我们是没有对应的根文件系统可用的,必须要自己做根文件系统。在没有根文件系统的情况下,Linux内核启动的时候就会有如图17.5.1所示的错误:
第十七章17813.png

图17.5.1 根文件系统缺失错误

        在图17.5.1中最后一行为:
  1. end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
复制代码

也就是提示内核崩溃,VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。解决方法就是制作根文件系统,并且设置uboot的bootargs环境变量,指定根文件系统所在的目录。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 09:09

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表