目录
一、什么是模块化编程
在嵌入式里所谓的模块化编程指的是你后期加载的这些驱动代码,就是所谓的模块化,你去编写这些驱动,就是在编译模块化编程,这里的模块化编程和你学习的 C 语言和 STM32 里的模块化还有所不同,那个模块化的意思就是说他一个.C 给拆分了多个.c 或是是.h,一般都是一个.c 对应一个.h,驱动里的模块化指的是你操作系统已经运行起来了,你后期去加载的这些驱动就会成为模块化,好处就是极大的缩小的内核的镜像大小。前期就给你提供一个最基本的嵌入式操作系统,后期你需要什么驱动你就去加载或者是编写什么驱动。
二、怎么把自己编译代码给加载到开发板上运行
驱动代码他编译生成的二进制文件的后缀名是固定的,是 xxx.ko,这个.ko 就是你要加载到驱动里去运行的驱动代码,怎么把一个驱动代码给加载到正在运行的一个操作系统里。
这里就通过指令来进行操作
加载指令:所谓加载指令指的就是把你自己编写的 xxx.ko 驱动文件,给放到内核里运行。insmod xxx.ko 当你执行完这指令之后,那么你自己编写驱动代码就会在内核里运行。
卸载指令:就是把你写的驱动代码,从内核里去删除掉,rmmod xxx.ko
查看指令: 就是查看当前你加载的驱动程序的加载情况 lsmod
驱动加载到内核里有两种方法:
第一种:静态编译
就是你编译的代码就直接集成到了内核镜像里,缺点就是你每次都需要从新编译内核,从新烧写内核,优点稳定。
第二种:动态编译
就是不用每次去编译内核,烧写内核,就是通过一个 makefile 去单独的编译你自己写的驱动码,优点 :效率高 缺点:有可能出现不稳定的情况,这种情况极少出现。
前期开发板一般都是使用的动态编译。
三、驱动编程的框架
驱动代码他和你编写的 C 语言和 STM32 有所不同,驱动还是很大的。这里不管里是编写 C 语言还好,还是编写 STM32 也罢,你们首先都会搞一个自己的 main 函数,驱动代码根本就不需要你编写 main 函数,因为内核的源码已经给你写好了。你只需要编写自动驱动的代码的入口和出口即可,当你去加载自己的代码到驱动里的时候,他会自动的去调用你代码里的加载函数(入口函数),当你去卸载驱动代码的时候,他会自动的去调用你自己写的卸载函数(出口函数)。
任何人只要你写的是 Linux 嵌入式驱动的代码,他们的框架都是一样的。
加载函数的声明:
module_init(加载函数的名字); 这里是声明加载函数的
卸载函数的声明
module_exit(卸载函数的名字); 这里是声明卸载函数的
开源协议的声明
MODULE_LICENSE(“GPL”);//声明开源协议
在内核里只要这个函数他不是 void 那么就需要要有返回值,哪怕你返回的是 0,否则内核在编译的时候会报警告
变量在使用的时候一定要先定义,并且要在函数最前面去定义。否则可能报错
具体框架:
#include <linux/module.h>
#include <linux/kernel.h>
//加载函数的定义
static int __init myhello_init(void)
{
return 0;
}
//卸载函数的定义
static void __exit myhello_exit(void)
{
}
module_init(myhello_init);//加载函数的声明
module_exit(myhello_exit);//卸载函数的声明
MODULE_LICENSE(“GPL”);//开源协议的声明
其中必须要有#include <linux/module.h> #include <linux/kernel.h>这两个头文件。
使用insmod xxx.ko 的时候就会自动的去调用你加载函数
使用rmmod xxx.ko 的时候就会自动的去调用你卸载函数
加载函数和卸载函数的命名规则
加载函数 xxx_init
卸载函数 xxx_exit
四、驱动编程具体实例
1、编写单模块化驱动代码
其中obj-m+=led.o相当于最后生成的.ko文件,是要推到板子上的。
使用make生成.ko文件
使用虚拟机连接到开发板上,将.ko推到开发板中。
可以看到代码在开发板运行结果,即内核中运行的结果。
2、编写多模块化驱动代码
leda调用ledb的函数。
3、编写向模块传参驱动代码
内核的传递参数没有这么简单,因为内核的主函数是人家已经写好的,是不不可以随便改变的,这里内核给咱们提供了你专门传递参数的函数,你想给自己编写的驱动代码里的某一个参数传递参数,你就要使用内核提供的函数进行声明,然后在 insmod xxx.ko 后加你要给那个变量传递什么值
int num = 5;这里代码原本里边的内容
insmod xxx.ko num=x;这是想修改的内容
向模块传递参数的函数
module_param(name,type,perm)
参数:
name:你给内核里的一个变量传递参数
Type:他是你传递变量的类型
*注意:如果你传递的这个变量是一个 char 类型的
这里的类型你就要写 charp
Perm:你要对这个变量进行什么操作
S_IRUGO|S_IWUSR — 可读可写
你传递的是一个动态的,就是他不会改变原来的内容,只是动态改变本次值的内容。如果你定义这个变量是一个数组类型的此时你就需要使用另一个函数做声明了
module_param_array(name,type,num,perm)
参数:
name:你要传递数组的变量名
Type:数组的类型
Num:记录你给数组传递的参数的个数 — 这个变量需要取地址
Perm:你要对这个变量进行什么操作
S_IRUGO|S_IWUSR — 可读可写
开始时传入的参数:
传入后现象:
4、编写多模块化驱动代码另一种方式
多个.c 编译生成一个.ko,就像 C 语言里的模块化一样,把一个.c 里内容给筛分了多个.c,最终编译还是一个.ko
b中函数被a调用:
obj-m := ledc.o #最终生成模块的名字就是 ledc.ko
ledc-objs = leda.o ledb.o#意思是生成的ledc.o是依赖于leda.o和ledb.o生成的。