驱动(RK3588S)第六课时:linux2.6的使用与GPIO子系统的使用

news2024/9/21 4:40:40

目录

  • 一、Linux2.6 字符设备驱动编写框架
    • 1、合成一个完整的设备号函数
    • 2、从完整的设备号里提取主设备号
    • 3、动态申请设备号
    • 4、静态申请设备号
    • 5、释放申请的设备号
    • 6、Linux2.6 字符设备驱动的核心结构体
    • 7、初始化核心结构体
    • 8、向内核去申请 linux2.6 字符设备
    • 9、释放申请的设备
    • 10、创建一个类,去管理你注册的设备 /sys/class/name
    • 11、自动创建设备节点
    • 12、销毁类
    • 13、销毁设备节点,节点位于 /dev/name
  • 二、GPIO子系统的使用
    • 1、申请你要使用 gpio 口的资源
    • 2、释放gpio口资源
    • 3、配置 gpio 口的工作模式为输出
    • 4、配置 gpio 口的工作模式为输入
    • 5、配置 gpio 口的工作模式为输入
    • 6、 设置 gpio 的电平状态
  • 三、代码实现

一、Linux2.6 字符设备驱动编写框架

Linux2.6 他是咱们字符设备驱动编写的第二种方法,这种方法要比杂项的复杂,过程和函数是比较多的,他最大的优点就在于他的资源很多 — 设备号,而杂项设备号是非常有限的,也就是 0 — 255 因为主设备号是固定是 10 。但是Linux2.6 他使用一个 32 位的数字表示设备号,这个设备号里包含了主设备号和次设备号,32 位他做一个划分,前 12 位是主设备号,后 20 位是次设备
号,因此他的设备号的取值范围是比较大的。主设备号的范围:2^12 次设备号的范围:2^20, 他使用主设备号和次设备号合成了一个完整的设备号。

1、合成一个完整的设备号函数

函数头文件:#include <linux/cdev.h>
函数参数:
ma: — 主设备号
mi: ---- 次设备号
函数返回值:合成之后的完整的设备号
函数功能:合成一个完整的设备号
函数原型:MKDEV(ma,mi)

2、从完整的设备号里提取主设备号

函数头文件:#include <linux/cdev.h>
函数参数:dev :设备号
函数返回值:提取的主设备,提取的次设备
函数功能:从完整的设备号里提取主设备号
函数原型:MAJOR(dev)
MINOR(dev)

3、动态申请设备号

**函数头文件:#include <linux/fs.h>
函数参数:dev:保存申请成功的设备号
baseminor:次设备号起始值
count:连续申请次设备号的数量
name:设备的名字 — 无所谓
函数返回值:成功返回 0 失败负数
函数功能:动态申请设备号
函数原型:int alloc_chrdev_region(
dev_t dev,
unsigned baseminor,
unsigned count,
const char name)

4、静态申请设备号

*函数功能:静态申请设备号
函数原型:int register_chrdev_region(
dev_t from,
unsigned count,
const char name)
函数头文件:同上
函数参数:from:是你自己提前使用 MKDEV 函数合成一个完整的设备号
count:申请的次设备号的数量
name:设备的名字 — 无所谓
函数返回值:成功返回 0 失败负数

5、释放申请的设备号

函数功能:释放申请的设备号
函数原型:void unregister_chrdev_region(
dev_t from,
unsigned count)
函数头文件:同上
函数参数:from:设备号
count:申请的次设备号的数量
函数返回值:无

6、Linux2.6 字符设备驱动的核心结构体

**这个结构体一般只需要咱们定义一个结构体变量即可。
struct cdev {
struct kobject kobj;
struct module owner;//代表这个模块 THIS_MODULE
const struct file_operations ops;//操作设备集合的方法
struct list_head list;
dev_t dev;//设备号
unsigned int count;//次设备号的数量
} __randomize_layout;

7、初始化核心结构体

**函数功能:初始化核心结构体
函数原型:void cdev_init(
struct cdev cdev,
const struct file_operations fops)
函数头文件:#include<linux/cdev.h>
函数参数:cdev:定义的核心结构体
fops:定义操作设备方法集合的结构体变量
函数返回值:无

8、向内核去申请 linux2.6 字符设备

*函数功能:向内核去申请 linux2.6 字符设备
函数原型:int cdev_add(
struct cdev p,
dev_t dev,
unsigned count)
函数头文件:#include<linux/cdev.h>
函数参数:p:定义的核心结构体
dev:设备号
count:次设备号的数量
函数返回值:成功返回 0 失败负数

9、释放申请的设备

*函数功能:释放申请的设备
函数原型:void cdev_del(struct cdev p)
函数头文件:#include<linux/cdev.h>
函数参数:p:定义的核心结构体
函数返回值:无

10、创建一个类,去管理你注册的设备 /sys/class/name

**函数功能:创建一个类,去管理你注册的设备
函数原型:struct class * class_create(
struct module owner,
const char name)
函数头文件:#include<linux/device.h>
函数参数:owner:他是一个固定的值 THIS_MODULE
name:创建类的名字
函数返回值:成功返回指向 struct class 失败 NULL

11、自动创建设备节点

**函数功能:自动创建设备节点
函数原型:struct device *device_create(
struct class *class,
struct device *parent,
dev_t devt,
void drvdata,
const char fmt,…)
函数头文件:#include<linux/device.h>
函数参数:class:创建的类
parent:父设备 — 写 NULL
devt:设备号
drvdata:内核的私有数据 — 写 NULL
fmt:他一般就是你创建的设备节点的名字
函数返回值:成功返回一个指向 struct device 失败 NULL

12、销毁类

*函数功能:销毁类
函数原型:void class_destroy(struct class cls)
函数头文件:#include<linux/device.h>
函数参数:cls:就是定义的类的变量名
函数返回值:无

13、销毁设备节点,节点位于 /dev/name

*函数功能:销毁设备节点 /dev/name
函数原型:void device_destroy(struct class class,dev_t devt)
函数头文件:#include<linux/device.h>
函数参数:class:定义的类名
devt:设备号

二、GPIO子系统的使用

所谓的 gpio 子系统值的就是使用内核封装好的函数对设备进行操作,这里一般操
作设备都是通过 gpio 口,因此咱们需要掌握操作 gpio 口函数。在使用 GPIO 口区操作硬件的时候,你需要先申请注册才能使用当前的 gpio 口的资源。我以下检测是灯,高电平亮,低电平不亮。
下面演示 GPIO1_D0 pin 脚计算方法:
bank = 1; //GPIO1_D0 => 1, bank ∈ [0,4]
group = 3; //GPIO1_D0 => 3, group ∈ {(A=0), (B=1), (C=2), (D=3)}
X = 0; //GPIO1_D0 => 0, X ∈ [0,7]
number = group * 8 + X = 3 * 8 + 0 = 24
pin = bank*32 + number= 1 * 32 + 24 = 56;
最后要得到的是pin

1、申请你要使用 gpio 口的资源

*函数功能:申请你要使用 gpio 口的资源
函数原型: int gpio_request(unsigned gpio, const char label)
函数头文件:#include <linux/gpio.h>
函数参数:gpio:这里就是你要申请注册的 gpio 口的编号
label:标签 — 一般没有太大作用,就是标识
函数返回值:成功返回 0 失败负数

2、释放gpio口资源

函数功能: 释放 gpio 口资源
函数原型: void gpio_free(unsigned gpio)
函数头文件: #include <linux/gpio.h>
函数参数:gpio:gpio:这里就是你要申请注册的 gpio 口的编号
函数返回值:无

3、配置 gpio 口的工作模式为输出

函数功能:配置 gpio 口的工作模式为输出
函数原型: int gpio_direction_output(unsigned gpio, int value)
函数头文件:#include <linux/gpio.h>
函数参数:gpio:这里就是你要申请注册的 gpio 口的编号
value:默认给的值 — 一般就是 0 或者是 1 代表高低电平
函数返回值:成功返回 0 失败负数

4、配置 gpio 口的工作模式为输入

函数功能: 配置 gpio 口的工作模式为输入
函数原型: int gpio_direction_input(unsigned gpio)
函数头文件:#include <linux/gpio.h>
函数参数:gpio:这里就是你要申请注册的 gpio 口的编号
函数返回值:成功返回 0 失败负数
这里配置的 gpio 口模式为输入和输出那么这里的输入和输出是针对于 CPU 来说的。

5、配置 gpio 口的工作模式为输入

函数功能:获取这个 gpio 口引脚上的电平的状态
函数原型: int gpio_get_value(unsigned gpio)
函数头文件:#include <linux/gpio.h>
函数参数:gpio:这里就是你要申请注册的 gpio 口的编号
函数返回值:返回获取的电平的状态 — 高电平或者是低电平 1/0

6、 设置 gpio 的电平状态

函数功能: 设置 gpio 的电平状态
函数原型: void gpio_set_value(unsigned gpio, int value)
函数头文件:#include <linux/gpio.h>
函数参数:gpio:这里就是你要申请注册的 gpio 口的编号
value:你要设置的电平的状态 — 高电平 1 低电平 0
函数返回值:无

三、代码实现

linux26.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;
int gpio_value = 0;
int myled_open (struct inode *inode, struct file *fp)
{
	gpio_set_value(21,1);
	printk("myled open ok\n");
	printk("myled open 正确打开\n");
	return 0;
}

int myled_close (struct inode *inode, struct file *fp)
{
	gpio_set_value(21,0);
	printk("myled close ok\n");
	printk("myled close 关闭正确\n");
	return 0;
}
struct file_operations myfops={
	.open = myled_open,
	.release = myled_close,

};
static int __init myled_init(void)
{	
	int all;
	gpio_value=gpio_request(21, "led5");
	printk("gpio_value:%d\n",gpio_value);
	if(gpio_value==0)
	{
		printk("申请成功\n");
	}
	gpio_direction_output(21, 1);
	all=alloc_chrdev_region(&dev,0, 1,"led");
	if(all<0)
	{
		printk("alloc_chrdev_region error\n");
		printk("动态创建失败\n");
		return -1;
	}
	printk("主设备号:%d\n",MAJOR(dev));
	printk("次设备号:%d\n",MINOR(dev));
	cdev_init(&mydev,&myfops);
	cdev_add(&mydev,dev,1);
	myclass=class_create(THIS_MODULE,"class_led");
	if(myclass == NULL)
	{
		printk("class_create error\n");
		printk("class_create 类创建失败\n");
		return -1;
	}
	device_create(myclass,NULL,dev,NULL,"myled");
	return 0;
}
static void __exit myled_exit(void)
{
	device_destroy(myclass,dev);
	class_destroy(myclass);
	cdev_del(&mydev);
	unregister_chrdev_region(dev,1);
	gpio_free(21);
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

app.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int fd = 0;
	if(argc < 2)
	{
		printf("请输入正确的参数\n");
		return -1;
	}
	while(1)
		{
			fd = open(argv[1],O_RDWR); // --- 底层的open函数
			sleep(5);
			close(fd);//底层的close
			sleep(5);
		}
	return 0;
}

Makefile

obj-m += linux26.o #最终生成模块的名字就是 led.ko      
    
KDIR:=/home/stephen/RK3588S/kernel  #他就是你现在rk3588s里内核的路径 
    
CROSS_COMPILE_FLAG=/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
    #这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE_FLAG)
	aarch64-none-linux-gnu-gcc app.c -o app
    #调用内核层 Makefile 编译目标为 modules->模块 文件在当前路径
    # 架构  ARCH=arm64 
clean:
	rm -f  *.o *.mod.o *.mod.c *.symvers *.markers *.order app  *.mod

编译之后将app和linux.ko推送金开发板中。
在这里插入图片描述
开发板中的现象:
在这里插入图片描述
灯现象:
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Linux实验报告2-初步使用shell

目录 一&#xff1a;实验目的 二&#xff1a;实验内容 1 请指出下面每条命令中哪部分是命令名、选项和参数 3 以列表及递归方式查看/dev目录下的文件。 4 修改当前系统时间为2015年1月1日。 7 查看/tmp目录下的所有文件&#xff0c;指出哪些属于隐藏文件。 8 统计文件/e…

Kaggle竞赛:Rossmann Store Sales第66名策略复现

之前做过一次Kaggle的时间序列竞赛数据集练习&#xff1a;CSDN链接效果并不理想&#xff0c;之后在Kaggle的评论中又找到了各式各样的模型方法&#xff0c;其中我还手动还原过第三名的Entity Embedding&#xff1a;CSDN链接。这个参赛方法中&#xff0c;使用了除了比赛给出的数…

MySQL之UDF提权复现

什么是UDF&#xff1a; UDF(Userfined function)用户自定义函数&#xff0c;是MySQL的一个扩展接口&#xff0c;用户通过自定义函数可以实现在 MySQL 中无法方便实现的功能&#xff0c;其添加的新函数都可以在 SQL 语句中调用。 提权条件&#xff1a; 知道MySQL用户名和密码…

探索 Nuxt Devtools:功能全面指南

title: 探索 Nuxt Devtools:功能全面指南 date: 2024/9/3 updated: 2024/9/3 author: cmdragon excerpt: 摘要:本文介绍了Nuxt Devtools的功能和使用方法,包括自动安装、手动安装和各项主要功能,如页面、组件、构建分析等。 categories: 前端开发tags: NuxtDevtools前端…

【AI】Pytorch_损失函数优化器

建议点赞收藏关注&#xff01;持续更新至pytorch大部分内容更完。 本文已达到10w字&#xff0c;故按模块拆开&#xff0c;详见目录导航。 整体框架如下 数据及预处理 模型及其构建 损失函数及优化器 本节目录 损失函数创建损失函数 &#xff08;共18个&#xff09;nn.CrossEnt…

《CounTR: Transformer-based Generalised Visual Counting》CVPR2023

摘要 本论文考虑了通用视觉对象计数问题&#xff0c;目标是开发一个计算模型&#xff0c;用于计算任意语义类别的对象数量&#xff0c;使用任意数量的“样本”&#xff08;即可能为零样本或少样本计数&#xff09;。作者提出了一个新颖的基于Transformer的架构&#xff0c;称为…

【前端面试】leetcode树javascript

写一个树 // 定义二叉树节点 function TreeNode(val, left, right) {this.val = (val === undefined ? 0 : val)this.left = (left === undefined ? null : left)this.right = (right === undefined ? null : right) }// 示例使用 const root = new TreeNode(1,new TreeNod…

Javaweb开发总结(2)

1.处理项目中的异常 利用全局异常处理器 单独创建一个类来处理全局的异常&#xff0c;并对其做出相应回应 /* * 全局异常处理器 * */ RestControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class)//代表我们要捕获所有异常public Result ex…

STL-string对字符串进行操作

C形式下的字符串:c_str() string s1("hello"); const char* str s1.c_str(); while (*str) {cout << *str << " ";str; } cout << "\n"; 获取字符数组首地址&#xff0c;用C字符串的形式遍历 区别&#xff1a; cout <…

c++实现生产者消费者的供需关系

一、生产者&消费者模式 生产者-消费者模式&#xff08;Producer-Consumer Pattern&#xff09;是一种常见的并发设计模式&#xff0c;这种模式最常见&#xff0c;所以把它单独拿出来&#xff0c;这种模式用于处理生产者和消费者之间的协调问题。生产者和消费者之间不直接关…

leveldb源码解析(一)——编解码

leveldb中&#xff0c;数字的存储统一采用小端序&#xff0c;通过对数字编码和压缩&#xff0c;节省了存储空间。 变长编码 小端序中&#xff0c;每个字节的最低位存储状态&#xff0c;其余7位存储数据。 status状态值说明&#xff1a; 1&#xff1a;该字节不是当前数字最后…

《Few-shot Object Counting and Detection》CVPR2022

概述 摘要&#xff1a; 论文提出了一个新的任务——少量样本目标计数和检测&#xff08;Few-shot Object Counting and Detection, FSCD&#xff09;。在这项任务中&#xff0c;研究者们旨在通过给定少量目标类别的示例边界框来计数和检测图像中所有目标对象。这项任务与少量样…

Your connection to this site is not secure

chrome 打开某一个网站的网页地址栏提示Your connection to this site is not secure,同一个网站的其它地址栏打开不会 无效的方案 浏览器地址栏输入: chrome://flags 找到下边的选项&#xff0c;从Default改为Disabled即可成功解决 亲测这个方法不行 解决方案 点击右上角的3个…

力扣每日一题 一个小组的最大实力值 线性DP

Problem: 2708. 一个小组的最大实力值 &#x1f468;‍&#x1f3eb; 灵神题解 class Solution {public long maxStrength(int[] nums) {// 初始化mn和mx为第一个元素的值long mn nums[0];long mx nums[0];// 从第二个元素开始遍历数组for (int i 1; i < nums.length; i…

vue项目生成插件的LICENSE文件

一、安装license-webpack-plugin npm install --save-dev license-webpack-plugin 二、添加webpack配置 const {LicenseWebpackPlugin} require(license-webpack-plugin)module.exports {configureWebpack: {plugins: [new LicenseWebpackPlugin()]} }三、执行npm run buil…

jpg图片怎么转换成png?值得推荐给大家的几种转换方法

jpg图片怎么转换成png&#xff1f;将jpg图像转换为png格式可以显著提升图像的质量和清晰度&#xff0c;并满足一些特殊需求&#xff0c;例如透明背景。png格式采用无损压缩&#xff0c;这意味着图像在转换过程中不会丢失任何细节或质量&#xff0c;相比于jpg的有损压缩&#xf…

day06 1.继承和多态

#include "work.h"Stack:: Stack():size(10) {data new char[size];top -1;cout <<"无参构造"<<endl; }Stack:: Stack(const char* s) {size strlen(s);data new char[size];strcpy(data,s);top size-1;cout <<"有参构造"…

Android之Handler的post方法和sendMessage的区别

目录 post 方法方法特点 sendMessage 方法方法特点 使用场景区别总结 Handler 类在 Android 中用于在不同线程之间传递消息和执行代码。它提供了两种主要的方式来执行任务&#xff1a;通过 post 方法和通过 sendMessage 方法。这两种方法有不同的使用场景和特点。 post 方法 方…

【系统架构设计】嵌入式系统设计(2)

【系统架构设计】嵌入式系统设计&#xff08;2&#xff09; 嵌入式网络系统嵌入式 Internet 的接入方式嵌入式 TCP/IP 协议栈 嵌入式数据库管理系统数据的一致性高效的事务处理数据的安全性 实时系统与嵌入式操作系统对实时系统划分根据实时性的强弱根据对错失时限的容忍程度或…

Android Google Maps

Android 谷歌地图 前言正文一、设置Google Cloud 项目二、项目配置① 设置SDK② 配置API密钥③ 配置AndroidManifest.xml 三、添加地图四、定位当前① 请求定位权限② 我的位置控件③ 获取当前位置 五、配置地图① xml配置地图② 代码配置地图③ 地图点击事件④ 管理Marker 六、…