驱动开发-GPIO子系统实例

该文介绍了如何在STM32MP157平台上添加LED设备树节点,通过分析原理图找到GPIO控制器,然后在设备树中添加LED结点。在内核空间,使用GPIO子系统API和unlocked_ioctl方法控制LED灯的亮灭。在用户空间,通过ioctl函数与内核通信来操作LED。此外,还涉及到了定时器的初始化和LED流水灯的控制逻辑。
摘要由CSDN通过智能技术生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值