Linux驱动——杂项驱动GPIO子系统

news2024/9/24 7:23:47

一:内核层框架

在介绍linux驱动之前先介绍一下系统。

系统分为两层:

        1.系统层

        2.内核层

对于内核层就要说一下其中的内核层运行的框架了

代码如下:

//头文件
#include "linux/kernel.h"
#include "linux/module.h"
//入口函数
static int __init myled_init(void)
{
return 0;
}
//出口函数
static void __exit myled_exit(void)
{ }
//函数声明
module_init(myled_init);
module_exit(myled_exit);
//协议
MODULE_LICENSE("GPL");

内核的框架已经写好了,那应该如何编译了

        这代码不同于以往的系统代码,如果直接编译会报错,它是运行在内核层,所以要靠内核编译。

所以就要提起一个名为:Makefile的驱动编译了,适用于任何驱动编译,任意的内核版本

通用makefile代码:

obj-m += led.o//如果要用来编译自己的程序代码,修改成自己的
#obj-m:代表模块 module (驱动) 目标-> led.o->led.c
KDIR:=/home/lyx/RK3588S/kernel
#代表你的编译的所用的内核的位置
CROSS_COMPILE_FLAG=/home/lyx/RK3588S/prebuilts/gcc/linuxx86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linuxgnu/bin/aarch64-none-linux-gnu-
#这是你的交叉编译器路径
all:
    make -C $(KDIR) M=$(PWD) modules ARCH=arm64
    CROSS_COMPILE=$(CROSS_COMPILE_FLAG)
#调用内核层 Makefile 编译目标为 modules->模块 文件在当前路径
# 架构 ARCH=arm64
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *mod *.symvers *.markers *.order

二:驱动开发

1:何为驱动

我的理解就是,驱动硬件正常工作的程序代码就是驱动。

在 STM32 里面:
无非就是编写寄存器代码初始化外设->通信传感器
在 Linux 下驱动:
        不像 STM32 一样,拿一个 LED 灯为例子
STM32:
        初始化 GPIO 寄存器(库函数)
        在 main-> 点灯/关灯/闪灯
Linux 下:
        也可以像 STM32 一样在内核层直接开灯!
        内核层不断的加载卸载实际上很浪费资源。效率很低
        Linux 下内核模块代码,是独立运行的单元
        很难像 STM32 那样一个代码一个工程代表整个单片机运行程序
        就算是我像 STM32 一样写代码把灯的功能写死->运行在内核层!
        请问系统开发工程师(不懂驱动) 他怎么操作 LED 灯

我写了一个驱动
        你作为智能家居的软件开发工程师
        你想在你的应用里面控制我写的驱动底层的 LED 灯!
        真正的 Linux 驱动:
                不像 STM32 一样把驱动写死了
                首先 Linux 下的驱动符合驱动的原则:驱使硬件正常工作代码
                把 LED 灯这个设备在 内核层 "抽象" 为一个 文件(设备文件)
                我们内部写的驱动就是在完成这样的事->硬件变成文件
                系统上层开发者他操作底层设备->操作文件
        以 LED 灯为例:
                        LED 灯->我写驱动->led 文件 /dev/myled
                        上层开发者->打开/dev/led->灯就开了
                上层开发者->关闭/dev/led->灯就灭了

2:linux分层思想

在是stm32中


是直接操作对应寄存器,这样的特点是高效,简单明了。

缺点为:

        难移植,不通用。如果换个芯片就要推到重新再来一遍。

而在linux中提出了分层的思想,如何实现的了,如下图所示:

        我认为所谓的分层就是对应的中间接口层,通用的接口函数,能最大的保证代码的通用性,而在Linux系统中,实现的是所有的系统都在做的是软硬件分离。

Linux因为分层软件硬件,我们不管使用什么平台->开发驱动的框架,代码都是一样的。

在 Linux 下分层思想处处可见,不仅仅把驱动做了分层
        底层的各种外设也做了大量的分层
        SPI 分为: Linux 通用接口层
                厂商 BSP 适配的驱动层
甚至最新的 platfrom 也在统一分层思想:
        一个简简单单的驱动他也想分为两层:
                硬件信息:提供 LED 灯的 引脚和电平状态
                通用驱动: LED 灯驱动->没有指定具体 LED 灯引脚、电平状态

总结:

Linux 分层思想:
        主要就是告诉大家, Linux 下的所有的函数接口
        是通用的,隔离硬件的,软硬件分离
        你写的代码原则上适用于任何的平台
        实际上在发展中:
                把驱动分层: 硬件层 : 硬件信息层 ->渐渐演变成 设备树
                软件层 : 软件代码层 ->通用了 所有的平台
                把 BSP 厂商驱动:分为两层: 硬件层
                软件层
Linux 下的驱动:
        我们现在初学驱动的时候暂时写驱动不考虑分层
Linux 下驱动的特点:
        不是直接在内核层操作硬件
        而是写好 "接口"(文件 打开 关闭 读写)
        让上层操作!

3:驱动开发的框架

1:写内核框架

2:在内核框架->

        把设备抽象为文件(内核驱动接口->杂项驱动)Linux中有一句话,叫一切皆文件

        设备的设备号->设备ID ->让内核管理

        设备内核操作接口->文件操作接口(内核你驱动开发者要单独实现一套)
        你写的内核层的 open close read write 跟 上层(系统层一一对应)
        这是也是你留给上层的 操作接口!

3:编译成 .ko –> insmod xxx.ko –>生成设备文件(/dev/xxxx)
4:调用 加载/入口函数->内核框架->生成设备文件
5:上层/你自己 调用 文件操作 -> 操作设备

4:驱动文件的特点:

1:系统的特殊文件之一:

管道、套接字、块设备文件、字符设备文件、低级IO/非缓冲区IO来操作文件

一共分为三大类:

        字符设备文件:

        一般指的是除了存储 网络 设备之外的所有的其他设备

块设备文件:

        正常的芯片->厂商都完成了这类芯片初始化

        小型存储器->SPI_FLAHS

        网络设备文件:

                基本上你开发网络设备只有两种:wifi和4G、5G

        设备号
                原则上是一个 32bit 的无符号数字
                理论来说内核最大挂在的设备-> 2^32->40 亿
                分为主设备号(占设备号的高 12bit)
                次设备号(占设备号的低 20bit)
                        其中主设备号的范围-> 0-254
                        其中次设备号的范围-> 0-255
                实际上系统最多运行挂载:
                        255 个 255: 255*256

字符设备传输方式:按字节传输

三:杂项的驱动开发

1:杂项驱动设备文件的特点

杂项:

        指一般设备原则上是不分类的设备。

        一类设备独占一个设备

        所有的杂项设备的主设备号为10

        次设备号杂项自动分配->杂项开发不需要考虑设备号的问题

2:杂项驱动开发的接口

        主要就是两个函数:

        misc_register();//杂项注册函数

        misc_deregister();//杂项注销函数

头文件为:

<linux/miscdevice.h>

函数原型:

int misc_register(struct miscdevice *misc)

函数的参数:
        misc:
                注册杂项设备的核心结构体
                内部尤其注册详细信息
                struct miscdevice {
                int minor;
                const char *name;
                const struct file_operations *fops;
                ...........
                其他成员变量不用理会->已经被函数内部做过了初始化
        };
        * minor:
                major:主设备号

                minor:次设备号
                原则上我们不清楚哪个设备号可以被占用
                我们一般提供 255 ->让函数自动分配可用的次设备号
        * name:
                这个就是你注册设备要生成的设备文件名
                比如你填写的是 xyd_led
                就会在 /dev/xyd_led 这个设备文件!
        * fops:
                这个结构体内部是大量的函数指针
                也是你注册这个设备文件对应的内核层的接口!
                虽说接口多的多,并不是让你都实现
                你根据自己的实际情况 选择性实现
        最起码需要实现三个成员变量:
                struct module *owner;
                int (*open) (struct inode *, struct file *);
                int (*release) (struct inode *, struct file *);
        其中 owner:
                固定填写 THIS_MODULE
        函数返回值:
注册设备成功返回 0
注册设备失败返回 非 0

然后是注销函数

void misc_deregister(struct miscdevice *misc)

3:举例使用

#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/miscdevice.h"
//1:申请一个杂项核心结构体
struct miscdevice misc;//变量全局的变量,空间为编译器系统开辟的全局区空间
//申请内核层文件操作接口集合结构体
struct file_operations misc_fops;
int xyd_led_open(struct inode * i, struct file * f)
{
    printk(KERN_EMERG"这是内核层的 open 被调用了!\r\n");
    return 0;
}
int xyd_led_close(struct inode * i, struct file * f)
{
    printk(KERN_EMERG"这是内核层的 close 被调用了!\r\n");
    return 0;
}
//加载函数
static int __init myled_init(void)
{
    //2:填充杂项核心结构体
    misc.minor = 255;//让系统自动给我分配一个次设备号
    misc.name = "xyd_led";//生成一个设备文件-> /dev/xyd_led
    misc.fops = &misc_fops; //指定一个操作函数集
    //3:填充 misc_fops 里面的函数接口
    misc_fops.owner = THIS_MODULE; //固定写法
    misc_fops.open = xyd_led_open;//系统层打开文件调用内核层接口函数
    misc_fops.release = xyd_led_close;//系统层关闭文件调用内核层接口函数
    return misc_register(&misc);
}
//卸载函数
static void __exit myled_exit(void)
{
    //4:卸载的时候取消注册杂项设备
    misc_deregister(&misc);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

四:GPIO子系统

1:什么是GPIO子系统

        在Linux提供的中间层接口中,这个接口是通用的接口之一,这个接口可以在任意的Linux系统下控制GPIO口,这个功能有限->只能控制GPIO的两大功能:

输入:获取点平状态

输出:输出 0/1

2 :GPIO子系统的接口

gpio_free(unsigned gpio);//释放不再使用这个 IO 口
gpio_request(unsigned gpio,const char *label);//获取/申请 一个 IO 口使用

gpio:
        他就是你要控制的 GPIO 口的编号
        可以算出来->Linux 下 GPIO 规律:
        引脚都是从 0 编号开始的

几乎所有的厂商的芯片的 GPIO 口 引脚都是规整(除了三星的一些芯片)
以瑞芯微:
        分大组和小组
        大组->数字编号 0 1 2 3
        每个大组有四个小组 ABCD
        小组->字母编号 A B C D
        每个小组有 8 个引脚
总结:
        一个大组: 32 个引脚 一个小组 8 个引脚
        举个例子:
                GPIO0_A0:
                就是 0 组的 第 0 小组的第 0 个引脚
                0*32 + 0*8 + 0 = 0
        GPIO4_B6
                就是 4 组的 第 1 小组的第 6 个引脚
                4*32 + 1*8 + 6 = 142
        GPIO0_C5: (LED1)
                0 + 2*8 + 5 = 21
        GPIO0_C6:(LED2)
                0 + 2*8 + 6 = 22
label:
标签名字,这个无所谓随便填写
函数返回值:
如果 这个 IO 口 没有人/没有驱动/没有程序之前申请过
那他就给你 返回一个正确-> 0
如果检测到了 有人之前申请过还没释放->返回非 0
gpio_direction_output(gpio_num,value);//引脚调节为 输出模式
gpio_direction_input(gpio_num);//调节引脚为 输入模式
gpio_set_value(gpio_num,value); // 设置引脚当前的输出状态
gpio_get_value(gpio_num); // 获取当前引脚的状态->不限制输入还是输出

头文件为:

#include <linux/gpio.h>

3:举例:点亮LED灯

#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/miscdevice.h"
#include "linux/gpio.h"
#include "linux/fs.h" // struct file_operations 结构体定义处
struct miscdevice xyd_led_device;
struct file_operations misc_device_ops;
//加载函数
int xyd_led_open(struct inode * i , struct file * f)
{
    gpio_set_value(21,1);
    gpio_set_value(22,1);
    return 0;
}
int xyd_led_close(struct inode * i , struct file * f)
{
    gpio_set_value(21,0);
    gpio_set_value(22,0);
    return 0;
}
static int __init xyd_init_led(void)
{
    //1:把我的 GPIO 口申请一下
    gpio_request(21, "xyd_led1");
    gpio_request(22, "xyd_led2");
    //2:我把我的引脚输出
    gpio_direction_output(21,0);
    gpio_direction_output(22,0);
    //3: 注册杂项设备
    xyd_led_device.minor = 255;
    xyd_led_device.name = "xyd_led";
    xyd_led_device.fops = &misc_device_ops;
    misc_device_ops.owner = THIS_MODULE;
    misc_device_ops.open = xyd_led_open;
    misc_device_ops.release = xyd_led_close;
    misc_register(&xyd_led_device);
    return 0;
}
static void __exit xyd_exit_led(void)
{
    misc_deregister(&xyd_led_device); //取消设备的注册
    gpio_free(21);
    gpio_free(22);
}
    module_init(xyd_init_led);
    module_exit(xyd_exit_led);
    MODULE_LICENSE("GPL");

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

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

相关文章

git-版本管理工具基本操作-创建仓库-拉取-推送-暂存库-版本库

1、创建仓库 2、克隆仓库到本地&#xff08;首次拉取需要输入用户名和密码&#xff0c;用户名用邮箱&#xff0c;密码用登录gitee的密码&#xff0c;后面配置密钥后可以直接clone&#xff09; 在命令行输出两行指令配置git才能克隆&#xff1a; username&#xff1a;gitee账号…

2D Inpainting 与NeRF 3D重建的多视角一致性问题

一 问题&#xff1a; NeRF依赖于输入图像的一致性。NeRF&#xff08;Neural Radiance Fields&#xff09;在生成三维场景时&#xff0c;依赖于从多个视角拍摄的输入图像之间的一致性来准确地推断场景的三维结构和颜色信息。 具体来说&#xff1a; 多视角一致性&#xff1a; Ne…

宝塔面板一键部署Inis博客网站结合内网穿透为本地站点配置公网地址

文章目录 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总…

Day42 | 739. 每日温度 496.下一个更大元素 I 503.下一个更大元素II

语言 Java 739. 每日温度 每日温度 题目 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该…

计算机网络基础详解:从网络概述到安全保障的全面指南

目录 网络基础详细概述 1. 网络概述 1.1数据通信 1.2资源共享 1.3分布式处理 1.4负载均衡 2. 网络分类 2.1按覆盖范围&#xff1a; 2.1.1局域网 (LAN)&#xff1a; 2.1.2城域网 (MAN)&#xff1a; 2.1.3广域网 (WAN)&#xff1a; 2.2按拓扑结构&#xff1a; 2.2.1…

IEEE802网络协议和标准

IEEE802网络协议和标准 802委员会IEEE 802介绍现有标准 IEEE 802.3介绍物理媒介类型MAC子层与LLC子层主要内容通讯标准POE供电标准802.3af、802.3at、802.3btIEEE802.3af的工作过程&#xff1a;IEEE802.3af主要供电参数&#xff1a;IEEE802.3af的分级参数&#xff1a;为什么会有…

C++的序列容器——数组

前言&#xff1a; 这篇文章我们就开始新的章节&#xff0c;我们之前说的C/C的缺陷那部分内容就结束了。在开始新的章之前我希望大家可以先对着题目思考一下&#xff0c;C的容器是什么&#xff1f;有什么作用&#xff1f;下面让我们开始新的内容&#xff1a; 目录 前言&#x…

从数据类型到变量、作用域、执行上下文

从数据类型到变量、作用域、执行上下文 JS数据类型 分类 1》基本类型&#xff1a;字符串String、数字Number、布尔值Boolean、undefined、null、symbol、bigint 2》引用类型&#xff1a;Object (Object、Array、Function、Date、RegExp、Error、Arguments) Symbol是ES6新出…

S7协议转HTTP协议

如下来源成都纵横智控-https://www.iotrouter.com/ 需求概述 本章要实现一个流程&#xff1a;EG8200采集西门子S7-200Smart的数据&#xff0c;并组装成JSON格式通过HTTP上报应用平台。 要采集的PLC点位表如下&#xff1a; PLC S7-200 Smart IP 192.168.0.34/102 点表(DB1…

C++第十一弹 -- STL之List的剖析与使用

文章索引 前言1. list的介绍2 list的使用2.1 list的构造函数2.2 iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers 3. list的迭代器失效4. list与vector的对比总结 前言 本篇我们旨在探讨对于STL中list的使用, 下一篇我们将会对list进行底层剖析以及…

目录操作(2)(21)

1.getpwuid struct passwd *getpwuid(uid_t uid); 功能: 根据用户id到ks文件下解析获得 结构体信息 参数: uid:用户id 返回值: 成功返回id对应用户的信息 失败返回NULL eg&#xff1a;接受返回值struct passwd * pw getpwuid(uid); struct passwd {char *pw_name; …

Servlet---axios框架 ▎路由守卫

前言 在现代Web应用中&#xff0c;前端和后端通常分离&#xff0c;前端使用框架&#xff08;如Vue.js、React&#xff09;与后端服务交互。Servlet是Java EE中处理HTTP请求的重要组成部分&#xff0c;能够生成动态Web内容。 Axios是一个基于Promise的HTTP客户端&#xff0c;简…

【layUI】只能选某个特定区间的日历

要实现的功能如下&#xff1a;业务要求让日历只有近3天可选&#xff0c;其它部分变灰且不可选。 代码实现 在html中加入如下代码&#xff1a; <label class"layui-form-label" style"">日期&#xff1a; </label> <div class"layu…

二、前后端分离通用权限系统(2)

&#x1f33b;&#x1f33b; 目录 一、 Mybatis-Plus 复习1.1、简介1.2、特点1.3、支持数据库1.4、在工程中引入依赖 二、Mybatis-Plus 入门2.1、导入配置文件2.2、导入启动类2.3、实体类2.4、创建 Mapper 类2.5、创建测试 Mapper接口2.6、CRUD 测试2.6.1、insert 添加2.6.2、主…

flink环境搭建

Flink会话模式 1.集群规划&#xff1a; 2. 将flink拖到/opt/so下 3. 将安装包解压到/opt/module下&#xff1a; tar -zxvf /opt/so/flink-1.15.4-bin-scala_2.12.tgz -C /opt/module 4. 改个名&#xff1a;mv flink-1.15.4 flink 5. 修改配置文件&#xff1a;cd /opt/mo…

CPU 绑核

随笔记录 目录 1. 背景介绍 2. 查询设备CPU 中断核 2.1 查询设备名 2.2 查询设备CPU 中断核 2.2.1 查询本服务上所有设备 CPU 中断核Number 2.2.2 查询 每个设备cpu 中断核的 3. 确定可绑定CPU 核 3.1 查询cpu 信息 3.2 绑核 3.3 更新group 3.4 重启后查看 4. 绑核…

9 算术、关系、逻辑、赋值、位操作、三元运算符及其优先级

目录​​​​​​​ 1 运算符基础 1.1 什么是运算符 1.2 什么是表达式 1.3 左操作数和右操作数 1.4 运算符分类 1.4.1 按照操作数个数分类 1.4.2 按照功能分类 1.5 如何掌握运算符 2 算术运算符 2.1 正号和负号 2.2 加、减、乘、除 2.3 取模&#xff08;取余&#…

Java八股整合(MySQL+Redis+Maven)

MySQL 数据库设计三范式 不可再分&#xff0c;部分依赖&#xff0c;传递依赖 主键和外键区别 主键非空约束&#xff0c;唯一性约束&#xff0c;唯一标识一个字段 外键用于和其他表建立连接&#xff0c;是另一张表的主键&#xff0c;可重复可为空可以有多个 为什么不推荐使…

记录一次生产jvm问题的排查

记录一次生产问题的排查 第一天晚上 现象 1、前援反馈页面有接口陆续出现请求超时 2、登录后台服务器top命令查看发现java进程发生高cpu占用情况 3、查看对应业务日志&#xff0c;报数据库连接等待超时-数据库连接池连接无空闲 对应处理 1、临时调大数据库连接池最大连接数限…

如何发布自己的NPM包详细步骤

前言 在前端开发中&#xff0c;将自己编写的 Vue 组件或插件打包并发布到 NPM 上&#xff0c;不仅可以方便自己在其他项目中复用&#xff0c;还能分享给更多的开发者使用。本文将从 NPM 注册、登录与发布流程&#xff0c;及如何通过 Vue CLI 打包插件的角度详细介绍如何发布 V…