在mini2440上编写linux应用程序、字符设备驱动程序的编写与编译

在mini2440上编写linux应用程序

结合前两篇的学习,一个linux操作系统已经在mini2440上运行起来了,结合交叉编译环境和nfs等工具,我们可以在mini2440上编写任何我们在linux系统编程中学到的应用程序。一个简要的多文件Makefile文件如下:
在这里插入图片描述

linux驱动程序

linux系统驱动程序分为三大类,字符设备驱动,块设备驱动和网络设备驱动。其中字符设备驱动是使用最多的一种,从点灯到IIC,SPI,音频设备等的驱动都是字符设备驱动。块设备和网络设备驱动要比字符设备驱动复杂,就是因为其复杂所以半导体厂商一般都给我们编写好了,大多数情况下都是直接可以使用的。所谓的块设备驱动就是存储器设备的驱动,比如 EMMC、NAND、SD 卡和U 盘等存储设备,因为这些存储设备的特点是以存储块为基础,因此叫做块设备。网络设备驱动就更好理解了,就是网络驱动,不管是有线的还是无线的,都属于网络设备驱动的范畴。一个设备可以属于多种设备驱动类型,比如USB WIFI,由于其使用USB 接口,所以属于字符设备,但是其又能上网,所以也属于网络设备驱动。
我们主要讨论如何编写字符设备驱动。

字符设备驱动程序简介

字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI、LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。
在详细的学习字符设备驱动架构之前,我们先来简单的了解一下 Linux 下的应用程序是如何调用驱动程序的:
在这里插入图片描述
linux系统中万物皆文件,驱动程序加载后会在/dev目录下生成一个对应的文件,如/dev/led。应用程序就是先用open打开该文件,用write控制led的亮灭,用read读取led的亮灭,用完之后用close关闭该文件。
这里需要注意的是,应用程序运行在用户空间,驱动程序运行在内核空间。应用程序必须使用一个叫做**“系统调用”**的方法来实现从用户空间“陷入”到内核空间,这样才能实现对底层驱动的操作。一个open函数执行的过程如下:
在这里插入图片描述

字符设备驱动程序的编写

linux源码中字符设备驱动程序存放在driver/char目录下,我们也可以将我们自己的驱动程序保存在该目录下
在driver/char下创建源文件first_driver.c并在文件中填入如下代码:
在这里插入图片描述
linux驱动程序对代码有着特定的要求,首先就是驱动程序加载和卸载时的函数调用,48和53行分别就是当驱动程序加载和卸载时调用的函数57和58行分别用两个带参宏指出驱动程序初始化入口点和退出入口点。59表示该驱动程序遵守的协议。这样就完成了驱动程序最基础的框架。
目前我们编写的是一个字符设备驱动程序,字符设备驱动程序将来被linux加载的时候需要注册这个驱动程序。其实无论哪种驱动程序,按照linux的做法在加载时都需要注册。字符设备驱动程序注册函数为:

int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

int register_chrdev函数

1.unsigned int major主设备号,这里就不得不提一下linux中的设备号了。一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的设备。简单来说,linux需要一个数来管理某个驱动程序和使用这个驱动程序的设备。很明显,这个设备号具有唯一性。我们可以使用cat /proc/devices命令即可查看当前系统中所有已经使用了的设备号。在接下来的程序中,我们可以设置一个静态的主设备号,比如200。设置时一定要注意不能使用已经用了的主设备号。
2. const char name:为你的驱动程序起一个名字
3. struct file_operations fops:这是一个指向
file_operations结构体变量的指针**,这个结构体里面的成员绝大多数都是函数的指针。这些函数的指针指向一个我们编写的函数,每个函数都有着各自的作用。这里列举常用的几个:
① open 函数用于打开设备文件
② release 函数用于释放(关闭)设备文件,与应用程序中的 close 函数对应
③ read 函数用于读取设备文件
④ write 函数用于向设备文件写入(发送)数据
⑤ poll 是个轮询函数,用于查询设备是否可以进行非阻塞的读写
⑥ owner 拥有该结构体的模块的指针,一般设置为THIS_MODULE
现在我们的方向已经非常明确了,为了调用注册字符设备驱动函数,不得不先准备一个file_operations结构体变量,而这个结构体变量中必要的成员,必须提前准备几个函数。
在这里插入图片描述

第12和13行:为了将来能看出这个函数调用了,输出一些信息,由于我们的程序现在运行在内核空间,所以不能用printf而是要用printk,用法和printf一样;
第19行:使用copy_to_user函数把内核空间的数据拷贝到用户空间
然后定义一个file_operations结构体变量,并将函数入口地址赋字符设备驱动值给各自的成员:
在这里插入图片描述
下面就可以调用register_chrdev函数注册字符设备驱动了。
在这里插入图片描述

#define DEVICE_MAJOR 200
#define DEVICE_NAME "first_device"

注册主设备号为200,名字为first_device的字符设备驱动。

程序写好了,如何编译呢?这里介绍两种方法

第一种方法是告诉linux的Makefile我们添加了一个新的驱动程序,这种方法需要我们的驱动源码就放在driver/char目录中,恰好我们就是这么做的。步骤如下:

第一种方法

  1. 打开 drivers/char/Kconfig 文件并添加如下内容:
    在这里插入图片描述
    Kconfig文件被称之为内核配置文件,这里我们添加了一个名为FIRST_DRIVER的配置选项,该配置选项为三态的,所谓三态是指将来的编译结果可以是模块,可以直接编译进内核还可以不编译default m是指默认编译成模块。最后那段是帮助文本
    之后我们运行make menuconfig,依次进入Device Driver-Character devices就可以看到My first driver了
    在这里插入图片描述
    选择该项按空格分别在编译成模块,编译进内核和不编译之间切换。我们选择’M’,编译成模块,以方便之后的调试。
  2. 通过上一步,我们虽然可以在配置内核的时候进行选择,但实际上此时执行编译内核还是不能把first_dirver.c 编译进去的,还需要在 Makefile 中把内核配置选项和真正的源代码联系起来。打开 drivers/char/Makefile并添加下面内容:
    在这里插入图片描述
    注意:CONFIG_之后的文字必须和Kconfig文件中配置选项名称一致,之后的文件名也必须和驱动源码文件名一致。
  3. 之后在源码顶层目录下执行make modules就可以完成编译了
    编译完成之后在driver/char目录下可以找到一个名为first_driver.ko文件,这个就是我们需要的驱动程序
    first_driver.ko通过nfs复制到开发板上
    使用insmod xxx.ko命令加载驱动程序
    使用lsmod查看已经加载的驱动
    使用rmmod xxx卸载驱动程序,注意不用加.ko
    加载好驱动程序以后,查看一下/dev目录,这里并没有出现我们所说的设备文件,这是因为目前我们的驱动程序还不能自动创建设备文件,之后我们会讲解如何自动创建设备文件。这里我们先用手动的方法创建设备文件,命令为mknod [OPTIONS] NAME TYPE MAJOR MINOR
    该命令中**[OPTIONS]选项可不填**;NAME就是/dev下的设备文件名;TYPE是设备型号,这里是字符设备用c表示;MAJOR主设备号;MINOR子设备号。
    如:mknod /dev/first c 200 0那么以后应用程序就是以文件/dev/first作为入口点调用驱动程序的。

第二种方法

将我们写好的驱动源码放在任意一个文件夹内,如linux源码目录下的mydriver目录,并在该目录下创建一个Makefile文件,内容如下:
在这里插入图片描述
第1行:获得linux源码顶层目录,根据实际情况填写
第2行:获得驱动源码所在目录
第3行:定义目标文件并指定目标文件为模块形式
第8行:具体的编译命令,后面的 modules 表示编译模块,-C 表示将当前的工作
目录切换到指定目录中,也就是 KERNERLDIR 目录。M 表示模块源码目录,”make modules”命令中加入 M=dir 以后程序会自动到指定的 dir 目录中读取模块的源码并将其编译为.ko 文件
之后使用make命令编译同样得到xxx.ko文件,加载什么的操作就和之前一样了。

正如之前所提到的,驱动程序的调用是通过应用程序的文件IO实现的。所以调用驱动程序就是编写一个简单的文件IO程序。
在这里插入图片描述
第11行:打开设备文件
第22行:调用read函数,此时就会调到我们在驱动中写的first_driver_read函数
可以看到的结果演示如下:
在这里插入图片描述

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

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

相关文章

数组*巴巴拉拉

一,数组的定义以及创建方式 数组的作用:数组可以把一组数据的集合存放在单个变量下 创建数组有两种方式: 1.利用new创建数组 var arr new Array() 2.利用数组字面量创建数组(常用) var arr [] 注意点:数组里可以存放任…

C++笔记2:基类的静态变量子类能共享吗

C里&#xff0c;基类的静态变量子类能共享吗&#xff0c;为了验证这个问题这里写了一段代码进行测试&#xff1a; #include <iostream>class Base { public:static int staticVariable; };class Derived : public Base { };int Base::staticVariable 10;int main() {Ba…

2013–2022年福建漳江口互花米草分布无人机遥感数据集

文章目录 摘要数据集内容数据集命名方式数据引用与参考文献引用 摘要 本数据集利用无人机搭载可见光相机&#xff0c;获取福建漳江口湿地2013-2022年期间的航拍影像&#xff0c;通过影像拼接生成整个研究区的正射影像&#xff0c;制作十年尺度的遥感影像数据集及互花米草空间数…

Vue-条件渲染(初识vue渲染)

目录 一、Vue条件渲染-介绍 1.概念 2.特点 3.功能 4.好处 5.应用 二、Vue条件渲染-使用 1.初识渲染 2.条件v-if的使用 3.条件v-if-else的使用 4.条件v-else-if使用 5.template元素使用 6.条件渲染-阶段案例 7.条件v-show 三、Vue条件渲染-实例 1.权限管理系统 …

数据结构:查找与排序

注&#xff1a;以下代码均为C 查找 一、线性表的查找 1. 顺序查找 int search_seq(vector<int> s, int key){for(int i 0; i < s.size(); i){if(s[i] key)return i;}return -1; }2. 折半查找 (1)非递归&#xff1a;循环 int search_bin1(vector<int> s,…

C++初阶学习第一弹——C++入门(上)

前言&#xff1a; 很高兴&#xff0c;从今天开始&#xff0c;我们就要步入C的学习了&#xff0c;在这之前我们已经对C语言有了不错的了解&#xff0c;对数据结构也有了一些自己的认识&#xff0c;今天开始&#xff0c;我们就进入这个新的主题的学习——C 目录 一、C的发展即其特…

一个快速克隆方法引出深浅拷贝说明

提供的代码是一个泛型扩展方法&#xff0c;用于对任意类型的对象T进行浅拷贝&#xff08;shallow copy&#xff09;。这个方法使用反射来调用对象的MemberwiseClone方法&#xff0c;该方法为所有字段&#xff08;包括值类型和引用类型字段&#xff09;创建新的副本&#xff0c;…

解析OceanBase v4.2函数索引进行查询优化

一、如何通过函数索引来进行查询优化 函数索引是一种优化查询的技术&#xff0c;其主要作用在于提升包含函数调用的查询语句的执行速度。当查询语句中包含函数调用时&#xff0c;数据库系统需要逐行执行函数计算&#xff0c;这无疑会增加查询的复杂性&#xff0c;导致查询速度…

【C语言】多字节字符、宽字符(涉及字符集和编码)

字符集、编码&#xff1a; 字符集&#xff1a;一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称&#xff0c;包括各国家文字、标点符号、图形符号、数字等。例如&#xff1a;ASCII、Unicode、GB2312、GBK、GB18030、BIG5(繁体中文) ... 编码方式&#xff1a;符号…

【Flutter】自动生成图片资源索引插件一:FlutterAssetRefGenerator

介绍 FlutterAssetRefGenerator 插件&#xff1a;windows上 点击生成图片索引按钮后&#xff0c;pubspec.yaml 会出现中文乱码&#xff0c;需要手动改乱码&#xff1b;mac上没问题。 优点&#xff1a;点击图标自动生成。 目录 介绍一、安装二、使用 一、安装 安装FlutterAsset…

移动端不居中问题/安卓和ios下line-height上下居中 css兼容问题

移动端开发过程&#xff0c;经常会写带0.5px边框角标类的样式&#xff0c;直接使用border设置0.5px边框&#xff0c;ios有些机型会出现显示不完整的情况。所以改用伪元素方法实现边框。代码如下&#xff1a; .comment-entry::after{content: ;position: absolute;left: 0;top: …

快速IO的方式|Java快读模板

处理IO的方式&#xff1a; 处理输入和输出的方式&#xff1a; C&#xff1a;cin / cout Java&#xff1a;Scanner / System.out 但是这两种有超时的风险 那么C处理方式&#xff1a;直接改为scanf / printf &#xff0c;也就是C语言中的读写方式 Java处理方式&#xff1a;准备一…

Python 中 + 和 += 赋值操作的性能比较

1. 问题背景 在 Python 中&#xff0c;我们可以通过 和 … 完成累加操作&#xff0c;在实际开发过程中我们一般会优先选择 &#xff0c;然而最近在对比 和 … 的性能时出现了 反而更慢的现象。因此&#xff0c;我们决定对此问题进行深入探讨。 2. 解决方案 为了准确地…

C语言---贪吃蛇(一)---准备工作

文章目录 前言1.Win32 API介绍1.1.Win32 API1.2. 控制台程序1.3.控制台屏幕上的坐标[COORD](https://learn.microsoft.com/zh-cn/windows/console/coord-str)1.4.[GetStdHandle](https://learn.microsoft.com/zh-cn/windows/console/getstdhandle)1.5.[GetConsoleCursorInfo](h…

视频太大怎么压缩变小?8种方法随时压缩视频大小

视频太大怎么压缩变小&#xff1f;视频压缩方式分为两种&#xff0c;有损压缩和无损压缩&#xff0c;什么是有损什么是无损压缩&#xff0c;什么时候视频用无损压缩更好&#xff1f;什么时候用有损压缩更好&#xff1f;如何调整视频参数实现基本无损压缩&#xff1f; 今天就借助…

九章云极DataCanvas AIDC OS智算操作系统正式发布,开启AI智算新纪元

4月18日&#xff0c;2024九章云极DataCanvas智算操作系统新品发布会于北京隆重召开&#xff0c;全新产品DATACANVAS AIDC OS智算操作系统&#xff08;以下简称AIDC OS&#xff09;正式官宣。AIDC OS以卓越的AI技术实力和AI基础软件为根基&#xff0c;以重新定义和突破传统为创新…

我独自升级崛起在哪里下载 我独自升级崛起一键下载方法极速体验

我独自升级崛起在哪里下载 我独自升级崛起一键下载方法极速体验 最近在游戏圈内爆火的一款游戏《我独自升级&#xff1a;崛起》是一款由韩国漫画改编而成的热门多人网络在线联机游戏&#xff0c;这款游戏是一款的角色扮演游戏&#xff0c;游戏有着引人入胜的剧情模式。玩家们…

Docker 入门指南:快速上手 Docker

Docker 是一种开源的容器化平台&#xff0c;它可以帮助开发者轻松地打包、发布和运行应用程序。本指南将介绍 Docker 的基本概念和常用操作&#xff0c;帮助你快速入门 Docker 技术。 1. 安装 Docker 首先&#xff0c;你需要在你的操作系统上安装 Docker。Docker 支持多种操作…

m4a转wav怎么转?3个简单的转换方法~

随着音频的迅猛发展&#xff0c;不同的音频文件格式应运而生&#xff0c;其中M4A和WAV都备受欢迎。M4A格式的兴起始于对音质和压缩效率的双重追求&#xff0c;而WAV则因其高保真特性而成为专业录音室的首选。 M4A格式的由来 M4A格式最初是由苹果公司引入的&#xff0c;旨在提供…

ZCC5080E USB 5V 输入 1A 双节锂电池充电管理 IC替代CS5080E

概要&#xff1a; ZCC5080E 是一款 5V USB 适配器输入&#xff0c;高精度双节锂离子电池充电管理芯片。具有0V充电功能&#xff0c;涓流充电、恒流充电、恒压充电和自动截止、自动再充等一套完整充电循环的充电管理芯片。芯片内部特设 9V 抗浪涌&#xff0c;芯片应用更安全可…