通俗地、有效地学习Linux驱动&应用(只要没更完有空就更)

目录

食用方法

Warning

Linux系统分层的意义

系统移植和烧写

Windows系统下通过OTG烧写

Ubuntu脚本烧写

Windows脚本烧写

通过uboot进行操作

Debian移植(EBF6ULL系列请看!!!)

系统烧写

给开发板安装nfs(注意!这里有坑) 

挂载   

取消挂载

uboot

uboot是什么

如何获得uboot

uboot的食用方法

uboot的初次编译&烧写

修改uboot配置

添加配置文件

修改makefile

修改imximage.cfg

修改imximage.cfg

修改MAINTAINERS

添加图形界面

修改配置内容

uboot的启动

uboot命令

信息查询命令:bdinfo\printenv\version

环境变量操作命令:setenv\saveenv

内存操作命令:md\nm\mm\mw\cp\cmp

网络操作命令:ping\dhcp\nfs\tftp

BOOT操作命令:bootz\bootm\boot

uboot阶段性总结

系统不能完全启动

Linux内核移植

系统内核获得&编译

移植测试

系统配置文件准备

设备树文件准备

CPU主频、从存储器、网络修改

根文件构建

根文件结构

Busy Box

编译Busy Box

让Busy Box支持中文

配置Busy Box

编译Busy Box

添加lib库

向“usr/lkib”添加文件

挂载测试

系统移植阶段性总结


食用方法

        需要有一块开发板(没有固定的哪一家,我用的是NXP的6ull,至于哪一家的我不说了),以及完善的、配套的学习资料。先学习一些Linux中常用的shell指令(例如你们熟知的删库跑路指令),还有makeflie的知识也学一下(用来写编译文件的)。还有最重要的一句话(大概意思):Linux里面的操作,实质上都是对文件的操作。然后慢慢食用这一句话。

        上述这一些知识点,无需全部一字不漏的记下来,背出来。大概了解一下即可学习,重点是掌握学习的思维。另外,如果可以的话,强烈建议学习过单片机的RTOS、bootloader、OTA再来学习Linux,这样会帮助你更好理解Linux。在这里,强烈推荐走马观花地学一下RT-Thread

 我不是官方托,是因为这玩意长得太像Linux了。

        最后,可以开始学习Linux(嵌入式方向)。

        注意,本文只提供学习参考(相当于课外参考书,详细内容请配合开发板教程进行学习,那才是上课的教材,这个只是参考书!!!),需要配合你的开发板和配套教程食用。到这里,默认你已经安装好虚拟机,安装好Ubuntu的,并且已经把单片机玩明白了的。如果没明白,不建议直接食用。

Warning

        提示:如果你看的是某火教程的话,他家板子是跑Debian的(他家连让你学uboot的机会都不给,直接把uboot、内核、设备树、根文件都打包好一个镜像的。不过他家板子硬件上确实要比某粒子的香),让你烧进去板子然后直奔驱动开发的,你可以跳过前面uboot、Linux移植、根文件这几部分。

Linux系统分层的意义

        在Linux中,它是把内核态(内核源码工作的空间)、底层硬件(gpio、uart、iic、adc、以太网、音频通信等外设)、用户态(用户可此可操作空间)分开。分开的原因很简单:防止不同区域的工作影响到其他区域的工作(为了系统的安全性),甚至使得系统内核崩溃。例如:

1)底层硬件和系统内核中间隔着抽象层,是为了整个系统有更好的交互性。

2)用户和系统内核中间隔着应用层,是为了防止用户的不正当操作:直接操作底层硬件或者操作内核导致系统崩溃。

         在底层的,实实在在的物理硬件,通过不同的逻辑电路和实实在在的物理寄存器一一对应。让底层硬件进行工作,需要把其对应的寄存器设置好,再利用函数对这一些操作进行封装,这一些写好的函数(用于操作底层硬件的函数)就是该硬件对应的Linux驱动。(Linux驱动工程师)

        平时所说的嵌入式QT开发,就是给这些跑Linux的板子接上屏幕,写一个图形界面的应用程序。这一些应用程序,需要在板子的Linux系统里面安装一个可以运行QT的环境,才可以把在电脑上交叉编译(在电脑上编译板卡的程序)好的QT应用程序移植到板子上并且运行。这一些应用程序,通常都需要调用上述做好的Linux驱动函数进行工作的。(Linux应用工程师)

        在这里,引申出两个工作岗位:Linux驱动工程师和Linux应用工程师

        理论上,只要处理器性能跟得上,单片机(这里只说ARM Cortex-M系列的,RISC-V的另提)也可以跑Linux。可是因为单片机没有一个叫MMU(Memory Management Unit内存管理单元)的东西,所以没跑上。但是,最新的Linux系统已经支持无MMU系列芯片。所以,你会看到有人在STM32H7跑Linux了。

系统移植和烧写

        基本上,所有能运行Linux系统的soc(片上系统)都需要将系统镜像烧写到NAND、EMMC、SD卡等其他不易失性的存储器上,或者板子有uboot(这个后面讲到)搭好了一个局域网,从指定机器里面下载打包好的镜像。当启动Linux系统的时候,需要将这些不易失性的存储器上的数据拷贝到DDR(双倍速率的RAM)上面运行。在这里有好几种移植的方法。

Windows系统下通过OTG烧写

        芯片厂商会提供芯片的匹配Linux系统,或者客户修改后的Linux系统(这个我还没学到),打包成一个烧写工具只需要把板子的设置到USB-OTG启动方式(板子需要有对应的硬件),板子通过USB线连接到电脑,使用这个工具进行烧写即可(烧写到NADA或者EMMC里面)。这个烧写方式适合已经完成开发的产品一键烧写。

Ubuntu脚本烧写

        利用芯片厂商提供的烧写脚本,把Ubuntu里面编译好全部文件烧写到SD卡里面。再把SD卡插到板子上,再启动。这一种方式比较笨比,不推荐。

Windows脚本烧写

        这个和ubuntu脚本烧写的工作原理差不多,通过windows下面的脚本烧写到SD卡里面,比较笨比,不推荐。

通过uboot进行操作

        在开发阶段强烈推荐使用该方法。先在Ubuntu里面将uboot编译好,使用官方脚本把编译好的uboot烧写到SD卡里面。把SD插到板卡上面,使用SD卡启动方式启动uboot。通过uboot配置好nfs、tftp的通信方式,从主服务器那里把系统镜像拷过来启动。后面将会针对这个展开学习。

Debian移植(EBF6ULL系列请看!!!)

        Debian系统其实也是Linux的一种,它的系统结构框架和Linux也是大同小异。需要移植这一个系统的是某火家的Linux开发板。如果你是使用他家开发板的话,后面的uboot、Linux内核移植、根文件目录可以跳过,但是本章你务必要看。因为你看了他家的教学视频,你会有以下几个坑要踩。还有,如果你想深入了解整个系统是怎么启动,由什么组成的,请按照顺序看下去。如果你不是他家的板子,请跳过本部分。不建议你用某火的板子跑某例子的系统。

系统烧写

        因为他家板子出厂的时候已经再EMMC里面固化好系统,为了方便起见,请你自备一个大于2G的SD卡即可。在拿到他家的资料包以后,建议烧写这一个镜像(选中的)。

        第二个镜像是已经移植了QT环境的镜像,目前驱动学习阶段暂不需要这一个。有的人是用NADA Falsh版本的,用OTG脚本把系统烧写到NADA Falsh里面会直接显示空间不足,所以需要烧写到SD卡里面启动。至于烧写这些XZ文件,需要用到一个东西:Etcher,这个东西自行安装。

         电脑插上SD卡之后,打开软件。我用的版本比较旧,你们用的应该是新版,界面有点不太一样。第一步先选择选中XZ文件;然后第二步选中你的SD卡;第三步开始烧写,有可能烧写失败,但是只要是在显示验证阶段的时候显示失败,那问题不大(如果是烧写阶段失败的,用擦除工具擦除之后重新烧写即可)。直接弹出SD卡,插到板子上面使用就可以了。

 (擦除工具)

给开发板安装nfs(注意!这里有坑) 

        到这里,默认你已经在Ubuntu里面安装好nfs了。在上一步完成系统烧写之后,开发板拨码开关设置到SD卡启动,然后打开串口终端,电路板上电等待系统启动完成(记得用网线接上网络,就是你的开发板的ETH1接口和你家路由器接上,和你的电脑主机形成一个局域网。)

        登录系统,用户名:root,密码:root。你如果按照他家当前的视频教程,你会发现你根本安装不了nfs。因为你没有更新源(不然你后面没办法安装软件)!!!执行以下命令:

sudo apt-get -y update

         然后执行以下命令,安装nfs:

sudo apt install nfs-common -y

挂载   

         然后等待安装完成,然后你用以下的这个命令,你就可以看到你主机上Ubuntu的共享文件夹位置(我主机的ip给分了206,不是固定的)。

showmount -e 192.168.31.206

        然后,使用以下这一个命令,把你主机的共享文件夹挂载到你的开发板子上:

sudo mount -t nfs 192.168.31.206:/home/linux/nfs /mnt

        其中,192.168.31.206是我Ubuntu的ip,记得ip后面加上:。然后就是Ubuntu里面的nfs位置。空格,再输入/mnt即可。然后你就可以看到,你在Ubuntu里面的nfs新建一个文件,你的开发板再mnt里面ls一下也可以看到。

取消挂载

        使用以下命令即可(在开发板上):

sudo umount /mnt

uboot

        在这一章节,会重点讲以下内容:uboot的编译&SD卡的烧写、uboot打印的信息、uboot的指令、nfs和tftp搭建与使用。至于uboot图像化操作,这个看情况,如果有时间就讲,没时间就算了。

uboot是什么

        如果你单片机玩的还挺溜,我就告诉你uboot相当于bootloader。如果你没玩过单片机,那么请看下面这一个示意图。uboot相当于一个裸机程序,一个用来启动Linux的程序。我们知道Linux系统功能十分庞大,而核心是系统内核。而uboot则是在Linux启动之前,修改一下系统将要启动时候的配置。通过uboot,我们可以在Linux启动之前升级系统,切换系统,重装系统等等。这个和平时用的PC电脑上的BIOS是相类似的。

如何获得uboot

        关于uboot如何获得,可以参考你的板子提供的教程(某粒子、某火、某山教程)。另外,uboot是开源的,免费的。一般地,uboot是可以修改的,但通常会使用soc厂商提供的uboot。

uboot的食用方法

        首先,uboot是一个工具。它可以帮助Linux驱动工程师进行Linux驱动开发,甚至是裸机开发(但是一般没有人这样做)。关于uboot,需要重点学习uboot的命令,uboot的操作方法,在uboot下配置nfs、tftp并且使用,以及uboot启动的时候打印出来的信息。学习玩这些以后,你可以利用uboot进行开发。

uboot的初次编译&烧写

        在获得uboot后,请把uboot拉到Ubuntu里面,把文件进行解压。这里要用到这一个软件。

        具体使用方法请参考根据各大教程进行学习,这里不再赘述。把uboot的压缩包拉到Ubuntu里面之后,需要对其进行解压。在终端里面,进入到uboot存放的文件夹,执行以下命令进行解压(我用的6ull使用这个包):

tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2

        然后,这个文件夹里面就会多了这一些内容。

         除了红色的哪个文件以外,其他都是uboot的源码。然后,需要对这里的文件进行编译。因为我用的板子是512MB的DDR+8GB的EMMC,那就依次执行下面的命令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 
mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12

        当然这样会累死人的,这里会弄一个shell文件,把这里的东西链接起来进行编译。内容是:

#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
                           mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12

        最后需要把这一个shell文件绿了,至于怎么绿了,执行sudo chomd 777命令就可以了。然后再执行这一个文件,就完成编译了,某粒子的教程都会讲这个怎么绿

        注意:不同板子不一样的,请跟着你的板子教程弄!!!!!!

        然后,进行编译,编译完成以后就会多了下面这么多的bin文件,这一些文件都是用来烧写到SD卡里面的。

        最后一步,还要再把imxdownload给绿了(提前把这个东西拷贝到uboot所在文件夹)。这个东西如果详细看裸机开发的话就会知道这个东西什么情况,就是一个官方脚本,用来烧写bin文件到SD卡的。

chmod 777 imxdownload

        把uboot.bin烧写到SD卡里面。然后就完成了烧写,SD卡插到板子上,给板子接上串口就可以看到uboot的启动信息。

/imxdownload u-boot.bin /dev/sdd

修改uboot配置

        如果你移植的是官方(芯片厂)的uboot,而不是用的开发板官方提供的uboot(用开发板官方的uboot可以跳过这一部分,有兴趣的可以细看一下)。那么,你需要生成一些uboot配置文件,让芯片厂的uboot适应你现在用的板子。其实市面上几乎所有的开发板的都是参照最原始的官方demo板设计。那么修改的内容主要是围绕端口的配置。如果你不修改配置,那么你后续使用uboot的话可能会出现各种问题。(例如不能ping网络)

添加配置文件

        理论上,这个芯片官方的uboot是可以在你手上的开发板上运行的,所以就直接在原来芯片官方提供的uboot上面做修改。先在之前编译好的uboot里面,找到configs这一个文件夹。然后进行如下操作:

cd configs
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_xxx_emmc_defconfig

        在configs里面,复制一个广泛的evk配置文件mx6ull_14x14_evk_emmc_defconfig并且从新命名为你喜欢的名字:mx6ull_xxx_emmc_defconfig。然后打开mx6ull_xxx_emmc_defconfig,做出下面修改(修改第一行和第四行,这个是修改后的):

CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_xxx_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_xxx_EMMC=y
CONFIG_CMD_GPIO=y

        在include/configs下面,添加你的开发板头文件。复制一份mx6ullevk.h并且改名为你喜欢的名字:mx6ull_xxx_emmc.h

cp include/configs/mx6ullevk.h mx6ull_xxx_emmc.h

        然后打开文件,把头文件的头部和尾部修改一下(主要是删除一些不要的功能):

#ifndef __MX6ULL_ALEITENK_EMMC_CONFIG_H
#define __MX6ULL_ALEITENK_EMMC_CONFIG_H

#include <asm/arch/imx-regs.h>
#include <linux/sizes.h>
#include "mx6_common.h"
#include <asm/imx-common/gpio.h>

/*......这里省略一些东西,其他参考改一下可以了*/
 
#define is_mx6ull_9x9_evk() CONFIG_IS_ENABLED(TARGET_MX6ULL_9X9_EVK)


#ifdef CONFIG_TARGET_MX6ULL_9X9_EVK
#define PHYS_SDRAM_SIZE SZ_256M
#define CONFIG_BOOTARGS_CMA_SIZE "cma=96M "
#else
#define PHYS_SDRAM_SIZE SZ_512M
#define CONFIG_BOOTARGS_CMA_SIZE ""
/* DCDC used on 14x14 EVK, no PMIC */
#undef CONFIG_LDO_BYPASS_CHECK
#endif

/* SPL options */
/* We default not support SPL
* #define CONFIG_SPL_LIBCOMMON_SUPPORT
* #define CONFIG_SPL_MMC_SUPPORT
* #include "imx6_spl.h"
*/
 
#define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
 
#define CONFIG_DISPLAY_CPUINFO
#define CONFIG_DISPLAY_BOARDINFO

/* Size of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN (16 * SZ_1M)

#define CONFIG_BOARD_EARLY_INIT_F
#define CONFIG_BOARD_LATE_INIT

#define CONFIG_MXC_UART
#define CONFIG_MXC_UART_BASE UART1_BASE

/* MMC Configs */
#ifdef CONFIG_FSL_USDHC
#define CONFIG_SYS_FSL_ESDHC_ADDR USDHC2_BASE_ADDR

/* NAND pin conflicts with usdhc2 */
#ifdef CONFIG_SYS_USE_NAND
#define CONFIG_SYS_FSL_USDHC_NUM 1
#else
#define CONFIG_SYS_FSL_USDHC_NUM 2
#endif
#endif

/* I2C configs */
#define CONFIG_CMD_I2C
#ifdef CONFIG_CMD_I2C
#define CONFIG_SYS_I2C
#define CONFIG_SYS_I2C_MXC
#define CONFIG_SYS_I2C_MXC_I2C1 /* enable I2C bus 1 */
#define CONFIG_SYS_I2C_MXC_I2C2 /* enable I2C bus 2 */
#define CONFIG_SYS_I2C_SPEED 100000

/*......这里省略一些东西,其他参考改一下可以了*/
 
#define CONFIG_SYS_MMC_IMG_LOAD_PART 1
 
#ifdef CONFIG_SYS_BOOT_NAND
#define CONFIG_MFG_NAND_PARTITION "mtdparts=gpminand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs) "
#else
#define CONFIG_MFG_NAND_PARTITION ""
#endif

#define CONFIG_MFG_ENV_SETTINGS \
"mfgtool_args=setenv bootargs console=${console},${baudrate} " \

/*......这里省略一些东西,其他参考改一下可以了*/

"bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} 
${fdt_addr};\0" \

#if defined(CONFIG_SYS_BOOT_NAND)
#define CONFIG_EXTRA_ENV_SETTINGS \
CONFIG_MFG_ENV_SETTINGS \
"panel=TFT43AB\0" \
/*......这里省略一些东西,其他参考改一下可以了*/

"bootz ${loadaddr} - ${fdt_addr}\0
#else
#define CONFIG_EXTRA_ENV_SETTINGS \
CONFIG_MFG_ENV_SETTINGS \
"script=boot.scr\0" \
/*......这里省略一些东西,其他参考改一下可以了*/

"fi;\0" \

#define CONFIG_BOOTCOMMAND \
"run findfdt;" \
/*......这里省略一些东西,其他参考改一下可以了*/

"else run netboot; fi"
#endif

/* Miscellaneous configurable options */
#define CONFIG_CMD_MEMTEST
#define CONFIG_SYS_MEMTEST_START 0x80000000
#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START +
0x8000000)

#define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR
#define CONFIG_SYS_HZ 1000

#define CONFIG_STACKSIZE SZ_128K

/* Physical Memory Map */
#define CONFIG_NR_DRAM_BANKS 1
#define PHYS_SDRAM MMDC0_ARB_BASE_ADDR

#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM
#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR
#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE

#define CONFIG_SYS_INIT_SP_OFFSET \
(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
#define CONFIG_SYS_INIT_SP_ADDR \
(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)

/* FLASH and environment organization */
#define CONFIG_SYS_NO_FLASH

/*......这里省略一些东西,其他参考改一下可以了*/

#define CONFIG_SYS_MMC_ENV_DEV 1 /* USDHC2 */
#define CONFIG_SYS_MMC_ENV_PART 0 /* user area */
#define CONFIG_MMCROOT "/dev/mmcblk1p2" /* USDHC2 */

#define CONFIG_CMD_BMODE

/*......这里省略一些东西,其他参考改一下可以了*/

/* NAND stuff */
#ifdef CONFIG_SYS_USE_NAND
#define CONFIG_CMD_NAND
#define CONFIG_CMD_NAND_TRIMFFS

#define CONFIG_NAND_MXS
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x40000000
#define CONFIG_SYS_NAND_5_ADDR_CYCLE
#define CONFIG_SYS_NAND_ONFI_DETECTION

/* DMA stuff, needed for GPMI/MXS NAND support */
#define CONFIG_APBH_DMA
#define CONFIG_APBH_DMA_BURST
#define CONFIG_APBH_DMA_BURST8
#endif

#define CONFIG_ENV_SIZE SZ_8K
#if defined(CONFIG_ENV_IS_IN_MMC)
#define CONFIG_ENV_OFFSET (12 * SZ_64K)
#elif defined(CONFIG_ENV_IS_IN_SPI_FLASH)
#define CONFIG_ENV_OFFSET (768 * 1024)
#define CONFIG_ENV_SECT_SIZE (64 * 1024)
#define CONFIG_ENV_SPI_BUS CONFIG_SF_DEFAULT_BUS
#define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS
#define CONFIG_ENV_SPI_MODE CONFIG_SF_DEFAULT_MODE
#define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED
#elif defined(CONFIG_ENV_IS_IN_NAND)
#undef CONFIG_ENV_SIZE
#define CONFIG_ENV_OFFSET (60 << 20)
#define CONFIG_ENV_SECT_SIZE (128 << 10)
#define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE
#endif

/* USB Configs */
#define CONFIG_CMD_USB
#ifdef CONFIG_CMD_USB
#define CONFIG_USB_EHCI
#define CONFIG_USB_EHCI_MX6
#define CONFIG_USB_STORAGE
#define CONFIG_EHCI_HCD_INIT_AFTER_RESET
#define CONFIG_USB_HOST_ETHER
#define CONFIG_USB_ETHER_ASIX
#define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW)
#define CONFIG_MXC_USB_FLAGS 0
#define CONFIG_USB_MAX_CONTROLLER_COUNT 2
#endif

#ifdef CONFIG_CMD_NET
#define CONFIG_CMD_PING
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MII
#define CONFIG_FEC_MXC
#define CONFIG_MII
#define CONFIG_FEC_ENET_DEV 1

#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x2
#define CONFIG_FEC_XCV_TYPE RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x1
#define CONFIG_FEC_XCV_TYPE RMII
#endif
#define CONFIG_ETHPRIME "FEC"

#define CONFIG_PHYLIB
#define CONFIG_PHY_MICREL
#endif

#define CONFIG_IMX_THERMAL

#ifndef CONFIG_SPL_BUILD
#define CONFIG_VIDEO
#ifdef CONFIG_VIDEO
#define CONFIG_CFB_CONSOLE

#define CONFIG_VIDEO_MXS
#define CONFIG_VIDEO_LOGO
#define CONFIG_VIDEO_SW_CURSOR
#define CONFIG_VGA_AS_SINGLE_DEVICE
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
#define CONFIG_SPLASH_SCREEN
#define CONFIG_SPLASH_SCREEN_ALIGN
#define CONFIG_CMD_BMP
#define CONFIG_BMP_16BPP
#define CONFIG_VIDEO_BMP_RLE8
#define CONFIG_VIDEO_BMP_LOGO
#define CONFIG_IMX_VIDEO_SKIP
#endif
#endif

#define CONFIG_IOMUX_LPSR

/*......这里省略一些东西,其他参考改一下可以了*/

#endif

        具体操作还需要根据你所用开发板的教程,这个并不特别针对某一块板子。

        这里,还需要添加板级文件。在board/freescale里面,复制 mx6ullevk,将其重命名为你喜欢的名字: mx6ull_xxx_emmc。

cd board/freescale/
cp mx6ullevk/ -r mx6ull_xxx_emmc

        然后,进去修改以下文件名:

cd mx6ull_xxx_emmc
mv mx6ullevk.c mx6ull_xxx_emmc.c

修改makefile

        打开makefile文件,修改内容:

# (C) Copyright 2015 Freescale Semiconductor, Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#

obj-y := mx6ull_xxx_emmc.o

extra-$(CONFIG_USE_PLUGIN) := plugin.bin
$(obj)/plugin.bin: $(obj)/plugin.o
$(OBJCOPY) -O binary --gap-fill 0xff $< $@

        obj-y那一行要注意,如果不把他修改就没办法编译mx6ull_xxx_emmc.c这个文件。

修改imximage.cfg

        修改mx6ull_xxx_emmc 目录下的 imximage.cfg 文件。将 imximage.cfg 中的下面一句:

PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000

        修改为:

PLUGIN board/freescale/mx6ull_xxx_emmc /plugin.bin 0x00907000

修改imximage.cfg

        修改如下:

if TARGET_MX6ULL_xxx_EMMC

config SYS_BOARD
default "mx6ull_xxx_emmc"

config SYS_VENDOR
default "freescale"

config SYS_SOC
default "mx6"

config SYS_CONFIG_NAME
default "mx6ull_xxx_emmc"

endif

修改MAINTAINERS

        修改为:

MX6ULL_XXX_EMMC BOARD
M: Peng Fan <peng.fan@nxp.com>
S: Maintained
F: board/freescale/mx6ull_xxx_emmc/
F: include/configs/mx6ull_xxx_emmc.h

添加图形界面

        在arch/arm/cpu/armv7/mx6/Kconfig里面修改一下:

config TARGET_MX6ULL_XXX_EMMC
bool "Support mx6ull_xxx_emmc"
select MX6ULL
select DM
select DM_THERMAL

/*........这里省略一下*/
/*在endif前面面加上以下这一句*/
source "board/freescale/mx6ull_xxx_emmc/Kconfig"

        需要添加的而文件已经添加了好,剩下的就是开始修改配置,但是修改前需要编译一下。

修改配置内容

        这里需要参考你的开发板进行修改,因为每一块板子的硬件部分都是不一样的。如果你插上LCD屏幕不能正常显示,说明你的uboot配置屏幕的端口不对。如果不能ping上网络,则说明你的uboot里面的网口部分引脚,驱动配置需要修改。

uboot的启动

        上一部分讲了uboot是怎样编译,怎样烧写到SD卡上面的。这里讲一下uboot启动之后是长啥样的。在接了串口打印之后,给板子上电可以看到这样的东西。

        记得!看到黄色标出的哪一行,数到0之前按一下回车。理论上是会跳转到不知道什么地方去的,因为你的板子EMMC或者NADA里面没有东西(跟你玩过单片机的bootloader一样【最后执行一个jump跳转到指定地址执行),如果后面没有写入任何东西,就会不知道跳到那里去】,但实际上我也没试过,反正我都摁了进入uboot。

        下面大概讲一下里面说的是什么东西。第一行是uboot的版本号和编译时间。第二行是你用的板子主频。第三行是工作环境温度。第四行的是你用的哪一家板子对应自己修改过的uboot。然后就是描述了一下当前板子一些硬件的情况......

        记得板子上拨码开关弄到SD卡启动。

uboot命令

        在uboot里面,也有一些shell命令。和Linux一样,输出一个“help”就可以把全部有的命令用法发出来。因为uboot的功能是可以配置的,所以这一个命令是可以配置的。一般地,使用的命令都是很常见的命令,都包括在soc厂商提供的uboot里面。其实这里,只要关注这一些命令是怎样使用的即可。(uboot也可以使用TAB补全的功能)

        打个比如,我想知道“bootm”这一个命令是怎样使用的。那么,我们只需要在命令行里面输入:

?bootm

信息查询命令:bdinfo\printenv\version

        这一些都是常见的信息查询命令。

        先看关于:bdinfo命令。这个命令用于查询板子的信息,包括DRAM的起始地址和大小、启动参数保存地址、波特率、sp(栈堆指针)起始地址。

        关于:printenv命令。这个命令用于输出环境变量信息(很多),只节选了一部分。

 

        注意,这些变量都是一些字符串。

        关于:version就不说了,打印版本号。

环境变量操作命令:setenv\saveenv

        这两个命令用于修改环境变量参数和保存环境变量参数的。

        那么,需要修改一个叫“bootdelay”的变量时,应该这么用:

setenv bootdelay 5

        上述为,把bootdelay修改为4。如果还要修改其他参数,就继续使用这个命令格式即可。当需要修改的环境变量参数都修改完成之后,需要保存。如果需要修改的环境变量有好几个参数,那么需要这样操作:

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'

        bootargs这个变量有四个参数。每个参数之间用空格隔开(“console=ttymxc0,115200”、“root=/dev/mmcblk1p2”、“rootwait”和“rw”),然后在外边用一个单引号包括在一起。最后进行参数保存,执行:

saveenv

        因为修改后的参数保存在SD卡里面,会标出MMC(0),如果保存到EMMC里面,则标出MMC(1),如果是保存到NADA里面,则显示保存在NADA。保存之后如下图所示。 

        复位一下板子,就会又打印出新的uboot信息。

        另外可以新建一些变量(就是在boot打印出来的),例如作者名字。

setenv author xxx
saveenv

        然后使用printenv命令就可以看到。如果需要把这个变量删除,那么把参数替换成空格保存即可完成删除。

内存操作命令:md\nm\mm\mw\cp\cmp

        这一些命令是对DRAM里面的数据进行操作的命令。可能到这里你不明白,uboot不是在SD卡里面吗?为什么操作的是DRAM(我当时也想了一下)。这里要说一下,就类似与计算机原理的知识。这里的SD相当于存放程序指令的地方,要运行的时候是要把数据挪到内存里面存放的,我们修改的是内存里面的数据,而不是把卡里面的指令给改了。所以是操作内存。

        就好像这个哈佛结构的框架,修改的是数据顶多是数据存储器里面的东西。总不能把自己的执行指令给改了,就算改对了算你走运,改错了直接跑飞。

        md:用于显示内存里面的值,格式如下:

md[.b, .w, .l] address [# of objects]

        其中:.b .w .l三个东西对应的是byte、word、long,也就是分别以1字节、2字节、4字节来显示内存值,后面很多参数也会用到,注意一下。address就是要查看的内存起始地址。[# of objects]例如这么用:

md.b 80000000 14

        表示想看0x80000000开始的地方后面连续20(十进制的20,在十六进制里面显示为14)个byte显示的内容。下面三种方式查询如下:

md.b 80000000 10
md.w 80000000 10
md.l 80000000 10

        结果出来的是这个:

        大概就这样吧,这个我也不太理解这个功能有什么用(知道可以这么用,但是暂时还不知道这些命令存在的价是什么,可能调试的时候用的)。

        nm:用于修改指定地址内存的数值,格式如下:

nm [.b, .w, .l] address

        和上面一样,三个‘·字母’表示三个长度。具体用法:

nm.l 80000000

        然后,回复如下:

         在这里,”?“后面输入你要修改的数值即可,然后按下回车。再需要在”?“后面输入一个‘q’即可退出并且保存。其中,0500e031是原来这个地址里面的数据。

         md一下就可以看到数值被修改了。

        mm:如果需要批量修改指定内存里面的数值,用这个命令。格式如下:

         因为l是3个内存块的(12个字节),所以一下子就修改了三个,其他如此类推。

        mw:用于填充一段内存,格式如下:

mw [.b, .w, .l] address value [count]

        例子如下:

mw.l 80000000 0A0A0A0A 10

        l类型的修改,在0x80000000开始,10个.l长度的内存写入0A0A0A0A。然后修改之后是这样的:

         cp命令:用于把一段数据拷贝到另外一段数据的命令,格式如下:

cp [.b, .w, .l] source target count

        例子如下:

cp.l 80000000 80000100 10

        结果如下:

         这个开始懒得写了,后面有时间补几张图。

        cmp:用来对比两段数据是不是一样的,格式如下:

cmp [.b, .w, .l] addr1 addr2 count

        打个比如,我要对比0x80000000 和 0X80000100这两段数据是不是相同的,那么执行以下命令,且结果如下:

cmp.l 80000000 80000100 10

         如果不一样会告诉你不一样的。

网络操作命令:ping\dhcp\nfs\tftp

        重点来了,这一部分十分重要。uboot是支持网络的,这一个功能十分重要!!!!!!因为后面移植Linux内核的时候需要反复用到uboot的网络功能进行调试。接下来将会讲一下这几个常用的网络命令是怎样使用的。

前情提要:

        有几个知识需要提前知道的。首先,你的板子上有一个以太网的端口。它的样子大概长成下面的样子 。常见的是RJ-45接口,就是路由器上面用的网线,那一种接口。

         然后,你需要知道一个叫“局域网”的东西。因为使用uboot的网络功能,需要你的板子和你用来开发的主机处于同一个局域网。可能到这里你还不理解的话,可以看下面示意图:

         如上图所示,你可以这样看。在局域网里面有两台开发主机和路由器相连,其中开发主机1和路由器通过WiFi连接(现在普遍路由器都有WiFi功能的),开发主机2和路由器通过网线相连,开发板和路由器通过网线相连。那么,开发板、开发主机1、开发主机2、路由器就处于同一个局域网里面。在这里,开发板、开发主机1、开发主机2都有自己的ip地址(开始涉及到tcp/ip协议的知识了,不用深入,想了解一下即可)

        假设,在这个局域网中,几个设备的性能如下:

设备开发板开发主机1开发主机2
性能

        此时,你的Linux内核是在开发主机1上的。现在,你的开发板需要把Linux内核拷贝过来开发板上面调试,就需要你的开发板和开发主机1的网络是通的。如果你用的开发主机1性能特别拉跨,编译代码很慢,刚好你有一台性能厉害的开发主机2。那么,你可以通过File Zilla把要编译的代码拉到开发主机2上编译完成之后再拷回来。现实中很多公司都是这么干,公司里面配备一台很好的电脑(当成服务器来用),就是用来干这个活的。由于一些不可控因数,公司里面的局域网有可能和外网是断开的,于是就变成了这一个样子。当然,公司里面不可能只有你自己一台电脑和一块开发板。所以,务必确保局域网里面的每个设备都是ip唯一的。

        ping:一个用来检测当前设备和目标设备网络是否畅通的命令,也就是人们口中说的“ping一下”,就是说的这一个命令。假如开发板需要看自己和开发主机1(假设其ip为:192.168.1.253)网络是否畅通,在uboot里面执行以下命令:

ping 192.168.1.253

        如果通了,会有以下信息打印出来:

         意思是说,你所ping的ip地址对应的设备是存活的。那就可以通过uboot,把主机上的代码拷贝过来调试了。注意一下,别拿你的主机ping在uboot里面的开发板,ping不通的。

        dhcp:开发板用来查询路由器给自己分配了个什么ip。例如,执行之后可以看到:

         你当前板子的ip是:192.168.1.137

        注意一下,别把板子和你的开发主机直接用网线连在一起然后dhcp。没用的,你的电脑不是路由器,不会给你的板子分配ip。

        到了超级重点的地方!!!!!!

        nfs:通过网络用来把开发主机上的代码拷贝过来的命令。执行格式例子如下:

nfs 80800000 192.168.1.253:/home/Chapman/linux/nfs/zImage

        对这个命令有如下解释:

nfs80800000192.168.1.253/home/.../nfs/zImage
nfs命令代码拷贝到内存的起始地址开发主机的ip需要从主机拷贝过来的文件所在位置以及名字

        来到这里应该也知道这个命令是怎么用的吧?就这样了。注意:存放nfs共享资源的文件夹名字为:nfs。

        nfs(Network File System)网络文件系统,通过nfs,不同设备之间可以通过网络进行资源共享。因为嵌入式Linux和单片机不一样(没有像JLINK等的专用下载器,也没有个像样的IDE,也没有很好的烧写算法),复杂多了,所以就使用这一种方式进行开发。

        这里解释一下,zImage是Linux内核的一个镜像文件。

        命令执行完成之后,有如下内容打印:

         到这里,文件拷贝完成了。

        tftp:它的作用和nfs是一样的,但是用的是nfs比较多,因为tftp太麻烦。首先你的开发主机里面的Ubuntu需要安装好tftp服务器,并且搭建好。执行以下命令安装(在主机Ubuntu里面):

sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd

        和nfs一样,tftp也要给弄一个文件夹,然后用来存放共享的资源。但是,记得把你新建用来存放共享资源的文件夹tftpboot(文件夹起这一个名字)给绿了(就是给这一个文件夹权限)。

mkdir /home/Chapman/linux/tftpboot
chmod 777 /home/Chapman/linux/tftpboot

        最后,还要配置tftp(真麻烦),创建这一个文件夹:/etc/xinetd.d用来存放配置文件的。然后配置文件名字为:tftp,内容如下(用VIM创建文件并且编写):

server tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /home/Chapman/linux/tftpboot/
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}

        然后启动tftp服务,使用下面命令:

sudo service tftpd-hpa start

        然后就会生成一个文件:tftpd-hpa,这个时候需要进入/etc/default里面把tftpd-hpa这一个文件给修改了,内容如下:

# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/Chapman/linux/tftpboot"
TFTP_ADDRESS=":69" 
TFTP_OPTIONS="-l -c -s"

        TFTP_DIRECTORY里面描述的就是前面建立用来共享资源的文件夹。最后重启tftp服务器:

sudo service tftpd-hpa restart

        tftp搭建好之后,把需要共享的文件放到tftpboot这一个文件夹里面,并且把他们给绿了。

cp zImage /home/Chapman/linux/tftpboot/   //拷贝文件到目标文件夹
cd /home/Chapman/linux/tftpboot/          //进入目标文件夹
chmod 777 zImage                              //把文件给绿了

        然后,在开发板子的uboot里面执行命令:

tftp 80800000 zImage

        完成文件拷贝!

nfs和tftp问题汇总:

1)如果没有给最大权限,会报错。

2)如果网络不通,也会报错。网络不通一般有两个情况:一个是真的物理上网络不通(网线不行或者wifi没信号),一个是ip地址冲突(ip重复)或者错了。

BOOT操作命令:bootz\bootm\boot

        上一部分讲到怎样把开发主机上的Linux镜像文件拉到开发板子上面。uboot的本质是引导Linux启动,所以肯定需要有命令来引导Linux启动。注意,这里会引入两个个新的概念——设备树和根文件但是先了解知道有这两个东西就可以。设备树、根文件后面会再讲,总之你先知道zImage(内核镜像)、设备树文件(通过网络共享nfs或者tftp从开发主机传输到开发板子上面)两个东西放一块组成启动Linux所需要的、最基础的资料。

        我们先从从开发主机拷贝过来的文件是Linux内核镜像和设备树文件。首先,需要选择正确的设备树文件,把文件拖到开发主机的Ubuntu上面的共享文件夹存放,然后发送到开发板子上。因为我用的是EMMC版本的开发板子,查过了资料对应的是一个文件:

imx6ull-14x14-emmc-7-1024x600-c.dtb
         通过 File Zilla 发送到Ubuntu里面的nfs文件夹, 然后把它们全绿了。 再到开发板上面,通过nfs把这两个文件都拉过来,执行熟悉的命令:
nfs 80800000 192.168.1.253:/home/Chapman/linux/nfs/zImage
nfs 83000000 192.168.1.253:/home/Chapman/linux/nfs/imx6ull-14x14-emmc-7-1024x600-c.dtb

        这时候,开发板子上的DRAM已经有了镜像和设备树文件了。其中镜像在DRAM的起始地址为:0x80800000,而设备树文件在DRAM的起始地址为:0x83000000。

        bootz:一个uboot用于启动镜像文件的命令,使用命令格式例子如下:

bootz 80800000 - 83000000

        命令的意思是,我需要启动地址在0x80800000到0x83000000的关于Linux启动的镜像文件(刚好把两个东西都包含进去。)有下面的图:

        然后就是往下刷启动信息。这个时候,等待Linux启动完成。 

        假如,不是从网络传输过来的镜像启动Linux,而是从EMMC或者NADA Flash里面启动的话,需要使用fatls命令查看EMMC分区1有没有镜像文件和设备树文件。则使用fatload命令把文件拷贝到DRAM里面再启动即可。但是,这里不打算讲对EMMC、NADA、FAT、EXT的命令,主要原因有以下:

1)这些命令都是对存储数据的操作,怕你们把数据弄没了。

2)如果纯粹靠网络启动系统,没必要,上面这一些够用了。

        所以有时间就补上,没有就算了。

        bootm:这个和bootz作用类似,只不过用来启动非镜像文件的。假如存放在DRAM指定地址的文件不是镜像,则使用这个命令启动Linux。过程和bootz一样,这里不再赘述。

        boot:这一个东西也是用来启动Linux系统的,只不过,这一个命令会根据你在uboot里面设置的环境变量进行系统启动。再这个命令下,uboot会根据bootcmd里面设置的环境变量参数启动系统。所以执行这个命令启动系统的时候,需要设置好系统环境变量。假设,已经在开发主机的网络共享文件夹里面存放好Linux系统启动需要的文件(并且已经把他们绿了)。执行bootcmd命令设置好变量,然后用boot启动系统。

setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; 
bootz 80800000 - 83000000'
saveenv
boot

         这个bootcmd是一次性把下载和启功一块执行的,所以比较省事。

uboot阶段性总结

        恭喜你看到了这里,到了这里,你已经知道了至少一种如何在开发板上面启动Linux系统的方式(最适合在开发调试阶段的启动方式)。然后,我再重申一下这一个过程,让你有更加深入的理解。

         在Ubuntu里面准备好全部所需要的文件,然后板子的uboot通过网络把文件拷贝到DRAM里面,再执行系统启动,就是这一个完整的启动过程。

        注意一点,麻烦自己跑一遍,并且用官方提供的uboot和Linux镜像系统跑一下这个流程。下面是我自己跑的:

        因为家里网线被扔了,我这个博客暂时写不下去了!真是要命!买了网线再更新。

系统不能完全启动

        来到了这里,你会发现系统在boot命令之后启动运行几秒就卡死。是因为前面只是实现如何从uboot里面启动系统,在复制挂在镜像和设备树的时候,没有把根文件带上。而根文件是系统启动的基础之一,所以系统启动到一半就卡住了。后面会继续讲根文件和内核的知识。

Linux内核移植

        前面讲了uboot是什么东西,而且怎么用。这一部分继续讲官方系统内核如何移植到你的开发板子上。如果你是一个有经验的工程师,你要设计一块电路板用到你从来没有接触过的芯片。那么关于这个芯片的外围电路设计,你最好的办法是参考(照搬,然后小改)官方方案。因为没人比官方更懂这个芯片。

        同样,厂商官方每推出一款芯片,就肯定有这一块板的demo板。而几乎所有用户都是根据这一块demo板子进行小改,去开发自己的产品。在这里将会参照官方给的内核,对里面内容进行小改,以适应手上的板子,有时候可能甚至不用修改直接能用。

系统内核获得&编译

        关于系统内核的获得方式,可以从你买的板子的找资料里面找,或者在芯片厂家官网里面找产出来。一般地,下载回来的系统内核是一个压缩包,这时候需要你把压缩包发送到Ubuntu里面并且解压,解压出来的是一整个文件夹。然后在vscode里面新建一个工程,并且把整个文件夹拉到里面。(VScode请自行安装,Linux版本的,不要钱的

        在VScode里面打开整个文件夹,这样就可以了。

         接下来需要对文件进行编译。不过需要有以下步骤:

1)在编译之前,还有一个事情要注意。就是建立一个件.vscode的文件夹,然后在文件夹里面创建一个settings.json文件。这一个文件适用于屏蔽你所用的电路板上用不到的框架相关文件、配置、设备树文件。具体操作,参照你所用的教程。

2)这一步看习惯,可以创建一个sh脚本,用于编译。

#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

        然后执行这一个脚本即可。

移植测试

        编译完成以后,会生成一个zImage和设备树。然后把镜像和设备树挂在到板子上,用前面讲的uboot启动。因为我用的是emmc的6ull的板子,所以设备树选imx6ull-14x14-evk-emmc.dtb,否则选imx6ull-14x14-evk.dtb。

        因为还没挂载根文件目录,所以还是启动到一半卡住。 能启动,说明系统和设备树设置匹配我现在用的EMMC&512MBDDR版本的板子。

系统配置文件准备

        如果很不幸,你需要配置一下移植大的系统。那么,你需要准备几个配置文件。在工程文件夹里面的arch/arm/configs下,把imx_v7_mfg_defconfig复制一份,并且重新命名。假设命名为:

imx_v7_xxx_defconfig

        以后你就在这一个文件里面配置你的开发板,并且使用以下命令来配置你的内核文件。

make imx_v7_xxx_defconfig

设备树文件准备

        在arch/arm/boot/dts里面,复制一份imx6ull-14x14-evk.dts,然后命名为:

imx6ull-xxx.dts

        然后打开这个为文件:arch/arm/boot/dts/Makefile,在:

dtb- $(CONFIG_SOC_IMX6ULL)

        下面配置,在里面加入你前面弄的imx6ull-xxx.dts内容如下:

dtb-$(CONFIG_SOC_IMX6ULL) += \
imx6ull-14x14-ddr3-arm2.dtb \
imx6ull-14x14-ddr3-arm2-adc.dtb \
imx6ull-14x14-ddr3-arm2-cs42888.dtb \
imx6ull-14x14-ddr3-arm2-ecspi.dtb \
imx6ull-14x14-ddr3-arm2-emmc.dtb \
imx6ull-14x14-ddr3-arm2-epdc.dtb \
imx6ull-14x14-ddr3-arm2-flexcan2.dtb \
imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \
imx6ull-14x14-ddr3-arm2-lcdif.dtb \
imx6ull-14x14-ddr3-arm2-ldo.dtb \
imx6ull-14x14-ddr3-arm2-qspi.dtb \
imx6ull-14x14-ddr3-arm2-qspi-all.dtb \
imx6ull-14x14-ddr3-arm2-tsc.dtb \
imx6ull-14x14-ddr3-arm2-uart2.dtb \
imx6ull-14x14-ddr3-arm2-usb.dtb \
imx6ull-14x14-ddr3-arm2-wm8958.dtb \
imx6ull-14x14-evk.dtb \
imx6ull-14x14-evk-btwifi.dtb \
imx6ull-14x14-evk-emmc.dtb \
imx6ull-14x14-evk-gpmi-weim.dtb \
imx6ull-14x14-evk-usb-certi.dtb \
imx6ull-xxx.dtb \
imx6ull-9x9-evk.dtb \
imx6ull-9x9-evk-btwifi.dtb \
imx6ull-9x9-evk-ldo.dtb

        这样,编译的时候就可以编译出imx6ull-xxx.dtb了。

        然后进行编译测试。

CPU主频、从存储器、网络修改

        在这一部分主要间接主频、存储器、网络修改。修改主频,可以提高或者降低一下当前板子的工作频率。修改存储器,这里主要是EMMC,为了提高存储器读写速度。网络修改和之前的uboot修改网络接口差不多,因为使用的网口和官方demo板的网口不一样,因此需要修改网络驱动。最后重新编译即可,这里需要参照你所用的板子具体情况修改,这里不提供参考。

根文件构建

        前面已经把uboot、Linux内核、设备树挂载到开发板子上了。这一部分主要讲一下之前一直没有讲的根文件系统。

        在Linux启动的时候,系统们没有读取到根文件的时候,系统会直接崩溃,这就是之前已经见过的情况:系统启动到一半卡死了。根文件对于系统来说很重要(没有根文件,Linux系统就无法正常启动),例如在Linux终端里面,使用的一些shell命令,就是存放在根文件里面。如果没有根文件,这一些命令就没办法使用。

根文件结构

        根文件,准确来说,是一个文件夹,里面放了很多很重要的文件。下图是一个根文件夹里面放的东西。

/bin 

        bin文件夹,主要放的是可执行文件,例如前面说的shell命令文件,所有用户都可以使用以下文件。

/dev

        这一个文件夹里面,放的都是驱动类别(硬件驱动)文件。

/etc

        这一个文件夹里面,放的都是配置文件。

/lib

        这一个文件夹里面,放的都是库文件。

/mnt

        这一个文件夹里面,放的的都是挂在文件(后面的nfs、tftp会用到)。

/proc

        这一个文件夹是虚拟的,用来挂在临时运行的文件新信息。

/usr

        这一个文件夹里面,放的是在Linux下安装的软件。

/var

        这一个文件夹里面,放的是可改变的数据。

/sbin

        这里也是存放一些可执行文件,只不过只能管理员使用。

/sys

        这个文件夹和/proc类似,也是用于系统文件挂载。

/opt

        一个可供用户自己选择的文件存放区。

        一般地,根文件系统也叫rootfs。在后面的测试中通过网络(nfs、tftp),将主机上创建好的根文件系统挂载到开发板的系统上。

Busy Box

        这一个工具用于生成Linux根文件系统的工具。一般你所用的开发板官方资料里面会提供,因为这个工具没有暂时国内官网,你可能下载不了(很慢)。下面讲一下这一个工具是怎样使用的。

编译Busy Box

        当你拿到这一个工具的压缩包时候,使用File Zilla将压缩包发送到你的Ubuntu主机里面。然后,在先前搭建好的nfs文件夹里面新建一个叫“rootfs”的文件夹:

mkdir rootfs

        假设我下载的是busybox-1.29.0.tar.bz2 ,那么对这个压缩包进行解压。

tar -vxjf busybox-1.29.0.tar.bz2

        解压之后,会有这么多的文件:

         和uboot和Linux内核一样,编译一个文件,需要修改其Makeflie。打开Makeflie文件,做以下修改(在164和190行修改)。

164 CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-
x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
......
190 ARCH ?= arm

让Busy Box支持中文

        打开/libbb/printable_string.c,找到一个叫printable_string的函数,在对应行对代码进行以下修改:

12 const char* FAST_FUNC printable_string(uni_stat_t *stats, const char
*str)
13 {
14 char *dst;
15 const char *s;
16
17 s = str;
18 while (1) {
...... 
30 if (c < ' ')
31 break;
32 /* 注释掉下面这个两行代码 */
33 /* if (c >= 0x7f)
34 break; */
35 s++;
36 }
37
38 #if ENABLE_UNICODE_SUPPORT
39 dst = unicode_conv_to_printable(stats, str);
40 #else
41 {
42 char *d = dst = xstrdup(str);
43 while (1) {
44 unsigned char c = *d;
45 if (c == '\0')
46 break;
47 /* 修改下面代码 */
48 /* if (c < ' ' || c >= 0x7f) */
49 if( c < ' ')
50 *d = '?';
51 d++;
52 }
......
59 #endif
60 return auto_string(dst);
61 }

        接着,找到以下函数对其修改:

1003 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
1004 {
1005 char *dst;
1006 unsigned dst_len;
1007 unsigned uni_count;
1008 unsigned uni_width;
1009
1010 if (unicode_status != UNICODE_ON) {
1011 char *d;
1012 if (flags & UNI_FLAG_PAD) {
1013 d = dst = xmalloc(width + 1);
......
1022 /* 修改下面一行代码 */
1023 /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
1024 *d++ = (c >= ' ') ? c : '?';
1025 src++;
1026 }
1027 *d = '\0';
1028 } else {
1029 d = dst = xstrndup(src, width);
1030 while (*d) {
1031 unsigned char c = *d;
1032 /* 修改下面一行代码 */
1033 /* if (c < ' ' || c >= 0x7f) */
1034 if(c < ' ')
1035 *d = '?';
1036 d++;
1037 }
1038 }
......
1044 return dst;
1045 }
......
1047
1048 return dst;
1049 }

配置Busy Box

        和前面的uboot、Linux内核一样,有一些选项需要另外配置修改。返回出去Busy Box的文件夹,使用以下命令对其进行配置:

make menuconfig

        选择以下的路径:Location-> Settings -> Build static binary (no shared libs)。这一个选项是决定Busy Box是动态还是静态编译的。这里选择动态编译。

         继续配置以下:Location-> Settings -> vi-style line editing commands。选择vi-style line editing commands 。

        继续下面配置:Location-> Linux Module Utilities -> Simplified modutils。取消选中:Simplified modutils。

         继续配置:Location-> Linux System Utilities -> mdev (16 kb)。需要全部选中:

         最后使能unicode编码,找到以下选项:Location: -> Settings -> Support Unicode //选中 -> Check $LC_ALL, $LC_CTYPE and $LANG environment variables //选中。

         如果你需要其他的功能的话,可以另作修改,建议跟住你所用开发板官方教程进行设置,以防出现一些bug。

编译Busy Box

        编译之后,需要把文件放到之前建立的rootfs里面:

make
make install CONFIG_PREFIX=/home/Chapman/linux/nfs/rootf

        编译完成后:

         随后在nfs里面的rootfs里面找到编译好的文件了。

         在这一个文件夹里面,有bin、sbin、usr三个目录和linuxrc这一个文件。当Linux内核启动完成之后,就会移动到用户态。如果前面uboot设置时,设置bootargs为init=/linuxrc,那么linuxrc将会作为用户的init程序。

添加lib库

        Linux中的应用程序一般都需要动态库(如果你没编译成静态库的话),因为静态库编译出来太大了,时不建议使用的。在rootfs里面建立一个lib文件夹:

mkdir lib

        文件夹建立好了,需要往里面添加一些库文件。lib的库文件是来自前面搭建好的交叉编译器环境下编译出来的文件。返回到rootfs目录下,在/usr/local/arm/这一个目录里面,全部文件拷贝到lib里面(因为里面东西太多了,而且还不知道哪个可以用,那个不需要)。进入这个目录:

/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib

        在这个目录下面,有很多*so*、.a文件都是库文件,将它们全部拷贝到lib里面:

cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/lib/ -d

        在lib里面有这么多文件:

向“usr/lkib”添加文件

        在usr这个目录里面,添加一个lib目录。将下面目录的文件:

/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linuxgnueabihf/libc/usr/lib

        然后把所有库文件拷贝到usr/lib里面:

cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d

        这里默认你是使用EMMC的板子,因为可能不用EMMC你的板子容量不够大了。

挂载测试

        到了这里,可以用nfs测试一下前面制作的根文件系统能不能使用。给板子插了网线,上电之后进入uboot。重新设置bootargs环境变量,执行下面的命令:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>

        其中<server-ip>是你的Ubuntu主机ip,<root-dir>是你存放根文件的路径,<nfs-options>设置为:proto=tcp rw,<client-ip>是你的板子ip,<server-ip>服务器ip,<gw-ip>网关IP,<netmask>子网掩码:255.255.255.0,<hostname>这个可以不设置,可以空着,<device>开发板网卡名,<autoconf>设置off,<dns0-ip>这个可以不设置,可以空着,<dns1-ip>这个可以不设置,可以空着。

        然后保存:

saveenv

        然后启动系统:

         到了这里,问题应该是没什么问题了,但是这一个根文件还是不够完善,需要你按照你的教程里面修改添加不同的文件,这里不再赘述。下面是本部分的流程总结:

系统移植阶段性总结

        来到这里,整个Linux系统的移植基本完成,下面就是要开始Linux驱动的学习。前面这么大篇幅的工作都是为了接下来学习Linux驱动学习而准备的。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RK3588是一款高性能的芯片,具有强大的图形处理能力,适用于开发高性能应用和显示设备。在开机过程中,可以使用RK3588的图形处理器来显示Qt界面。 Qt是一种跨平台的应用程序框架,提供了丰富的图形界面组件和工具,开发者可以利用Qt轻松地创建各种界面风格的应用程序。在RK3588上,我们可以使用Qt来设计和开发一个用户友好的界面,在开机时显示出来。 在实现LCD开机显示Qt界面的过程中,首先需要确保RK3588芯片正常工作,并已经正确连接到LCD显示屏上。然后,我们需要安装Qt开发环境,并编写一个Qt应用程序来实现我们想要的界面。 接下来,我们可以使用RK3588的图形处理器来渲染并显示Qt界面。通过调用Qt提供的绘制API,我们可以在LCD显示屏上绘制出我们设计的界面元素,例如按钮、文本框、图片等。然后,通过调用Qt的显示API,将绘制好的界面显示到LCD屏幕上。 在开机过程中,我们可以在RK3588的启动脚本中添加相应的命令,以在启动时启动Qt应用程序,并将其显示到LCD显示屏上。这样,当RK3588芯片启动成后,LCD屏幕上就会显示出我们设计的Qt界面。 总结来说,通过利用RK3588芯片的图形处理能力和Qt开发环境,我们可以实现LCD开机显示Qt界面。这样做可以为用户提供一个友好的交互界面,同时展示RK3588芯片的强大性能和多功能的特点。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值