驱动开发-1

一、驱动课程大纲

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

二、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) cleanobj-m += hello.o#要编译生成驱动模块的目标程序

       

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开关电源厚膜集成电路引脚功能

开关电源厚膜集成电路引脚功能 一、 STR51213、STR50213、STR50103 引脚号 引脚功能 1 接地&#xff0c;内接稳压基准电路 2 开关管基极 3 开关管集电极 4 开关管发射极 5 误差比较电压信号输入&#xff0c;兼待机控制 二、 STR3302、STR3202 引脚号 引脚功能 1内部半…

华为vrrp+mstp+ospf+dhcp+dhcp relay配置案例

1、左边是vlan 10主桥&#xff0c;右边是vlan 20的主桥&#xff0c;并且互为备桥 2、 vlan 10 vrrp网关默认用左边&#xff0c;vlan 20的vrrp 网关默认用右边&#xff0c;对应mstp生成树 3、两边都track检测&#xff0c;不通就把vrrp减掉60&#xff0c;这样就会自动切另一边了 …

四、UART_阻塞发送中断接收

1、开发环境 (1)Keil MDK: V5.38.0.0 (2)MCU: mm320163D7P 2、实验目的&原理图 2.1、实验目的 (1)上位机串口助手给MCU发送信息&#xff0c;MCU串口通过通过串口助手接收后&#xff0c;将接收到的内容通过串口助手发送到上位机。 (2)串口在whil循环中每隔1秒发送一次…

【一起学Rust | 框架篇 | Tauri2.0框架】Tauri2.0环境搭建与项目创建

文章目录 前言一、搭建 Tauri 2.0 开发环境二、创建 Tauri 2.0 项目1.创建项目2.安装依赖4. 编译运行 三、设置开发环境四、项目结构 前言 Tauri在Rust圈内成名已久&#xff0c;凭借Rust的可靠性&#xff0c;使用系统原生的Webview构建更小的App 以及开发人员可以灵活的使用各…

离散型制造企业为什么要注重MES管理系统的实施

离散型制造企业经常面临三个核心问题&#xff1a;生产什么、生产多少以及如何生产。尽管许多企业都实施了ERP系统&#xff0c;但仍然绕不开MES管理系统的话题。本文将从三个方面详细解释为什么离散型企业需要实施MES管理系统。 一、生产线经常出现的问题 在离散型企业中&#…

Blazor 混合开发_MAUI+Vue_WPF+Vue

Blazor 混合开发_MAUIVue_WPFVue 背景混合开发的核心为什么必须使用 wwwroot 文件夹放置 Web 项目文件 创建 MAUI 项目创建 wwwroot 文件夹服务注册创建 _import.razor添加 Main.razor 组件修改 MainPage.xaml 文件 创建 WPF 项目创建 wwwroot 文件夹服务注册创建 _import.razo…

OpenCV利用HSV颜色区间分离不同物体

需求 当前有个需求是从一个场景中将三个不同的颜色的二维码分离出来&#xff0c;如下图所示。 这里有两个思路可以使用 思路一是通过深度学习的方式&#xff0c;训练一个能够识别旋转边界框的模型&#xff0c;但是需要大量的数据进行模型训练&#xff0c;此处缺少训练数据&a…

华清远见嵌入式学习——ARM——作业2

目录 作业要求&#xff1a; 现象&#xff1a; 代码&#xff1a; 思维导图&#xff1a; 模拟面试题&#xff1a; 作业要求&#xff1a; GPIO实验——3颗LED灯的流水灯实现 现象&#xff1a; 代码&#xff1a; .text .global _start _start: 设置GPIOEF时钟使能 0X50000…

Openwrt AP 发射 WiFi 信号

问题 想一次把 OpenWrt 路由器 wifi 问题给解决&#xff0c;完全取代路由器。 使用 倍控的 N5105 设备&#xff0c;有 mPCIe 接口&#xff0c;使用了 intel AX200 无线网卡&#xff0c;支持 2.4G 与 5G。 设置步骤 OpenWrt 镜像 第一次使用的镜像不支持 wifi&#xff0c;在…

量化服务器 - 后台挂载运行

服务器 - 后台运行 pip3命令被kill 在正常的pip命令后面加上 -no-cache-dir tmux 使用教程 https://codeleading.com/article/40954761108/ 如果你希望在 tmux 中后台执行一个 Python 脚本&#xff0c;你可以按照以下步骤操作&#xff1a; 启动 tmux: tmux这将会创建一个新…

Http---HTTP响应报文

1. HTTP响应报文分析 HTTP 响应报文效果图: 响应报文说明: --- 响应行/状态行 --- HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述 --- 响应头 --- Server: Tengine # 服务器名称 Content-Type: text/html; charsetUTF-8 # 内容类型 Transfer-Encoding: chunked # 发送给客…

HBase基础知识(三):HBase架构进阶、读写流程、MemStoreFlush、StoreFile Compaction、Region Split

1. 架构原理 1&#xff09;StoreFile 保存实际数据的物理文件&#xff0c;StoreFile以HFile的形式存储在HDFS上。每个Store会有一个或多个StoreFile&#xff08;HFile&#xff09;&#xff0c;数据在每个StoreFile中都是有序的。 2&#xff09;MemStore 写缓存&#xff0c;由于…

Hadoop入门学习笔记——三、使用HDFS文件系统

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 三、使用HDFS文件系统3.1. 使用命令操作HDFS文件系统3.1.…

SecureCRT连接vmware虚拟机的centos系统配置

软件版本&#xff1a;VMware10.0.3&#xff0c;centos 7&#xff0c;securecrt 8.7.2 1&#xff0c;虚拟网络编辑器选择桥接模式&#xff0c; 2&#xff0c;如果不小心删除网络&#xff0c;centos关机状态下&#xff0c;选择恢复默认设置。 3&#xff0c;进入linux系统&#…

【线性代数】决定张成空间的最少向量线性无关吗?

答1&#xff1a; 是的&#xff0c;张成空间的最少向量是线性无关的。 在数学中&#xff0c;张成空间&#xff08;span space&#xff09;是一个向量空间&#xff0c;它由一组向量通过线性组合&#xff08;即每个向量乘以一个标量&#xff09;生成。如果这组向量是线性无关的&…

[JS设计模式]Mixin Pattern

Mixin是一个对象&#xff0c;我们可以使用它来为另一个对象或类添加可重用的功能&#xff0c;而无需使用继承。我们不能单独使用mixins:它们的唯一目的是在没有继承的情况下向对象或类添加功能。 假设对于我们的应用程序&#xff0c;我们需要创建多个狗。然而&#xff0c;我们…

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…

Docker - 镜像 | 容器 日常开发常用指令 + 演示(一文通关)

目录 Docker 开发常用指令汇总 辅助命令 docker version docker info docker --help 镜像命令 查看镜像信息 下载镜像 搜索镜像 删除镜像 容器命令 查看运行中的容器 运行容器 停止、启动、重启、暂停、恢复容器 杀死容器 删除容器 查看容器日志 进入容器内部…

Linux创建macvlan 测试bridge、private和vepa模式

Linux创建macvlan&#xff0c;测试bridge、private和vepa模式 最近在看Docker的网络&#xff0c;看到关于macvlan网络的介绍。查阅了相关资料&#xff0c;记录如下。 参考 1.Linux Macvlan 2.图解几个与Linux网络虚拟化相关的虚拟网卡-VETH/MACVLAN/MACVTAP/IPVLAN 环境 操…

谷粒商城-商品服务-新增商品功能开发(商品图片无法展示问题没有解决)

在网关配置路由 - id: member_routeuri: lb://gulimemberpredicates:- Path/api/gulimember/**filters:- RewritePath/api/(?<segment>.*),/$\{segment}并将所有逆向生成的工程调式出来 获取分类关联的品牌 例如&#xff1a;手机&#xff08;分类&#xff09;-> 品…