新闻详情

新闻详情

首页 / 资讯中心 / 详情

嵌入式Linux内核移植实战:基于i.MX6ULL的配置、编译与设备树启动

发布时间:2026/9/29 1:46:10来源:尧图网络
嵌入式Linux内核移植实战:基于i.MX6ULL的配置、编译与设备树启动
先回答一个很多人第一次听到“内核移植”都会问的问题内核移植是不是要从零写一个 Linux 内核还真不是。芯片厂商和开发板厂商通常已经帮你做好了 80% 的底层适配你要做的是把这一份“为某款 CPU 平台定制好的内核工程”拿到自己的板子上改配置、裁剪、重新编译让它能稳定启动、外设可用、能跑你的业务程序。这个过程中你会接触到交叉编译、设备树、defconfig、U-Boot 启动参数、根文件系统挂载等一整套嵌入式 Linux 开发的基础操作。我这次用的平台是一块基于 NXP i.MX6ULL 的 LV12 开发板核心板加底板的结构属于嵌入式 Linux 学习里非常典型的入门级硬件。i.MX6ULL 是 Cortex-A7 单核处理器主频 800MHz 级别资源不算奢华但是做 Linux 内核移植、驱动开发、项目原型验证绰绰有余。整个移植过程我整理成了这一篇笔记记录的是“LV12 linux 内核移植”这个系列第 10 期的完整过程从源码准备、环境搭建到内核配置、编译、下载启动再到常见问题排查。如果你是刚开始接触嵌入式 Linux 的开发者这篇内容可以帮你省下不少翻论坛和手册的时间。1. 移植前先弄清三件事移植的本质、平台选型、环境准备1.1 到底在移植什么“内核移植”四个字听起来像是一项浩大的工程但放到现代嵌入式 Linux 开发里它的核心动作其实是配置和编译。Linux 内核是一个体积很大的软件项目它通过 Kconfig 和 Makefile 这套机制允许你在编译之前先通过配置文件决定哪些功能要、哪些功能不要然后针对你所选的 CPU 架构和具体板卡生成对应的二进制镜像。站在 ARM Linux 的角度一次完整的内核移植通常包含这几个部分架构级适配确定 CPU 架构、ARM 指令集、大小端、页大小等基础参数。SoC 级适配针对具体芯片这里就是 i.MX6ULL初始化内存、外设控制器、中断控制器、时钟树等。板级适配通过设备树Device Tree描述这块板子用了哪些外设、哪些引脚、哪些时序参数。驱动配置在内核配置菜单里选好需要的驱动网卡、串口、LCD、触摸、SD 卡等编译成内置或模块。启动与启动参数让内核能在 U-Boot 引导下找到根文件系统并完成挂载。你不需要、也不可能从零开始写一个能跑的内核。NXP 官方维护的 Linux BSP 已经把 i.MX6ULL 的大部分底层代码写好了你的任务是“选择一个对的起点然后把它做减法改造成你的需求”。1.2 为什么选厂家 BSP 而不是主线内核很多新手会纠结到底是用 Linux 主线内核kernel.org 上的 mainline还是用芯片厂商提供的 BSP 内核我个人的建议是在 LV12 这种学习板上直接用开发板和芯片厂商配合好的 BSP 内核不要轻易去碰主线内核。原因很现实。主线内核版本迭代快对具体某款开发板的支持取决于提交补丁的时间和维护活跃度。i.MX6ULL 在主线里确实有基本支持但部分外设驱动比如某些 LCD 屏的时序、PHY 芯片的复位逻辑、板载 codec 的上下电顺序可能没有人专门维护需要你自己去填坑。而 BSP 内核是 NXP 和板卡厂商在产品上验证过的版本比如我们用的 4.1.15 内核配合 NXP 提供的交叉编译工具链编译出来就能跑的概率非常高。用 BSP 的另一个好处是生态和学习资料多。网上大量 i.MX6ULL 相关教程、驱动例程基本都是基于 3.x/4.x 内核的照着做不容易卡住。你先把 BSP 这套流程跑通后面如果真有性能或安全需求再移植主线内核那时候你的基础已经足够应付差异了。1.3 环境准备交叉编译器不是越新越好内核移植第一步不是解压源码而是准备开发环境。我用的宿主机是一台安装了 Ubuntu 22.04 的虚拟机分配了 4 核 CPU 和 8GB 内存编译 4.1.15 内核大概需要 5 到 10 分钟完全可以接受。交叉编译器选择几个关键点版本匹配老内核对应老工具链新内核对应新工具链这不是玄学而是因为内核代码和编译器之间的兼容性有明确约束。4.1.15 内核我推荐 arm-linux-gnueabihf-gcc 4.9.4 或相近版本实测编译过程中基本不会出现因为编译器太新而导致的奇怪报错。前缀统一建议把交叉编译器的 bin 目录加入 PATH然后在编译时通过环境变量指定前缀。最稳的方案是写进一个环境变量脚本每次打开终端先 source 一下。32 位库老版本的工具链经常是 32 位程序Ubuntu 22.04 上需要安装对应兼容库否则会提示“cannot execute binary file”。环境准备好之后我习惯先跑一遍arm-linux-gnueabihf-gcc -v确认编译器可执行再解压内核源码。这一步看起来简单但很多人第一次编译失败恰恰是交叉编译器没配好、直接调用了本机 gcc 导致的。内核顶层 Makefile 里的CROSS_COMPILE一旦留空它会默认去用 x86 的 gcc编译 ARM 内核必然报一堆难以理解的错误。2. 核心文件逐个拆解Makefile、defconfig、设备树2.1 内核源码顶层 Makefile两个变量定乾坤拿到内核源码后第一件要改的文件就是顶层 Makefile。打开文件在开头附近找到ARCH ? $(SUBARCH) CROSS_COMPILE ? $(CONFIG_CROSS_COMPILE:%%)默认情况下这两个变量是空的或者继承宿主机架构交叉编译 ARM 内核时必须改成ARCH ? arm CROSS_COMPILE ? arm-linux-gnueabihf-有的教程会告诉你直接在命令行里传参比如make ARCHarm CROSS_COMPILEarm-linux-gnueabihf-这样当然也行但每次敲命令都写这一串比较累而且容易漏。我的习惯是直接改 Makefile让这两个变量固定下来。反正这个内核源码工程就是给 ARM 开发板用的改死反而更不容易出错。这里要顺带解释一下ARCHarm的作用。Linux 内核源码里针对不同 CPU 架构有独立的目录arch/arm/、arch/x86/、arch/arm64/等等。ARCH变量就是告诉 Kconfig 和 Makefile 去读哪个架构下的配置和编译规则。CROSS_COMPILE则是给所有编译动作加一个前缀让编译系统调用的是交叉编译器而不是本机 gcc。2.2 defconfig 到底怎么选配置内核有两种主流方式一是make menuconfig图形化界面手工勾选二是直接使用厂商预先写好的默认配置文件即 defconfig。对于 LV12 这款 i.MX6ULL 板子NXP BSP 里已经提供了很多现成的 defconfig 文件常见的有imx_v7_mfg_defconfig、imx_v7_defconfig。两者区别不大前者启用了制造模式MFG相关支持用于厂内烧录场景后者是通用配置。学习开发一般用imx_v7_defconfig就行。我第一次做内核移植的时候对 defconfig 的理解有个误区以为 defconfig 是一个完整的、最终版的配置。实际上它只是“最小起点”里面只启用了默认项很多板级具体外设的支持需要后续通过 menuconfig 补充或者通过设备树描述后由内核自动发现。执行配置的命令make imx_v7_defconfig这一步会生成.config文件也就是真正的内核配置文件。执行完可以打开.config看看内容里面有大量的CONFIG_XXXy/m/n选项y 表示编译进内核镜像m 表示编译成模块n 表示不编译。后面的 menuconfig 操作改的就是这个文件。2.3 dts 设备树管引脚、管外设、管启动设备树可以说是 ARM Linux 内核移植里最容易迷糊、也最关键的部分。它的本质是一种描述硬件信息的数据结构用来替代早期 ARM Linux 里充斥内核源码的“板级文件”。以前每出来一款开发板就要往内核里加一个 board 文件在新内核里这些信息被统一放到.dts文件里由设备树编译器dtc编译成.dtb二进制文件再由 U-Boot 启动时传递给内核。LV12 这块板子的设备树文件可以在 BSP 的arch/arm/boot/dts/目录下找到名字一般是imx6ull-14x14-evk.dts或者开发板厂商自定义的名字。设备树里重点看这几个节点cpuCPU 配置一般不需要动。memory内存起始地址和大小开发板如果改了 DDR 容量要同步修改。** chosen**启动参数传递包括bootargs中 console 串口设置。iomuxc引脚复用配置随处可见的MX6UL_PAD_GPIO1_IO01__GPIO1_IO01之类就是在这里定义的用来把芯片引脚复用为 GPIO、UART、I2C 等功能。uart1、usdhc1、fec1这类引用节点通过pinctrl和具体外设绑定配置波特率、时钟、中断等。刚开始看不懂设备树很正常我建议的学法是“按需去改”屏幕不亮就去找 LCD 相关的节点网卡不通就找 FEC 以太网的节点按键不稳定就找 GPIO 中断的配置。有目标地改比从头到尾通读设备树规范高效得多。3. 手把手编译与下载验证流程3.1 第一次编译先烧掉所有坑当你改好了 Makefile、选好了 defconfig、也检查了设备树后就可以开始编译了。完整的编译命令如下make -j4 zImage make dtbs分开执行比一次性make更直观因为make默认会编译所有架构需要的镜像格式包括 zImage、uImage、modules 等新手会遇到杂七杂八的路径问题。先单独编译 zImage 能让你集中处理内核本体的问题编译 dtbs 则是把设备树源文件编译成二进制文件这个过程很快。编译过程中常见的第一类错误是头文件找不到比如error: asm/bitsperlong.h: No such file or directory这个基本就是交叉编译器没匹配好或者你的工具链里的include路径有问题。可以检查一下make时的CROSS_COMPILE变量是否生效。第一类容易踩的坑是编译版本不匹配内核源码里有的文件依赖较老的 GNU 扩展语法新版 GCC 会报multiple definition或隐式函数声明错误。我曾经用 GCC 9 编译 4.1.15 内核出现了大量error: implicit declaration of function换回 4.9.4 之后一遍过。所以如果你用的也是老 BSP 内核编译器版本一定不要追求新。编译成功后在arch/arm/boot/目录下会生成zImage在arch/arm/boot/dts/目录下生成对应的.dtb文件。这两个文件就是内核移植的主体产物。3.2 网络下载启动TFTP/NFS 三分钟跑起来内核编译出来之后接下来要验证它能不能在板子上启动。常见的方式有三种SD 卡启动、EMMC 烧录启动、网络启动。这里我强烈推荐网络启动尤其是在调试阶段。网络启动的总体思路是U-Boot 已经跑起来并初始化了网络硬件然后通过 TFTP 协议从宿主机下载zImage和.dtb到内存指定地址再通过 NFS 挂载根文件系统。这样每次改内核只需要重新编译并在宿主机更新 TFTP 目录里的文件不用反复拔插 SD 卡也不用反复烧写 EMMC调试效率高出好几倍。我的宿主机环境大致这样准备TFTP 服务使用tftpd-hpa根目录设为/tftpboot把zImage和.dtb拷到这个目录。NFS 服务使用nfs-kernel-server导出一个目录作为根文件系统比如/home/xxx/rootfs。开发板和宿主机联网同一局域网板子 IP 设为192.168.1.110宿主机 IP 设为192.168.1.10。U-Boot 环境变量可以这样配置setenv ipaddr 192.168.1.110 setenv serverip 192.168.1.10 setenv bootcmd tftp 0x80800000 zImage; tftp 0x83000000 imx6ull-lv12.dtb; bootz 0x80800000 - 0x83000000 setenv bootargs consolettymxc0,115200 root/dev/nfs nfsroot192.168.1.10:/home/xxx/rootfs,prototcp rw ipdhcp saveenv boot解释一下几个细节。地址0x80800000和0x83000000是 i.MX6ULL 上约定俗成的内存加载地址前者装内核镜像后者装设备树不能随便乱用否则可能和 U-Boot 自身占用的内存重叠导致启动失败。bootz是启动 zImage 的命令格式镜像和 dtb 之间用-占位表示没有 initramfs。如果一切顺利串口会输出内核启动日志最终进入文件系统 shell。如果卡在Starting kernel ...大概率是 dtb 和内核不匹配或者内存地址配置有问题如果文件系统挂载失败重点查 NFS 配置和网络连接。3.3 从内核到驱动一个最小模块的编译与安装内核启动验证通过之后建议马上做一个小实验编译一个最小内核模块。这一步的意义在于验证内核源码的模块编译环境是否完整同时也为你后续真正的驱动开发打通流程。模块代码很简单写一个mytest.c#include linux/module.h #include linux/kernel.h #include linux/init.h static int __init mytest_init(void) { printk(KERN_INFO mytest module init\n); return 0; } static void __exit mytest_exit(void) { printk(KERN_INFO mytest module exit\n); } module_init(mytest_init); module_exit(mytest_exit); MODULE_LICENSE(GPL);对应的 Makefileobj-m : mytest.o KERN_DIR : /home/xxx/linux-imx-rel_imx_4.1.15_2.1.0_ga PWD : $(shell pwd) all: make -C $(KERN_DIR) M$(PWD) modules clean: make -C $(KERN_DIR) M$(PWD) clean注意KERN_DIR必须指向已经编译过、并且make modules_prepare已完成的内核源码目录。执行make后会生成mytest.ko。把它拷贝到板子的文件系统里执行insmod mytest.ko用dmesg | tail就能看到打印信息rmmod mytest则能卸载模块。这一步能跑通说明你的内核源代码树、模块编译机制、文件系统环境都是健康的。后面开发串口驱动、GPIO 驱动、SPI 驱动流程框架都是这样无非是把file_operations结构体里的read/write等接口函数实现得更完整。4. 移植后的板级适配与驱动验证思路4.1 设备树改动如何影响外设以 LCD 和网络为例内核能启动只是第一步板子上的外设能不能全部工作才是移植工作量的重头戏。LV12 这类开发板常见的适配目标包括 LCD 显示、电阻/电容触摸、以太网、SD 卡、USB Host、音频 codec 等。拿 LCD 来说设备树里通常要关注三个层面的信息pinctrl引脚复用设置为 LCD 的 RGB 数据线、行场同步信号、时钟和使能信号。display-timings单屏的像素时钟、水平/垂直前肩后肩、同步极性等参数。backlight背光 GPIO 的极性、PWM 通道等。如果屏幕不亮显示驱动本身一般没问题多数是这几项配置和实际屏的规格书对不上。比如像素时钟设置过快屏幕会闪或不同步同步极性反了会画面偏左或偏下。我的经验是拿到一块新屏先从规格书里把 timings 参数逐个填进设备树然后通过调整clock-frequency和hback-porch/vback-porch来微调显示效果。网卡适配同样是高频任务。i.MX6ULL 的板载网卡通常是内部 MAC 加外部 PHY 芯片的结构设备树里需要指定使用哪个 MDIO 总线、PHY 的地址、复位 GPIO、中断触发方式等。常见的网络不通问题很多不是驱动代码问题而是phy-mode配置错误比如实际硬件是 RGMII 但你配成了 RMII或者 PHY 复位 GPIO 的上下电时序没对齐。4.2 一个实用的驱动验证方向拦截文件读写操作不少人在学完基础的内核移植后会想做一些真正“有业务价值”的驱动实验。结合 Linux 驱动开发的热门方向我建议可以尝试写一个基于file_operations的字符设备驱动用自己的 read/write 逻辑去覆盖系统调用从而拦截应用程序对某个文件的操作。这也是很多安全类、监控类项目里常见的技术思路纯私有实现和名称为“xx防护/xx审计”的软件里都有类似的影子。实现上并不复杂。你只需要注册一个字符设备在file_operations结构体里实现专属的.read和.write方法然后在其中加入自己的统计、过滤或日志记录逻辑。比如你想统计某个进程对/dev/mychardev的读写次数可以这样写核心逻辑static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { // 这里可以做读写次数统计、数据镜像等操作 return 0; } static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset) { // 这里可以记录写入的数据或者决定是否放行 return count; } static struct file_operations my_fops { .owner THIS_MODULE, .read my_read, .write my_write, };这个实验的价值在于让你真正理解 Linux 应用层系统调用和内核驱动之间的对应关系应用调用open/read/write/close内核通过 VFS 层找到对应驱动的file_operations回调函数。移植内核之后你手里已经有了一个能跑的系统这两者连起来理解整个知识链条就通了。5. 常见问题与排查技巧实录5.1 高频问题速查表整理了一份内核移植过程中的高频问题表都是我在同型号板子上以及帮别人排查时遇到过的问题。现象大概率原因处理建议编译报错找不到asm/bitsperlong.h编译器没走交叉编译或工具链 include 路径错误检查 ARCH 和 CROSS_COMPILE确认编译器路径编译报错隐式函数声明编译器太新与老内核不兼容换 4.9.x 版本交叉编译器启动卡在Starting kernel ...dtb 与内核不匹配或加载地址不对检查内存地址和 dtb 文件重新生成 dtb内核起不来串口无输出U-Boot 引导参数错误或内核 console 参数错检查consolettymxc0与实际串口对应关系文件系统挂载失败NFS 导出目录权限或root/dev/nfs参数错误宿主机导出目录加no_subtree_check,insecure屏幕白屏或不显示设备树时序不对或引脚复用错误核对屏规格书和 pinctrl 配置网卡 ping 不通PHY 地址或 phy-mode 配置错误查看 PHY 芯片手册核对设备树 PHY 节点模块加载Invalid module format模块编译用的内核源码和当前运行内核不一致用当前内核源码重新编译模块内核启动到一半挂死内存配置错误或驱动 probe 时访问了无效地址检查内存大小、设备树 reg 属性5.2 一点个人排查习惯做内核移植调试久了我养成了几个习惯对提升排查效率帮助很大。第一日志分级。串口是内核移植初期最重要的调试手段务必把console参数设对并且养成看完整启动日志的习惯。有的问题在日志前面几行就有提示只是很多新手只盯着最后的 panic 信息。第二小步快跑。不要指望一次修改一大片配置然后启动成功。改一次设备树或内核配置只动一个点然后编译、下载、验证。这样出了问题很容易定位是哪一个改动引起的。第三文件系统从 NFS 开始。能用 NFS 就不用 SD 卡启动能少烧写就少烧写。等内核稳定下来功能验证得差不多了再做固化到 EMMC 的步骤。这一步能帮你节省大量重复时间。第四备份每一步可用的成果。内核源码树、设备树、U-Boot 环境变量、配置文件这些建议在测量验证通过后及时保存并备注。后面的工作很可能在某个改动后变得不可用你还能回退到上一版恢复现场。6. 经验与思考移植只是开始不是终点内核移植跑通之后很多人会有一种“任务完成”的感觉但我个人的体会是真正有价值的开发工作其实是内核移植之后才开始的。你自己确认过设备树、搭建过启动流程、验证过驱动模块这些经验会让你在后续开发驱动、定位硬件问题上少走很多弯路。这个阶段我建议延伸做几个练习试着在同样的 BSP 源码下换一块不同尺寸的 LCD 屏改动设备树并成功点亮。试着把一个简单的 GPIO 按键驱动从静态编译改成模块加载模拟真实驱动开发流程。试着读一遍imx6ull.dtsi和imx6ull-14x14-evk.dts把里面主要的节点和板子上的硬件对应起来。你在这一阶段积累的“板子硬件对应关系”和“内核编译调试习惯”比单纯记住几条命令有用得多。看多了之后你会发现所谓移植就是弄清楚这三件事内核如何知道这块板子长什么样设备树内核如何被加载U-Boot 与 bootargs内核如何把外设驱动跑起来defconfig 与驱动。后续无论是换芯片平台、换开发板、还是升级内核版本只要抓住这三点你都能快速上手。最后再给大家一个小技巧每次拿到一个新的 BSP 内核先不要急着改配置第一时间用厂商默认的 defconfig 和默认的设备树完整编译一次并尝试启动。如果这一步就失败说明 BSP 本身或者你的环境有问题别急着往下走如果成功了记得归档第一次成功的产物和日志后面所有改动都基于这个“已知可用”的基线来做。这个习惯能让你在后面的开发里始终有一个清晰的参考点和回退路径。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

国产安规测试仪突围:耐压绝缘与泄漏电流测量技术实战解析 2026/9/29 3:28:03

国产安规测试仪突围:耐压绝缘与泄漏电流测量技术实战解析

1. 项目背景与行业痛点1.1 被海外品牌统治的安规测试市场在产线上做电气安全测试的朋友,应该都有同感:过去十几年,车间里那些负责耐压、绝缘、接地和泄漏电流检测的仪器,基本被几个海外品牌包圆了。无论是家用电器、电动工具、充电…

阅读更多 →
基于SpringBoot的乐器推荐系统设计与实现(源码+文档+部署讲解等) 2026/9/29 3:27:50

基于SpringBoot的乐器推荐系统设计与实现(源码+文档+部署讲解等)

联系博主 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 …

阅读更多 →
HLW8112直流测量精度优化与工业级校准实践 2026/9/29 3:27:43

HLW8112直流测量精度优化与工业级校准实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Excel/Word表格转HTML:合并单元格、编码与样式清洗实战 2026/9/29 3:27:43

Excel/Word表格转HTML:合并单元格、编码与样式清洗实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
揭开3G开关:如何将32位.NET应用内存上限从2GB提升至4GB 2026/9/29 3:27:37

揭开3G开关:如何将32位.NET应用内存上限从2GB提升至4GB

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
高可靠芯片烧录零缺陷:六大失效路径与全流程管控方法 2026/9/29 3:27:37

高可靠芯片烧录零缺陷:六大失效路径与全流程管控方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉