简要介绍:

也曾尝试过搭建过交叉编译环境,但是太痛苦了好久都没搞好。一直倒在交叉编译器那一关=-=气得我我直接在树莓派上搭建驱动开发环境。大家有合适的arm交叉编译器推荐给我一下=-=

工具

树莓派4B+树莓派官方系统

一、下载树莓派内核源码

su注入灵魂
复制以下命令到树莓派上执行(会自动下载与自己系统内核对应的源码

wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source
chmod +x rpi-source
./rpi-source -q --tag-update
./rpi-source
apt-get install bc

之后会在/root下生成如下文件
在这里插入图片描述
.tar.gz 是压缩包,linux-3xxxxx(太长了……)是解压后的文件,linux是前者软连接。ls -hl 可查看文件属性
直接mv 保存到 /usr/src 并且重命名为 rpi_code

mv linux-3a33f11c48572b9dd0fecac164b3990fc9234da8 /usr/src/rpi_code
cd /usr/src/rpi_code
ls

二、编译内核

如果是交叉编译的话可以把上一步下载好的.tar.gz文件复制到自己的linux虚拟机上在解压编译内核,后面的步骤都是一样的,不过要用arm交叉编译器编译。

安装依赖项

cd /usr/src/rpi_code
apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev

设置内核默认配置

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-  bcm2711_defconfig  

或者自定义配置(没用过)

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

树莓派4核CPU4线程编译内核(大概需要1h20min,可以打三把排位)

make -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

三、内核驱动程序

引自: https://blog.csdn.net/Chhjnavy/article/details/106494941
hellodriver.c 文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
 
#define HELLOWORLD_MAJOR      200           //主设备号
#define HELLOWORLD_NAME     "helloworld"    //名字
 
static char readbuf[30]; /*读缓冲 */
static char writebuf[30];  /* 写缓冲 */
static char kerneldata[] = {"kerneldata:-!Hello World-"};
 
 
static int helloworld_open(struct inode *inode, struct file *filp)
{
   	printk("printk:-helloworld_open\r\n");
    return 0;
}
 
static int helloworld_release(struct inode *inode, struct file *filp)
{
   	printk("printk:-helloworld_release\r\n");
    return 0;   
}
 
static ssize_t helloworld_read(struct file *filp, __user char *buf, size_t count,
			loff_t *ppos)
{ 
    int ret  = 0;
    printk("printk:-helloworld_read\r\n");
    memcpy(readbuf, kerneldata, sizeof(kerneldata));
    ret = copy_to_user(buf, readbuf, count);
 
    return 0;  
}
 
static ssize_t helloworld_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
    int ret = 0;
    printk("printk:-helloworld_write\r\n");
    ret = copy_from_user(writebuf, buf, count);
    if(ret == 0) {
        printk("printk:-kernel recevdata:%s\r\n", writebuf);
    }
 
    return 0; 
}
 
/*
 * 字符设备 操作集合
 */
static struct file_operations helloworld_fops={
    .owner = THIS_MODULE,
    .open = helloworld_open,
    .release = helloworld_release,
    .read = helloworld_read,
    .write = helloworld_write,
};
 
 
static int __init helloworld_init(void)
{
    int ret = 0;
    printk("printk:-hello world init\r\n");
 
    /* 注册字符设备 */
    ret = register_chrdev(HELLOWORLD_MAJOR, HELLOWORLD_NAME, &helloworld_fops);
    if(ret < 0) {
        printk("printk:-helloworld init failed!\r\n");
    }
	return 0;
}
 
static void __exit helloworld_exit(void)
{
    printk("printk:-helloworld_exit\r\n");	
    /* 注销字符设备 */
    unregister_chrdev(HELLOWORLD_MAJOR, HELLOWORLD_NAME); 
}
 
/*
 模块入口与出口
 */
module_init(helloworld_init);  /* 入口 */
module_exit(helloworld_exit);  /* 出口 */
 
MODULE_LICENSE("GPL");      
MODULE_AUTHOR("changhaijun");

makefile 文件 用树莓派自带的arm编译器/usr/bin/arm-linux-gnueabihf-xxx

ifneq ($(KERNELRELEASE),)
obj-m := hellodriver.o
else  
KDIR := /usr/src/rpi_code/#树莓派内核源码目录
PWD := $(shell pwd)
ENV:=ARCH=arm CROSS_COMPILE=/usr/bin/arm-linux-gnueabihf-

all:
	make $(ENV) -C $(KDIR) M:=$(PWD) modules 
clean:
	rm *.o *.ko *.mod.c modules.order Module.symvers
.PHONY:clean
endif

make 编译驱动
可以看到生成了驱动文件 hellodriver.ko
在这里插入图片描述

四、应用层测试程序

找一个合适的目录新建一个hello.c测试文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char *argv[])
{
    int ret = 0;
    int fd = 0;
    char *filename;
    char readbuf[30], writebuf[30];
    static char usrdata[] = {"user data!"};
 
    if(argc != 3) {
        printf("命令行参数错误!请输驱动文件路径+数字1或2 1:read 2:write\r\n");
        return -1;
    }
 
    filename = argv[1];
 
    fd = open(filename, O_RDWR);
    if(fd < 0 ) {
        printf("Can't open driver file-- %s\r\n", filename);
        return -1;
    }
 
    if(atoi(argv[2]) == 1){ /*  璇?*/
        /* read */
        ret = read(fd, readbuf, 13);
        if (ret < 0) {
            printf("read file %s failed!\r\n", filename);
        }
        else {
            printf("APP read data:%s\r\n", readbuf);
        }
    }
 
    /* write */
    if(atoi(argv[2]) == 2) { /* 鍐?*/ 
        memcpy(writebuf, usrdata, sizeof(usrdata));
        ret = write(fd, writebuf, 11);
        if (ret < 0) {
            printf("write file %s failed!\r\n", filename);
        }else {
            printf("APP writedata:%s\r\n", writebuf);
        }
    }
 
    /* close */
    ret = close(fd);
    if(ret < 0) {
        printf("close file %s falied!\r\n", filename);
    }
 
    return 0 ;
 
}

保存退出编译
todo gcc编译

gcc hello.c -o hello

五、安装驱动、运行程序

回到编译好驱动的地方安装驱动

insmod  helloworld.ko    

查看驱动是否加载上执行命令:(看到helloworld 200)

cat /proc/devices | grep hello

(卸载驱动执行命令):

rmmod helloworld.ko

创建设备节点drivertest自定义设备命名,c 表示字符设备 ,200 主设备号, 0 次设备号

mknod /dev/drivertest  c 200  0  

查看内核数据输出(printk函数输出位置) tail -10 为最后10行的意思

cat /var/log/messages | tail -6

在这里插入图片描述

运行hello应用层程序

./hello /dev/drivertest 1
./hello /dev/drivertest 2

在这里插入图片描述
其中内核printk 在cat /var/log/messages | tail -6 中查看

Logo

更多推荐