[驱动开发]gpio子系统及中断实现led亮灭

编写LED灯的驱动,使用GPIO子系统,里面添加按键的中断处理

1.应用程序发送指令控制发光二极管亮灭

2.按键1按下,led1电位反转;按键2按下,led2电位反转;按键3按下,led3电位反转

cs 复制代码
//头文件
#ifndef __LED_H__
#define __LED_H__

//功能码
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)
#endif
cs 复制代码
//应用程序实现LED的亮灭
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include "led.h"
int main(int argc, const char *argv[])
{
	int a,b;
	while(1)
	{
		int fd_led = open("/dev/myled0",O_RDWR);	
		if(fd_led < 0)
		{
			printf("打开设备文件失败\n");
			exit(-1);
		}
		printf("请输入要控制的灯:1 2 3>>");
		scanf("%d",&b);
		printf("输入控制命令:0熄灭 1开灯>>>");
		scanf("%d",&a);
		switch(a)
		{
		case 1:
			ioctl(fd_led,LED_ON,&b);
			break;
		case 0:
			ioctl(fd_led,LED_OFF,&b);
			break;
		}
		close(fd_led);
	}
	return 0;
}
cs 复制代码
//驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
#include "led.h"
/*  leds{
	led1-gpios=<&gpioe 10 0>;//10表示引脚编号  0表示默认
	led2-gpios=<&gpiof 10 0>;
	led3-gpios=<&gpioe 8 0>;
	};*/
struct device_node *dnode1;
struct device_node *dnode2;
struct gpio_desc *gpiono1;
struct gpio_desc *gpiono2;
struct gpio_desc *gpiono3;
struct cdev *cdev;
int major=168;
int minor=0;
struct class *cls;
struct device *dev;
dev_t devno;
int i;
unsigned int irqno[3];      //软中断号

//中断处理函数
irqreturn_t myirq_handler(int irqno, void *dev_id)
{
	unsigned int id=(unsigned int)dev_id;
	switch(id)
	{
		case 0:
			printk("KEY1_INTERRUPT\n");
			gpiod_set_value(gpiono1,!gpiod_get_value(gpiono1));
			break;
		case 1:
			printk("KEY2_INTERRUPT\n");
			gpiod_set_value(gpiono2,!gpiod_get_value(gpiono2));
			break;
		case 2:
			printk("KEY3_INTERRUPT\n");
			gpiod_set_value(gpiono3,!gpiod_get_value(gpiono3));
			break;
	}

	return IRQ_HANDLED;
}
/*********************封装操作方法***************************/
int mycdev_open(struct inode *inode, struct file *file)
{
	//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}


long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int which;
	copy_from_user(&which,(void *)arg,4);
	switch(cmd)
	{
	case LED_ON:         // 亮灯
		switch(which)
		{
		case 1:   
			gpiod_set_value(gpiono1, 1);
			break;
		case 2:
			gpiod_set_value(gpiono2, 1);
			break;
		case 3:
			gpiod_set_value(gpiono3, 1);
			break;
		}
		break;
	case LED_OFF:
		switch(which)
		{
		case 1:
			gpiod_set_value(gpiono1, 0);
			break;
		case 2:
			gpiod_set_value(gpiono2, 0);
			break;
		case 3:
			gpiod_set_value(gpiono3, 0);
		}
		break;
	}
	//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
	//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}
/***********************************************************************/

//定义一个操作方法结构体变量并初始化
struct file_operations fops =
{
	.open    =  mycdev_open,
	.release =  mycdev_close,
	.unlocked_ioctl = mycdev_ioctl,
};
static int __init mycdev_init(void)
{
	int ret,i;
	//1.申请对象空间 cdev_alloc
	cdev = cdev_alloc();
	if(cdev == NULL)
	{
		printk("申请对象空间失败\n");
		ret=-EFAULT;
		goto out1;
	}
	printk("申请对象空间成功\n");
	//2.初始化对象 cdev_init
	cdev_init(cdev,&fops);
	printk("初始化对象成功\n");
	//3.申请设备号 register_chrdev_region()或clloc_chrdev_region()
	if(major != 0)  //静态申请
	{
		ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
		if(ret)
		{
			printk("申请静态设备号失败\n");
			goto out2;
		}
		printk("申请静态设备号成功major=%d\n",major);
	}
	else    //动态申请
	{
		ret=alloc_chrdev_region(&devno,minor,3,"myled");
		if(ret)
		{
			printk("申请动态设备号失败\n");
			goto out2;
		}
		major=MAJOR(devno);
		minor=MINOR(devno);
		printk("申请动态设备号成功major=%d\n",major);
	}

	//4.注册驱动对象 cdev_add()
	ret=cdev_add(cdev,MKDEV(major,minor),3);
	if(ret)
	{
		printk("注册驱动对象失败\n");
		goto out3;
	}
	printk("注册驱动对象成功\n");
	//5.向上提交目录 class_create()
	cls=class_create(THIS_MODULE,"myled");
	if(IS_ERR(cls))
	{
		printk("向上提交目录失败\n");
		ret = -PTR_ERR(cls);
		goto out4;
	}
	printk("向上提交目录成功\n");
	//6.向上提交设备信息 device_create()
	for(i=0;i<3;i++)
	{
		dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
		if(IS_ERR(dev))
		{
			printk("向上提交设备节点失败\n");
			ret = -PTR_ERR(dev);
			goto out5;
		}
	}
	printk("向上提交设备信息成功\n");
	// 根据设备树节点的路径解析设备树信息
	dnode1 = of_find_node_by_path("/leds");
	if (dnode1 == NULL)
	{
		printk("解析设备树节点led失败\n");
		return -EFAULT;
	}
	printk("解析设备树节点led成功\n");
	// 申请gpio_desc对象并设置输出为低电平
	gpiono1 = gpiod_get_from_of_node(dnode1, "led1-gpios", 0, GPIOD_OUT_LOW, NULL);
	if (IS_ERR(gpiono1))
	{
		printk("申请gpio1对象失败\n");
		return -PTR_ERR(gpiono1);
	}
	printk("申请gpio1对象成功\n");
	gpiono2 = gpiod_get_from_of_node(dnode1, "led2-gpios", 0, GPIOD_OUT_LOW, NULL);
	if (IS_ERR(gpiono2))
	{
		printk("申请gpio2对象失败\n");
		return -PTR_ERR(gpiono2);
	}
	printk("申请gpio2对象成功\n");
	gpiono3 = gpiod_get_from_of_node(dnode1, "led3-gpios", 0, GPIOD_OUT_LOW, NULL);
	if (IS_ERR(gpiono3))
	{
		printk("申请gpio3对象失败\n");
		return -PTR_ERR(gpiono3);
	}
	printk("申请gpio3对象成功\n");

	// 根据设备树节点的路径解析设备树信息
	dnode2 = of_find_node_by_path("/myirq");
	if (dnode2 == NULL)
	{
		printk("解析设备树节点myirq失败\n");
		return -EFAULT;
	}
	printk("解析设备树节点myirq成功\n");
	for(i=0;i<3;i++)
	{
		//获取软中断号
		irqno[i]=irq_of_parse_and_map(dnode2,i);
		if(!irqno[i])
		{
			printk("软中断号irqno[%d]获取失败\n",i);
			return -ENXIO;
		}
		printk("软中断号获取成功irqno[%d]=%d\n",i,irqno[i]);

		//注册中断
		ret=request_irq(irqno[i],myirq_handler,IRQF_TRIGGER_FALLING,"key",(void *)i);
		if(ret)
		{
			printk("注册驱动失败\n");
			return ret;
		}
		printk("key%d中断注册成功\n",i);
	}
	return 0;
out5:
	for(--i;i>=0;i--)
	{
		//销毁上面提交的设备信息
		device_destroy(cls,MKDEV(major,i));
	}
	class_destroy(cls);
out4:
	cdev_del(cdev);
out3:
	unregister_chrdev_region(MKDEV(major,minor),3);
out2:
	kfree(cdev);
out1:
	return ret;
}
static void __exit mycdev_exit(void)
{
	// 灭灯
	gpiod_set_value(gpiono1, 0);
	gpiod_set_value(gpiono2, 0);
	gpiod_set_value(gpiono3, 0);
	// 释放gpio编号
	gpiod_put(gpiono1);
	gpiod_put(gpiono2);
	gpiod_put(gpiono3);
	//注销中断
	for(i=0;i<3;i++)
	{
		free_irq(irqno[i],NULL);
	}
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
相关推荐
cxr8281 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot2 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶2 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot3 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot3 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday3 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8285 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday6 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发