驱动——gpio子系统(LED灯的操控实验)

  • Post author:
  • Post category:其他


使用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灯操作实验



版权声明:本文为ww1106原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。