当前位置: 首页 > article >正文

Linux LED驱动(设备树)

Linux LED驱动(设备树)

之前的LED驱动直接在驱动文件中定义有关寄存器物理地址,然后使用io_remap函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对GPIO的初始化。
但也可以先在设备树文件中创建相应的设备节点,再使用设备树向Linux内核传递相关的寄存器物理地址,再使用OF函数从设备树中获取所需的属性值,完成驱动程序的编写。

0. 设备树

	led{compatible = "alientek,led";status = "okay";default-state = "on";reg = < 0xE000A040 0x40xE000A204 0x40xE000A208 0x40xE000A214 0x40xF800012C 0x4>;}

1. 根据设备树编写驱动

  1. 定义设备结构体
    设备结构体包含cdev、类、设备、设备号等。
  2. 实现开关读写4个系统调用函数
    在文件打开函数中使用filp->private_data设置私有数据;使用copy_from_user()实现用户空间和内存空间数据交互,并使用readl()函数读取寄存器映射后的虚拟地址的数据,使用writel()函数根据寄存器映射后的虚拟地址将数据写入寄存器,从而实现文件写函数(LED的亮灭)。
  3. 初始化设备操作函数结构体
  4. 实现设备注册和注销函数
    使用of_find_node_by_path(“/led”)函数led设备节点,使用of_property_read_string()函数读取status属性,使用of_property_read_string()函数获取compatible属性值并进行匹配,与直接使用ioremap()根据物理地址映射虚拟地址不同,这里使用of_iomap()函数获取从设备树节点获取寄存器地址进行内存映射,然后使能GPIO、关闭中断、设置方向,使用register_chrdev_region()函数、alloc_chrdev_region()函数创建设备号,并使用module_init()函数指定设别注册函数实现insmod,使用cdev_init()初始化cdev,使用cdev_add()添加cdev,使用class_create()创建类,使用device_create()创建设备;使用device_destroy()注销设备,使用class_destroy()注销类,使用cdev_del()删除cdev,使用unregister_chrdev_region()注销设备号,使用led_iounmap()取消地址映射,并使用module_exit()函数指定设备注销函数实现rmmod。
  5. 添加LICENSE和作者
    使用MODULE_LICENSE()函数和MODULE_AUTHOR()函数添加LICENSE和作者。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>#define DTSLED_CNT		1				/* 设备号个数 */
#define DTSLED_NAME		"dtsled"		/* 名字 *//* 映射后的寄存器虚拟地址指针 */
static void __iomem *data_addr;
static void __iomem *dirm_addr;
static void __iomem *outen_addr;
static void __iomem *intdis_addr;
static void __iomem *aper_clk_ctrl_addr;// 0. **定义设备结构体**
/* dtsled设备结构体 */
struct dtsled_dev {dev_t devid;			/* 设备号 */struct cdev cdev;		/* cdev */struct class *class;	/* 类 */struct device *device;	/* 设备 */int major;				/* 主设备号 */int minor;				/* 次设备号 */struct device_node *nd;	/* 设备节点 */
};static struct dtsled_dev dtsled;	/* led设备 */// 1. **实现开关读写4个系统调用函数**
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &dtsled;	/* 设置私有数据 */return 0;
}static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{return 0;
}static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{int ret;int val;char kern_buf[1];ret = copy_from_user(kern_buf, buf, cnt);	// 得到应用层传递过来的数据if(0 > ret) {printk(KERN_ERR "kernel write failed!\r\n");return -EFAULT;}val = readl(data_addr);if (0 == kern_buf[0])val &= ~(0x1U << 7);		// 如果传递过来的数据是0则关闭ledelse if (1 == kern_buf[0])val |= (0x1U << 7);			// 如果传递过来的数据是1则点亮ledwritel(val, data_addr);return 0;
}static int led_release(struct inode *inode, struct file *filp)
{return 0;
}// 2. **初始化设备操作函数结构体**
static struct file_operations dtsled_fops = {.owner		= THIS_MODULE,.open		= led_open,.read		= led_read,.write		= led_write,.release	= led_release,
};// 3. **实现设备注册和注销函数**
static inline void led_ioremap(void)
{data_addr = of_iomap(dtsled.nd, 0);dirm_addr = of_iomap(dtsled.nd, 1);outen_addr = of_iomap(dtsled.nd, 2);intdis_addr = of_iomap(dtsled.nd, 3);aper_clk_ctrl_addr = of_iomap(dtsled.nd, 4);
}static inline void led_iounmap(void)
{iounmap(data_addr);iounmap(dirm_addr);iounmap(outen_addr);iounmap(intdis_addr);iounmap(aper_clk_ctrl_addr);
}static int __init led_init(void)
{const char *str;u32 val;int ret;/* 1.获取led设备节点 */dtsled.nd = of_find_node_by_path("/led");if(NULL == dtsled.nd) {printk(KERN_ERR "led node can not found!\r\n");return -EINVAL;}/* 2.读取status属性 */ret = of_property_read_string(dtsled.nd, "status", &str);if(!ret) {if (strcmp(str, "okay"))return -EINVAL;}/* 2、获取compatible属性值并进行匹配 */ret = of_property_read_string(dtsled.nd, "compatible", &str);if(0 > ret)return -EINVAL;if (strcmp(str, "alientek,led"))return -EINVAL;printk(KERN_ERR "led device matching successful!\r\n");/* 4.寄存器地址映射 */led_ioremap();/* 5.使能GPIO时钟 */val = readl(aper_clk_ctrl_addr);val |= (0x1U << 22);writel(val, aper_clk_ctrl_addr);/* 6.关闭中断功能 */val |= (0x1U << 7);writel(val, intdis_addr);/* 7.设置GPIO为输出功能 */val = readl(dirm_addr);val |= (0x1U << 7);writel(val, dirm_addr);/* 8.使能GPIO输出功能 */val = readl(outen_addr);val |= (0x1U << 7);writel(val, outen_addr);/* 9.初始化LED的默认状态 */val = readl(data_addr);ret = of_property_read_string(dtsled.nd, "default-state", &str);if(!ret) {if (!strcmp(str, "on"))val |= (0x1U << 7);elseval &= ~(0x1U << 7);} elseval &= ~(0x1U << 7);writel(val, data_addr);/* 10.注册字符设备驱动 *//* 创建设备号 */if (dtsled.major) {dtsled.devid = MKDEV(dtsled.major, 0);ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);if (ret)goto out1;} else {ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);if (ret)goto out1;dtsled.major = MAJOR(dtsled.devid);dtsled.minor = MINOR(dtsled.devid);}printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor); /* 初始化cdev */dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);/* 添加一个cdev */ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);if (ret)goto out2;/* 创建类 */dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);if (IS_ERR(dtsled.class)) {ret = PTR_ERR(dtsled.class);goto out3;}/* 创建设备 */dtsled.device = device_create(dtsled.class, NULL,dtsled.devid, NULL, DTSLED_NAME);if (IS_ERR(dtsled.device)) {ret = PTR_ERR(dtsled.device);goto out4;}return 0;out4:class_destroy(dtsled.class);out3:cdev_del(&dtsled.cdev);out2:unregister_chrdev_region(dtsled.devid, DTSLED_CNT);out1:led_iounmap();return ret;
}static void __exit led_exit(void)
{/* 注销设备 */device_destroy(dtsled.class, dtsled.devid);/* 注销类 */class_destroy(dtsled.class);/* 删除cdev */cdev_del(&dtsled.cdev);/* 注销设备号 */unregister_chrdev_region(dtsled.devid, DTSLED_CNT);/* 取消地址映射 */led_iounmap();
}/* 驱动模块入口和出口函数注册 */
module_init(led_init);
module_exit(led_exit);MODULE_AUTHOR("DengTao <773904075@qq.com>");
MODULE_DESCRIPTION("Alientek ZYNQ GPIO LED Driver");
MODULE_LICENSE("GPL");

应用程序:

  1. 打开文件
  2. 将字符串转为数据,并将数据写入文件(开关LED)
  3. 关闭文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>/** @description		: main主程序* @param - argc	: argv数组元素个数* @param - argv	: 具体参数* @return			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, ret;unsigned char buf[1];if(3 != argc) {printf("Usage:\n""\t./ledApp /dev/led 1		@ close LED\n""\t./ledApp /dev/led 0		@ open LED\n");return -1;}/* 打开设备 */fd = open(argv[1], O_RDWR);if(0 > fd) {printf("file %s open failed!\r\n", argv[1]);return -1;}/* 将字符串转换为int型数据 */buf[0] = atoi(argv[2]);/* 向驱动写入数据 */ret = write(fd, buf, sizeof(buf));if(0 > ret){printf("LED Control Failed!\r\n");close(fd);return -1;}/* 关闭设备 */close(fd);return 0;
}

相关文章:

Linux LED驱动(设备树)

Linux LED驱动&#xff08;设备树&#xff09; 之前的LED驱动直接在驱动文件中定义有关寄存器物理地址&#xff0c;然后使用io_remap函数进行内存映射&#xff0c;得到对应的虚拟地址&#xff0c;最后操作寄存器对应的虚拟地址完成对GPIO的初始化。 但也可以先在设备树文件中创…...

Zookeeper的典型应用场景?

大家好&#xff0c;我是锋哥。今天分享关于【Zookeeper的典型应用场景?】面试题。希望对大家有帮助&#xff1b; Zookeeper的典型应用场景? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 ZooKeeper 是一个开源的分布式协调服务&#xff0c;主要用于管理和协调大…...

数据分析不只是跑个SQL!

数据分析不只是跑个SQL&#xff01; 数据分析五大闭环&#xff0c;你做到哪一步了&#xff1f;闭环一&#xff1a;认识现状闭环二&#xff1a;原因分析闭环三&#xff1a;优化表现闭环四&#xff1a;预测走势闭环五&#xff1a;主动解读数据 数据思维&#xff1a;WHY-WHAT-HOW模…...

面试篇 - GPT-3(Generative Pre-trained Transformer 3)模型

GPT-3&#xff08;Generative Pre-trained Transformer 3&#xff09;模型 模型结构 与GPT-2一样&#xff0c;但是应用了Sparse attention&#xff1a; Dense attention&#xff1a;每个token之间两两计算attention&#xff0c;复杂度为O(n2)。 Sparse attention&#xff1a;…...

Dify智能体平台源码二次开发笔记(4) - 多租户的SAAS版实现

前言 Dify 的多租户功能是其商业版的标准功能&#xff0c;我们应当尊重其盈利模式。只有保持良性的商业运作&#xff0c;Dify 才能持续发展&#xff0c;并为用户提供更优质的功能。因此&#xff0c;此功能仅限学习使用。 我们的需求是&#xff1a;实现类似 SaaS 版的账号隔离&a…...

C# 13新特性 - .NET 9

转载&#xff1a; C# 13 中的新增功能 | Microsoft Learn C# 13 包括以下新增功能。 可以使用最新的 Visual Studio 2022 版本或 .NET 9 SDK 尝试这些功能&#xff1a;Introduced in Visual Studio 2022 Version 17.12 and newer when using C# 13 C# 13 中的新增功能 | Micr…...

【Code】《代码整洁之道》笔记-Chapter9-单元测试

第9章 单元测试 过去十年以来&#xff0c;编程专业领域进步很大。1997年时&#xff0c;没人听说过测试驱动开发。对于我们之中的大多数人来说&#xff0c;单元测试是那种用来确保程序“可运行”的用过即扔的短代码。我们辛勤地编写类和方法&#xff0c;再弄出一些特殊代码来测…...

java -jar 如何持久化运行

在 Linux 中,直接通过 java -jar 启动服务后关闭 SSH 客户端(如 Xshell)会导致服务终止,因为进程默认与当前终端会话绑定。以下是几种解决方案,确保服务在后台持久运行: (1)使用nohup命令,让进程忽略挂断信号,并在后台运行。 ps -ef | grep xxx.jar 或者 ps -ef …...

layui中transfer两个table展示不同的数据列

在项目的任务开发中需要达到transfer右侧table需要有下拉框可选择状态&#xff0c;左侧table不变 使用的layui版本为2.4.5&#xff0c;该版本没有对transfer可自定义数据列的配置&#xff0c;所以改动transfer.js中的源码 以下为transfer.js部分源码 也是transfer.js去render的…...

如何通过Radius认证服务器实现虚拟云桌面安全登录认证:安当ASP身份认证系统解决方案

引言&#xff1a;虚拟化时代的安全挑战 随着云计算和远程办公的普及&#xff0c;虚拟云桌面&#xff08;如VMware Horizon、Citrix&#xff09;已成为企业数字化办公的核心基础设施。然而&#xff0c;传统的用户名密码认证方式暴露了诸多安全隐患&#xff1a;弱密码易被暴力破…...

如何用DeepSeek大模型提升MySQL DBA工作效率?实战案例解析

如何用DeepSeek大模型提升MySQL DBA工作效率&#xff1f;实战案例解析 MySQL DBA&#xff08;数据库管理员&#xff09;的工作涉及数据库监控、SQL优化、故障排查、备份恢复等复杂任务&#xff0c;传统方式依赖手动操作和经验判断&#xff0c;效率较低。而DeepSeek大模型可以结…...

【机器学习】机器学习笔记

1 机器学习定义 计算机程序从经验E中学习&#xff0c;解决某一任务T&#xff0c;进行某一性能P&#xff0c;通过P测定在T上的表现因经验E而提高。 eg&#xff1a;跳棋程序 E&#xff1a; 程序自身下的上万盘棋局 T&#xff1a; 下跳棋 P&#xff1a; 与新对手下跳棋时赢的概率…...

CFD中的动量方程非守恒形式详解

在计算流体力学&#xff08;CFD&#xff09;中&#xff0c;动量方程可以写成守恒形式和非守恒形式&#xff0c;两者在数学上等价&#xff0c;但推导方式和应用场景不同。以下是对非守恒形式的详细解释&#xff1a; 1. 动量方程的守恒形式 首先回顾守恒形式的动量方程&#xff…...

如何在本地修改 Git 项目的远程仓库地址

✅ 场景说明 你当前的 Git 项目地址是&#xff1a; http://192.168.0.16/xxx.git你希望把它改成&#xff1a; http://192.168.0.22:8099/xxx.git&#x1f9e9; 操作步骤 步骤 ①&#xff1a;进入项目所在目录 你已经在正确路径下了&#xff1a; cd C:\Develop\xxx确认这个…...

clickhouse中的窗口函数

窗口函数 边界核心参数 窗口边界通过 ROWS、RANGE 或 GROUPS 模式定义,语法为: ROWS BETWEEN AND 基于 ​物理行位置 定义窗口,与排序键的实际值无关,适用于精确控制窗口行数 – 或 RANGE BETWEEN AND 基于 ​排序键的数值范围 定义窗口,适用于时间序列或连续数值的场景(…...

如何从项目目标到成功标准:构建可量化、可落地的项目评估体系

引言 在项目管理领域&#xff0c;"项目成功"的定义往往比表面看起来更复杂。根据PMI的行业报告&#xff0c;67%的项目失败源于目标与成功标准的不匹配。当项目团队仅关注"按时交付"或"预算达标"时&#xff0c;常会忽视真正的价值创造。本文将通…...

fbx/obj/glb/gltf/b3dm等通用格式批量转换成osgb

fbx/obj/glb/gltf/b3dm等通用格式批量转换成osgb fbx/obj/glb/gltf/b3dm等通用格式批量转换成osgb...

STM32 BOOT设置,bootloader,死锁使用方法

目录 BOOT0 BOOT1的配置含义 bootloader使用方法 芯片死锁解决方法开发调试过程中&#xff0c;由于某种原因导致内部Flash锁死&#xff0c;无法连接SWD以及JTAG调试&#xff0c;无法读到设备&#xff0c;可以通过修改BOOT模式重新刷写代码。修改为BOOT01&#xff0c;BOOT10…...

vue2 设置ant-table和el-table隔行变色

vue2 设置ant-table和el-table隔行变色 ant-table /* 奇数行 */ ::v-deep .ant-table-tbody > tr:nth-child(odd) {background-color: transparent; } /* 偶数行 */ ::v-deep .ant-table-tbody > tr:nth-child(even) {background-color: rgba(15, 166, 255, 0.26); }el…...

【Redis】string类型

目录 1、介绍2、底层实现【1】SDS【2】int编码【3】embstr编码【4】raw编码【5】embstr和raw的区别 3、常用指令【1】字符串基本操作&#xff1a;【2】批量操作【3】计数器【4】过期时间【5】不存在就插入 4、使用场景 1、介绍 string是redis中最简单的键值对形式&#xff0c;…...

《解锁分布式软总线:构建智能设备统一管理平台》

智能设备的数量呈爆发式增长&#xff0c;从智能家居里的各类电器&#xff0c;到智能办公中的电脑、打印机&#xff0c;再到工业领域的各种自动化设备&#xff0c;不一而足。如何对这些纷繁复杂的智能设备进行有效管理&#xff0c;成为摆在我们面前的一道难题。分布式软总线技术…...

PostgreSQL全平台安装指南:从入门到生产环境部署

一、PostgreSQL核心特性全景解析 1.1 技术架构深度剖析 graph TDA[客户端] --> B(连接池)B --> C{查询解析器}C --> D[优化器]D --> E[执行引擎]E --> F[存储引擎]F --> G[物理存储]G --> H[WAL日志]H --> I[备份恢复] 1.2 特性优势对比矩阵 特性维度…...

UE5 物理模拟 与 触发检测

文章目录 碰撞条件开启模拟关闭模拟 多层级的MeshUE的BUG 触发触发条件 碰撞 条件 1必须有网格体组件 2网格体组件必须有网格&#xff0c;没有网格虽然可以开启物理模拟&#xff0c;但是不会有任何效果 注意开启的模拟的网格体组件会计算自己和所有子网格的mesh范围 3只有网格…...

做仪器UI用到的颜色工具网站

https://color.adobe.com/zh/create/color-wheel 1. 图片取颜色工具 2. 对比度工具&#xff0c;煤矿井下设备&#xff0c;光线暗&#xff0c;要求背景与文字有合适的对比度&#xff0c;可以用这个软件 3. 颜色生成ARGB的值工具&#xff0c;这三个工具&#xff0c;都在上面这…...

【算法】【蓝桥23国A软件C】四版代码思路分析与逐步优化

题目来源&#xff1a;第十四届蓝桥杯大赛软件赛国赛C/C 大学 A 组 题目描述&#xff1a; 问题描述 给定一个 WH 的长方形&#xff0c;两边长度均为整数。小蓝想把它切割为很多个边长为整数的小正方形。假设切割没有任何损耗&#xff0c;正方形的边长至少为 2&#xff0c;不允…...

网络安全·第三天·ICMP协议安全分析

一、ICMP功能介绍 ICMP&#xff08;Internet Control Message Protocal&#xff09;是一种差错和控制报文协议&#xff0c;不仅用于传输差错报文&#xff0c; 还传输控制报文&#xff0c;但是ICMP只是尽可能交付&#xff0c;提供的服务是无连接、不可靠的&#xff0c;并不能保…...

SpringBoot对接火山引擎大模型api实现图片识别与分析

文章目录 一、前言二、创建应用三、后端1.SDK集成2.调用Rest API 四、前端 一、前言 Spring AI实战初体验——实现可切换模型AI聊天助手-CSDN博客 如上&#xff0c;在上一篇博客&#xff0c;我们已经实现了spring ai对接本地大模型实现了聊天机器人&#xff0c;但是目前有个新…...

单片机方案开发 代写程序/烧录芯片 九齐/应广等 电动玩具 小家电 语音开发

在电子产品设计中&#xff0c;单片机&#xff08;MCU&#xff09;无疑是最重要的组成部分之一。无论是消费电子、智能家居、工业控制&#xff0c;还是可穿戴设备&#xff0c;小家电等&#xff0c;单片机的应用无处不在。 单片机&#xff0c;简而言之&#xff0c;就是将计算机…...

Open Interpreter:重新定义人机交互的开源革命

引言 在人工智能技术蓬勃发展的今天&#xff0c;人机交互的方式正经历着前所未有的变革。Open Interpreter&#xff0c;作为一个开源项目&#xff0c;正在重新定义我们与计算机的互动方式。它允许大型语言模型&#xff08;LLMs&#xff09;在本地运行代码&#xff0c;通过自然…...

解决前端使用Axios时的跨域问题

跨域问题是前端开发中常见的问题&#xff0c;当你的前端应用尝试访问不同域名、端口或协议的API时就会出现。以下是几种解决方案&#xff1a; 1. 后端解决方案 CORS (推荐) 后端需要设置正确的响应头&#xff1a; Access-Control-Allow-Origin: * // 或指定具体域名 Acces…...