驱动开发-1

news2024/12/31 6:26:02

一、驱动课程大纲

  • 内核模块
  • 字符设备驱动
  • 中断

二、ARM裸机代码和驱动有什么区别?

1、共同点:

都能够操作硬件

2、不同点:

1)裸机就是用C语言给对应的寄存器里面写值,驱动是按照一定的套路往寄存器里面写值

2)arm裸机单独编译单独执行,驱动依赖内核编译,依赖内核执行(根据内核指定好的架构和配置去实现

3)arm裸机同时只能执行一份代码,驱动可以同时执行多分代码(且当要操作串口的时候,内核写的一部分代码咱们程序员就不用去写了,比较方便)

4)arm裸机只需要一个main就可以了,在main函数中写相应的逻辑代码即可驱动是依赖内核的框架和操作硬件的过程。

(驱动里面操作LED灯的寄存器)(驱动模块是依赖内核框架执行代码)

三、linux系统组成

1、0-3G空间

的用户空间是每个进程单独拥有0-3G的空间

2、系统调用(软中断swi)----

(应用层通过系统调用与底层交互,swi,将应用层切换到内核层。

注:1G的物理内存映射成0~4G的虚拟内存,每个进程都可以访问内核,0~3G是每个进程单独拥有的,3G~4G是所有的共有的。代码运行在物理内存上,向虚拟内存上面写值,其实是写在物理内存上面的

3、kernel  :  【3-4G】内核

内核的5大功能

1)进程管理:进程的创建,销毁,调度等功能

注:可中断,不可中断,就是是否被信号打断。从运行状态怎样改到可中断等待态,和不可中断等待态操作系统开始会对每个进程分配一个时间片,当进程里面写了sleep函数,进程由运行到休眠态,但是此时CPU不可能等着。有两种方法,1:根据时间片,CPU自动跳转,2:程序里面自己写能引起CPU调度的代码就可以

2)文件管理:通过文件系统ext2/ext3/ext4 yaff jiffs等来组织管理文件

3)网络管理:通过网络协议栈(OSI,TCP)对数据进程封装和拆解过程(数据发送和接收是通过网卡驱动完成的,网卡驱动不会产生文件(在Linux系统dev下面没有相应的文件),所以不能用open等函数,而是使用的socket)。

4)内存管理:通过内存管理器对用户空间和内核空间内存的申请和释放

5)设备管理: 设备驱动的管理(驱动工程师所对应的)

字符设备驱动: (led 鼠标  键盘 lcd touchscreen(触摸屏))

1.按照字节为单位进行访问,顺序访问(有先后顺序去访问)

2.会创建设备文件,open read  write close来访问

块设备驱动  :(camera  u盘  emmc)

1.按照块(512字节)(扇区)来访问,可以顺序访问,可以无序访问

2.会创建设备文件,open read  write close来访问

网卡设备驱动:(猫)

1.按照网络数据包来收发的。

4、宏内核、微内核

1)宏内核:将进程,网络,文件,设备,内存等功能集成到一个内核中

特点:代码运行效率高。

缺点:如果有一个部分出错整个内核就崩溃了。

eg:ubuntu Android

2)微内核:只将进程,内存机制集成到这个内核中,文件,设备,驱动在操作系统之外。通过API接口让整个系统运行起来。

缺点:效率低 优点:稳定性强(华为手机)

eg:鸿蒙 

5.驱动模块(三要素:入口;出口;许可证)

1)入口:资源的申请   

2)出口:资源的释放

3)许可证:GPL(写一个模块需要开源,因为Linux系统是开源的,所以需要写许可协议)

#include <linux/init.h>

#include <linux/module.h>                                                                          

1)驱动函数框架

申请资源函数		
static int __init  hello_init(void)
    //(__init可以不指定,及可以不写,但是正常是写的)
	//__init将hello_init放到.init.text段中
{
		return 0;
}
释放资源函数
static void __exit hello_exit(void) 
//__exit将hello_exit放到.exit.text段中
{

}
module_init(hello_init);//告诉内核驱动的入口地址(函数名为函数首地址)
module_exit(hello_exit);//告诉内核驱动的出口地址
MODULE_LICENSE("GPL");//许可证

2)Makefile函数

Makefile:
KERNELDIR:= /lib/modules/$(shell uname -r)/build/  //Ubuntu内核的路径
#KERNELDIR:= /home/linux/kernel/kernel-3.4.39/(板子内核路径)
PWD:=$(shell pwd)//驱动文件的路径(打开一个终端看终端的路径)
all:  //目标
make -C $(KERNELDIR) M=$(PWD) modules(-C:进入顶层目录执行)
//注:进入内核目录下执行make modules这条命令
//如果不指定 M=$(PWD) 会把内核目录下的.c文件编译生成.ko
M=$(PWD) 想编译模块的路径(驱动模块所在路径)
clean:
	 make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o 	 //指定编译模块的名字

3)make运行结果

4)追代码命令

注:在内核路径下进行创建索引,追踪时也需要在内核路径下追踪

make tags

创建索引文件

ctags -R

在终端上

vi -t xxx

在代码中跳转

ctrl + ]

ctrl + t

Ubuntu内核所对应的内核路径

6.命令

1、sudo insmod hello.ko   安装驱动模块

2、sudo rmmod  hello      卸载驱动模块

3、lsmod                  查看模块

4、dmesg                  查看消息

5、sudo dmesg -C          直接清空消息不回显

6、sudo dmesg -c          回显后清空

7.内核中的打印函数printk

搜索函数,搜到以后,在里面任意找到一个,看函数原形就OK

printk(打印级别 "内容")

printk(KERN_ERR "Fail%d",a);

printk(KERN_ERR "%s:%s:%d\n",__FILE__,__func__,__LINE__);

(驱动在哪一个文件,哪一个函数,哪一行)

printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

8.查看内核打印级别:vi -t  KERN_ERR(查看内核打印级别)

1)include/linux/printk.h

#define KERN_EMERG  "<0>"   /* system is unusable        */(系统不用)

#define KERN_ALERT  "<1>"   /* action must be taken immediately */(被立即处理)

#define KERN_CRIT   "<2>"   /* critical conditions          */(临界条件,临界资源)

#define KERN_ERR    "<3>"   /* error conditions         */(出错)

#define KERN_WARNING    "<4>"   /* warning conditions           */(警告)

#define KERN_NOTICE "<5>"   /* normal but significant condition */(提示)

#define KERN_INFO   "<6>"   /* informational            */(打印信息时候的级别)

#define KERN_DEBUG  "<7>"   /* debug-level messages         */ (调试级别)

0 ------ 7

最高的  最低的

2)linux@ubuntu:~$ cat  /proc/sys/kernel/printk

 4         4          1          7

终端的级别     消息的默认级别   终端的最大级别  终端的最小级别

#define console_loglevel (console_printk[0])

#define default_message_loglevel (console_printk[1])

#define minimum_console_loglevel (console_printk[2])                                               

#define default_console_loglevel (console_printk[3])

只有当消息的级别大于终端级别,消息才会被显示

但对与咱们的这个Ubuntu被开发者修改过来,所有消息不会主动回显

3)修改系统默认的级别

su root

echo 4 3 1 7 > /proc/sys/kernel/printk

虚拟机的默认情况:

板子的默认情况: 

4)如果想修改开发板对应的打印级别

vi  rootfs/etc/init.d/rcS

echo 4 3 1 7 > /proc/sys/kernel/printk

rootfs/etc/init.d/rcS里面添加上以后再起板子,板子的级别就为如下:

安装驱动和卸载驱动时,消息会打印。

9、驱动多文件编译

hello.c  add.c

 Makefile

demo-y+=hello.o 

demo-y+=add.o

(-y作用:将hello.o add.o放到demo.o中:可放置多个文件,打包为一个文件)

obj-m:=demo.o

最终生成demo.ko文件

1)hello.c

#include<linux/module.h>
#include<linux/init.h>
#include<linux/printk.h>
#include"add.h"
//申请资源函数	
//存储类型 数据类型 指定存放位置 函数名(行参) 
//(__init可以不指定,及可以不写,但是正常是写的)	
static int __init  hello_init(void)//__init将hello_init放到.init.text段中
{
    printk(KERN_ERR "HELLO WORLD add=%d\n",add(2,3));//KERN_ERR后必须加空格
    printk("%s %s %d\n",__FILE__,__func__,__LINE__);
		return 0;
}
//释放资源函数
static void __exit hello_exit(void) //__exit将hello_exit放到.exit.text段中
{
    printk(KERN_INFO "welcome......sub=%d\n",sub(2,3));
    printk("%s %s %d\n",__FILE__,__func__,__LINE__);
}
//入口
module_init(hello_init);//告诉内核驱动的入口地址(函数名为函数首地址)
//出口
module_exit(hello_exit);//告诉内核驱动的出口地址
//许可证
MODULE_LICENSE("GPL");//许可证

2)add.c

int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}

3)add.h

#ifndef ADD_H
#define __ADD_H__
int add(int a,int b);
int sub(int a,int b);

#endif

4)makefile

#KERNEL_PATH=/lib/modules/4.15.0-142-generic/build/  #Ubuntu内核的路径
KERNEL_PATH=/home/hq/kernel/kernel-3.4.39/ #(板子内核路径)
PWD=$(shell pwd) #将shell命令pwd执行的结果赋值给pwd,驱动文件的路径(想编译模块的路径)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	#(-C进入顶层目录)
	#注:进入内核目录下执行make modules这条命令,如果不指定M=$(pwd)会把内核目录下的.c文件编译生成.ko
	#M=路径:指定需要编译的驱动代码所在的路径
.PHONY:clean
clean:
	 make -C $(KERNEL_PATH) M=$(PWD) clean
#将hello.c和add.c编译成二进制文件打包到一起生成一个驱动模块
demo-y += hello.o
demo-y += add.o
obj-m = demo.o 	 #指定编译模块的名字

注意修改路径,可在开发板或ubuntu下运行,开发板下需先在ubuntu下编译再到板子上运行

10、模块传递参数

命令传递的方式

sudo insmod demo.ko hello world

---------------------------------------------------------

 * Standard types are:                                                                             

 *  byte, short, ushort, int, uint, long, ulong  (没有找到char!!!!!!!!)

 *  charp: a character pointer

 *  bool: a bool, values 0/1, y/n, Y/N.

 *  invbool: the above, only sense-reversed (N = true).

  1. 接收命令行传递的参数module_param

module_param(name, type, perm) 
功能:接收命令行传递的参数
参数:
	@name :变量的名字
	@type :变量的类型
	@perm :权限  0664  0775(其它用户对我的只有读和执行权限,没有写的权限)
	modinfo hello.ko(查看变量情况)

2)对变量的功能进行描述MODULE_PARM_DESC

MODULE_PARM_DESC(_parm, desc)
功能:对变量的功能进行描述
参数:
	@_parm:变量
	@desc :描述字段
只能传十进制,不可以写十六进制

练习:

1.byte类型如何使用 (传递参数用ascii)

2.如何给一个指针传递一个字符串

sudo insmod hello.ko a=20 b=30 c=65 p="hello_world"

3)接收命令行传递的数组module_param_array

module_param_array(name, type, nump, perm) 
功能:接收命令行传递的数组
参数:
		@name :数组名
		@type :数组的类型
		@nump :参数的个数,变量的地址
		@perm :权限

注意:传字符的时候写ASCII码值;传递字符串的时候,不能有空格

字符串不能有空格,数组元素以逗号“ ,”间隔。

sudo insmod hello.ko a=121 b=10 c=65 p="hello" ww=1,2,3,4,5

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/moduleparam.h>

int a=100;
module_param(a,int,0775);
MODULE_PARM_DESC(a,"this is int number.");

char ch='A';
module_param(ch,byte,0775);
MODULE_PARM_DESC(ch,"this is char.");

char *sp=NULL;
module_param(sp,charp,0775);
MODULE_PARM_DESC(sp,"this is char pointe.");

int st[10];
int num;
module_param_array(st,int, &num, 0775);
MODULE_PARM_DESC(st,"this is int array.");
//驱动三要素
//存储类型  数据类型  指定存放的位置  函数名(行参)
static int __init hello_init(void)
{
   int i;
    printk(KERN_ERR "hello world\n");
    printk("%s %s %d a=%d\n",__FILE__,__func__,__LINE__,a);
    printk("%d %c %s\n",a,ch,sp);
    for( i=0;i<num;i++)
    {
        printk("st[%d]=%d\n",i,st[i]);
    }
    return 0;
}
static void __exit hello_exit(void)
{
    printk(KERN_INFO "welcome ……\n");
    printk(KERN_ERR "%s %s %d\n",__FILE__,__func__,__LINE__);
}
//入口:申请资源
module_init(hello_init);
//出口:释放资源
module_exit(hello_exit);
//许可证
MODULE_LICENSE("GPL");

makefile

#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39  #开发板的路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build #虚拟机路径

PWD=$(shell pwd)  #将shell命令pwd执行的结果赋值给PWD变量
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	# -C 路径:指定到切换到那个路径,执行make modules命令
	#  M=路径:指定需要编译的驱动代码所在的路径

.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m += hello.o

#要编译生成驱动模块的目标程序

       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么有的开关电源需要加自举电容?

一、什么是自举电路&#xff1f; 1.1 自举的概念 首先&#xff0c;自举电路也叫升压电路&#xff0c;是利用自举升压二极管&#xff0c;自举升压电容等电子元件&#xff0c;使电容放电电压和电源电压叠加&#xff0c;从而使电压升高。有的电路升高的电压能达到数倍电源电压。…

阶段十-物业项目

可能遇到的错误&#xff1a; 解决jdk17javax.xml.bind.DatatypeConverter错误 <!--解决jdk17javax.xml.bind.DatatypeConverter错误--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>…

git 使用方法自用(勿进)本地开发分支推上线上开发分支

一、//查看状态 1.git status 二、//查看改了哪个文件夹 1.git diff 2.//会出现改了哪个文件夹src/components/partials/Slider.js 三、//查看改了的文件夹里面具体改了啥内容 1.git diff src/components/partials/Slider.js 四、提交所有 1. git add . 五、写备注…

连锁便利店管理系统有什么用

连锁便利店管理系统对于连锁便利店的运营和管理非常有用。以下是一些常见的用途&#xff1a; 1. 库存管理&#xff1a;连锁便利店通常需要管理多个门店的库存&#xff0c;管理系统可以帮助实时掌握各个门店的库存情况&#xff0c;包括商品数量、进货记录、库存调拨等。这样可以…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch、Zookeeper、Kafka、NoSQL等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

为什么c++的开源库那么少?

为什么c的开源库那么少&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「 C的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&#xff01;…

Skywalking 中 Agent 自动同步配置源码解析

文章目录 前言正文实现架构实现模型OAP 同步 ApolloConfigWatcherRegisterConfigChangeWatcher Agent 侧 前言 本文代码 OAP 基于 v9.7&#xff0c;Java Agent 基于 v9.1&#xff0c;配置中心使用 apollo。 看本文需要配合代码“食用”。 正文 Skywalking 中就使用这种模型…

基于SSM在线协同过滤汽车推荐销售系统

SSM毕设分享 基于SSM在线协同过滤汽车推荐销售系统 1 项目简介 Hi&#xff0c;各位同学好&#xff0c;这里是郑师兄&#xff01; 今天向大家分享一个毕业设计项目作品【】 师兄根据实现的难度和等级对项目进行评分(最低0分&#xff0c;满分5分) 难度系数&#xff1a;3分 工作…

Springsecurty【2】认证连接MySQL

1.前期准备 基于Spring Initializr创建SpringBoot项目&#xff08;基于SpringBoot 2.7.12版本&#xff09;&#xff0c;实现与MyBatisPlus的项目整合。分别导入&#xff1a;CodeGenerator和MyBatisPlusConfig。 CodeGenerator&#xff1a;用于MybatisPlus代码生成&#xff1b;…

JavaScript原型,原型链 ? 有什么特点?

一、原型 JavaScript 常被描述为一种基于原型的语言——每个对象拥有一个原型对象 当试图访问一个对象的属性时&#xff0c;它不仅仅在该对象上搜寻&#xff0c;还会搜寻该对象的原型&#xff0c;以及该对象的原型的原型&#xff0c;依次层层向上搜索&#xff0c;直到找到一个…

<各国地图轮廓app>技术支持

如在app使用过程中遇到任何问题&#xff0c;请与开发者联系caohechunhotmail.com

贴片晶振无源石英谐振器直插晶振

贴片晶振 贴片晶振3.579M~25MHz无源石英谐振器直插晶振 文章目录 贴片晶振前言一、贴片晶振3.579M~25MHz无源石英谐振器直插晶振二、属性三、技术参数总结前言 贴片晶振(Surface Mount Crystal Oscillator)是一种采用表面贴装技术进行安装的晶振。它的主要特点是封装小巧、安…

【黑马甄选离线数仓day10_会员主题域开发_DWS和ADS层】

day10_会员主题域开发 会员主题_DWS和ADS层 DWS层开发 门店会员分类天表: 维度指标: 指标&#xff1a;新增注册会员数、累计注册会员数、新增消费会员数、累计消费会员数、新增复购会员数、累计复购会员数、活跃会员数、沉睡会员数、会员消费金额 维度: 时间维度&#xff08…

SpringBoot增删改查接口实例

前言 增删改查是后端最基本的技能。下面我就带领小伙伴们通过一个简单的示例来讲解SpringBoot的增删改查。Spring Boot框架层次从上至下可分为5层&#xff1a;分别为View层&#xff0c;Controller层&#xff0c;Service层&#xff0c;Mapper层&#xff0c;Model层 1. View层&a…

机械臂快速接触刚性环境阻抗对相互作用力的影响

当机械臂快速接触刚性环境时&#xff0c;阻抗对相互作用力的影响尤为显著。由于刚性环境对机械臂产生的阻力&#xff0c;机械臂在接触时会受到一个与运动方向相反的作用力&#xff0c;即接触力。阻抗参数的设置对接触力的大小具有重要影响。 一方面&#xff0c;阻尼参数决定了…

求三角形面积 C语言xdoj91

题目描述&#xff1a; 输入三角形的三边长&#xff0c;计算三角形的面积&#xff0c;结果保留两位小数。 边长分别为a,b,c&#xff0c;三角形的面积公式为ssqrt(p(p-a)(p-b)(p-c)),其中p(abc)/2。 输入格式&#xff1a;共一行&#xff0c;输入三个数&#xff0c;保留两位小数&a…

SQL注入【sqli靶场第23-28关】(七)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、总体思路 先确认是否可以SQL注入&#xff0c;使用单…

Apache Commons IO: 简化文件和IO操作

第1章&#xff1a;引言 咱们在做Java编程的时候&#xff0c;经常会遇到各种文件操作和输入输出&#xff08;IO&#xff09;的问题。不论是读取一个配置文件&#xff0c;还是把数据写入日志&#xff0c;这些看似简单的任务有时候会让人头疼。传统的Java IO操作&#xff0c;虽然…

多维时序 | MATLAB实CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测

多维时序 | MATLAB实现CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测预测效果基本介…

IP地址定位解析

文章目录 1 IP地址定位解析1.1 获取用户 IP 地址1.2 淘宝库获取用户 IP 地址属地1.3 通过 Ip2region 定位1.3.1 Ip2region 介绍1.3.2 Ip2region 特性1.3.3 客户端实现1.3.3.1 引入 Maven 仓库1.3.3.2 基于文件查询1.3.3.3 缓存VectorIndex索引1.3.3.4 缓存整个 xdb 数据 1 IP地…