惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
S
Securelist
MyScale Blog
MyScale Blog
Jina AI
Jina AI
罗磊的独立博客
The Cloudflare Blog
美团技术团队
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
雷峰网
雷峰网
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
博客园 - 聂微东
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
T
Tailwind CSS Blog
Attack and Defense Labs
Attack and Defense Labs
博客园_首页
Latest news
Latest news
Apple Machine Learning Research
Apple Machine Learning Research
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Hacker News
The Hacker News
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
D
Docker
Webroot Blog
Webroot Blog
N
Netflix TechBlog - Medium
T
Tor Project blog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
B
Blog
Recent Announcements
Recent Announcements
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
Security Latest
Security Latest
V2EX - 技术
V2EX - 技术
N
News | PayPal Newsroom
Microsoft Security Blog
Microsoft Security Blog

博客园 - 什么都没有

rk3588 docker 安装 rk3568 ubuntu20.04 oop问题分析 rk3288 buildroot 编译webkit yocto简介 linux 调试串口测试程序(pc与开发板通信) spin_lock spin_lock_irq spin_lock_irqsave INIT: version 2.88 booting linux 进程读书笔记 linux kvm源码分析 虚拟化技术慢谈 linux中断子系统 注释规范 linux下C获取文件的大小 imx6 工具链下载地址 程序、任务、进程和线程的联系与区别 linux下socket connect 阻塞方式 阻塞时间控制 实时操作系统性能指标 这段代码可以,佩服作者 imx6sl 调试记录
imx6 gpio irq
什么都没有 · 2016-10-17 · via 博客园 - 什么都没有
/*****************************************************************
 *                    gpio irq
 *
 *    一直以来都没了解过gpio的irq,如何使用,今天正好了解下。 
 *   本文从内核和应用层分析gpio的irq,并写验证程序。
 *            
 *    本文部分程序内容源自以下链接,并自己更改进行验证。验证平台freescal imx6.
 *        http://blog.ittraining.com.tw/2015/05/raspberry-pi-b-pi2-linux-gpio-button.html
 *        http://m.blog.csdn.net/article/details?id=51187244
 *        http://blog.csdn.net/gqb_driver/article/details/8620809
 *    参考文档:
 *        kernel/Documentation/gpio.txt
 *
 *                                    Tony Liu, 2016-5-26, Shenzhen
 *****************************************************************/
1.内核中验证
1.1 程序

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>

#define LED                IMX_GPIO_NR(2, 3)        
#define BUTTON            IMX_GPIO_NR(3, 23)    
#define INT_NAME        "BUTTON_INT"
#define DEV_NAME        "INTERRUPT"

unsigned short int button_irq = 0;
static unsigned long flags = 0;
static int led_trigger = 0;

static irqreturn_t button_isr(int irq, void *data)
{
    /* 使cpu不接受其他中断 */
    local_irq_save(flags);
    printk("button_isr !!!!\n");
    gpio_set_value(LED, led_trigger);
    led_trigger = led_trigger ? (0):(1);
    local_irq_restore(flags);
    return IRQ_HANDLED;
}

int __init gpio_irq_init(void)
{
    int ret;
    ret = gpio_request(LED, "led");
    if (ret) {
        printk("get led FAILED!\n");
        return ret;
    }
    gpio_direction_output(LED, 0);
    ret = gpio_request(BUTTON, "button");
    if (ret) {
        printk("get button FAILED!\n");
        return ret;
    }
    button_irq = gpio_to_irq(BUTTON);
    if (button_irq < 0) {
        printk("gpio_to_irq FAILED!\n");
        return -1;
    }
    printk("irq:%d\n", button_irq);
    ret = request_irq(button_irq, button_isr, IRQF_TRIGGER_RISING, INT_NAME, DEV_NAME);
    if (ret < 0)
        return -1;

    return ret;
}

void __exit gpio_irq_exit(void)
{
    gpio_free(LED);
    gpio_free(BUTTON);
    //需要释放申请的中断,否这下次不能使用,并且id要是申请使跳得内容相同,否则会出错。
    free_irq(button_irq, DEV_NAME);
}

module_init(gpio_irq_init);
module_exit(gpio_irq_exit);

MODULE_LICENSE("GPL");

1.2 分析
/*
    irq:gpio_to_irq获得的中断号
    handler:    中断处理函数
    *devname: 会在/proc/intertupt中显示
    *dev_id: request和free的时候必须相同,否则会出错。可以为NULL
*/
int request_irq(unsigned int irq,
        irq_handler_t handler,
        unsigned long flags, const char *devname, void *dev_id)

//文件系统中查看缩写的dev_id,开头的343是中断号
root@freescale ~$ cat /proc/interrupts 
           CPU0       CPU1       
......
343:          0          0      GPIO  BUTTON_INT

2.应用层验证
2.1 验证代码(poll函数)

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

int main(int argc, char *argv[])
{
    int fd, ret;
    char value;
    struct pollfd pfd;
    //打开gpio87,对应imx6的IMX_GPIO_NR(3, 23),我连接了一个按键
    system("echo \"87\" > /sys/class/gpio/export");
    /*
     *    none    表示引脚为输入,不是中断引脚
     *  rising    表示引脚为中断输入,上升沿触发
     *  falling    表示引脚为中断输入,下降沿触发
     *  both    表示引脚为中断输入,边沿触发
     */
    system("echo \"both\" > /sys/class/gpio/gpio87/edge");

    fd = open("/sys/class/gpio/gpio87/value", O_RDWR);
    if (fd < 0) {
        printf("open file fail\n");
        return -1;
    }
    /* poll要监听的文件描述符号 */
    pfd.fd = fd;
    /* 监听的文件的事件类型,当配置为中断'edge‘的时候.events必须设置为POLLPRI
    * 详情查看内核中的文档gpio.txt */
    pfd.events = POLLPRI | POLLERR;
    /* 由于value文件中本来就有数据需要读取,没有读取的话,会被当成一个中断而进行处理 */
    //到文件开头读取
    ret = lseek(fd, 0, SEEK_SET);
    if (ret == -1) {
        printf("lseek error\n");
        return -1; 
    }   
    //读取1字节
    ret = read(fd, &value, 1);

    while (1) {
        /* 监听个数1, 等待时间无限 */
        ret = poll(&pfd, 1, -1);
        if (ret == -1) {
            printf("poll error\n");
            return -1;
        }
        /* 监听的时间会保存在revents成员中 */
        if (pfd.revents & POLLPRI) {
            //文件指针只想文件的开头
            ret = lseek(fd, 0, SEEK_SET);
            if (ret == -1) {
                printf("lseek error\n");
                return -1;
            }
            //读取,结果为字符'0'或者‘1’
            read(fd, &value, 1);
            printf("value:%c\n", value);
        }
        if (pfd.revents & POLLERR) {
            printf("poll error\n");
        }
    }
    
    close(fd);

    return 0;
}

2.1 验证代码(select函数)

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

int main(int argc, char *argv[])
{
    int fd[2], ret;
    int maxfd;
    char value;
    fd_set readfd;
    fd_set exceptfd;
    //打开gpio87,对应imx6的IMX_GPIO_NR(3, 23),我连接了一个按键
    system("echo 87 > /sys/class/gpio/export");
    //打开gpio94,对应imx6的IMX_GPIO_NR(3, 30),我连接了一个按键
    system("echo 94 > /sys/class/gpio/export");

    system("echo in > /sys/class/gpio/gpio94/direction");
    system("echo in > /sys/class/gpio/gpio87/direction");
    /*
     *    none    表示引脚为输入,不是中断引脚
     *  rising    表示引脚为中断输入,上升沿触发
     *  falling    表示引脚为中断输入,下降沿触发
     *  both    表示引脚为中断输入,边沿触发
     */
    system("echo falling > /sys/class/gpio/gpio87/edge");
    system("echo falling > /sys/class/gpio/gpio94/edge");


    fd[0] = open("/sys/class/gpio/gpio87/value", O_RDWR);
    if (fd < 0) {
        printf("open file fail\n");
        return -1;
    }

    fd[1] = open("/sys/class/gpio/gpio94/value", O_RDWR);
    if (fd < 0) {
        printf("open file fail\n");
        return -1;
    }

    /* 由于value文件中本来就有数据需要读取,没有读取的话,会被当成一个中断而进行处理 */
    //到文件开头读取
    ret = lseek(fd[0], 0, SEEK_SET);
    if (ret == -1) {
        printf("lseek error\n");
        return -1; 
    }   
    //读取1字节
    ret = read(fd[0], &value, 1);

    ret = lseek(fd[1], 0, SEEK_SET);
    if (ret == -1) {
        printf("lseek error\n");
        return -1; 
    }   
    //读取1字节
    ret = read(fd[1], &value, 1);

    while (1) {
        FD_ZERO(&exceptfd);
        FD_SET(fd[0], &exceptfd);
        FD_SET(fd[1], &exceptfd);
        maxfd = (fd[0] > fd[1] ? fd[0] : fd[1]);
        //使用select获取中断,必须将fd放在exceptfd的位置
        ret = select(maxfd +1, NULL, NULL, &exceptfd, NULL);
        if (ret == -1) {
            printf("select operation fail\n");
            return ret;
        }
        if (FD_ISSET(fd[0], &exceptfd)) {
            ret = lseek(fd[0], 0, SEEK_SET);
            if (ret == -1) {
                printf("lseek error\n");
                return -1;
            }
            read(fd[0], &value, 1);
                printf("0: value:%c\n", value);
        }
        if (FD_ISSET(fd[1], &exceptfd)) {
            ret = lseek(fd[1], 0, SEEK_SET);
            if (ret == -1) {
                printf("lseek error\n");
                return -1;
            } read(fd[1], &value, 1); printf("1: value:%c\n", value); }
    }
    //关闭文件描述符    
    close(fd[0]);
    close(fd[1]);

    //操作完毕,释放gpio
    system("echo 87 > /sys/class/gpio/unexport");
    system("echo 94 > /sys/class/gpio/unexport");

    return 0;
}

2.1分析
    <1>申请gpio, gpio_request
    echo "87" > /sys/class/gpio/export
    会在/sys/class/gpio目录下边生成一个gpio87的目录

    <2>释放gpio, gpio_free
    echo "87" > /sys/class/gpio/unexport

    <3>申请之后,gpio87目录的内容如下:
    root@freescale /sys/class/gpio/gpio87$ ls -l
    -rw-r--r--    1 root     root          4096 Jan  1 00:33 active_low
    -rw-r--r--    1 root     root          4096 Jan  1 00:33 direction
    -rw-r--r--    1 root     root          4096 Jan  1 00:15 edge
    drwxr-xr-x    2 root     root             0 Jan  1 00:33 power
    lrwxrwxrwx    1 root     root             0 Jan  1 00:33 subsystem -> ../../../../class/gpio
    -rw-r--r--    1 root     root          4096 Jan  1 00:33 uevent
    -rw-r--r--    1 root     root          4096 Jan  1 00:15 value
    <4>设置输入输出
        echo "in" > direction
        echo "out" > direction
    <5>设置输出值
        echo "1" > value         //非0值都会当作1
        echo "0" > value

详细参考内核的文档:
         kernel/Documentation/gpio.txt
如下:
There are three kinds of entry in /sys/class/gpio:

   -    Control interfaces used to get userspace control over GPIOs;

   -    GPIOs themselves; and

   -    GPIO controllers ("gpio_chip" instances).

That's in addition to standard files including the "device" symlink.

The control interfaces are write-only:

    /sys/class/gpio/

        "export" ... Userspace may ask the kernel to export control of
        a GPIO to userspace by writing its number to this file.

        Example:  "echo 19 > export" will create a "gpio19" node
        for GPIO #19, if that's not requested by kernel code.

        "unexport" ... Reverses the effect of exporting to userspace.

        Example:  "echo 19 > unexport" will remove a "gpio19"
        node exported using the "export" file.

GPIO signals have paths like /sys/class/gpio/gpio42/ (for GPIO #42)
and have the following read/write attributes:

    /sys/class/gpio/gpioN/

    "direction" ... reads as either "in" or "out".  This value may
        normally be written.  Writing as "out" defaults to
        initializing the value as low.  To ensure glitch free
        operation, values "low" and "high" may be written to
        configure the GPIO as an output with that initial value.

        Note that this attribute *will not exist* if the kernel
        doesn't support changing the direction of a GPIO, or
        it was exported by kernel code that didn't explicitly
        allow userspace to reconfigure this GPIO's direction.

    "value" ... reads as either 0 (low) or 1 (high).  If the GPIO
        is configured as an output, this value may be written;
        any nonzero value is treated as high.

        If the pin can be configured as interrupt-generating interrupt
        and if it has been configured to generate interrupts (see the
        description of "edge"), you can poll(2) on that file and
        poll(2) will return whenever the interrupt was triggered. If
        you use poll(2), set the events POLLPRI and POLLERR. If you
        use select(2), set the file descriptor in exceptfds. After
        poll(2) returns, either lseek(2) to the beginning of the sysfs
        file and read the new value or close the file and re-open it
        to read the value.

    "edge" ... reads as either "none", "rising", "falling", or
        "both". Write these strings to select the signal edge(s)
        that will make poll(2) on the "value" file return.

        This file exists only if the pin can be configured as an
        interrupt generating input pin.

    "active_low" ... reads as either 0 (false) or 1 (true).  Write
        any nonzero value to invert the value attribute both
        for reading and writing.  Existing and subsequent
        poll(2) support configuration via the edge attribute
        for "rising" and "falling" edges will follow this
        setting.

GPIO controllers have paths like /sys/class/gpio/gpiochip42/ (for the
controller implementing GPIOs starting at #42) and have the following
read-only attributes:

    /sys/class/gpio/gpiochipN/

        "base" ... same as N, the first GPIO managed by this chip

        "label" ... provided for diagnostics (not always unique)

        "ngpio" ... how many GPIOs this manges (N to N + ngpio - 1)

Board documentation should in most cases cover what GPIOs are used for
what purposes.  However, those numbers are not always stable; GPIOs on
a daughtercard might be different depending on the base board being used,
or other cards in the stack.  In such cases, you may need to use the
gpiochip nodes (possibly in conjunction with schematics) to determine
the correct GPIO number to use for a given signal.