树莓派基于Linux内核驱动开发详解

一、驱动认知

首先理解Linux内核框图
文件系统认知,Linux内核框图

1、什么是驱动

  1. linux内核驱动。软件层面上的驱动 广义上是指:这一段代码操作了硬件去动,所以这一段代码就叫硬件的驱动程序。
  2. 狭义上驱动程序就是专指操作系统中用来操控硬件的逻辑方法的部分代码。而我们这里讲的驱动就指的是这个狭义上的驱动。

2、Linux驱动的体系架构

  1. 分离、分层思想
  2. 驱动的上面是系统调用API
  3. 驱动的下面是硬件
  4. 驱动本身的实现也是基于分离、分层的思想(比如分成不同类型的驱动)

3、Linux驱动的分类

  1. 分3类:字符设备驱动、块设备驱动、网络设备驱动
  2. 分类原则:设备本身读写操作的特征差异
  3. 时刻要注意一点,我们的驱动本质上是不分类的,因为我们的硬件是不同的,有不同的读写特性,所以的硬件是分类的,所以我们的驱动也就要跟着分类了

字符设备:准确的说应该叫“字节设备”,软件操作设备时是以字节为单位进行的。典型的如LCD、串口、GPIO、蜂鸣器、触摸屏······
块设备:块设备是相对于字符设备定义的,块设备被软件操作时是以块(多个字节构成的一个单位)为单位的。设备的块大小是设备本身设计时定义好的,软件是不能去更改的,不同设备的块大小可以不一样。常见的块设备都是存储类设备,如:硬盘、NandFlash、iNand、SD····操作块设备时,我们必须以块单位进行操作,将一块的数据读取到内存中去,然后在内存中找到相应的那个字节数据修改之后再以一块的数据写入到我们的块设备中去
网络设备:网络设备是专为网卡设计的驱动模型,linux中网络设备驱动主要目的是为了支持API中socket相关的那些函数工作

4.为什么要学习写驱动

  1. 树莓派开发简单是因为有厂家提供的wiringPi库,实现超声波,实现继电器操作,做灯的点亮…都非常简单
  2. 但未来做开发时,不一定都是用树莓派,则可能没有wiringPi库可以用。但只要能运行Linux系统,linux的标准C库一定有
  3. 所以我们可以基于C库、Linux内核和拿到芯片手册,电路图…就能做开发,写一套属于自己的驱动库

二、开始树莓派Linux内核驱动开发

  1. 设备驱动文件路径:/home/pi/lessonPI/linux-rpi-4.14.y/drivers,在Linux环境编写
  2. 设备文件(文件显示黄色)路径:根目录下的dev文件/dev,在树莓派环境使用
  3. 在Linux环境通过写好设备驱动文件然后进行内核编译生成驱动模块xxx.ko,接着把驱动模块发送给树莓派,树莓派把驱动模块加载到设备文件。从而用户可以通过/dev目录下的这些设备文件访问外部硬件设备,比如通过open(/dev/mouse,O_RDONLY)来访问鼠标的输入 从而可以进行各种骚操作
  4. 内核底层其实是用链表去管理这些驱动,学过FreeRTOS系统源码的可以从FreeRTOS源码的底层链表去理解
  5. 设备驱动文件需要有设备号、主设备号、次设备号、设备文件名。这些可以理解为驱动任务节点所需要的内容,可以从FreeRTOS源码的任务节点去结合这个理解
  6. 整个过程大致:C库open——>sys_call——>sys_open(属于VFS层虚拟文件系统)——>根据open里的那个设备文件里的设备名去内核找到驱动链表——>接着根据其主设备号与次设备号找到相关驱动函数,所以我们只需要让内核执行我们自己写的设备名(即执行我们最后得到的设备文件),就可以执行我们写的驱动函数了
  1. 拷贝设备驱动文件代码
    gpio属于字符设备,所以把该设备驱动文件拷贝到/home/pi/lessonPI/linux-rpi-4.14.y/drivers/char
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init  module_exit声明
#include <linux/init.h> //__init  __exit 宏定义声明
#include <linux/device.h> //class  devise声明
#include <linux/types.h> //设备号  dev_t 类型声明
#include <asm/io.h>  //ioremap iounmap的头文件

static struct class *pin4_class;
static struct device *pin4_dev;

static dev_t devno;          //设备号
static int major = 232;      //主设备号
static int minor = 0;        //次设备号
static char *module_name = "pin4"; //设备文件名

//.open = pin4_open,所以执行open(fd,O_RDWR)时会进入此函数
static int pin4_open(struct inode *inode, struct file *file)
{
        printk("pin4_open\n"); //内核的打印函数和printf类似

        return 0;
}

//.write= pin4_write,所以执行write(fd,'1',1)时会进入此函数
static int pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        printk("pin4_write\n"); //内核的打印函数和printf类似

        return 0;
}

//内核链表里的结构体相关配置
static struct file_operations pin4_fops =
{
        .owner = THIS_MODULE,
        .open = pin4_open,
        .write = pin4_write,
};

//入口函数
int __init pin4_drv_init(void)
{
        int ret;
        devno = MKDEV(major,minor); //创建设备号
        //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中
        ret = register_chrdev(major, module_name, &pin4_fops);

        pin4_class = class_create(THIS_MODULE, "myfirstdemo");
        pin4_dev = device_create(pin4_class, NULL, devno, NULL, module_name); //创建设备文件

        return 0;
}

void __init pin4_drv_exit(void)
{
        device_destroy(pin4_class, devno);
        class_destroy(pin4_class);
        unregister_chrdev(major, module_name); //卸载驱动
}

module_init(pin4_drv_init); //入口
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
  1. 拷贝用户层应用代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main()
{
        int fd;
		//调用C库open,执行我们做好的设备文件pin4,因为我们把.open=pin4_open,所以会运行pin4_open函数
        fd = open("/dev/pin4",O_RDWR);
        if(fd < 0){
                printf("open failed\n");
                perror("reson");
        }else{
                printf("open success\n");
        }
        //调用C库wirte,因为我们把.wrute=pin4_write,所以会运行pin4_write函数
        fd = write(fd,'1',1);

        return 0;
}
  1. 在/drivers/char目录下修改Makefile配置文件,打开后加入obj-m += pin4driver.o,为了进行内核编译时能把设备驱动文件生成为驱动模块xxx.ko
    在这里插入图片描述
  2. 在Linux里回到树莓派内核源码根目录linux-rpi-4.14.y,执行指令进行编译内核:ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
    补充:modules:我们只需生成驱动模块,所以只需modules参数即可
    在这里插入图片描述
    编译成功,生成pin4driver.ko驱动模块
    编译完成后执行指令发送给树莓派scp drivers/char/pin4driver.ko pi@192.168.1.1:/home/pi
  3. 执行指令编译应用代码:arm-linux-gnueabihf-gcc pin4text.c -o pin4text
    编译完成后执行指令发送给树莓派scp pin4text pi@192.168.1.1:/home/pi
  4. Linux操作完了,接下来到树莓派操作了。
    执行指令加载到设备文件(即把pin4driver.ko驱动模块加载到/dev设备文件里)sudo insmod pin4driver.ko
  5. 查看加入后的设备号是否和代码写的一样ls pin4 -l,可见主设备号和次设备号和代码里的一样
    在这里插入图片描述
    执行lsmod查看是否有pin4driver驱动模块
    在这里插入图片描述
    若一样和有说明以上操作成功了
  6. 还需要给pin4这个设备文件一个权限,执行指令:sudo chmod 666 /dev/pin4
    补充:666代表所有人都可以读写pin4
  7. 运行用户代码./pin4text,运行完成后输入指令查看运行完./pin4text后内核打印了什么dmesg | grep pin4
    在这里插入图片描述
    可见,确实是按照我们写好的设备驱动文件打印。
    到此,你已经实现了调用自己的设备文件了,然后就可以进行各种骚操作啦

回顾:第二大点灰色款里的第3小点,用户可以通过/dev目录下的这些设备文件访问外部硬件设备,所以我们需要学会这套技能

转载请标明出处,谢谢
作者:星辰~念

热门文章

暂无图片
编程学习 ·

exe4j详细使用教程(附下载安装链接)

一、exe4j介绍 ​ exe4j是一个帮助你集成Java应用程序到Windows操作环境的java可执行文件生成工具&#xff0c;无论这些应用是用于服务器&#xff0c;还是图形用户界面&#xff08;GUI&#xff09;或命令行的应用程序。如果你想在任务管理器中及Windows XP分组的用户友好任务栏…
暂无图片
编程学习 ·

AUTOSAR从入门到精通100讲(126)-浅谈车载充电系统通信方案

01 引言 本文深入研究车载充电系统策略,设计出一套基于电动汽车电池管理系统与车载充电机的CAN通信协议,可供电动汽车设计人员参考借鉴。 02 电动汽车充电系统通讯网络 电动汽车整车控制系统中采用的是CAN总线通信方式,由一个整车内部高速CAN网络、内部低速CAN网络和一个充电…
暂无图片
编程学习 ·

CMake(九):生成器表达式

当运行CMake时&#xff0c;开发人员倾向于认为它是一个简单的步骤&#xff0c;需要读取项目的CMakeLists.txt文件&#xff0c;并生成相关的特定于生成器的项目文件集(例如Visual Studio解决方案和项目文件&#xff0c;Xcode项目&#xff0c;Unix Makefiles或Ninja输入文件)。然…
暂无图片
编程学习 ·

47.第十章 网络协议和管理配置 -- 网络配置(八)

4.3.3 route 命令 路由表管理命令 路由表主要构成: Destination: 目标网络ID,表示可以到达的目标网络ID,0.0.0.0/0 表示所有未知网络,又称为默认路由,优先级最低Genmask:目标网络对应的netmaskIface: 到达对应网络,应该从当前主机哪个网卡发送出来Gateway: 到达非直连的网络,…
暂无图片
编程学习 ·

元宇宙技术基础

请看图&#xff1a; 1、通过AR、VR等交互技术提升游戏的沉浸感 回顾游戏的发展历程&#xff0c;沉浸感的提升一直是技术突破的主要方向。从《愤怒的小鸟》到CSGO,游戏建模方式从2D到3D的提升使游戏中的物体呈现立体感。玩家在游戏中可以只有切换视角&#xff0c;进而提升沉浸…
暂无图片
编程学习 ·

flink的伪分布式搭建

一 flink的伪分布式搭建 1.1 执行架构图 1.Flink程序需要提交给 Job Client2.Job Client将作业提交给 Job Manager3.Job Manager负责协调资源分配和作业执行。 资源分配完成后&#xff0c;任务将提交给相应的 Task Manage。4.Task Manager启动一个线程以开始执行。Task Manage…
暂无图片
编程学习 ·

十进制正整数与二进制字符串的转换(C++)

Function one&#xff1a; //十进制数字转成二进制字符串 string Binary(int x) {string s "";while(x){if(x % 2 0) s 0 s;else s 1 s;x / 2;}return s; } Function two&#xff1a; //二进制字符串变为十进制数字 int Decimal(string s) {int num 0, …
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统&#xff1a; 微信小程序——辩论管理前台涉及技术&#xff1a;WXML 和 WXS…
暂无图片
编程学习 ·

树莓派驱动DHT11温湿度传感器

1&#xff0c;直接使用python库 代码如下 import RPi.GPIO as GPIO import dht11 import time import datetimeGPIO.setwarnings(True) GPIO.setmode(GPIO.BCM)instance dht11.DHT11(pin14)try:while True:result instance.read()if result.is_valid():print(ok)print(&quo…
暂无图片
编程学习 ·

ELK简介

ELK简介 ELK是三个开源软件的缩写&#xff0c;Elasticsearch、Logstash、Kibana。它们都是开源软件。不过现在还新增了一个 Beats&#xff0c;它是一个轻量级的日志收集处理工具(Agent)&#xff0c;Beats 占用资源少&#xff0c;适合于在各个服务器上搜集日志后传输给 Logstas…
暂无图片
编程学习 ·

Linux 基础

通常大数据框架都部署在 Linux 服务器上&#xff0c;所以需要具备一定的 Linux 知识。Linux 书籍当中比较著名的是 《鸟哥私房菜》系列&#xff0c;这个系列很全面也很经典。但如果你希望能够快速地入门&#xff0c;这里推荐《Linux 就该这么学》&#xff0c;其网站上有免费的电…
暂无图片
编程学习 ·

Windows2022 无线网卡装不上驱动

想来 Windows2022 和 windows10/11 的驱动应该差不多通用的&#xff0c;但是死活装不上呢&#xff1f; 搜一下&#xff0c;有人提到 “默认安装时‘无线LAN服务’是关闭的&#xff0c;如果需要开启&#xff0c;只需要在“添加角色和功能”中&#xff0c;选择开启“无线LAN服务…
暂无图片
编程学习 ·

【嵌入式面试宝典】版本控制工具Git常用命令总结

目录 创建仓库 查看信息 版本回退 版本检出 远程库 Git 创建仓库 git initgit add <file> 可反复多次使用&#xff0c;添加多个文件git commit -m <message> 查看信息 git status 仓库当前的状态git diff 差异对比git log 历史记录&#xff0c;提交日志--pret…
暂无图片
编程学习 ·

用Postman生成测试报告

newman newman是一款基于nodejs开发的可以运行postman脚本的工具&#xff0c;使用Newman&#xff0c;可以直接从命令运行和测试postman集合。 安装nodejs 下载地址&#xff1a;https://nodejs.org/en/download/ 选择自己系统相对应的版本内容进行下载&#xff0c;然后傻瓜式安…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…