一、准备工作
1、安装好相关环境(Ubuntu 18.04)
开发板:orangepi-zero2
交叉编译器:aarch64-none-linux-gnu-
2、安装交叉编译工具:
(1)下载并安装交叉编译工具,下载地址如下:Index of /armbian-releases/_toolchain/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror
下载好压缩包,将压缩包放到Ubuntu里如下所示:
输入以下命令进行压缩包的解压:
tar -xf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
解压完后,会有一个文件夹,输入命令进入文件夹,可看到对应的交叉编译工具:
(2)设置环境变量临时有效
1)输入以下命令,输出环境变量:
echo $PATH
2)pwd,打印当前路径:
3)设置临时环境变量,用到以下命令:
export PATH=$PATH:<路径>
4)我们再次输出环境变量,看看是否已经添加进来:
但是,这个方法只适用于当前终端,一旦当前终端关闭或者在其他终端中则无效。它是一个临时的环境变量。
(3)设置永久有效
修改工作目录的 .bashrc 是一个隐藏文件,用来配置命令终端(这里要注意每个人的工作目录可能不太一样,不要盲目复制命令,有些命令是需要改动的)。
vi /home/wsm/.bashrc
注意:上面路径中的wsm是我自己的工作目录,每个人的不一样,需要按照自己的工作目录修改。
在最后一行加上:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/wsm/orangePiZero2/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin
保存并退出,然后输入以下命令,使其生效:
source /home/wsm/.bashrc
(5)测试,验证是否成功:
在虚拟机编写一个hello.c的文件如下:
/*************************************************************************
> File Name: helloc.c
> Author:阿哈、小吴
> Mail: 1971363937@qq.com
> Created Time: Sun 05 Nov 2023 06:23:58 AM PST
************************************************************************/
#include <stdio.h>
int main(void){
printf("hello!\n");
return 0;
}
用交叉编译工具编译为 test可执行文件:
aarch64-none-linux-gnu-gcc hello.c -o test
查看test文件是否是arm架构:
file test
如下:
证明我们交叉编译工具已经安装好,你也可以将test文件拷贝到板子上运行看是否正常。
3、将Linux SDK 的源码准备好,并放在Ubuntu上,如下所示:
4、先将Linux 内核压缩包解压出来
unzip orangepi-build-main.zip
解压后,会多出解压好的文件夹:
二、编译 Linux 内核
1、先进入我们解压好的源码文件夹,如下所示:
2、运行 buid.sh 脚本,输入如下命令:
sudo ./build.sh
它会自动帮我们下载编译工具和源码。
3、运行上述脚本后,弹出下图,我们选择 Kernel package ,然后按回车:
4、紧接着,需要我们选择开发板的型号,这里我的开发板的型号是 OrangePiZero2,选择按回车即可:
5、接着选择型号分支,我的开发板是linux 4.9的,按回车即可:
(1)current 会去编译 linux 5.13
(2)legacy 会去编译 linux 4.9
6、然后会弹出 make menuconfig 配置内核的界面,此时,可以直接修改内核的配置,这里我没有配置,直接退出,退出后就会开始编译内核源码:
(1)如果不需要修改内核的配置选项,在一开始运行 build.sh 脚本时,传入 KERNEL_CONFINGRE=no 就可以临时屏蔽弹出内核的配置界面了。
sudo ./build.sh KERNEL_CONFIIGURE=no
(2)如果你想永久禁用内核配置界面的弹出,也可以设置 orangepi-buid-main/userpatches/config-default.conf 配置文件中的 KERNEL_CONFINGRE=no ,这样就可以永久禁用这个功能了:
(3)编译内核的时候,如果提示错误信息为 Your display is too small to run MenuConfig! ,这是由于你的 Ubuntu PC 的终端界面太小,导致 MenuConfig 的界面无法显示,这时,只要重新把终端调大,然后重新运行 build.sh 脚本即可。
7、编译内核源码时,提示的部分信息说明如下(了解)
(1)Linux 内核源码的版本信息:
[ o.k. ]Compiling legacy Kernel [ 4.9.170 ]
(2)使用的交叉编译工具链的版本:
[ o.k. ]Compiler version [ aarch64-none-linux-gnu-gcc 9.2.1 ]
(3)内核默认使用的配置文件以及它存放的路径:
[ o.k. ]Using kernel config file [ config/kernel/linux-sun50iw9-legacy.config ]
(4)如果 KERNEL_CONFINGRE=yes ,内核最终使用的配置文件 .config 会复制到 output/config 中,如果没有对内核配置进行修改,最终的配置文件和默认的配置文件是一致的:
[ o.k. ]Exporting new kernel config [ output/config/linux-sun50iw9-legacy.config ]
(5)编译生成的内核相关的 deb 包的路径:
[ o.k. ]Target directory [ output/debs ]
(6)编译生成的内核镜像 deb 包的包名:
[ o.k. ]File name [ linux-image-legacy-sun50iw9_2.2.0_arm64.deb ]
(7)编译使用的时间:
[ o.k. ]Runtime [ 5 min ]
(8)最后会显示重复编译上一次选择的内核的编译命令,使用下面的命令无需通过图形界面选择,可以直接开始编译内核源码:
8、编译好后,生成的目录如下:
以上文件释义如下所示:
a. build.sh: 编译启动脚本
b. external: 包含编译镜像需要用的配置文件、特定功能的脚本以及部分程序的源码,编译镜像过程中缓存的 rootfs 压缩包也存放在 external 中
c. kernel: 存放 linux 内核的源码,里面名为 orange-pi-4.9-sun50iw9 的文件夹存 放 的 就 是 H616 系 列 开 发 板 legacy 分 支 的 内 核 源 码 , 里 面 名 为orange-pi-5.13-sunxi64 的文件夹存放的就是 H616 开发板 current 分支的内核源码(如果只编译了 legacy 分支的 linux 镜像,那么则只能看到 legacy 分支的内核源码;如果只编译了 current 分支的 linux 镜像那么则只能看到current 分支的内核源码),内核源码的文件夹的名字请不要手动修改,如果修改了,编译系统运行时会重新下载内核源码
d. LICENSE: GPL 2 许可证文件
e. README.md: orangepi-build 说明文件
f. output: 存放编译生成的 u-boot、linux 等 deb 包、编译日志以及编译生成的镜像等文件
g. scripts: 编译 linux 镜像的通用脚本
h. toolchains: 存放交叉编译工具链
i. u-boot: 存放 u-boot 的源码,里面名为 v2018.05-sun50iw9 的文件夹存放的就是 H616 系列开发板 legacy 分支的 u-boot 源码,里面名为 v2021.07-sunxi的文件夹存放的就是 H616 开发板 current 分支的 u-boot 源码(如果只编译了current 分支的 linux 镜像,那么则只能看到 current 分支的 u-boot源码),u-boot 源码的文件夹的名字请不要手动修改,如果修改了,编译系统运行时会重新下载 u-boot 源码
j. userpatches: 存放编译脚本需要用到的配置文件
编译好的内核文件就在 kernel 下。
三、内核移植
1、查看我们编译生成的内核相关的 deb 包:
我生成的如下所示:
2、将编译好的 deb 上传至 开发板的 /root 目录下:
可以通过SSH连接,使用scp命令上传
scp linux-image-legacy-sun50iw9_2.2.2_arm64.deb root@orangepi:/root
3、查看已经安装了的 deb 文件:
dpkg -l | grep linux-image
4、卸载开发板中的 deb 文件:
apt purge -y linux-image-legacy-sun50iw9
5、安装上传的 deb 文件:
dpkg -i linux-image-legacy-sun50iw9_2.2.2_arm64.deb
6、重启开发板
sudo reboot.
7、再次查看安装的 deb 文件:
可见,已经安装好了最新的 deb 文件。
四、驱动编译
1、写一个示例代码(放在orangepi-build-main/kernel/orange-pi-4.9-sun50iw9/drivers/char 下):
新建一个 pin4driver.c 文件:
文件写入代码如下:
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
// 将该文件放在 orangepi-build-main/kernel/orange-pi-4.9-sun50iw9/drivers/char 下
static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno; //设备号
static int major =231; //主设备号
static int minor =0; //次设备号
static char *module_name="pin4"; //模块名
//led_read函数
static ssize_t pin4_read(struct file *file1,char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_read\n"); //内核的打印函数和printf类似
return 0;
}
//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n"); //内核的打印函数和printf类似
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_write\n");
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
.read = pin4_read,
};
int __init pin4_drv_init(void) //真实入口
{
int ret;
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中
pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在Dev下自动生成设备
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件
return 0;
}
void __exit pin4_drv_exit(void)
{
device_destroy(pin4_class,devno);
class_destroy(pin4_class);
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(pin4_drv_init); //入口 内核加载该驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit); //出口,卸载模块函数
MODULE_LICENSE("GPL v2");
2、怎么样才能编译到这个驱动代码呢?需要我们去修改 Makefile,如下所示:
注意:我们修改的是 orangepi-build-main/kernel/orange-pi-4.9-sun50iw9/drivers/char 这个路径下的 Makefile 文件!!!
我们需要编译成模块的方式,obj-m 就是模块的编译方式,我们需要在Makefile中添加:
obj-m += pin4driver.o
如下所示:
3、然后我们需要回到内核的源码目录下,进行模块的编译,即 orangepi-build-main/kernel/orange-pi-4.9-sun50iw9 这个路径下:
(1)方法一:输入以下命令进行编译,这条命令是将所有内容都编译了,需要蛮久的:
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -j4
编译好后,可以看到编译生成的 .ko 文件所在的路径:
(2)方法二:输入以下命令,只是编译模块,较快:
make modules ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu-
编译如下所示:
4、将编译好的 pin4driver.ko 模块,拷贝到我们的开发板(可以用scp指令拷贝,也可用U盘拷贝)
这里讲一下用U盘拷贝的方法:
(1)首先先将 pin4driver.ko 模块 拷贝到 共享文件夹中:
cp pin4driver.ko /mnt/hgfs/share/
(2)查看共享文件夹中的文件:
ls /mnt/hgfs/share/
(3)从 windows 上的共享文件夹 将 pin4driver.ko 模块 拷贝到U盘上
(4)将U盘插到板子上, 用 dmesg 命令查看识别到的是哪个设备:
可以看到内核识别到的设备是 sda1。
(5)输入指令挂载U盘,挂载到 /mnt 目录下:
sudo mount /dev/sda1 /mnt/
挂载好后,cd 进入 /mnt 目录,再 ls,即可看到U盘下的文件:
(6)用cp命令将 pin4driver.ko 模块 拷贝出来即可
(7)拷贝完后,需要取消U盘的挂载:
(8)拔出U盘即可
5、在开发板中加载驱动模块:
(1)输入以下命令即可:
sudo insmod pin4driver.ko
(2)我们看一下是否生成了pin4这个设备驱动:
模块名、主设备号与次设备号都与我们的驱动代码对上了:
6、接下来就是测试
我们需要写一个应用程序来去调用我们的驱动程序:
写一个test.c程序如下(这是用户空间的代码):
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
int fd;
fd = open("/dev/pin4",O_RDWR);
if(fd<0){
printf("open failed!\n");
perror("res");
}else{
printf("open success!\n");
}
fd = write(fd,'1',1);
}
编译运行:
为什么无法打开呢?这是因为,我们加载的驱动的权限问题,我们需要给它可读可写的权限:
修改权限,再次执行即可:
7、查看驱动层的输出语句:
输入dmesg查看:
这样子,就完成了简单的字符设备驱动开发。
五、总结
(1)通过insmod将模块,加载到内核中去,它会去 module_init 函数 加载驱动模块,再会去调用我们写的 pin4_drv_init 驱动入口函数,而这个函数又会去通过 register_chardev 函数去注册驱动,把这个驱动(整个file_operations结构体变量)加入到内核链表中。
(2)当驱动装载成功后,会在 /dev/ 文件下生成 一个驱动(如pin4),但是要注意,我们需要给这个驱动一个所有用户均可读可写的权限。
(3)(用户空间)应用程序中的 open 函数会去通过 系统调用sys_call(软中断,中断号是0x80) “陷入” 到内核空间(sys_call会调用sys_open),然后根据文件名找到相关的设备号,根据设备号从驱动链表里面找出驱动,如果找到了驱动就返回一个 fd 文件句柄。 文章来源:https://www.toymoban.com/news/detail-770849.html
(4)因为它会去调用驱动里面的 open 函数,于是,我们就会在内核里看到打印了相关的信息。文章来源地址https://www.toymoban.com/news/detail-770849.html
到了这里,关于香橙派(orangePiZero2):交叉编译、内核编译及驱动开发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!