编写led灯的设备树节点并驱动三盏灯点亮

步骤:

1.解析对应设备的设备树节点
    struct device_node *of_find_node_by_name(struct device_node *from,
                                            const char *name);
2.根据解析得到的设备树节点结构体去解析得到对应的gpio编号
    int of_get_named_gpio(struct device_node *np,
                           const char *propname, int index)
3.向内核申请要使用的gpio编号
    int gpio_request(unsigned gpio, const char *label)

4.设置gpio编号对应的gpio管脚为输出模式,并且输出电平(0:低电平,1:高电平)
    int gpio_direction_output(unsigned gpio, int value)

5.设置输出指定的数值(0:低电平,1:高电平)
    void gpio_set_value(unsigned gpio, int value)

6.将注册的gpio编号在内核中注销
    void gpio_free(unsigned gpio);

//编写自己的led设备树节点
myleds{
    led1=<&gpioe 10 0>;
    led2=<&gpiof 10 0>;
    led3=<&gpioe 8 0>;
};

驱动代码:

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include<linux/device.h>
#include<linux/poll.h>
#include <linux/of.h>
#include <linux/timer.h>
#include <linux/of_gpio.h>

int major;
struct class *cls;
struct device *dev;

//分配定时器对象
struct timer_list timer;
char kbuf[128]={0};
struct device_node *dnode;
int gpiono1;
int gpiono2;
int gpiono3;
//定时器处理函数
void timer_handler(struct timer_list *timer)
{
    
    gpio_set_value(gpiono1,!gpio_get_value(gpiono1));
    gpio_set_value(gpiono2,!gpio_get_value(gpiono2));
    gpio_set_value(gpiono3,!gpio_get_value(gpiono3));
    mod_timer(timer,jiffies+HZ);
}


//入口函数
static int __init mycdev_init(void)
{
    
    //解析设备树节点
    dnode=of_find_node_by_name(NULL,"myleds");
    if(dnode==NULL)
    {
        printk("解析设备树节点失败\n");
        return -EIO;
    }
    printk("解析设备树节点成功\n");
    //根据设备树节点解析gpio编号
    gpiono1=of_get_named_gpio(dnode,"led1",0);
    if(gpiono1<0)
    {
        printk("解析gpio编号失败\n");
        return -EIO;
    }
    gpiono2=of_get_named_gpio(dnode,"led2",0);
    if(gpiono2<0)
    {
        printk("解析gpio编号失败\n");
        return -EIO;
    }
    gpiono3=of_get_named_gpio(dnode,"led3",0);
    if(gpiono3<0)
    {
        printk("解析gpio编号失败\n");
        return -EIO;
    }
    //申请gpio编号
    gpio_request(gpiono1,NULL);
    //申请gpio编号
    gpio_request(gpiono2,NULL);
    //申请gpio编号
    gpio_request(gpiono3,NULL);
    
    //设置gpio为输出模式并且初始化数值为0
    gpio_direction_output(gpiono1,0);
    gpio_direction_output(gpiono2,0);
    gpio_direction_output(gpiono3,0);
    timer.expires=jiffies+HZ;
    timer_setup(&timer,timer_handler,0);
    add_timer(&timer);
    
    return 0;
}

//出口函数
static void __exit mycdev_exit(void)
{
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
  
    //删除目录信息
    class_destroy(cls);
    //字符设备驱动的注销
    unregister_chrdev(major,"mycdev");
    gpio_free(gpiono1);
    gpio_free(gpiono2);
    gpio_free(gpiono3);

    del_timer(&timer);
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

工程管理文件:

ARCH ?=x86
modname ?= mydtb
ifeq ($(ARCH),arm)
#定义一个变量,存放linux内核源码目录,arm架构
KERNEDIR:=/home/ubuntu/linux-5.10.61
#x86架构
else
KERNEDIR:=/lib/modules/$(shell uname -r)/build
#定义一个变量,开启一个终端,执行pwd命令
endif
PWD:=$(shell pwd)

all:
	@#-C:跳转到内核顶层目录下,读取内核顶层目录下的Makefile文件
	@#在内核源码顶层目录下执行:make M=$(shell pwd) modules
	@#M=$(shell pwd):回到当前目录下,只编译当前目录下的文件
	@#make modules:采用模块化方式进行编译
	make -C $(KERNEDIR) M=$(shell pwd) modules

clean:
	make -C $(KERNEDIR) M=$(shell pwd) clean

#指定模块化方式编译的文件
obj-m:=$(modname).o

运行效果: