环境介绍

最近在学习ARM Linux驱动开发,由于手头的ARM开发板版本太老,和教程里用的驱动内核相差太多,于式打算用树莓派3B来进行学习。 要用树莓派开发内核,就必须先让树莓派跑上自己编译的内核,否则insmod会执行失败教程介绍了如何交叉编译树莓派3B内核并运行ko模块,本教程不再使用虚拟机,而是使用win10的WSL搭配vscode进行开发 硬件:WIN10电脑 、树莓派

WSL安装

参考:win10安装WSL 注意:我安装的是Ubuntu18.4,使用的是WSL1,WSL1完全够用。当然WSL2也是可以的。

安装VSCode

很简单,下载安装就行了,然后安装Remote WSL插件,喜欢折腾的朋友可以再装几个插件。 主要是配置方面需要介绍,文章后面会进行补充。

连接树莓派

连接树莓的方法多种多样,有树莓派的朋友应该都会吧。我采用的是ssh的方式,树莓派通过wifi连接路由器,电脑也是通过wifi连接同一个路由器。

  1. 树莓派系统烧录: 资源下载
  2. 连接SSH 树莓派默认是关闭SSH的,打开方法:烧录好后在boot目录下新建一个SSH文件
为树莓派安装sz、rz

开发过程中需要频繁地在PC和树莓派间传输文件,传输方法也很多,这里使用sz和rz树莓派安装sz,rz 使用方法:

#这些命令都在树莓派执行
sz ~/test.txt	#将树莓派的文件发送到PC
rz -y 	#将PC的文件拷贝到当前目录,-y表示覆盖已存在的文件

下载内核源码与工具

  1. 查看当前树莓派内核版本uanme -a
  2. pi节点搭建容器错误_内核

  3. 下载对应版本的内核,比如我的是4.19.97内核源码下载地址 注意:这里有很多版本,一定要选对版本,否则编译后可能开不了机
  4. pi节点搭建容器错误_linux_02

  5. 如何找到自己版本内核呢?可以看树莓派官网的raspberrypi OS的release note 点这里打开 比如我的是4.19.79,可以看出日期是2020-02-13和2020-02-05
  6. pi节点搭建容器错误_ubuntu_03

  7. 下载对应的代码即可
  8. pi节点搭建容器错误_嵌入式_04

  9. 下载前可以看下Makefile的log是不是对的
  10. pi节点搭建容器错误_ubuntu_05

  11. 下载交叉编译工具 下载地址

配置交叉编译环境

  1. 打开wsl,把内核源码和工具拷贝到wsl中
#WSL可以直接访问windows的磁盘,/mnt/d 代表D盘
cp /mnt/d/Download/linux-raspberrypi-kernel_1.20200205-1.zip  ./
cp /mnt/d/Download/infinitystar-raspberrypi-tools-master.zip  ./
#解压
mkdir linux-4.19
unzip -d linux-4.19 linux-raspberrypi-kernel_1.20200205-1.zip 
mkdir tools
unzip -d tools infinitystar-raspberrypi-tools-master.zip
  1. 配置交叉编译环境~/tools/raspberrypi-tools/arm-bcm2708目录下有几个交叉编译工具,我们使用的WSL是64位的,所以使用gcc-linaro-arm-linux-gnueabihf-raspbian-x64
vi ~/.bashrc
#再最后一行添加如下内容
export PATH=$PATH:/root/tools/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
#重新运行一遍.bashrc,使配置生效
source ~/.bashrc
#查看是否配置成功
arm-linux-gnueabihf-gcc -v
  1. 下载一些依赖( 这是我从其它教程里抄的,不一定全,大家编译出错以后百度一下,一般很容易搜到少了哪个依赖 )
sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev

编译内核

  1. 进入内核源码目录,修改Makefile

这一步的作用:我们在make后需要带上两个参数 如:make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- ,这两个参数最后传递给Makefile,如果我们在Makefile中直接写死,就可以直接输入make就行了。

pi节点搭建容器错误_linux_06

2. 从树莓派获取config文件

内核编译执行make menuconfig时会默认读取.config文件,里面配置了内核会编译哪些模块,不编译哪些模块

#在树莓派上执行
sudo modprobe configs
zcat /proc/config.gz > board.config
sz board.config
#在WSL上执行
cd ~/linux-4.19/linux-raspberrypi-kernel_1.20200205-1/
cp /mnt/d/xxx/board.config ./
cp board.config .config
  1. 执行make menuconfig并重新保存.config

虽然已经有了.config文件,但最好还是执行下make menuconfig,然后重新保存一下(原因我也不清楚。。。)

  1. 这一步和下一步一般第一次编译都会出错,原因是系统有些东西没有安装,百度很容易找到答案
  2. 编译内核
KERNEL=kernel7
make -j4
  1. 拷贝内核到树莓派
#生成树莓派可用的kernel镜像
./scripts/mkknlimg arch/arm/boot/zImage kernel7.img
#把kernel7.img拷贝到树莓派的/boot下即可
  1. 树莓派重启后uname -a看下日期,如果没问题的话应该已经是自己编译的内核了

用VSCode编译内核驱动

  1. 先写个驱动小demo
  • Makefile文件
KERNELDIR := /root/linux-4.19/linux-raspberrypi-kernel_1.20200205-1
CURRENT_PATH := $(shell pwd)
obj-m := led.o
build: kernel_modules
kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
  • led.c文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>

#define MYDEV_NAME "LedTest"

struct led_dev
{
    struct cdev dev;
    struct class *class; 
    struct device *device;
    dev_t devid;
    int major;
    int minor;
    
};


static int led_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    return 0;
}

ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    return 0;
}

ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    return 0;
}

static const struct file_operations led_fops = {
	.read		=	led_read,
	.write		=	led_write,
	.open		=	led_open,
	.release	=	led_release,
	.owner		=	THIS_MODULE,
};

struct led_dev my_dev;


static int __init led_init(void)
{
    int ret = 0;
    printk("led_init\n");
    /*  设备号分配  */
    if(my_dev.major){   //给定主设备号
        my_dev.devid = MKDEV(my_dev.major,0);
        ret = register_chrdev_region(my_dev.devid,1,MYDEV_NAME);
    }else{  //没有指定主设备号
        ret = alloc_chrdev_region(&my_dev.devid,0,1,MYDEV_NAME);
    }
    if(ret < 0){
        printk("register char dev failed!\n");
        return -1;
    }
    my_dev.major = MAJOR(my_dev.devid);
    my_dev.minor = MINOR(my_dev.devid);
    printk("register chardev major:%d minor:%d\n", my_dev.major, my_dev.minor);
    /*  创建字符设备  */
    cdev_init(&my_dev.dev, &led_fops);
    ret = cdev_add(&my_dev.dev, my_dev.devid, 1);
    if(ret){
        unregister_chrdev_region(my_dev.devid, 1);
    }
    /*  创建设备节点  */
    my_dev.class = class_create(THIS_MODULE, MYDEV_NAME);
    if(IS_ERR(my_dev.class)){
        return PTR_ERR(my_dev.class);
    }
    my_dev.device = device_create(my_dev.class,NULL,my_dev.devid,NULL,MYDEV_NAME);
    if(IS_ERR(my_dev.device)){
        return PTR_ERR(my_dev.device);
    }

    return 0;
}

static void __exit led_exit(void)
{
    printk("led_exit\n");
    cdev_del(&my_dev.dev);
    unregister_chrdev_region(my_dev.devid, 1);
    device_destroy(my_dev.class,my_dev.devid);
    class_destroy(my_dev.class);
    return;
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LIUJIANHUA");
  1. 打开VSCode,点击左下角图标,选择WSL
  2. 用VSCode打开自己驱动的文件夹 WSL系统路径:C:\Users\liujh\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\root
  3. VSCode配置 打开后会显示很多头文件找不到,编写代码的时候也不会有补全,使用下面的配置方法可以解决大部分问题。 文件:驱动代码路径/.vscode/Settings.json
{
	"editor.insertSpaces": false,
	"editor.renderWhitespace": "all",
	"C_Cpp.default.includePath": [
		"/root/linux-4.19/linux-raspberrypi-kernel_1.20200205-1/include",
		"/root/linux-4.19/linux-raspberrypi-kernel_1.20200205-1/include/uapi",
		"/root/linux-4.19/linux-raspberrypi-kernel_1.20200205-1/arch/arm/include",
		"/root/linux-4.19/linux-raspberrypi-kernel_1.20200205-1/arch/arm/include/generated",
	],
	"C_Cpp.default.defines": [
		"__KERNEL__",
	]
}

另外还可以直接再VSCODE中打开一个WSL终端,非常方便。

pi节点搭建容器错误_pi节点搭建容器错误_07