驱动开发-GPIO子系统实例

1 添加LED设备树结点

1.1 分析LED灯的原理图

        找出LED灯引脚对应的GPIO控制器,而在内核源码中设备树文件已经将GPIO控制节点编写完成,我们只需要添加一下LED灯的结点以及对应的属性

eg:

pinctrl: pin-controller@50002000 {
            #address-cells = <1>;//指定子节点用来描述地址的u32个数为1
            #size-cells = <1>;//指定子节点用来描述地址长度的u32个数为1
            compatible = "st,stm32mp157-pinctrl";//厂商信息
            ranges = <0 0x50002000 0xa400>;//用来指定子节点的父地址    和地址大小        
          gpiof: gpio@50007000 {
                gpio-controller;//空属性,起到标识作用,当前节点为GPIO控制器
                #gpio-cells = <2>;//在别的节点中引用当前节点时需要2个u32类型的数据进行描述        
                reg = <0x5000 0x400>;//当前节点的地址信息
                clocks = <&rcc GPIOF>;//GPIOF总线使能
                st,bank-name = "GPIOF";//指定控制器名字
                status = "disabled";//当前控制器状态
                                        //okay:工作状态      //disabled:不工作状态
            };
   };

  1.2 要添加的LED结点

myleds{
    led1=<&gpioe 10 0>;//&gpioe表示使用的是那一组gpio控制器  10表示用的是这一组的第几个管脚  0表示默认状态  ,在这里引用gpio控制器节点时用的u32个数由gpio控制器节点中的#gpio-cells属性决定
    led2=<&gpiof 10 0>;
    led3=<&gpioe 8 0>;
};

2  调用GPIO子系统API进行LED控制操作

        在设备树节点中解析出GPIO对象,并向内核申请使用GPIO编号。在内核空间使用unlocked_ioctl操作方法来实现控制LED灯的亮灭

/*
 * Filename: /home/ubuntu/vscode/day2_/char_dev/mycdev.c
 * Path: /home/ubuntu/vscode/day2_/char_dev
 * Created Date: Tuesday, June 13th 2023, 1:39:34 pm
 * Author: xinyue
 * 
 * Copyright (c) 2023 Your Company
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include "led.h"
#include <linux/timer.h>

//主设备号
int major = 0;
struct gpio_desc *gpionum1, *gpionum2, *gpionum3; //描述GPIO结构体
struct timer_list mytimer; //定时器对象
char kbuf[1024] = {0};

struct class* cls; //向上提交目录信息结构体指针
struct device* dev; //向上提交设备结点结构体指针

struct device_node* dnode;

int timer_init(void);

//定义虚拟地址
unsigned int* vir_motor_rcc;

int mycdev_open (struct inode * inode, struct file * file)
{
    printk("%s:%s:%d\n", __FUNCTION__, __FILE__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    //逻辑控制
    switch(cmd)
    {
        case LED1_ON:
            gpiod_set_value(gpionum1, 1);
            break;
        case LED1_OFF:
            gpiod_set_value(gpionum1, 0);
            break;
        case LED2_ON:
            gpiod_set_value(gpionum2, 1);
            break;
        case LED2_OFF:
            gpiod_set_value(gpionum2, 0);
            break;
        case LED3_ON:
            gpiod_set_value(gpionum3, 1);
            break;
        case LED3_OFF:
            gpiod_set_value(gpionum3, 0);
            break;
        case LED_WHILE:
            timer_init();//启动
            break;
        case LED_STOP:
            del_timer(&mytimer);//删除
            break;
        default:
            break;
    }
    return 0;
}

int mycdev_close (struct inode * inode, struct file * file)
{
    printk("%s:%s:%d\n", __FUNCTION__, __FILE__, __LINE__);
    return 0;
}

struct file_operations fops = {
    .open=mycdev_open,
    .unlocked_ioctl=mycdev_ioctl,
    .release=mycdev_close
};

int dev_create(void)
{
    //向上提交目录信息
    cls = class_create(THIS_MODULE, "mycdev");
    if(IS_ERR(cls)) //返回值指向错误码区
    {
        printk("class_create failed\n");
        return -PTR_ERR(cls);
    }
    printk("class_create succeeded\n");
    //向上提交设备结点信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "mycleds");
    if(IS_ERR(dev))
    {
        printk("device_create failed\n");
        return -PTR_ERR(dev);
    }
    printk("device_create succeeded\n");
    return 0;
}

//解析设备树结点,找出GPIO编号
int find_gpiox(void)
{
    //找出设备树结点首地址
    dnode = of_find_node_by_name(NULL, "myleds");
    if(dnode == NULL)
    {
        printk("of_find_node_by_name failed\n");
        return -1;
    }
    //根据结点找到对应的GPIO编号,并申请使用编号,初始化为低电平输出模式
    gpionum1 = gpiod_get_from_of_node(dnode, "led1", 0, GPIOD_OUT_LOW, NULL);//led1
    if(IS_ERR(gpionum1))
    {
        printk("gpiod_get_from_of_node failed\n");
        return -PTR_ERR(gpionum1);
    }
    gpionum2 = gpiod_get_from_of_node(dnode, "led2", 0, GPIOD_OUT_LOW, NULL);//led2
    if(IS_ERR(gpionum2))
    {
        printk("gpiod_get_from_of_node failed\n");
        return -PTR_ERR(gpionum2);
    }
    gpionum3 = gpiod_get_from_of_node(dnode, "led3", 0, GPIOD_OUT_LOW, NULL);//led3
    if(IS_ERR(gpionum3))
    {
        printk("gpiod_get_from_of_node failed\n");
        return -PTR_ERR(gpionum3);
    }
    return 0;
}
int flags = 0;
//定时器处理函数
void mytimer_func(struct timer_list *timer)
{
    if(0 == flags)
    {
        gpiod_set_value(gpionum3, 0);
        gpiod_set_value(gpionum1, 1);
        flags = 1;
    }
    else if(1 == flags)
    {
        gpiod_set_value(gpionum1, 0);
        gpiod_set_value(gpionum2, 1);
        flags = 2;
    }
    else if(2 == flags)
    {
        gpiod_set_value(gpionum2, 0);
        gpiod_set_value(gpionum3, 1);
        flags = 0;
    }
    mod_timer(&mytimer, jiffies + HZ); //重启定时器
}

//初始化一个定时器
int timer_init(void)
{
    mytimer.expires = jiffies + HZ;
    //初始化定时器
    timer_setup(&mytimer, mytimer_func, 0);
    //将定时器注册进内核并启用
    add_timer(&mytimer);
    return 0;
}

static int __init mycdev_init(void)
{
    major = register_chrdev(0, "mycleds", &fops);
    if(0 == major)
    {
        printk("%s\n", "mycdev register failed");
        return -1;
    }
    printk("%s:%s:%d major=%d\n", __FUNCTION__, __FILE__, __LINE__, major);
    dev_create(); //向上提交信息(创建设备结点)
    find_gpiox(); //初始化GPIO引脚
    return 0;
}

static void __exit mycdev_exit(void)
{
    //注销定时器
    del_timer(&mytimer);
    //释放申请的编号
    gpiod_put(gpionum1);
    gpiod_put(gpionum2);
    gpiod_put(gpionum3);
    //销毁设备节点
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    //注销设备驱动
    unregister_chrdev(major, "mycleds");
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

        在用户空间使用ioctl函数配合内核空间的操作方法unlocked_ioctl来实现LED灯的控制

/*
 * Filename: /home/ubuntu/vscode/day7_/test.c
 * Path: /home/ubuntu/vscode/day7_
 * Created Date: Tuesday, June 20th 2023, 2:30:34 pm
 * Author: xinyue
 * 
 * Copyright (c) 2023 Your Company
 */
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include <sys/ioctl.h>
#include"led.h"


int main(int argc, char const *argv[])
{
    int a,b;
    char buf[128]={0};
    int fd=open("/dev/mycleds",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        printf("请输入:11(led1开灯) 10(led1关灯) 1(流水灯启动) 0(流水灯停止)>>>");
        scanf("%d",&a);
        switch(a)
        {
            case 11:
                ioctl(fd,LED1_ON);//开灯
                break;
            case 10:
                ioctl(fd,LED1_OFF);//关灯
                break;
            case 21:
                ioctl(fd,LED2_ON);//开灯
                break;
            case 20:
                ioctl(fd,LED2_OFF);//关灯
                break;
            case 31:
                ioctl(fd,LED3_ON);//开灯
                break;
            case 30:
                ioctl(fd,LED3_OFF);//关灯
                break;
            case 1:
                ioctl(fd,LED_WHILE);//流水灯启动
                break;
            case 0:
                ioctl(fd,LED_STOP);//流水灯停止
                break;
            default:
                printf("输入有误,请重新输入>>>\n");
                break;
        }
    }

    
    close(fd);

    return 0;
}

        头文件

/*
 * Filename: /home/ubuntu/vscode/day7_/led.h
 * Path: /home/ubuntu/vscode/day7_
 * Created Date: Tuesday, June 20th 2023, 2:04:59 pm
 * Author: xinyue
 * 
 * Copyright (c) 2023 Your Company
 */
#ifndef __LED_H__
#define __LED_H__

#define LED1_ON _IO('l', 11)
#define LED1_OFF _IO('l', 10)

#define LED2_ON _IO('l', 21)
#define LED2_OFF _IO('l', 20)

#define LED3_ON _IO('l', 31)
#define LED3_OFF _IO('l', 30)

#define LED_WHILE _IO('l', 1)
#define LED_STOP _IO('l', 0)

#endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是高通 MSM8953 SoC 上使用 DTS(Device Tree Source)描述的 GPIO 驱动实例: ``` &tlmm { gpio_keys: gpio-keys { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&button_pins>; status = "okay"; button@4 { label = "power"; gpios = <&tlmm 4 GPIO_ACTIVE_LOW>; linux,code = <KEY_POWER>; debounce-interval = <50>; gpio-key,wakeup; gpio-key,wakeup-source; }; button@3 { label = "volume_up"; gpios = <&tlmm 3 GPIO_ACTIVE_LOW>; linux,code = <KEY_VOLUMEUP>; debounce-interval = <50>; }; button@2 { label = "volume_down"; gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; linux,code = <KEY_VOLUMEDOWN>; debounce-interval = <50>; }; }; button_pins: button_pins { pins = "gpio4", "gpio3", "gpio2"; function = "gpio_in"; drive-strength = <2>; bias-disable; }; }; ``` 在上面的示例中,我们定义了一个名为“gpio_keys”的节点,它表示一个 GPIO 按键设备。在该节点下,我们定义了三个名为“button@*”(*表示数字)的子节点,它们分别表示三个按键。每个按键节点都指定了其使用的 GPIO 引脚编号,所以我们需要在该文件中定义一个名为“button_pins”的节点,它表示我们将使用哪些 GPIO 引脚。在“button_pins”节点中,我们指定了使用 GPIO 引脚 2、3 和 4,它们的功能为“gpio_in”,表示我们将使用它们作为输入引脚,而不是输出引脚。最后,我们指定了每个按键的 Linux 按键代码(即在 Linux 内核中定义的按键码)、抖动时间和唤醒源等属性。 当系统启动时,内核会根据设备树中的 GPIO 配置自动加载相应的 GPIO 驱动程序。在本例中,内核将加载“gpio-keys”驱动程序,并将其绑定到我们定义的“gpio_keys”节点上。这样,我们就可以在 Linux 系统中使用这三个按键了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值