Linux系统驱动(十一)GPIO子系统

发布于:2024-08-09 ⋅ 阅读:(131) ⋅ 点赞:(0)

一、GPIO子系统

(一)框架结构

在这里插入图片描述

(二)GPIO子系统的API

#include <linux/of_gpio.h>
int of_get_named_gpio(struct device_node *np,const char *propname, int index)
功能:
	获取gpio号
参数:
	 @np:节点的指针
	 @propname:键
	 @index:下标 (键值的第几个数)
返回值:
	成功返回gpio号,失败返回错误码
 
int gpio_request(unsigned gpio, const char *label)
功能:
	申请要使用的GPIO
参数:
    @gpio:gpio号(设备树中获取)
 	@label:名字,也可以写NULL
返回值:
	成功返回0,失败返回错误码

int gpio_direction_input(unsigned gpio)
功能:将gpio的方向设置为输入
参数:
 @gpio:gpio号
返回值:成功返回0,失败返回错误码
        
int gpio_direction_output(unsigned gpio, int value)
功能:将gpio的方向设置为输出
参数:
 @gpio:gpio号
 @value:默认输出电平状态  1高电平  0低电平
返回值:成功返回0,失败返回错误码

int gpio_get_value(unsigned gpio)
功能:获取管脚电平
参数:
   @gpio:gpio号
返回值:返回1高电平 返回0低电平

void gpio_set_value(unsigned int gpio, int value)
功能:设置管脚电平的状态
参数:
 @gpio:gpio号
 @value:输出电平状态  1高电平  0低电平
返回值:无

void gpio_free(unsigned gpio)
功能:释放gpio
参数:
 @gpio:gpio号
返回值:无    

(三)gpio子系统控制LED灯的设备树

1. 画出硬件连接图

在这里插入图片描述

2. 找出控制器的设备树

目录:
linux-5.10.61/arch/arm/boot/dts/stm32mp151.dtsi
在这里插入图片描述

//stm32mp151.dtsi
gpioe: gpio@50006000 {
    gpio-controller; //空属性,标识这个节点是gpio控制器的设备树节点
    #gpio-cells = <2>;       //2.对子节点成员个数修饰
    reg = <0x50006000 0x400>;//控制器的地址,长度
    clocks = <&rcc GPIOE>;   //时钟
    st,bank-name = "GPIOE";  //gpioe
    status = "disabled";     //1.控制器的状态,disabled没有使能,okay使能
};   

3. 参考内核帮助文档

帮助文档路径:
linux-5.10.61/Documentation/devicetree/bindings/gpio
在这里插入图片描述
在gpio目录下对应这不同厂商的帮助文档。
有厂商的帮助文档看厂商的帮助文档,没有就看通用的文档
在这里插入图片描述

myleds{
    led1 = <&gpioe 10 0>; //10gpioe的第10个管脚,0默认状态
    led2 = <&gpiof 10 0>; //10gpiof的第10个管脚,0默认状态
    led3 = <&gpioe 8 0>; //8gpioe的第8个管脚,0默认状态
};

二、使用GPIO子系统实现流水灯

扩展版三个LED:

PE10	---		LED1
PF10	---	 	LED2
PE8		---	 	LED3

主控板三个LED:
在这里插入图片描述

1. 设备树文件:

stm32mp157a-fsmp1a.dts

/{
//在根节点中添加以下节点
myleds{
	core_leds{
		led1 = <&gpioz 5 0>;
		led2 = <&gpioz 6 0>;
		led3 = <&gpioz 7 0>;
	};
	expend_leds{
		led1 = <&gpioe 10 0>;
		led2 = <&gpiof 10 0>;
		led3 = <&gpioe 8 0>;
	};
};

2. 驱动文件

mynode.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include "mynode.h"

const char *led[3]={"led1","led2","led3"};
int core_gpiono[3];
int expend_gpiono[3];
struct cdev *led_cdev;
struct class *led_class;
struct device *led_device;

int major = 0; //主设备号
int minor = 0;
dev_t led_dev_num;

int my_led_open(struct inode *inode, struct file *file){
    struct device_node *core_node,*expend_node;
    int i,ret;
    //1. 获取节点
    //core节点
    core_node = of_find_node_by_path("/myleds/core_leds");
    if(NULL == core_node){
        pr_err("of_find_node_by_path error");
        return -EINVAL;
    }
    
    //expend节点
    expend_node = of_find_node_by_path("/myleds/expend_leds");
    if(NULL == expend_node){
        pr_err("of_find_node_by_path error");
        return -EINVAL;
    }

    //2.获取gpio号
    for(i=0;i<3;i++){
        //core
        core_gpiono[i] = of_get_named_gpio(core_node,led[i],0);
        if(core_gpiono[i] < 0){
            pr_err("of_get_named_gpio error");
            return core_gpiono[i];
        }
        //expend
        expend_gpiono[i] = of_get_named_gpio(expend_node,led[i],0);
        if(expend_gpiono[i] < 0){
            pr_err("of_get_named_gpio error");
            return expend_gpiono[i];
        }
    }

    //3. 申请gpio
    for(i=0;i<3;i++){
        ret=gpio_request(core_gpiono[i],NULL);
        if(ret){
            pr_err("gpio_request error");
            for(i--;i>0;i--){
                gpio_free(core_gpiono[i]);
            }
            return ret;
        }
    }
    for(i=0;i<3;i++){
        ret=gpio_request(expend_gpiono[i],NULL);
        if(ret){
            pr_err("gpio_request error");
            for(i--;i>0;i--){
                gpio_free(expend_gpiono[i]);
            }
            for(i=0;i<3;i++){
                gpio_free(core_gpiono[i]);
            }
            return ret;
        }
    }
    //4.设置方向为输出
    for(i=0;i<3;i++){
        ret = gpio_direction_output(core_gpiono[i], 0);
        if (ret) {
            pr_err("gpio_direction_output error\n");
            goto err;
        }
        ret = gpio_direction_output(expend_gpiono[i], 0);
        if (ret) {
            pr_err("gpio_direction_output error\n");
            goto err;
        }
    }
    return 0;
err:
    for(i=0;i<3;i++){
        gpio_free(core_gpiono[i]);
        gpio_free(expend_gpiono[i]);
    }
    return ret;

}
int my_led_close(struct inode *inode, struct file *file){
    return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
    switch(cmd){
        case LED1_ON:
            gpio_set_value(core_gpiono[0],1);
            break;
        case LED1_OFF:
            gpio_set_value(core_gpiono[0],0);
            break;
        case LED2_ON:
            gpio_set_value(core_gpiono[1],1);
            break;
        case LED2_OFF:
            gpio_set_value(core_gpiono[1],0);
            break;
        case LED3_ON:
            gpio_set_value(core_gpiono[2],1);
            break;
        case LED3_OFF:
            gpio_set_value(core_gpiono[2],0);
            break;
        case LED4_ON:
            gpio_set_value(expend_gpiono[0],1);
            break;
        case LED4_OFF:
            gpio_set_value(expend_gpiono[0],0);
            break;
        case LED5_ON:
            gpio_set_value(expend_gpiono[1],1);
            break;
        case LED5_OFF:
            gpio_set_value(expend_gpiono[1],0);
            break;
        case LED6_ON:
            gpio_set_value(expend_gpiono[2],1);
            break;
        case LED6_OFF:
            gpio_set_value(expend_gpiono[2],0);
            break;
    }
    return 0;
}

const struct file_operations ledfops={
    .open=my_led_open,
    .release=my_led_close,
    .unlocked_ioctl=myled_ioctl,
};

static int __init mynode_init(void){
    //分配对象
    led_cdev = cdev_alloc();
    if(NULL == led_cdev){ //成功返回结构体指针,失败返回NULL
        pr_err("cdv_err error");
        return -ENOMEM;
    }
    //初始化对象:部分成员初始化
    cdev_init(led_cdev,&ledfops);
    //申请设备号:如果major为0,则动态申请,否则就静态指定
    if(major > 0){
        register_chrdev_region(MKDEV(major,minor),1,"mynode");
    }else if(major == 0){
        alloc_chrdev_region(&led_dev_num,0,1,"mynode"); 
        major=MAJOR(led_dev_num);
        minor=MINOR(led_dev_num);
    }
    //注册
    cdev_add(led_cdev,MKDEV(major,minor),1); 
    //自动创建设备节点
    led_class=class_create(THIS_MODULE,"mynode");
    led_device = device_create(led_class,NULL,MKDEV(major,minor),NULL,"mynode");
    
    return 0;
}
static void __exit mynode_exit(void){
    int i;
    for(i=0;i<3;i++){
        gpio_free(core_gpiono[i]);
        gpio_free(expend_gpiono[i]);
    }
    device_destroy(led_class, MKDEV(major, minor));
    class_destroy(led_class);
    cdev_del(led_cdev);
    unregister_chrdev_region(MKDEV(major, minor), 1);
    kfree(led_cdev);
}

module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");

mynode.h

#ifndef __MYNODE_H__
#define __MYNODE_H__

#define LED1_ON _IOW('L',000,int)
#define LED1_OFF _IOW('L',001,int)
#define LED2_ON _IOW('L',010,int)
#define LED2_OFF _IOW('L',011,int)
#define LED3_ON _IOW('L',020,int)
#define LED3_OFF _IOW('L',021,int)
#define LED4_ON _IOW('L',100,int)
#define LED4_OFF _IOW('L',101,int)
#define LED5_ON _IOW('L',110,int)
#define LED5_OFF _IOW('L',111,int)
#define LED6_ON _IOW('L',120,int)
#define LED6_OFF _IOW('L',121,int)

#endif

网站公告

今日签到

点亮在社区的每一天
去签到