uboot 移植
uboot 的移植并不是说我们完完全全的从零开始将 uboot 移植到我们现在所使用的开发板
或者开发平台上。 半导体厂商负责将 uboot 移植到他们的芯片上,因此半导体厂商都会自己做一个开发板,这个开发板就叫做原厂开发板,比如大家学习 STM32的时候听说过的discover 开发板就是ST自己做的。
BSP:半导体厂商会将 uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个 uboot 发布出去,这就是大家常说的原厂 BSP 包。我们一般做产品的时候就会参考原厂的开发板做硬件,然后在原厂提供的 BSP 包上做修改,将 uboot 或者 linux kernel 移植到我们的硬件上。这个就是uboot 移植的一般流程:
①、在 uboot 中找到参考的开发平台,一般是原厂的开发板。
②、参考原厂开发板移植 uboot 到我们所使用的开发板上。
- 将原厂的uboot移植到开发板上,首先需要配置uboot,configs目录下有很多关于im6ull的有关配置
mx6l :指的是imx6ul芯片 im6ull 是imx6ull 开发板的
2、imx6ull 、imx6ul,都有9x9 14x14的默认配置文件,只关心自己的使用的,笔者是14x14,而正点原子的只有nand 、emmc,关注 mx6ull_14x14_evk_emmc_defconfig 和mx6ull_14x14_evk_nand_defconfig 这两个配置文件就行了
编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
也可以写个shell脚本加执行权限,或者改顶层makefile 文件,或者直接运行该指令
#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean #清理上次编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihfmx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
ARCH=arm: 这是一个环境变量,指定了编译目标的体系结构,这里是ARM架构。
CROSS_COMPILE=arm-linux-gnueabihf-: 这是另一个环境变量,用于指定交叉编译工具链的前缀。交叉编译工具链是
用于在开发主机上生成目标架构(ARM)的可执行文件的工具。在这里,工具链的前缀是 arm-linux-gnueabihf-。
mx6ull_14x14_evk_emmc_defconfig: 这是一个配置文件名称,它告诉make使用预定义的配置来设置构建参数。这通常包括定义了系统的硬件和功能配置,以及所需的驱动程序和软件包。
make V=1: 这个选项会使make以详细模式运行,显示正在执行的命令以及相应的输出。这对于调试构建过程非常有用,因为您可以看到每个步骤的详细信息。
-j16: 这个选项指示make并行构建,使用16个并行任务来加速构建过程。这可以提高构建速度,尤其是在多核处理器上
是使用ARM交叉编译工具链,根据名为 mx6ull_14x14_evk_emmc_defconfig 的配置文件构建嵌入式系统。
make工具将根据配置文件和源代码生成目标文件,库和可执行文件,并在详细模式下显示构建过程的详细信息。
同时,-j16选项允许并行构建,以提高构建速度。
修改makefile
编译成功后
编译完成以后会生成 u-boot.bin、 u-boot.imx 等文件
将 imxdownload 软件拷贝到 uboot 源码根目录下,然后使用 imxdownload 软件将 u-boot.bin 烧写到
SD 卡中,烧写命令如下: chmod 777 imxdownload //给予 imxdownload 可执行权限
./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 里面 先后查看得sdd是目标sdc1
将拨码开关置成SD启动,插入SD,等待启动, 检查SD EMMC驱动检测
通过命令查看设备名称
mmc list 查看储存设备,有两个
通过 mmc dev 0 和mmc info 根据内存空间可知
mmc dev 1 和 mmc info 是emmc
MMC设备通常用于嵌入式它是一种闪存存储设备标准,用于存储数据,类似于SD卡系统中作为存储介质,用来存储操作系统、应用程序、数据等。
开发板uboot 起来后,是否是编译时间
闪存存储设备是一类广泛用于数据存储的非易失性存储设备,它们使用了闪存存储技术,包括 NAND Flash 和 NOR Flash。这些存储设备通常具有高速读写性能、低功耗、耐用性和稳定性,因此在各种电子设备和应用中得到广泛应用。以下是一些常见类型的闪存存储设备:
USB闪存驱动器(USB闪存盘):
这些便携式存储设备使用USB接口连接到计算机,用于存储和传输数据。它们常常被用作临时数据存储和文件传输工具。
SD卡(Secure Digital卡):
SD卡是一种广泛用于照相机、手机、嵌入式系统等设备的存储卡。它们通常分为标准SD、微型SD和迷你SD卡。microSD卡: 这是SD卡的一种更小型号,常用于手机、平板电脑、摄像机和其他小型设备中。
SSD(固态硬盘):
固态硬盘是一种用于替代传统机械硬盘的存储设备。它们使用闪存芯片来存储数据,并提供了更快的读写速度和更高的耐用性。SSD广泛用于个人电脑、服务器、笔记本电脑和数据中心。eMMC(嵌入式多媒体卡):
eMMC是一种嵌入式存储解决方案,通常集成在嵌入式系统板上,用于存储操作系统、固件和数据。它是一种集成式的闪存存储设备。CF卡(CompactFlash卡): 这是一种用于相机和嵌入式系统的存储卡,通常较大,提供了高容量存储选项。
NAND Flash芯片: NAND
Flash芯片是一种内置在嵌入式系统中的存储设备,用于存储固件、操作系统和应用程序数据。它们通常被集成在嵌入式系统的主板上。NOR Flash芯片: NOR Flash芯片是另一种内置在嵌入式系统中的存储设备,通常用于存储固件、引导程序和嵌入式操作系统。
这些存储设备在不同的应用中具有各自的优势和用途。选择适当的闪存存储设备取决于您的应用需求,包括容量、读写速度、耐用性和接口等因素。
网络驱动
uboot 启动的时候提示“Board Net Initialization Failed”和“No ethernet found.”这两行,说
明网络驱动也有问题
在uboot 中添加自己开发板
内核移植
vim 命令
光标移动
以下是一些在 Vim 中移动光标的快捷键:
0 移动到行首
$ 移动到行尾
gg 移动到文件开头
G 移动到文件结尾
:n 移动到第 n 行
文本编辑
以下是一些在 Vim 中编辑文本的快捷键:
r 替换当前字符
R 进入替换模式,可以一次性替换多个字符
x 删除当前字符
dd 删除当前行
D 删除当前行中光标后的所有字符
操作撤销
以下是一些在 Vim 中操作撤销的快捷键:
u 撤销最后一次操作
Ctrl + r 恢复上一次被撤销的操作
搜索和替换
以下是一些在 Vim 中搜索和替换的快捷键:
/pattern 搜索指定的字符串 pattern
n 查找下一个匹配项
N 查找上一个匹配项
:%s/old/new/g 全局替换文本中的 old 字符串为 new 字符串
复制和粘贴
以下是一些在 Vim 中复制和粘贴的快捷键:
yy 复制当前行
p 粘贴复制的内容到当前光标位置的下一行
显示行号 set nu 或者nu
1. nxp的内核
将nxp的提供的内核copy到ubantu上
2.修改linux 源码根目录的makefile
交叉编译工具链写的不对,则会找不到
如果写的对仍报错,则确保交叉编译工具链已安装 arm-linux-gnueabihf-gcc -v
查看安装路径
3. 修改配置文件
和 uboot 一样,在编译 Linux 内核之前要先配置 Linux 内核。每个板子都有其对应的默认
配 置 文 件 , 这 些 默 认 配 置 文 件 保 存 在 arch/arm/configs 目 录 中 。 imx_v7_defconfig 和
imx_v7_mfg_defconfig 都可作为 I.MX6ULL EVK 开发板所使用的默认配置文件。但是这里建议
使用 imx_v7_mfg_defconfig 这个默认配置文件,首先此配置文件默认支持 I.MX6UL 这款芯片,
而且重要的一点就是此文件编译出来的 zImage 可以通过 NXP 官方提供的 MfgTool 工具烧写!!
编译前进入linux 源码 的根目录下 编译前先清理下
make clean
make imx_v7_mfg_defconfig 配置linux内核
配置结束后,编译
make -j16
4 编译出的映像文件
Linux 内核编译完成以后会在 arch/arm/boot 目录下生成 zImage 镜像文件,如果使用设备树 的话还会在
arch/arm/boot/dts 目录下开发板对应的.dtb(设备树)文件,比如 imx6ull-14x14-evk.dtb 就是 NXP
官方的 I.MX6ULL EVK 开发板对应的设备树文件。至此我们得到两个文件: ①、 Linux 内核镜像文件: zImage。 ②、
NXP 官方 I.MX6ULL EVK 开发板对应的设备树文件: imx6ull-14x14-evk.dtb。
注意 tftp 服务一定提前配置好
Error code 1: File not found错误
TFTP安装教程
vim /etc/xinetd.d/tftp
service tftp
{
protocol = udp
port = 69
socket_type = dgram
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /home/answer/linux/tftpboot/
disable = no
flags =IPv4
}
server_args = -s /tftpboot -c
-c参数是允许上传用的,参数/tftpboot则是你的tftp目录,修改成你的目录即可,但是笔者建议在根目录下,方便
/etc/init.d/xinetd reload
配置后重启
内核移植
makefile分析
VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 15
EXTRAVERSION =
NAME = Series 4800
#版本号 4.1.15
MAKEFLAGS += -rR --include-dir=$(CURDIR)
是一个Makefile中的变量定义。它定义了
MAKEFLAGS
这个变量的值,该变量用于配置make
命令的行为。让我解释这个代码的含义:
-
MAKEFLAGS
:这是一个特殊的环境变量,用于配置make
命令的行为。它可以包含一系列标志和选项,以影响make
命令的操作。 -
+=
:这是一个赋值运算符,表示将右边的值追加到左边的变量值之后。也就是说,它会将-rR --include-dir=$(CURDIR)
这个字符串追加到MAKEFLAGS
变量的当前值之后。 -
-rR
:这是make
命令的选项。具体含义如下:-r
:这个选项告诉make
在处理规则时不要使用内置的规则。-R
:这个选项告诉make
在处理规则时不要使用内置的变量设置。
-
--include-dir=$(CURDIR)
:这部分是另一个选项,它指定了make
在搜索include
文件(包含其他Makefile的文件)时应该包括的目录。$(CURDIR)
是一个Makefile中的预定义变量,表示当前工作目录的路径。
综合起来,这行代码的作用是将-rR
选项添加到make
命令的默认选项中,并指定在当前工作目录中搜索include
文件。这可以用来自定义make
命令的行为,例如,禁用内置的规则和变量设置,并控制include
文件的搜索路径。
驱动开发
50 make releaseversion
51 make releversion
52 make help
53 make kernelversion
54 make
55 export ARCH=arm
56 make
57 cd -
58 make
59 make clean
60 make
scp chrdevbase.ko root@192.168.31.100:/lib/modules/4.1.15
root@king:/home/answer/linux/alientek_linux/linux-imx-rel_imx_4.1.15# make
加载驱动模块和卸载模块
设备树
DTS是设备树源码文件、DTB编译出二进制文件,编译工具DTC工具
DTC在linux内核中script/dtc目录下,makefile
1 hostprogs-y := dtc
2 always := $(hostprogs-y)
3 4
dtc-objs:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \
5 srcpos.o checks.o util.o
6 dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o
DTC 工具依赖于 dtc.c、 flattree.c、 fstree.c 等文件,最终编译并链接出 DTC 这
个主机文件。如果要编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命
令:
make all
或者:
make dtbs
“make all”命令是编译 Linux 源码中的所有东西,包括 zImage, .ko 驱动模块以及设备
树,如果只是编译设备树的话建议使用“make dtbs”命令
##设备树语法
imx6ull.dtsi 后缀为soc的内部的外设的消息,CPU的架构,主频,外设寄存器地址
aliases、 cpus 和 intc 是三个子节点,在设备树中节点命名格式如下:
node-name@unit-address
节点名称@这个节点的物理地址
但是一般这样写
label: node-name@unit-address
节点标签:节点名称@节点地址,之后可以通过 &label 来访问这个节点
1.2 节点属性
每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流
如:
字符串
compatible = "arm,cortex-a7";
32 位无符号整数
reg = <0>; 或者 reg = <0 0x123456 100>;
字符串列表:属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:
compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
1.2.1 标准属性
节点是具体的设备,设备具有一堆属性,不同的设备的属性不同,用户可以自定义之外,还有很多是标准属性
compatible 属性
compatible 属性也叫做“兼容性”属性,这是非常重要的一个属性! compatible 属性的值是
一个字符串列表
, compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序, compatible 属性的值格式如下所示:
"manufacturer,model"
其中 manufacturer 表示厂商, model 一般是模块对应的驱动名字。比如 imx6ull-alientekemmc.dts 中
sound 节点是 I.MX6U-ALPHA 开发板的音频设备节点, I.MX6U-ALPHA 开发板上
的音频芯片采用的欧胜(WOLFSON)出品的 WM8960, sound 节点的 compatible 属性值如下:
compatible= "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960"
; 属性值有两个,
分别为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl” 表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字。
sound 这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件, 如果没有找到的话就使用第二个兼容值查。**
一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设 备节点的 compatible属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个 驱动
提供举例说明OF驱动表 代码 编译器 gcc
#include <stdio.h>
#include <string.h>
// 模拟设备树中的节点信息
struct of_device_id
{
const char *compatible;
};
// 模拟 platform_driver 结构
struct platform_driver
{
struct
{
const char *name;
const struct of_device_id *of_match_table;
} driver;
int (*probe)(void); // probe 函数指针
void (*remove)(void); // remove 函数指针
};
// 模拟设备驱动程序的 probe 函数
int imx_wm8960_probe()
{
printf("imx_wm8960_probe() called\n");
return 0;
}
// 模拟设备驱动程序的 remove 函数
void imx_wm8960_remove()
{
printf("imx_wm8960_remove() called\n");
}
int main()
{
// 模拟设备树中的节点信息
struct of_device_id imx_wm8960_dt_ids[] = {
{.compatible = "fsl,imx-audio-wm8960"},
{.compatible = NULL} // sentinel
};
/*结构体数组的最后一个元素是一个 sentinel(哨兵),它具有 compatible 值为 NULL,用于标识数组的结尾。这是一种常见的做法,以便程序可以遍历数组并确定何时结束。
struct of_device_id imx_wm8960_dt_ids[2];
imx_wm8960_dt_ids[0].compatible = "fsl,imx-audio-wm8960";
imx_wm8960_dt_ids[1].compatible = NULL;*/
// 模拟 platform_driver 结构
struct platform_driver imx_wm8960_driver = {
.driver = {
.name = "imx-wm8960",
.of_match_table = imx_wm8960_dt_ids,
},
.probe = imx_wm8960_probe,
.remove = imx_wm8960_remove,
};
// 模拟设备树节点的 compatible 属性值
const char *compatible_property = "fsl,imx-audio-wm8960";
// 检查设备是否匹配
const struct of_device_id *match = NULL;
for (int i = 0; imx_wm8960_dt_ids[i].compatible; i++)
{
if (strcmp(compatible_property, imx_wm8960_dt_ids[i].compatible) == 0)
{
match = &imx_wm8960_dt_ids[i]; // 取第i元素的地址给match {.compatible = "fsl,imx-audio-wm8960"}
break;
}
}
// 如果找到匹配,调用 probe 函数
if (match)
{
printf("Device matched: %s\n", match->compatible);
imx_wm8960_driver.probe();
}
else
{
printf("Device not found in the device tree.\n");
}
return 0;
}
makefile
CC = gcc
CFLAGS = -Wall -g
SRCS = main.c function.c
OBJS = $(SRCS:.c=.o)
EXEC = app
.PHONY: all clean
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(EXEC)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(EXEC)
model 属性
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比 如:
model ="wm8960-audio";
status 属性
status 属性看名字就知道是和设备状态有关的, status 属性值也是字符串,字符串是设备的
状态信息
address-cells 和#size-cells 属性
这两个属性的值都是无符号 32 位整形, #address-cells 和#size-cells 这两个属性可以用在任
何拥有子节点的设备中,用于描述子节点的地址信息。 #address-cells 属性值决定了子节点 reg 属
性中地址信息所占用的字长(32 位), #size-cells 属性值决定了子节点 reg 属性中长度信息所占的 字长(32 位)。
#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性 都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度, reg 属性的格式一为
:reg = <address1 length1 address2 length2 address3 length3……>
> 每个“address length”组合表示一个地址范围,其中 address 是起始地址, length 是地址长 度,
#address-cells 表明 address 这个数据所占用的字长, #size-cells 表明 length 这个数据所占用的字长
“字长”(Word Length)和 “字节”(Byte)是计算机和计算机体系结构中的两个不同的概念,它们有以下区别:
字节(Byte):
- 字节是计算机中的基本存储单元,通常由8位组成。
- 一个字节可以存储8位二进制数据,即8个0或1,或者256个不同的整数值(0到255)。
- 字节通常用于存储和传输数据,以及表示字符、整数、图像像素等。
字长(Word Length):
- 字长是一个描述计算机处理器的特性的概念,表示处理器一次能够处理的二进制数据位数。
- 字长通常表示为位数(例如,32位或64位)。
- 较长的字长通常意味着处理器能够在一次操作中处理更多的数据,这有助于提高性能。
主要区别在于字节是存储和数据传输的单位,而字长是处理器内部数据处理的单位。字节是8位的二进制数据单元,而字长是处理器能够同时处理的位数,它可以是8位、16位、32位、64位或其他值,具体取决于处理器的体系结构。字长与计算机的性能和数据处理能力密切相关,较长的字长通常允许更快的数据处理和更大范围的数值表示。
设备树语法
ranges 属性 imx6ull 基本一致,所以可以看到ranges为空
如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换
device_type 属性值为字符串, IEEE 1275 会用到此属性,用于描述设备的 FCode,但是设备树没有
FCode,所以此属性也被抛弃了。此属性只能用于 cpu 节点或者 memory 节点。
例如:一个64位的系统有两块内存空间:RAM1: 起始地址是0x0,地址空间是 0x80000000;RAM2: 起始地址是0x10000000,地址空间也是0x80000000;同时根节点下的 #address-cells = <2>和#size-cells = <2>,这个memory节点描述为:两个字长,一个起始地址、一个是地址长度
![在这里插入图片描述](https://img-blog.csdnimg.cn/ac54a6d493a241f9bb052b59f5e2c4e7.png
怎么获取绑定信息文档
设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。那么我们在设备树中添加一个硬件对应的节点的时候从哪里查阅相关的说明呢?在 Linux 内核源码中有详细的.txt
文档描述了如何添加节点,这些.txt 文档叫做绑定文档,路径为: Linux源码目录/Documentation/devicetree/bindings**
有时候使用的一些芯片在 Documentation/devicetree/bindings 目录下找不到对应的文档,这
个时候就要咨询芯片的提供商,让他们给你提供参考的设备树文件。
查找结点的OF函数
设备都是以节点的形式“挂”到设备树上的,因此要想获取这个设备的其他属性信息,必
须先获取到这个设备的节点。 Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件 include/linux/of.h 中,定义如下:
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
<mux_reg conf_reg input_reg mux_mode input_val>
0x0090 IOMUXC_
*SW_MUX_CTL_PAD*
UART1_RTS_B:mux_reg 寄 存 器 值,寄存器偏移地址 复用GPIO
0x031C SW*PAD_CTL_PAD
*_UART1_RTS_B:上拉模式 conf_reg 寄存器偏移地址上/下拉、驱动能力和速度
0x0000 input_reg 寄存器偏移地址
有些外设有 input_reg 寄存器,有 input_reg 寄存器的
外设需要配置 input_reg 寄存器。没有的话就不需要设置, UART1_RTS_B 这个 PIN 在做
GPIO1_IO19 的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。
0x5 :mux_reg 寄 存 器 值
0x0: input_reg 寄存器的值