Linux驱动开发(一)树莓派4B驱动开发环境搭建
文章目录交叉编译简要介绍:工具一、下载交叉编译工具二、编译测试程序三、将编译好的程序下载到树莓派并运行交叉编译简要介绍:交叉编译器(Cross compiler)是指一个在某个系统平台下可以产生另一个系统平台的可执行文件的编译器。交叉编译器的存在对于从一个开发主机为多个平台编译代码是非常有必要的。直接在平台上编译有时行不通,例如在一个嵌入式系统的单片机 ,因为它们没有完整的现代操作系统环境,无法直
简要介绍:
也曾尝试过搭建过交叉编译环境,但是太痛苦了好久都没搞好。一直倒在交叉编译器那一关=-=气得我我直接在树莓派上搭建驱动开发环境。大家有合适的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 中查看
更多推荐
所有评论(0)