Linux LED驱动(gpio子系统)

0. gpio子系统

gpio子系统是linux内核当中用于管理GPIO资源的一套系统,它提供了很多GPIO相关的API接口,驱动程序中使用GPIO之前需要向gpio子系统申请。
gpio子系统的主要目的就是方便驱动开发者使用gpio,驱动开发者在设备树中添加gpio相关信息,然后就可以在驱动程序中使用gpio子系统提供的API函数来操作GPIO。
Linux内核向驱动开发者屏蔽掉了GPIO的设置过程,极大的方便了驱动开发者使用GPIO。

  • 设备树

使用gpio子系统时,需要更改设备树。
led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;表示led引脚使用的IO属于gpio0,是gpio0的7号引脚,高电平有效。

led {compatible = "alientek,led";status = "okay";default-state = "on";led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;
}
  • GPIO驱动程序

在使用gpio子系统之前,需要向内核gpio子系统注册这一套操作硬件寄存器的“方法”,也就是GPIO驱动。但对于驱动开发,设置好设备树以后就可以使用gpio子系统提供的API函数来操作指定的GPIO,gpio子系统向驱动开发人员屏蔽了具体的读写寄存器过程。

  • gpio_request()
  • gpio_free()
  • gpio_direction_input()
  • gpio_direction_output()
  • gpio_get_value()
  • gpio_set_value()

1. gpio子系统下的LED驱动

  • 修改设备树
led {compatible = "alientek,led";status = "okay";default-state = "on";led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;
}
  • 编写gpio子系统下的LED驱动
  1. 定义设备结构体
    设备结构体包含cdev、类、设备、设备号等。
  2. 实现开关读写4个系统调用函数
    在文件打开函数中使用filp->private_data设置私有数据;使用copy_from_user()实现用户空间和内存空间数据交互,并使用gpio_set_value()设置IO值(LED的亮灭)。
  3. 初始化设备操作函数结构体
  4. 实现设备注册和注销函数
    使用of_find_node_by_path(“/led”)函数led设备节点,使用of_property_read_string()函数读取status属性,使用of_property_read_string()函数获取compatible属性值并进行匹配,使用of_get_named_gpio()获取设备树中的led-gpio属性,得到LED所使用的GPIO编号,然后使用gpio_request()向gpio子系统申请使用GPIO,使用gpio_direction_output()将led 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>
#include <linux/of_gpio.h>#define GPIOLED_CNT		1				/* 设备号个数 */
#define GPIOLED_NAME	"gpioled"		/* 名字 */// 0. **定义设备结构体**
/* dtsled设备结构体 */
struct gpioled_dev {dev_t devid;			/* 设备号 */struct cdev cdev;		/* cdev */struct class *class;	/* 类 */struct device *device;	/* 设备 */int major;				/* 主设备号 */int minor;				/* 次设备号 */struct device_node *nd;	/* 设备节点 */int led_gpio;			/* LED所使用的GPIO编号 */
};static struct gpioled_dev gpioled;	/* led设备 */// 1. **实现开关读写4个系统调用函数**
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &gpioled;	/* 设置私有数据 */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;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;}if (0 == kern_buf[0])gpio_set_value(gpioled.led_gpio, 0);	// 如果传递过来的数据是0则关闭ledelse if (1 == kern_buf[0])gpio_set_value(gpioled.led_gpio, 1);	// 如果传递过来的数据是1则点亮ledreturn 0;
}static int led_release(struct inode *inode, struct file *filp)
{return 0;
}// 2. **初始化设备操作函数结构体**
static struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= led_open,.read		= led_read,.write		= led_write,.release	= led_release,
};// 3. **实现设备注册和注销函数**
static int __init led_init(void)
{const char *str;int ret;/* 1.获取led设备节点 */gpioled.nd = of_find_node_by_path("/led");if(NULL == gpioled.nd) {printk(KERN_ERR "gpioled: Failed to get /led node\n");return -EINVAL;}/* 2.读取status属性 */ret = of_property_read_string(gpioled.nd, "status", &str);if(!ret) {if (strcmp(str, "okay"))return -EINVAL;}/* 2、获取compatible属性值并进行匹配 */ret = of_property_read_string(gpioled.nd, "compatible", &str);if(0 > ret) {printk(KERN_ERR "gpioled: Failed to get compatible property\n");return ret;}if (strcmp(str, "alientek,led")) {printk(KERN_ERR "gpioled: Compatible match failed\n");return -EINVAL;}printk(KERN_INFO "gpioled: device matching successful!\r\n");/* 4.获取设备树中的led-gpio属性,得到LED所使用的GPIO编号 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(!gpio_is_valid(gpioled.led_gpio)) {printk(KERN_ERR "gpioled: Failed to get led-gpio\n");return -EINVAL;}printk(KERN_INFO "gpioled: led-gpio num = %d\r\n", gpioled.led_gpio);/* 5.向gpio子系统申请使用GPIO */ret = gpio_request(gpioled.led_gpio, "LED-GPIO");if (ret) {printk(KERN_ERR "gpioled: Failed to request led-gpio\n");return ret;}/* 6.将led gpio管脚设置为输出模式 */gpio_direction_output(gpioled.led_gpio, 0);/* 7.初始化LED的默认状态 */ret = of_property_read_string(gpioled.nd, "default-state", &str);if(!ret) {if (!strcmp(str, "on"))gpio_set_value(gpioled.led_gpio, 1);elsegpio_set_value(gpioled.led_gpio, 0);} elsegpio_set_value(gpioled.led_gpio, 0);/* 8.注册字符设备驱动 *//* 创建设备号 */if (gpioled.major) {gpioled.devid = MKDEV(gpioled.major, 0);ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);if (ret)goto out1;} else {ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);if (ret)goto out1;gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);}printk("gpioled: major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 添加一个cdev */ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);if (ret)goto out2;/* 创建类 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {ret = PTR_ERR(gpioled.class);goto out3;}/* 创建设备 */gpioled.device = device_create(gpioled.class, NULL,gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {ret = PTR_ERR(gpioled.device);goto out4;}return 0;out4:class_destroy(gpioled.class);out3:cdev_del(&gpioled.cdev);out2:unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);out1:gpio_free(gpioled.led_gpio);return ret;
}static void __exit led_exit(void)
{/* 注销设备 */device_destroy(gpioled.class, gpioled.devid);/* 注销类 */class_destroy(gpioled.class);/* 删除cdev */cdev_del(&gpioled.cdev);/* 注销设备号 */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);/* 释放GPIO */gpio_free(gpioled.led_gpio);
}/* 驱动模块入口和出口函数注册 */
module_init(led_init);
module_exit(led_exit);// 4. **添加LICENSE和作者**
MODULE_AUTHOR("DengTao <773904075@qq.com>");
MODULE_DESCRIPTION("Alientek ZYNQ GPIO LED Driver");
MODULE_LICENSE("GPL");

应用程序:

  1. 打开文件
  2. 从文件读取数据
  3. 将数据写入文件
  4. 关闭文件
#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;
}

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

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

相关文章

go中new和make有什么异同?

相同点&#xff1a;都是给变量分配内存 不同点&#xff1a; 作用类型不同。new通常给int、string、数组类型的变量分配内存&#xff0c;而make通常给slice、map、channel分配内存。返回值类型不同。new返回指向变量的指针&#xff0c;make返回的是变量本身new分配内存空间后&…

C/C++基础知识点

随着工作中琐事越来越多&#xff0c;静下来好好敲代码的时间越来越少&#xff0c;基础知识虽然简单&#xff0c;但常看常新&#xff0c;并记录下来共勉。 一、基础知识点 1. 内存区域中数据管理 在C和C中&#xff0c;内存分为多个区域&#xff0c;每个区域负责存储不同类型的…

消息中间件kafka,rabbitMQ

在分布式系统中,消息中间件是实现不同组件之间异步通信的关键技术。Kafka 和 RabbitMQ 是两个非常流行的消息中间件系统,它们各自有着不同的特点和应用场景。下面将分别介绍 Kafka 和 RabbitMQ,并讨论它们在消息队列中的使用。 一、Kafka (Apache Kafka) 主要特点: 高吞吐…

2k1000LA , 调试串口改成通信串口, uart.

客户的问题解决了&#xff0c;但是 调试串口 改成通信串口的问题&#xff0c;并没有解决&#xff0c;我走的其他的路径。 先准备一些资料。 以备以后使用。 网上的资料。 总结&#xff1a; 实际上 有几种思路了。 1 就是更改 设备树的 chosen 节点&#xff0c; 瑞芯微又单独…

springboot集成spring-cloud-context手动刷新并读取更新后的配置文件

背景 springboot单体项目在运行过程需要刷新springboot配置文件值&#xff0c;比如某个接口限流阈值&#xff0c;新增某个账户等场景。分布式设计的可以直接引入一些持久化中间件比如redis等&#xff0c;也可以用相关配置中心中间件如nacos等。处于成本等场景单体项目可以考虑①…

proteus8.17 环境配置

Proteus介绍 Proteus 8.17 是一款功能强大的电子设计自动化&#xff08;EDA&#xff09;软件&#xff0c;广泛应用于电子电路设计、仿真和分析。以下是其主要特点和新功能&#xff1a; ### 主要功能 - **电路仿真**&#xff1a;支持数字和模拟电路的仿真&#xff0c;包括静态…

手机端可部署的开源大模型; 通义千问2.5训练和推理需要的内存和外存

手机端可部署的开源大模型 目录 手机端可部署的开源大模型Qwen2.5 0.5B 7b 推理采用手机内存需要多少Qwen2.5 0.5B不同量化精度下的内存需求Qwen2.5 7B不同量化精度下的内存需求通义千问2.5训练和推理需要的内存和外存推理阶段1. Qwen2.5 - 7B2. Qwen2.5 - 14B3. Qwen2.5 - 72B…

【uniapp-兼容性处理】swiper在iOS上偶发出现后几张图片白屏情况

【日期】2025-04-14 【问题】 swiper在iOS上偶发出现后几张图片白屏情况 swiper内部的几个swiper-item垂直排列&#xff0c;各自进行滚动&#xff0c;样式方面兼容性出现问题 【原因】&#xff1a; 原代码&#xff1a;&#xff08;不应在swiper-item添加style属性&#xf…

SpringBoot连接MQTT客户端

引入依赖 <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.client.mqttv3</artifactId><version>1.2.2</version> </dependency> 启动类 SpringBootApplication public class AxiosDemoApplic…

HTML:网页的骨架 — 入门详解教程

HTML&#xff1a;网页的骨架 — 入门详解教程 HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是构建网页的基础语言&#xff0c;负责定义网页的结构和内容。无论是简单的个人博客&#xff0c;还是复杂的企业网站&#xff0c;HTML都是不可或…

212、【图论】字符串接龙(Python)

题目描述 题目链接&#xff1a;110. 字符串接龙 代码实现 import collectionsn int(input()) beginStr, endStr input().split() strList [input() for _ in range(n)]deque collections.deque() # 使用队列遍历结点 deque.append([beginStr, 1]) # 存储当前字符串和遍…

操作系统导论——第19章 分页:快速地址转换(TLB)

使用分页作为核心机制来实现虚拟内存&#xff0c;可能会带来较高的性能开销。使用分页&#xff0c;就要将内存地址空间切分成大量固定大小的单元&#xff08;页&#xff09;&#xff0c;并且需要记录这些单元的地址映射信息。因为这些映射信息一般存储在物理内存中&#xff0c;…

使用Apache POI(Java)创建docx文档和表格

1、引入poi 依赖组件 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.0.0</version> </dependency> <dependency><groupId>org.apache.poi</groupId>&…

python cv2 安装

在Python中安装opencv-python&#xff08;即OpenCV库&#xff09;&#xff0c;通常有两种方法&#xff1a;使用pip命令或通过conda&#xff08;如果你使用的是Anaconda或Miniconda&#xff09;。以下是详细的步骤&#xff1a; 方法1&#xff1a;使用pip 打开你的命令行界面&am…

读者、写者问题优化

#include <stdio.h> #include <time.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define NUM_READERS 5 #define NUM_WRITERS 5 // 定义信号量和全局变量 sem_t sdata, srcount; int rea…

如何通过前端表格控件实现自动化报表?1

背景 最近伙伴客户的项目经理遇见一个问题&#xff0c;他们在给甲方做自动化报表工具&#xff0c;项目已经基本做好了&#xff0c;但拿给最终甲方&#xff0c;业务人员不太买账&#xff0c;项目经理为此也是天天抓狂&#xff0c;没有想到合适的应对方案。 现阶段主要面临的问…

RabbitMQ 优先级队列详解

本文是博主在记录使用 RabbitMQ 在执行业务时遇到的问题和解决办法&#xff0c;因此查阅了相关资料并做了以下记载&#xff0c;记录了优先级队列的机制和使用要点。 本文为长文&#xff0c;详细介绍了相关的知识&#xff0c;可作为学习资料看。 文章目录 一、优先级队列介绍1、…

代理模式简述

目录 一、主要角色 二、类型划分 三、静态代理 示例 缺点 四、动态代理 JDK动态代理 示例 缺点 CGLib动态代理 导入依赖 示例 五、Spring AOP 代理模式是一种结构型设计模式&#xff0c;通过代理对象控制对目标对象的访问&#xff0c;可在不改变目标对象情况下增强…

每日一题——云服务计费问题

云服务计费问题&#xff08;哈希表 排序&#xff09;| 附详细 C源码解析 一、题目描述二、输入描述三、输出描述四、样例输入输出输入示例&#xff1a;输出示例&#xff1a;说明&#xff1a; 五、解题思路分析六、C实现源码详解&#xff08;完整&#xff09;七、复杂度分析 一…

【JVM】运行时数据区域

文章目录 1. 程序计数器补充 2. 虚拟机栈2.1 栈帧1. 局部变量表2. 操作数栈3. 动态链接4. 方法返回地址补充 3. 本地方法栈4. 堆5. 方法区静态常量池&#xff08;Class常量池&#xff09;运行时常量池字符串常量池&#xff08;1&#xff09;位置变化&#xff08;2&#xff09;放…