【学习日记】【第五十一章 Linux中断实验】【流程图】——正点原子I.MX6U嵌入式Linux驱动开发

1 概述

本文主要是笔者根据《正点原子I.MX6U嵌入式Linux驱动开发》中 "第五十一章 Linux中断实验" 的程序绘制的流程图,方便理解中断和定时器的使用。

2 流程图

驱动程序 按键按下 按键松开 中断触发 按键释放 无按键释放 构建设备号 驱动入口函数 注册字符设备 创建类 创建设备 初始化按键 申请中断 创建定时器 等待中断 中断服务函数 启动定时器 定时器服务函数 读取GPIO值 设置按键值 设置释放标记 设备打开 设置私有数据 设备打开完成 设备读取 读取按键值 返回按键值 返回错误
应用程序 参数错误 参数正确 打开失败 打开成功 读取错误 读取成功 退出 检查参数 主程序开始 打印错误信息 打开设备文件 打印错误信息 进入读取循环 读取设备数据 处理错误 打印按键值 关闭设备文件 主程序结束

3 正点原子的代码

3.1 驱动程序

c 复制代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define IMX6UIRQ_CNT 1 /* 设备号个数 */
#define IMX6UIRQ_NAME "imx6uirq" /* 名字 */
#define KEY0VALUE 0X01 /* KEY0 按键值 */
#define INVAKEY 0XFF /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 */

/* 中断 IO 描述结构体 */
struct irq_keydesc {
    int gpio; /* gpio */
    int irqnum; /* 中断号 */
    unsigned char value; /* 按键对应的键值 */
    char name[10]; /* 名字 */
    irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
};

/* imx6uirq 设备结构体 */
struct imx6uirq_dev {
    dev_t devid; /* 设备号 */
    struct cdev cdev; /* cdev */
    struct class *class; /* 类 */
    struct device *device; /* 设备 */
    int major; /* 主设备号 */
    int minor; /* 次设备号 */
    struct device_node *nd; /* 设备节点 */
    atomic_t keyvalue; /* 有效的按键键值 */
    atomic_t releasekey; /* 标记是否完成一次完成的按键 */
    struct timer_list timer; /* 定义一个定时器 */
    struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键描述数组 */
    unsigned char curkeynum; /* 当前的按键号 */
};

struct imx6uirq_dev imx6uirq; /* irq 设备 */

/* 中断服务函数,开启定时器,延时 10ms,定时器用于按键消抖。 */
static irqreturn_t key0_handler(int irq, void *dev_id) {
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;
    dev->curkeynum = 0;
    dev->timer.data = (volatile long)dev_id;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}

/* 定时器服务函数,用于按键消抖 */
void timer_function(unsigned long arg) {
    unsigned char value;
    unsigned char num;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;

    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num];

    value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
    if (value == 0) { /* 按下按键 */
        atomic_set(&dev->keyvalue, keydesc->value);
    } else { /* 按键松开 */
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1); /* 标记松开按键 */
    }
}

/* 按键 IO 初始化 */
static int keyio_init(void) {
    unsigned char i = 0;
    int ret = 0;

    imx6uirq.nd = of_find_node_by_path("/key");
    if (imx6uirq.nd == NULL) {
        printk("key node not find!\r\n");
        return -EINVAL;
    }

    /* 提取 GPIO */
    for (i = 0; i < KEY_NUM; i++) {
        imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd, "key-gpio", i);
        if (imx6uirq.irqkeydesc[i].gpio < 0) {
            printk("can't get key%d\r\n", i);
        }
    }

    /* 初始化 key 所使用的 IO,并且设置成中断模式 */
    for (i = 0; i < KEY_NUM; i++) {
        memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(imx6uirq.irqkeydesc[i].name));
        sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);
        gpio_request(imx6uirq.irqkeydesc[i].gpio, imx6uirq.irqkeydesc[i].name);
        gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);
        imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i);
        
        printk("key%d:gpio=%d, irqnum=%d\r\n", i, imx6uirq.irqkeydesc[i].gpio, imx6uirq.irqkeydesc[i].irqnum);
    }
    
    /* 申请中断 */
    imx6uirq.irqkeydesc[0].handler = key0_handler;
    imx6uirq.irqkeydesc[0].value = KEY0VALUE;

    for (i = 0; i < KEY_NUM; i++) {
        ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler,
                          IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                          imx6uirq.irqkeydesc[i].name, &imx6uirq);
        if (ret < 0) {
            printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    /* 创建定时器 */
    init_timer(&imx6uirq.timer);
    imx6uirq.timer.function = timer_function;
    return 0;
}

/* 打开设备 */
static int imx6uirq_open(struct inode *inode, struct file *filp) {
    filp->private_data = &imx6uirq; /* 设置私有数据 */
    return 0;
}

/* 从设备读取数据 */
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {
    int ret = 0;
    unsigned char keyvalue = 0;
    unsigned char releasekey = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey) { /* 有按键按下 */
        if (keyvalue & 0x80) {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        } else {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0); /* 按下标志清零 */
    } else {
        goto data_error;
    }
    return 0;

data_error:
    return -EINVAL;
}

/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .read = imx6uirq_read,
};

/* 驱动入口函数 */
static int __init imx6uirq_init(void) {
    /* 1、构建设备号 */
    if (imx6uirq.major) {
        imx6uirq.devid = MKDEV(imx6uirq.major, 0);
        register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    } else {
        alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
    }

    /* 2、注册字符设备 */
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
    cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);

    /* 3、创建类 */
    imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.class)) {
        return PTR_ERR(imx6uirq.class);
    }

    /* 4、创建设备 */
    imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.device)) {
        return PTR_ERR(imx6uirq.device);
    }

    /* 5、 初始化按键 */
    atomic_set(&imx6uirq.keyvalue, INVAKEY);
    atomic_set(&imx6uirq.releasekey, 0);
    keyio_init();
    return 0;
}

/* 驱动出口函数 */
static void __exit imx6uirq_exit(void) {
    unsigned int i = 0;
    /* 删除定时器 */
    del_timer_sync(&imx6uirq.timer);

    /* 释放中断 */
    for (i = 0; i < KEY_NUM; i++) {
        free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);
        gpio_free(imx6uirq.irqkeydesc[i].gpio);
    }
    cdev_del(&imx6uirq.cdev);
    unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
    device_destroy(imx6uirq.class, imx6uirq.devid);
    class_destroy(imx6uirq.class);
}

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

3.2 应用程序

c 复制代码
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"


/*
 * @description : main 主程序
 * @param - argc : argv 数组元素个数
 * @param - argv : 具体参数
 * @return : 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
    int fd;
    int ret = 0;
    char *filename;
    unsigned char data;

    if (argc != 2) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];
    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    while (1) {
        ret = read(fd, &data, sizeof(data));
        if (ret < 0) { /* 数据读取错误或者无效 */
            // 处理错误
        } else { /* 数据读取正确 */
            if (data) /* 读取到数据 */
                printf("key value = %#X\r\n", data);
        }
    }

    close(fd);
    return ret;
}
相关推荐
花嫁代二娃31 分钟前
Linux:环境变量
linux
数字芯片实验室2 小时前
分享一个可以学习正则表达式的网址:Pythex.org
学习·正则表达式
陈洪奇2 小时前
注册中心学习笔记整理
笔记·学习
光影少年2 小时前
从前端转go开发的学习路线
前端·学习·golang
l1x1n06 小时前
Vim 编辑器常用操作详解(新手快速上手指南)
linux·编辑器·vim
ajassi20007 小时前
开源 python 应用 开发(三)python语法介绍
linux·python·开源·自动化
o不ok!7 小时前
Linux面试问题-软件测试
linux·运维·服务器
fen_fen7 小时前
学习笔记(32):matplotlib绘制简单图表-数据分布图
笔记·学习·matplotlib
DaxiaLeeSuper7 小时前
Prometheus+Grafana+node_exporter监控linux服务器资源的方案
linux·grafana·prometheus
尽兴-9 小时前
如何将多个.sql文件合并成一个:Windows和Linux/Mac详细指南
linux·数据库·windows·sql·macos