zoukankan      html  css  js  c++  java
  • Linux驱动入门(四)非阻塞方式实现按键驱动

    copy from :https://blog.csdn.net/weixin_42462202/article/details/99998992

    文章目录
    Linux驱动入门(四)非阻塞方式实现按键驱动
    一、注册字符设备
    二、操作硬件
    2.1 操作按键
    2.2 通用方式实现
    2.3 gpiolib实现
    三、源码
    3.1 通用方式实现
    3.2 gpiolib实现
    四、测试

    本文目标:实现一个按键驱动,通过read函数非阻塞读取按键的状态
    一、注册字符设备
    首先第一步,先编写字符设备的框架,这一部分在Linux驱动入门(一)字符设备驱动基础中已经做了详细介绍,这里直接给出源码,不做过多的解释

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/slab.h>
    #include <linux/device.h>

    #include <asm/io.h>
    #include <asm/uaccess.h>

    static dev_t dev_id;
    static struct cdev *button_dev;
    static struct class *button_class;

    int button_open(struct inode *inode, struct file *file)
    {
    /* 将GPIO设置为输入模式 */

    return 0;
    }

    ssize_t button_read(struct file *file, char __user *data, size_t size, loff_t *loff)
    {
    int ret;

    /* 读取按键状态 */

    /* 将结果放回给应用层 */
    ret = copy_to_user(data, &val, sizeof(val));

    return 0;
    }

    static struct file_operations button_fops = {
    .owner = THIS_MODULE,
    .open = button_open,
    .read = button_read,
    };

    static __init int button_init(void)
    {
    /* 申请设备号 */
    alloc_chrdev_region(&dev_id, 1, 1, "button");

    /* 分配字符设备 */
    button_dev = cdev_alloc();

    /* 设置字符设备 */
    cdev_init(button_dev, &button_fops);

    /* 注册字符设备 */
    cdev_add(button_dev, dev_id, 1);

    /* 创建设备节点 */
    button_class = class_create(THIS_MODULE, "button"); //创建类
    device_create(button_class, NULL, dev_id, NULL, "button"); //创建设备节点

    return 0;
    }

    static __exit void button_exit(void)
    {
    /* 注销设备节点 */
    device_destroy(button_class, dev_id);
    class_destroy(button_class);

    /* 注销字符设备 */
    cdev_del(button_dev);
    kfree(button_dev);

    /* 注销注册的设备号 */
    unregister_chrdev_region(dev_id, 1);
    }

    module_init(button_init);
    module_exit(button_exit);

    MODULE_LICENSE("GPL");


    编译上述驱动程序,就在模块,就会生成/dev/button设备节点,这是我们的按键驱动的基本框架,我们还需要在button_open和button_read中操作硬件来完善驱动程序

    这里介绍一个知识点,用户空间和内核空间不能通过指针直接访问,必须使用内核提供的函数

    从用户空间到内核空间使用

    static inline long copy_from_user(void *to,
    const void __user * from, unsigned long n)

    从内核空间到用户空间使用

    static inline long copy_to_user(void __user *to,
    const void *from, unsigned long n)

    例如在button_read中使用copy_to_user将结果返还给应用层

    上述程序还未涉及到硬件,下面来讲一讲如何操作按键

    二、操作硬件
    2.1 操作按键
    首先打开原理图,查看按键

    这里我想操作SW5,所以要看SW5接在芯片的哪个引脚,继续在原理图中搜索EINT2

    发现EIN2接在GPH0_2引脚上

    从原理图上可以看出,当按键没有按下的时候,GPIO为高电平,按下时,GPIO为低电平,所以要看按键是否被按下,只需要查看GPIO是否为低电平

    下面查看芯片的datasheet,查看如何读取读取GPIO的高低电平状态

    从芯片手册中找到了下面两个寄存器

    v

     GPH0CON设置GPIO的功能,GPH0DAT设置或者读取GPIO的值

    要读取GPH0_2引脚的状态,可以这样做

    GPH0CON &= (0xF<<8); //将GPH0_2设置为输入模式

    int val = GPH0DAT & (1<<2); //读取GPH0_2的状态

    在Linux驱动入门(二)操作硬件中详细地讲解了如何去操作硬件,分别有通用方法和使用gpiolib的方法,下面将介绍这两种方法

    2.2 通用方式实现
    通过直接操作寄存器的方式,来操作按键

    首先从datasheet中找到寄存器的物理地址

    #define GPH0CON_PHY_ADDR 0xE0200C00
    #define GPH0DAT_PHY_ADDR 0xE0200C04

    映射物理地址

    static volatile unsigned int *gph0_con = NULL;
    static volatile unsigned int *gph0_dat = NULL;

    gph0_con = (volatile unsigned int *)ioremap(GPH0CON_PHY_ADDR, 8);
    gph0_dat = gph0_con+1;

    设置GPH0_2为输入模式

    unsigned int cfg;

    cfg = readl(gph0_con);
    writel(cfg & ~(0xF<<8), gph0_con);

    读取GPH0_2的状态

    unsigned int val = readl(gph0_dat);
    val = val & (1<<2);

    取消地址映射

    iounmap(gph0_con);

    2.3 gpiolib实现
    申请GPIO



    设置为输入模式

    gpio_direction_input(S5PV210_GPH0(2));

    获取GPIO的状态

    int val = gpio_get_value(S5PV210_GPH0(2));

    释放GPIO

    gpio_free(S5PV210_GPH0(2));

    三、源码
    3.1 通用方式实现
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/slab.h>
    #include <linux/device.h>

    #include <asm/io.h>
    #include <asm/uaccess.h>

    #define GPH0CON_PHY_ADDR 0xE0200C00
    #define GPH0DAT_PHY_ADDR 0xE0200C04

    static dev_t dev_id;
    static struct cdev *button_dev;
    static struct class *button_class;

    static volatile unsigned int *gph0_con = NULL;
    static volatile unsigned int *gph0_dat = NULL;

    int button_open(struct inode *inode, struct file *file)
    {
    unsigned int cfg;

    /* 将GPIO设置为输入模式 */
    cfg = readl(gph0_con);
    writel(cfg & ~(0xF<<8), gph0_con);

    return 0;
    }

    ssize_t button_read(struct file *file, char __user *data, size_t size, loff_t *loff)
    {
    int ret;
    unsigned int val = readl(gph0_dat);
    val = val & (1<<2);

    ret = copy_to_user(data, &val, sizeof(val));

    return 0;
    }

    static struct file_operations button_fops = {
    .owner = THIS_MODULE,
    .open = button_open,
    .read = button_read,
    };

    static __init int button_init(void)
    {
    /* 申请设备号 */
    alloc_chrdev_region(&dev_id, 1, 1, "button");

    /* 分配字符设备 */
    button_dev = cdev_alloc();

    /* 设置字符设备 */
    cdev_init(button_dev, &button_fops);

    /* 注册字符设备 */
    cdev_add(button_dev, dev_id, 1);

    /* 创建设备节点 */
    button_class = class_create(THIS_MODULE, "button"); //创建类
    device_create(button_class, NULL, dev_id, NULL, "button"); //创建设备节点

    /* 映射物理地址 */
    gph0_con = (volatile unsigned int *)ioremap(GPH0CON_PHY_ADDR, 8);
    gph0_dat = gph0_con+1;

    return 0;
    }

    static __exit void button_exit(void)
    {
    /* 注销设备节点 */
    device_destroy(button_class, dev_id);
    class_destroy(button_class);

    /* 注销字符设备 */
    cdev_del(button_dev);
    kfree(button_dev);

    /* 注销注册的设备号 */
    unregister_chrdev_region(dev_id, 1);

    /* 注销映射的地址 */
    iounmap(gph0_con);
    }

    module_init(button_init);
    module_exit(button_exit);

    MODULE_LICENSE("GPL");

    3.2 gpiolib实现
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/slab.h>
    #include <linux/device.h>
    #include <linux/gpio.h>

    #include <asm/io.h>
    #include <asm/uaccess.h>

    static dev_t dev_id;
    static struct cdev *button_dev;
    static struct class *button_class;

    int button_open(struct inode *inode, struct file *file)
    {
    /* 将GPIO设置为输出模式 */
    gpio_direction_input(S5PV210_GPH0(2));

    return 0;
    }

    ssize_t button_read(struct file *file, char __user *data, size_t size, loff_t *loff)
    {
    int ret;
    int val = gpio_get_value(S5PV210_GPH0(2));

    ret = copy_to_user(data, &val, sizeof(val));

    return 0;
    }

    static struct file_operations button_fops = {
    .owner = THIS_MODULE,
    .open = button_open,
    .read = button_read,
    };

    static __init int button_init(void)
    {
    /* 申请设备号 */
    alloc_chrdev_region(&dev_id, 1, 1, "button");

    /* 分配字符设备 */
    button_dev = cdev_alloc();

    /* 设置字符设备 */
    cdev_init(button_dev, &button_fops);

    /* 注册字符设备 */
    cdev_add(button_dev, dev_id, 1);

    /* 创建设备节点 */
    button_class = class_create(THIS_MODULE, "button"); //创建类
    device_create(button_class, NULL, dev_id, NULL, "button"); //创建设备节点

    gpio_request(S5PV210_GPH0(2), "button");

    return 0;
    }

    static __exit void button_exit(void)
    {
    /* 注销设备节点 */
    device_destroy(button_class, dev_id);
    class_destroy(button_class);

    /* 注销字符设备 */
    cdev_del(button_dev);
    kfree(button_dev);

    /* 注销注册的设备号 */
    unregister_chrdev_region(dev_id, 1);

    gpio_free(S5PV210_GPH0(2));
    }

    module_init(button_init);
    module_exit(button_exit);

    MODULE_LICENSE("GPL");


    四、测试
    将上面两个驱动任意一个保存为button_drv.c

    保存下面Makefile

    KERN_DIR = /mFile/arm/boardSupport/linux+QT4.8/bsp/kernel/

    all:
    make -C $(KERN_DIR) M=`pwd` modules

    clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

    obj-m += button_drv.o

    修改内核源码树,执行make,生成button_drv.ko,加载模块

    应用测试程序

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>

    #define BUTTON_DEV "/dev/button"

    int main(int argc, char* argv[])
    {
    int val;

    int fd = open(BUTTON_DEV, O_RDONLY);
    if(fd < 0)
    {
    printf("failed to open %s ", BUTTON_DEV);
    return -1;
    }

    while(1)
    {
    read(fd, &val, sizeof(val));
    if(val == 0)
    {
    printf("button press ");
    }

    usleep(1000*10); //10ms
    }

    close(fd);

    return 0;
    }

    将上述程序保存为button_test.c,执行arm-linux-gcc button_test.c

    执行./a.out,当按下按键的时候,可以看到控制台打印出"button press"
    ————————————————
    版权声明:本文为CSDN博主「JT同学」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_42462202/article/details/99998992

    Always Believe Something Beauitful Will Be Happen
  • 相关阅读:
    iOS中Zbar二维码扫描的使用
    SOJ 1135. 飞跃原野
    SOJ 1048.Inverso
    SOJ 1219. 新红黑树
    SOJ 1171. The Game of Efil
    SOJ 1180. Pasting Strings
    1215. 脱离地牢
    1317. Sudoku
    SOJ 1119. Factstone Benchmark
    soj 1099. Packing Passengers
  • 原文地址:https://www.cnblogs.com/Oude/p/12456186.html
Copyright © 2011-2022 走看看