驱动就是获取外设、传感器数据和控制外设。数据会提交给应用程序。
Linux 驱动编译既要编写一个驱动,还要编写一个简单的测试应用程序。
而单片机下驱动和应用都是放在一个文件里,也就是杂在一块。而 Linux 则是分开了。
一、字符设备驱动开发流程
Linux 里一切皆文件,驱动设备表现就是一个/dev/下的文件,/dev/led。应用程序调用 open 函数 打开设备,比如 led。应用程序通过 write 函数向 /dev/led 写数据,比如写1打开,写0关闭。如果要关闭设备就是 close 函数。
字符设备驱动的编写主要是驱动对应的 open、close、read。其实就是 file_operations 结构体的成员变量的实现。
二、驱动模块的加载与卸载
Linux 驱动程序可以编译到 kernel 里,也就是 zImage。也可以编译成模块ko。测试的时候只需要加载ko即可。
1. 驱动编写
编写驱动的注意事项!
编译驱动的时候需要用到 linux 内核源码!因此需要解压缩 Linux 内核源码,编译 Linux 内核源码。得到 zImage 和 dtb。需要使用编译后得到的 zImage 和 dtb 启动系统。这部分不懂的回去看 Linux 内核移植部分。
先编写一个简单的源码,用于测试驱动。
#include <linux/module.h>
static int __init chrdevbase_init(void)
{
return 0;
}
static void __exit chrdevbase_exit(void)
{
}
/*
模块入口与出口
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
Makefile 的编写
KERNELDIR := /home/prover/linux/linux_ok
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o
build : kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
需要隐藏的文件
{
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.o":true,
"**/*.su":true,
"**/*.cmd":true,
"Documentation":true,
},
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/*.o":true,
"**/*.su":true,
"**/*.cmd":true,
"Documentation":true,
}
}
指定内核源码路径
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/home/prover/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include",
"/home/prover/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include",
"/home/prover/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include/generated/"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}
编译后,.ko就是我们需要的驱动文件了。
2. 驱动模块的加载和卸载
开发板上使用命令 modprobe
发现需要创建/lib/modules。
将 .ko文件和可执行文件 chrdevba