【IMX6ULL驱动开发学习】08.IMX6ULL通过GPIO子系统函数点亮LED

news2024/11/25 23:27:24

通过GPIO子系统函数点亮LED

1、GPIO子系统函数

1.1 确定 led 的GPIO标号,查看内核中的gpiochip

查看 gpiochip ,以正点原子的IMX6ULL阿尔法开发板为例

[root@100ask:/sys/class/gpio]# cat /sys/kernel/debug/gpio 

在这里插入图片描述
查看原理图,发现led接的引脚是 GPIO1_IO3,对应 /sys/kernel/debug/gpio 中的 gpiochip0 组,gpiochip0 组从0开始算起,所以 GPIO1_IO3 对应的标号就是 0+3 = 3
(可是实际操作中设置为4才能点亮LED,这里不知道是什么道理,懂得朋友麻烦评论区指点一下)

在这里插入图片描述

1.2 请求GPIO引脚(代码添加到驱动模块入口函数里)

int gpio_request(unsigned gpio, const char *label)

参数:
gpio : GPIO引脚在 gpiochip 中对应的标号
label : 为GPIO起的标签名
返回值:小于0失败

代码示例:

int err;
unsigned int led_gpio = 4;   //对照原理图  led控制引脚
/*申请led_gpio引脚*/
err = gpio_request(led_gpio, "user_led");
if(err < 0){
    printk("led gpio request failed!\n");
    return -ENODEV;
}

记住这里传入的第二个参数 “user_led”,后面验证的时候会看到

1.3 读GPIO

int gpio_get_value(unsigned int gpio)

参数:
gpio : GPIO引脚在 gpiochip 中对应的标号
返回值:返回引脚状态,0或1
代码示例:

static ssize_t led_read (struct file *filp, char *buf, size_t size, loff_t *offset)
{
	printk("led_drv_read\n");
	/*read led_gpio value*/
	buff[0] = gpio_get_value(led_gpio);
	//将读取到的值传给用户程序(也可以直接通过return的方式传到用户程序)
	copy_to_user(buf, buff, 1);
	
	/*返回什么不重要,也可以直接返回gpio的值
	但是最好还是按照驱动程序模式来写,使用copy_to_user*/
	return buff[0];  
}

1.4 写GPIO

void gpio_set_value(unsigned int gpio, int value)

参数:
gpio : GPIO引脚在 gpiochip 中对应的标号
value : 写值,0或1
返回值:无
代码示例:

static ssize_t led_write (struct file *filp, const char *buf, size_t size, loff_t *offset)
{
	printk("led_drv_write\n");
	// 从应用程序获取要写入的值,buf[0]
	copy_from_user(buff, buf, 1);   
	
	/*write led_gpio*/
	gpio_set_value(led_gpio, buff[0]);
	
	return led_gpio;
}

1.5 设置GPIO方向

int gpio_direction_output(unsigned gpio, int value)

参数:
gpio : GPIO引脚在 gpiochip 中对应的标号
value : 初始输出电平,0或1,
返回值:小于0失败
代码示例:

/*设置led_gpio输出模式*/
gpio_direction_output(led_gpio, 1);

2、在驱动卸载函数中释放GPIO引脚

/*释放led_gpio引脚*/
gpio_free(led_gpio);

3、完成LED驱动程序

在之前的hello驱动程序基础上修改,使用上面的GPIO子系统函数,添加一些逻辑性的代码即可
这里要注意的是需要增加一个头文件

#include <linux/gpio.h>

先展示一下驱动的流程,然后贴代码

在这里插入图片描述
IMX6ULL—LED驱动程序

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/therm.h>
#include <linux/string.h>
#include <linux/gpio.h>

static int major;
static unsigned char buff[100];
static struct class *led_class;

/*led args*/
//unsigned int led_gpio = 129;    //对照原理图  蜂鸣器
//unsigned int led_gpio = 19;     //对照原理图  按键
unsigned int led_gpio = 4;        //对照原理图       led


static int led_open (struct inode *node, struct file *filp)
{
	printk("led_open\n");
	printk("%s %s %d\n",__FILE__, __FUNCTION__, __LINE__);

	return 0;
}

static ssize_t led_read (struct file *filp, char *buf, size_t size, loff_t *offset)
{
	printk("led_drv_read\n");

	/*read led_gpio value*/
	buff[0] = gpio_get_value(led_gpio);
	
	copy_to_user(buf, buff, 1);

	//返回什么不重要,也可以直接返回gpio的值,但是最好还是按照驱动程序模式来写,使用copy_to_user
	return buff[0];  
}

static ssize_t led_write (struct file *filp, const char *buf, size_t size, loff_t *offset)
{
	printk("led_drv_write\n");
	copy_from_user(buff, buf, 1);
	
	/*write led_gpio*/
	gpio_set_value(led_gpio, buff[0]);
	
	return led_gpio;
}

static int led_release (struct inode *node, struct file *filp)
{
	printk("led_release\n");
	return 0;
}

/*1.定义 file_operations 结构体*/
static const struct file_operations led_fops = {
    .owner 		= THIS_MODULE,
	.read		= led_read,
	.write		= led_write,
	.open		= led_open,
	.release    = led_release,
};


/*3.入口函数*/
static int led_init(void)
{
	struct device *dev;
	int err;

	/************* 1.注册设备,返回设备号 ************/
	major = register_chrdev(0,"led_drv",&led_fops);

	/*2.在内核中创建设备*/
	led_class = class_create(THIS_MODULE, "led_class");
	if (IS_ERR(led_class)) {
		printk("led class create failed!\n");
	}

	/*3.在/dev下面创建设备节点*/
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led_drv");
	if(IS_ERR(dev)) {
		printk("led device_create  failed!\n");
	}

	/************* 4.初始化led引脚 ************/
	/*申请led_gpio引脚*/
	err = gpio_request(led_gpio, "user_led");
	if(err < 0){
		printk("led gpio request failed!\n");
		return -ENODEV;
	}
	
	/*设置led_gpio输出模式*/
	gpio_direction_output(led_gpio, 1);
	
	return 0;
}

/*4.退出函数*/
static int led_exit(void)
{
	//卸载设备
	unregister_chrdev(major,"led_fops");

	//销毁设备
	device_destroy(led_class, MKDEV(major, 0));
	//删除设备类
	class_destroy(led_class);

	/*释放led_gpio引脚*/
	gpio_free(led_gpio);
	printk("led_exit\n");

	return 0;
}	

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

4、编写LED应用程序

第一步确定LED应用程序的使用方式

./led_test /dev/led_drv on    点亮LED
./led_test /dev/led_drv off   熄灭LED
./led_test /dev/led_drv       读取LED状态

与之前的hello应用程序区别不大,直接贴代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

/*
	./led_test /dev/led_drv on
	./led_test /dev/led_drv off
	./led_test /dev/led_drv
*/

int main(int argc, char *argv[])
{
    int len;
    char buf[10];

    if(argc < 2){
        printf("please input  at least 2 args\n");
        printf("%s /dev/led_drv [on/off]\n", argv[0]);
        return -1;
    }

    /*open*/
    int fd;
    fd = open(argv[1], O_RDWR);
    if(fd < 0){
        printf("open failed\n");
        return -2;
    }

    /*read led*/
    if(argc == 2){  
        int res = read(fd, buf, 1);
        printf("led state : %s \n", buf[0] == 1 ? "off" : "on");
    }

    /*write led*/
    if(argc == 3){
		if(strcmp(argv[2], "on") == 0)
			buf[0] = 0;
		else if(strcmp(argv[2], "off") == 0)
			buf[0] = 1;
		write(fd, buf, 1);
    }

    close(fd);

    return 0;
}

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

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

相关文章

Linux虚拟网络设备---之使用Veth pair连接linux网桥bridge

本文目录 1、我们可以用以下命令来创建veth pair: veth0----veth12、创建五个命名空间namespaces后&#xff0c;可以用以下命令将veth设备对的一端移入namespaces命名空间&#xff0c;并开启veth2、将veth设备对的另外一端连接到linux网桥&#xff0c;并将所有namespaces命名空…

【Redis应用】UV统计(四)

&#x1f697;Redis应用学习第四站~ &#x1f6a9;本文已收录至专栏&#xff1a;Redis技术学习 一.引入 首先我们要搞懂两个概念&#xff1a; UV&#xff1a;全称Unique Visitor&#xff0c;也叫独立访客量&#xff0c;是指通过互联网访问、浏览这个网页的自然人。1天内同一个…

【动态规划】斐波那契数列模型

冻龟算法系列之斐波那契数列模型 文章目录 【动态规划】斐波那契数列模型1. 第N个泰波那契数1.1 题目解析1.2 算法原理1.2.1 状态表示1.2.2 状态转移方程1.2.3 初始化1.2.4 填表顺序1.2.5 返回值 1.3 编写代码1.4 空间优化 2. 三步问题2.1 题目解析2.2 算法原理2.2.1 状态表示2…

解决Centos安装时找不到磁盘:未选择任何磁盘(no diks selected),本地标准磁盘为空

文章目录 问题描述问题原因解决办法 问题描述 笔者最近又买了一台新电脑&#xff0c;并打算在上面安装 Linux 来充当一个新的服务器结点。但很不幸的是&#xff0c;每次笔者略微尝试新事物时&#xff0c;都要踩很多坑。笔者在使用 U 盘刻录 CentOS 8 镜像之后&#xff0c;准备在…

公平锁/非公平锁/可重入锁/自旋锁

在JAVA中我们知道有很多加锁的方式&#xff0c;比如常见的 通过synchronized关键字&#xff0c;还有Lock&#xff0c;还有之前说原子CAS操作时有看到过的死循环方式的自旋锁。 借此来说一下锁的分类: 公平锁: 是指多个线程按照申请的顺序来获取锁&#xff0c;每次获取锁时会…

dp算法篇Day1

"多希望有人来陪我&#xff0c;度过末日啊~" 讲讲我为什么突然想更新这篇栏目。 想想自己也算 "系统" 接触计算机这个学科也有差不多一年了&#xff0c;回想起当初下定决心要全身心投入到这个专业或者说行业中来&#xff0c;现在到了这样的地步&#xff0c…

CSS基础学习--10 margin(外边距)

一、定义&#xff1a; CSS margin(外边距)属性定义元素周围的空间。 二、margin margin 清除周围的&#xff08;外边框&#xff09;元素区域。margin 没有背景颜色&#xff0c;是完全透明的。 margin 可以单独改变元素的上&#xff0c;下&#xff0c;左&#xff0c;右边距&a…

今天面了个35k字节跳动出来,真是砂纸擦屁股,给我露了一手...

​2023年春招已经结束&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c;他山之石…

AI 绘画(0):导论

文章目录 导论感谢人员Ai绘画前期准备软件环境硬件条件 Ai绘画介绍Ai绘画简单流程介绍Ai绘画软件介绍参数输入介绍 Ai绘画公约 导论 Ai绘画是最近比较热门的绘画方式&#xff0c;以干掉原画师为口号&#xff0c;引起了激烈的讨论。Ai绘画能否取代人工我们先不谈&#xff0c;但…

一个专科生的 Python 转行之路

自学之路 正式开始学编程是在十月底的样子, 那时候在知乎 flask 话题下看到一个问题 「有多少人按萧井陌大神给出的PythonFlask路线找到工作了&#xff1f;」。就觉得自己也可以啊, 就开始辞职自学 Python,。 刚开始的那一个月确实能够做到每天 10 个小时写代码, 学了大概一个…

java设计模式之:外观模式

前言 举个现实生活中例子&#xff0c;泡茶和去茶馆喝茶的区别&#xff0c;如果是自己泡茶需要自行准备茶叶、茶具和开水&#xff0c;而去茶馆喝茶&#xff0c;最简单的方式就是跟茶馆服务员说想要一杯什么样的茶&#xff0c;是铁观音、碧螺春还是西湖龙井&#xff1f;正因为茶…

互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

多线程访问共享资源的时候&#xff0c;避免不了资源竞争而导致数据错乱的问题&#xff0c;所以我们通常为了解决这一问题&#xff0c;都会在访问共享资源之前加锁。 最常用的就是互斥锁&#xff0c;当然还有很多种不同的锁&#xff0c;比如自旋锁、读写锁、乐观锁等&#xff0…

数据结构——树和二叉树

文章目录 **一 数的基本概念****1 定义****2 基本术语****3 树的性质** **二 二叉树的概念****1 二叉树的定义和特性****1.1 定义****1.2 特殊的二叉树****1.3 二叉树的性质** **2 二叉树的存储结构****2.1 顺序存储结构****2.2 链式存储结构** **三 二叉树的遍历和线索二叉树*…

Spark SQL数据源的基本操作(更新ing)

文章目录 一、基本操作二、默认数据源&#xff08;一&#xff09;默认数据源Parquet&#xff08;二&#xff09;案例演示读取Parquet文件1、在Spark Shell中演示练习1、将student.txt文件转换成student.parquet练习2、读取student.parquet文件得到学生数据帧&#xff0c;并显示…

K8S minikube本地安装

一. mac安装K8S 1.brew安装 brew install kubectl 2.查看版本 kubectl version --outputjson { "clientVersion": { "major": "1", "minor": "27", "gitVersion": "v1.27.2", &…

基于深度学习的高精度奶牛检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度奶牛检测识别系统可用于日常生活中或野外来检测与定位奶牛目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的奶牛目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

locked勒索病毒利用零日漏洞,企业服务器数据瞬间遭受致命加密

目录 引言&#xff1a; 事件概述&#xff1a; .locked勒索病毒加密算法&#xff1a; 数据恢复建议&#xff1a; locked勒索病毒数据恢复案例&#xff1a; 什么叫零日漏洞&#xff1f; 对策建议&#xff1a; 引言&#xff1a; 近日&#xff0c;网络安全界再次爆发了一起…

RK3588平台开发系列讲解(系统篇)开机启动原因

文章目录 一、系统开机启动原因二、开机启动场景沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解平台系统开机启动原因介绍。 一、系统开机启动原因 开机原因记录文件在/proc/sys/kernel/boot_reason,那么开机后可以从这个文件中读取数值来获知本次开机…

锁升级:无锁、偏向锁、轻量级锁、重量级锁

锁升级 JDK 1.6之前&#xff0c;synchronized 还是一个重量级锁&#xff0c;是一个效率比较低下的锁。但是在JDK 1.6后&#xff0c;JVM为了提高锁的获取与释放效率对synchronized 进行了优化&#xff0c;引入了偏向锁和轻量级锁 &#xff0c;从此以后锁的状态就有了四种&#…

开源SCRM营销平台MarketGo-数据管理

一、概述 企业在私域运营的场景下&#xff0c;系统在运行中会产生一些用户数据和行为数据。 用户数据包含年龄&#xff0c;性别&#xff0c;生日&#xff0c;电话&#xff0c;用户标签&#xff0c;还有用户和员工的关系等信息。行为数据包含在SCRM中创建活动的用户事件&#…