编写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
运行效果: