使用GPIO子系统操控6盏LED灯的亮灭
1、编写设备树节点:
添加如下所示代码到stm32mp157a-fdmp1a.dts中根节点里面,给LED灯定义名字,并引用GPIO寄存器及相对应的引脚信息;
myleds{
myled1 = <&gpioe 10 0>;
myled2 = <&gpiof 10 0>;
myled3 = <&gpioe 8 0>;
myled4 = <&gpioz 5 0>;
myled5 = <&gpioz 6 0>;
myled6 = <&gpioz 7 0>;
};
2、实现字符设备驱动并创建与LED灯相应的6个设备节点(具体详情见驱动——串口点灯实验内容)
3、获取设备树节点信息(获取方式不唯一,本次采用通过节点名字获取)
struct device_node *of_find_node_by_path(const char *path)
功能:获取设备树节点信息通过路径
参数: path:节点路径
返回值:成功返回目标节点的信息结构体地址,失败返回NULL
struct device_node *of_find_node_by_name(struct device_node *from, const char *name)
功能:通过节点名字获取节点信息
参数:from:已知设备树节点的首地址 (填NULL,默认从根节点解析)
name:设备树节点的名字 “myleds”
返回值:成功返回目标节点的信息结构体地址,失败返回NULL
4、根据GPIO子系统函数API对LED灯进行操作(子系统函数有新旧两个版本,本次采用新版本)
①在在设备树节点信息结构体中获取并申请要使用的gpio编号
旧版本:
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
功能:根据gpio节点解析获取GPIO编号
参数:
np:节点结构体首地址
proname:键名
index:索引号
返回值:成功返回GPIO编号,失败返回错误码
int gpio_request(unsigned gpio, const char *label)
作用:申请指定GPIO编号的使用权
参数:
gpio:目标GPIO编号
label:一般填写NULL
成功返回0,失败返回错误码
新版本:
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, const char *propname, int index, enum gpiod_flags dflags, const char *label)
功能:在设备树节点信息结构体中获取并申请要使用的gpio编号
参数:
node:设备树节点信息结构体指针
propname:键名
index:索引
dflags:gpio状态值
GPIOD_OUT_LOW
GPIOD_OUT_HIGH
label:标签,填写NULL
返回值:成功返回gpio描述结构体指针,失败返回错误码指针
②设置管脚为输出模式
int gpio_direction_output(unsigned gpio, int value)
作用:设置GPIO为输出
参数:
gpio:GPIO编号 value:1:高电平 0:低电平
返回值:成功返回0,失败返回错误码
int gpio_direction_input(unsigned gpio)
作用:设置GPIO为输入
参数:gpio:GPIO编号
返回值:成功返回0,失败返回错误码
新版本:
int gpiod_direction_input(struct gpio_desc *desc) —–》输入模式
int gpiod_direction_output(struct gpio_desc *desc, int value)——》输出模式
③通过ioctl函数传参获取信息,根据应用层逻辑让LED灯输出高低电平,达到操控灯亮灭的过程
旧版本:
int gpio_get_value(unsigned int gpio)
功能:获取GPIO电平状态
参数:GPIO编号
返回值: 1:高电平 0:低电平
void gpio_set_value(unsigned int gpio, int value)
作用:让GPIO输出高低电平
参数: gpio:GPIO编号
value:1:高电平 0:低电平
返回值:无
新版本
int gpiod_get_value(const struct gpio_desc *desc)
void gpiod_set_value(struct gpio_desc *desc, int value)
④释放申请的GPIO编号
旧版本
void gpio_free(unsigned gpio)
作用:释放申请的GPIO编号
参数:目标GPIO编号
新版本
void gpiod_put(struct gpio_desc *desc)
具体代码实现:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/fs.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include"./six.h"
/*
myleds{
myled1 = <&gpioe 10 0>;
myled2 = <&gpiof 10 0>;
myled3 = <&gpioe 8 0>;
myled4 = <&gpioz 5 0>;
myled5 = <&gpioz 6 0>;
myled7 = <&gpioz 7 0>;
*/
#define GNAME "mydev"
unsigned int major =0;
char kbuf[128]={0};
struct cdev* cdev;
struct class * cls;
struct device *devic;
dev_t dev1;
int minor = 0;
unsigned count=6;
wait_queue_head_t wq;
int condition=0;
int mydev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
long mydev_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
int addr;
switch(cmd)
{
case LED_ON:
{
ret = copy_from_user(&addr,(void*)arg,sizeof(int));
if(ret)
{
printk("copy from user on is error\n");
return -EIO;
}
switch(addr)
{
case LED1:
{
gpiod_set_value(gpiono1,1);
break;
}
case LED2:
{
gpiod_set_value(gpiono2,1);
break;
}
case LED3:
{
gpiod_set_value(gpiono3,1);
break;
}
case LED4:
{
gpiod_set_value(gpiono4,1);
break;
}
case LED5:
{
gpiod_set_value(gpiono5,1);
break;
}
case LED6:
{
gpiod_set_value(gpiono6,1);
break;
}
}
break;
}
case LED_OFF:
{
ret = copy_from_user(&addr,(void*)arg,sizeof(int));
if(ret)
{
printk("copy from user on is error\n");
return -EIO;
}
switch(addr)
{
case LED1:
{
gpiod_set_value(gpiono1,0);
break;
}
case LED2:
{
gpiod_set_value(gpiono2,0);
break;
}
case LED3:
{
gpiod_set_value(gpiono3,0);
break;
}
case LED4:
{
gpiod_set_value(gpiono4,0);
break;
}
case LED5:
{
gpiod_set_value(gpiono5,0);
break;
}
case LED6:
{
gpiod_set_value(gpiono6,0);
break;
}
}
break;
}
}
return 0;
}
int mydev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
struct file_operations fops={
.open=mydev_open,
.unlocked_ioctl=mydev_ioctl,
.release=mydev_close,
};
static int __init mynode_init(void)
{
int i;
int ret;
//分配字符设备驱动
cdev=cdev_alloc();
if(NULL==cdev)
{
printk("cdev alloc error\n");
goto ERR1;
}
//设备驱动初始化
cdev_init(cdev,&fops);
//申请设备号
if(major>0)
{
ret=register_chrdev_region(MKDEV(major,minor),count,GNAME);
if(ret!=0)
{
printk("register chrdev region error\n");
ret = -ENOMEM;
goto ERR2;
}
}
else
{
ret=alloc_chrdev_region(&dev1,0,count,GNAME);
if(ret!=0)
{
printk("alloc chrdev region error\n");
ret = -ENOMEM;
goto ERR2;
}
major = MAJOR(dev1);
minor = MINOR(dev1);
}
//驱动的注册
ret = cdev_add(cdev,MKDEV(major,minor),count);
if(ret!=0)
{
printk("cdev add error\n");
ret = -EIO;
goto ERR3;
}
//通过名字获取设备树节点信息
node = of_find_node_by_name(NULL,"myleds");
if(NULL == node)
{
printk("of find node by name error\n");
return -EFAULT;
}
//获取并申请LED1的gpio编号
gpiono1 = gpiod_get_from_of_node(node,"myled1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono1))
{
printk("1gpiod get from of node error\n");
return PTR_ERR(gpiono1);
}
//获取并申请LED2的gpio编号
gpiono2 = gpiod_get_from_of_node(node,"myled2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono2))
{
printk("2gpiod get from of node error\n");
return PTR_ERR(gpiono2);
}
//获取并申请LED3的gpio编号
gpiono3 = gpiod_get_from_of_node(node,"myled3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono3))
{
printk("3gpiod get from of node error\n");
return PTR_ERR(gpiono3);
}
//获取并申请LED4的gpio编号
gpiono4 = gpiod_get_from_of_node(node,"myled4",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono4))
{
printk("4gpiod get from of node error\n");
return PTR_ERR(gpiono4);
}
//获取并申请LED5的gpio编号
gpiono5 = gpiod_get_from_of_node(node,"myled5",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono5))
{
printk("5piod get from of node error\n");
return PTR_ERR(gpiono5);
}
//获取并申请LED6的gpio编号
gpiono6 = gpiod_get_from_of_node(node,"myled6",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono6))
{
printk("6gpiod get from of node error\n");
return PTR_ERR(gpiono6);
}
//设置LED1管脚为输出
gpiod_direction_output(gpiono1,0);
//设置LED2管脚为输出
gpiod_direction_output(gpiono2,0);
//设置LED3管脚为输出
gpiod_direction_output(gpiono3,0);
//设置LED4管脚为输出
gpiod_direction_output(gpiono4,0);
//设置LED5管脚为输出
gpiod_direction_output(gpiono5,0);
//设置LED6管脚为输出
gpiod_direction_output(gpiono6,0);
//自动创建设备节点
cls = class_create(THIS_MODULE,GNAME);
if(IS_ERR(cls))
{
ret = PTR_ERR(cls);
goto ERR4;
}
for(i=0;i<count;i++)
{
devic = device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
if(IS_ERR(devic))
{
ret = PTR_ERR(devic);
goto ERR5;
}
}
init_waitqueue_head(&wq);
return 0;
ERR5:
for(--i;i>=0;i--)
{
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:
kfree(cdev);
ERR1:
return -EIO;
}
static void __exit mynode_exit(void)
{
int i;
//卸载驱动前熄灭灯LED1
gpiod_set_value(gpiono1,0);
//卸载驱动前熄灭灯LED1
gpiod_set_value(gpiono2,0);
//卸载驱动前熄灭灯LED1
gpiod_set_value(gpiono3,0);
//卸载驱动前熄灭灯LED1
gpiod_set_value(gpiono4,0);
//卸载驱动前熄灭灯LED1
gpiod_set_value(gpiono5,0);
//卸载驱动前熄灭灯LED1
gpiod_set_value(gpiono6,0);
//释放申请得到的LED1gpio编号
gpiod_put(gpiono1);
//释放申请得到的LED2gpio编号
gpiod_put(gpiono2);
//释放申请得到的LED3gpio编号
gpiod_put(gpiono3);
//释放申请得到的LED4gpio编号
gpiod_put(gpiono4);
//释放申请得到的LED5gpio编号
gpiod_put(gpiono5);
//释放申请得到的LED6gpio编号
gpiod_put(gpiono6);
//销毁设备节点信息
for(i=0;i<count;i++)
{
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
//释放驱动
cdev_del(cdev);
//释放设备号
unregister_chrdev_region(MKDEV(major,minor),count);
//注销字符设备驱动
kfree(cdev);
}
module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");
头文件代码
#ifndef __LED_H__
#define __LED_H__
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)
struct device_node *node;
struct gpio_desc *gpiono1;
struct gpio_desc *gpiono2;
struct gpio_desc *gpiono3;
struct gpio_desc *gpiono4;
struct gpio_desc *gpiono5;
struct gpio_desc *gpiono6;
int ret;
typedef enum{
LED1,
LED2,
LED3,
LED4,
LED5,
LED6
}led_t;
#endif
应用层测试代码:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/ioctl.h>
#include"./six.h"
int main(int argc, char const *argv[])
{
int fd1 = -1;
int fd2 = -1;
int fd3 = -1;
int fd4 = -1;
int fd5 = -1;
int fd6 = -1;
int i=0;
int whitch;
fd1 = open("/dev/myled1",O_RDWR);
if(-1 == fd1)
{
perror("open is error");
exit(1);
}
fd2 = open("/dev/myled2",O_RDWR);
if(-1 == fd2)
{
perror("open is error");
exit(1);
}
fd3 = open("/dev/myled3",O_RDWR);
if(-1 == fd3)
{
perror("open is error");
exit(1);
}
fd4 = open("/dev/myled4",O_RDWR);
if(-1 == fd4)
{
perror("open is error");
exit(1);
}
fd5 = open("/dev/myled5",O_RDWR);
if(-1 == fd5)
{
perror("open is error");
exit(1);
}
fd6 = open("/dev/myled0",O_RDWR);
if(-1 == fd6)
{
perror("open is error");
exit(1);
}
while(1)
{
whitch=LED1;
ioctl(fd1,LED_ON,&whitch);
sleep(1);
ioctl(fd1,LED_OFF,&whitch);
whitch=LED2;
ioctl(fd2,LED_ON,&whitch);
sleep(1);
ioctl(fd2,LED_OFF,&whitch);
whitch=LED3;
ioctl(fd3,LED_ON,&whitch);
sleep(1);
ioctl(fd3,LED_OFF,&whitch);
whitch=LED4;
ioctl(fd4,LED_ON,&whitch);
sleep(1);
ioctl(fd4,LED_OFF,&whitch);
whitch=LED5;
ioctl(fd5,LED_ON,&whitch);
sleep(1);
ioctl(fd5,LED_OFF,&whitch);
whitch=LED6;
ioctl(fd6,LED_ON,&whitch);
sleep(1);
ioctl(fd6,LED_OFF,&whitch);
}
close(fd1);
close(fd2);
close(fd3);
close(fd4);
close(fd5);
close(fd6);
return 0;
}
makefile脚本代码:
ARCH ?= arm
FILE ?= led
ARM:=arm
X86:=x86
ifeq ($(ARCH),$(ARM))
KERNEDIR:=/home/ubuntu/linux-5.10.61
endif
ifeq ($(ARCH),$(X86))
KERNEDIR:=/lib/modules/$(shell uname -r)/build
endif
PWD:=$(shell pwd)
KBUILD_EXTRA_SYMBOLS:=/home/ubuntu/ww/driver/01_linux/03_sym/01_demo/Module.symvers
all:
make -C $(KERNEDIR) M=$(PWD) modules
clean:
make -C $(KERNEDIR) M=$(PWD) clean
obj-m:=$(FILE).o
实验现象:
gpio子系统6盏LED灯操作实验