目录
一、GPIO子系统
1.1GPIO子系统框架图
二、gpio设备树的编写
2.1 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设备树结点
在/home/ubuntu/linux-5.10.61/arch/arm/boot/dts下
打开stm32mp157a-fsmp1a.dts文件
添加如下设备结点
四、编写驱动代码
驱动代码
#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>
/*myleds{
led1=<&gpioe 10 0>;
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
};*/
struct device_node *dnode;
int gpiono1,gpiono2,gpiono3;
//定义一个定时器变量
struct timer_list timer;
//定时器处理函数
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));
printk("LED1 light\n");
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 -ENOENT;
}
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(gpiono1<0)
{
printk("解析gpio编号失败\n");
return -EIO;
}
gpiono3 = of_get_named_gpio(dnode,"led3",0);
if(gpiono1<0)
{
printk("解析gpio编号失败\n");
return -EIO;
}
printk("解析gpio编号成功\n");
//申请gpio编号
gpio_request(gpiono1,NULL);
gpio_request(gpiono2,NULL);
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)
{
gpio_free(gpiono1);
gpio_free(gpiono2);
gpio_free(gpiono3);
del_timer(&timer);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
五、 Makefile
#定义一个变量,存放架构
ARCH ?= x86
#定义一个变量,存放文件名
modname ?= demo
ifeq ($(ARCH),arm)
#定义一个变量,存放linux内核源码目录
KERNEDIR:=/home/ubuntu/linux-5.10.61
else
#定义一个变量,存放ubuntu的linux内核源码目录,生成X86架构
KERNEDIR:=/lib/modules/$(shell uname -r)/build
endif
#定义一个变量,开启一个终端,执行pwd命令
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
六、实现结果