Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)
Linux嵌入式驱动开发02——驱动编译到内核
Linux嵌入式驱动开发03——杂项设备驱动(附源码)
Linux嵌入式驱动开发04——应用层和内核层数据传输
Linux嵌入式驱动开发05——物理地址到虚拟地址映射
Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写
Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)
Linux嵌入式驱动开发08——字符设备(步步为营)
Linux嵌入式驱动开发09——平台总线详解及实战
Linux嵌入式驱动开发10——设备树开发详解
Linux嵌入式驱动开发11——平台总线模型修改为设备树实例
Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作
Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)
Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)
Linux嵌入式驱动开发15——等待队列和工作队列
Linux嵌入式驱动开发16——按键消抖实验(内核定时器)
Linux嵌入式驱动开发17——输入子系统
Linux嵌入式驱动开发18——I2C通信
之前也算是一直在学习嵌入式Linux的开发,裸机开发,uboot配置,系统编译,驱动开发,Qt开发, 这一套一知半解的看下来对于怎么开发Linux,还是一头雾水 ,没有一个明确的认知,所以对于这方面的知识打算从头重新建立一个完整的学习框架,这次更加去注重理论的理解和相通性。
驱动分为四个部分
#include
#include
#include
#include
module_init(XXXX_init);
module_exit(XXXX_exit);
MODULE_LICENSE("GPL"); //声明模块拥有开源许可
static int hello_init(void)
{
printk("hello world\n"); // 在内核中无法使用c语言库,所以不用printf
return 0;
}
static void hello_exit(void)
{
printk("bye\n");
}
Linux嵌入式驱动模块modules_helloworld
#include
#include
static int hello_init(void)
{
printk("hello world\n"); // 在内核中无法使用c语言库,所以不用printf
return 0;
}
static void hello_exit(void)
{
printk("bye\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL"); //声明模块拥有开源许可
因为我使用的是vscode进行编辑,对于一些库,不知道自己有没有选择正确怎么办?
因为是编写 Linux驱动,因此会用到 Linux源码中的函数。我们需要在 VSCode中添加 Linux源码中的头文件路径。打开 VSCode,按下 Crtl+Shift+P”打开 VSCode的控制台,然后输入
C/C++: Edit configurations(JSON)
打开 C/C++编辑配置文件,如图 所示:
打开以后会自动在 .vscode目录下生成一个名为 c_cpp_properties.json的文件,此文件默认内容如下所示:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "gnu++14",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
第 5行的 includePath表示头文件路径,需要将 Linux源码里面的头文件路径添加进来,也就是我们前面移植的 Linux源码中的头文件路径。添加头文件路径以后的 c_cpp_properties.json的文件内容如下所示:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/work/linux-4.1.15/include",
"/work/linux-4.1.15/arch/arm/include",
"/work/linux-4.1.15/arch/arm/include/generated"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "gnu++14",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
第 7~9行就是添加好的 Linux头文件路径。分别是开发板所使用的 Linux源码下的 include、arch/arm/include和 arch/arm/include/generated这三个目录的路径,注意,这里使用了绝对路径。
把驱动编译成模块,然后使用命令把驱动加载到内核里
直接把驱动编译到内核
我们用的第一种方法,编译成模块,然后加载
所以我们需要写一个Makefile
# 开发板Linux内核的实际路径
# KDIR变量
KDIR:=/work/linux-4.1.15
# 获取当前目录
PWD:=$(shell pwd)
# obj-m表示将 chrdevbase.c这个文件 编译为 chrdevbase.ko模块。
obj-m += helloworld.o
# 编译成模块
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
编译驱动之前需要注意的问题:
使用命令
make menuconfig
可以打开这个可视化的配置界面
如果上方的红圈位置显示x86,那么输入
export ARCH=arm
修改成arm才可以
在使用make指令前,要先进行环境变量的配置
. /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa9hf-neon-poky-linux-gnueabi
使用指令
make
使用指令进行网络发送文件
scp helloworld.ko root@192.168.0.232:/lib/modules/4.1.15-dirty/
然后在我们的板子上的/lib/modules/4.1.15-dirty/目录下就可以看到文件了,加载驱动
insmod helloworld.ko
可以看到执行了我们之前写的printk(“hello world\n”);指令,进一步查看加载过的模块
lsmod
使用指令
rmmod hellworld
注意:这里没有ko后缀
再次查看列表,可以看到已经没有了helloword驱动模块
开发板上自带的串口不够使用的情况下,我们需要对串口进行扩展,其中一个方法就是使用usb转串口来进行实现,扩展我们的串口,但是我们的开发板是没有这个驱动来支持这个工作,所以,我们要进行驱动的加载。
USB转串口用到的芯片都比较成熟,比如CH340,CH340支持的驱动非常的全面,我们去官网下载就可以了
http://www.wch.cn/download/CH341SER_LINUX_ZIP.html
打开压缩包,我们只需要里面的c源文件就可以
然后复制到wsl中,把上面helloworld的makefile也复制过来
修改makefile的文件名,就可以了
然后就是配置环境变量,编译
生成了ko文件,发送到板子指定目录
ssh登录开发板,然后就可以看到了,insmod安装
这样,我们从网上找到源码然后移植加载,就成功了。
因为大部分的开发板都会继承这个常见的驱动模块,所以,我i们只需要熟悉这个过程就好了。
后面我们继续研究怎么把我们的驱动编译到内核中