驱动开发 day8 (设备树驱动,按键中断实现led亮灭)

//编译驱动 (注意Makefile的编译到移植到开发板的内核)

make arch=arm

//清除编译生成文件

make clean

******************************************

//安装驱动

insmod mycdev.ko

//卸载驱动

rmmod mycdev

需要在<内核路径>/arch/arm/boot/dts/

修改 stm32mp157a-fsmp1a-dts 文件

***************************

添加以下内容

cpp 复制代码
    leds{
    led1-gpios=<&gpioe 10 0>;//10表示引脚编号  0表示默认
    led2-gpios=<&gpiof 10 0>;
    led3-gpios=<&gpioe 8 0>;
};

    myirq{
    interrupt-parent=<&gpiof>;//引用中断父节点
    interrupts=<9 0>,<7 0>,<8 0>;//声明和中断父节点的关系 9表示索引号,0表示默认设置
};

mycdev.c

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>

struct device_node *dev;
/*  leds{
    led1-gpios=<&gpioe 10 0>;//10表示引脚编号  0表示默认
    led2-gpios=<&gpiof 10 0>;
    led3-gpios=<&gpioe 8 0>;
};*/
struct gpio_desc *gpiono1; // led1
struct gpio_desc *gpiono2; // led2
struct gpio_desc *gpiono3; // led3
/*
    myirq{
    interrupt-parent=<&gpiof>;//引用中断父节点
    interrupts=<9 0>,<7 0>,<8 0>;//声明和中断父节点的关系 9表示索引号,0表示默认设置
};
*/
unsigned int irqno1; // key1
unsigned int irqno2; // key2
unsigned int irqno3; // key3

// 定时器处理函数
void ctl_led(struct gpio_desc *gpiono)
{
    // led状态取反
    gpiod_set_value(gpiono, !gpiod_get_value(gpiono));
}
// 中断处理函数
irqreturn_t myirq_handler(int irq, void *dev)
{
    if (irq == irqno1)
    {
        printk("KEY1_INTERRUPT\n");
        ctl_led(gpiono3);
    }
    else if (irq == irqno2)
    {
        printk("KEY2_INTERRUPT\n");
         ctl_led(gpiono2);
    }
    else if (irq == irqno3)
    {
        printk("KEY3_INTERRUPT\n");
         ctl_led(gpiono1);
    }
    return IRQ_HANDLED;
}

static int myled_to_init(int a)
{
    // 根据设备树节点的路径解析设备树信息
    dev = of_find_node_by_path("/leds");
    if (dev == NULL)
    {
        printk("解析设备树节点失败\n");
        return -EFAULT;
    }
    printk("解析设备树节点成功\n");

    // 申请gpio_desc对象并设置输出为低电平
    gpiono1 = gpiod_get_from_of_node(dev, "led1-gpios", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono1))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono1);
    }
    printk("申请gpio对象成功\n");
    gpiono2 = gpiod_get_from_of_node(dev, "led2-gpios", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono2))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono2);
    }
    printk("申请gpio对象成功\n");
    gpiono3 = gpiod_get_from_of_node(dev, "led3-gpios", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono3))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono3);
    }
    printk("申请gpio对象成功\n");
    return 0;
}

static void myled_to_exit(int a)
{
        // 灭灯
    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);
    printk("led注销成功");
}

static int __init mycdev_init(void)
{
    int ret;
    myled_to_init(1);
    // 解析按键的设备树节点
    dev = of_find_node_by_path("/myirq");
    if (dev == NULL)
    {
        printk("解析设备树节点失败\n");
        return -EFAULT;
    }
    printk("解析设备树节点成功\n");
    // 根据设备树节点解析出软中断号
    irqno1 = irq_of_parse_and_map(dev, 0); // 按键1索引号为0
    if (!irqno1)
    {
        printk("解析软中断号1失败\n");
        return -ENXIO;
    }
    printk("解析软中断号1成功 irqno=%d\n", irqno1);
    irqno2 = irq_of_parse_and_map(dev, 1); // 按键2索引号为1
    if (!irqno2)
    {
        printk("解析软中断号2失败\n");
        return -ENXIO;
    }
    printk("解析软中断号2成功 irqno=%d\n", irqno2);
    irqno3 = irq_of_parse_and_map(dev, 2); // 按键3索引号为2
    if (!irqno3)
    {
        printk("解析软中断号3失败\n");
        return -ENXIO;
    }
    printk("解析软中断号3成功 irqno=%d\n", irqno3);
    // 注册中断
    ret = request_irq(irqno1, myirq_handler, IRQF_TRIGGER_FALLING, "key1", NULL);
    if (ret)
    {
        printk("注册中断1失败\n");
        return ret;
    }
    printk("注册中断1成功\n");
    ret = request_irq(irqno2, myirq_handler, IRQF_TRIGGER_FALLING, "key2", NULL);
    if (ret)
    {
        printk("注册中断2失败\n");
        return ret;
    }
    printk("注册中断2成功\n");
    ret = request_irq(irqno3, myirq_handler, IRQF_TRIGGER_FALLING, "key3", NULL);
    if (ret)
    {
        printk("注册中断3失败\n");
        return ret;
    }
    printk("注册中断3成功\n");
    return 0;
}
static void __exit mycdev_exit(void)
{
    myled_to_exit(1);
    // 注销中断
    free_irq(irqno1, NULL);
    free_irq(irqno2, NULL);
    free_irq(irqno3, NULL);

    printk("irq注销成功\n");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

Makefile

KERNELDIR 赋予的路径可能有所不同

bash 复制代码
modname ?= mycdev
arch ?= arm

ifeq ($(arch),arm)
KERNELDIR := /home/ubuntu/13_UBOOT/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
endif

PWD := $(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules


clean:
	make -C $(KERNELDIR) M=$(PWD) clean

distclean:
	make -C $(KERNELDIR) M=$(PWD) clean


obj-m := $(modname).o
相关推荐
gopher95111 天前
linux驱动开发-中断子系统
linux·运维·驱动开发
gopher95112 天前
linux驱动开发-设备树
linux·驱动开发
三菱-Liu3 天前
三菱变频器以模拟量电流进行频率设定(电流输入)
驱动开发·单片机·嵌入式硬件·硬件工程·制造
三菱-Liu3 天前
三菱FX5U CPU 内置以太网功能
网络·驱动开发·硬件工程·制造·mr
让开,我要吃人了3 天前
OpenHarmony鸿蒙( Beta5.0)摄像头实践开发详解
驱动开发·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统·openharmony
OH五星上将4 天前
如何更换OpenHarmony SDK API 10
驱动开发·嵌入式硬件·sdk·harmonyos·openharmony·鸿蒙开发
OH五星上将5 天前
OpenHarmony(鸿蒙南向开发)——标准系统移植指南(二)Linux内核
linux·驱动开发·嵌入式硬件·移动开发·harmonyos·鸿蒙开发·鸿蒙内核
芊言芊语5 天前
蓝牙驱动开发详解
驱动开发
让开,我要吃人了6 天前
OpenHarmony鸿蒙( Beta5.0)RTSPServer实现播放视频详解
驱动开发·嵌入式硬件·华为·移动开发·harmonyos·鸿蒙·openharmony
OH五星上将6 天前
OpenHarmony(鸿蒙南向开发)——轻量和小型系统三方库移植指南(二)
驱动开发·移动开发·harmonyos·内存管理·openharmony·鸿蒙内核·鸿蒙移植