Widora-neo GPIO中断试验



  • 最近想在widora-neo上装个红外接收头,以此来控制音乐播放等软件。第一步设想用GPIO脚电平变化产生中断来唤醒红外信号解码,但在mt7688手册上找了半天也没有发现GPIO对应的中断号,"必应“中搜索了一阵子,发现有个gpio_to_irq()函数可以获得对应的中断号,不知道widora中能不能用,试验了一下,发现居然OK! 将发现和大家分享一下:

    1. 内核模块模板
    继续采用"编制第一个widora-neo驱动" 中的模板。

    2. mt7688 GPIO中断相关寄存器
    用GPIO 17 脚上的上升沿来触发中断,主要用到如下的寄存器。
    volatile unsigned long *GPIO_CTRL_0; //--- GPIO0 to GPIO31 direction control register 0-input 1-output
    volatile unsigned long *GINT_REDGE_0; //--GPIO0 to GPIO31 rising edge interrupt enable register
    volatile unsigned long *GINT_FEDGE_0; //--GPIO0 to GPIO31 falling edge interrupt enable register
    volatile unsigned long *GINT_STAT_0; //---GPIO0 to GPIO31 interrupt status register 1-int 0 -no int
    volatile unsigned long *GINT_EDGE_0; //---GPIO0 to GPIO31 interrupt edge status register 1-rising 0-falling
    volatile unsigned long *GPIO1_MODE; // GPIO1 purpose selection register,for SPIS or GPIO14-17 mode selection
    volatile unsigned long *AGPIO_CFG; // analog GPIO configuartion,GPIO14-17 purpose

    3. 申请和注册GPIO中断
    3.1 用gpio_to_irq()函数取得GPIO中断号
    调用函数 gpio_to_irq(GPIO_PIN_NUM) 来获得中断号,需要包含相关头文件#include <linux/gpio.h>

    3.2 申请注册中断
    调用函数request_irq(GPIO_INT_NUM,gpio_int_handler,IRQF_DISABLED,"GPIO_INT_midas",NULL) 申请注册中断,相关要素可查阅网上资料。 IRQF_TRIGGER_RISING 标志应该也可以用,没有试过。 申请注册成功后,可以用 cat /proc/interrupts 命令看到对应的中断号。

    4. 模块程序
    4.1 主要自定函数功能
    static void Init_GPIO(void) /* 映射并设定相关寄存器功能 /
    static void GPIO_unmap(void) /
    释放内存映射 /
    static void get_gpio_INT_num(void) /
    获得GPIO映射的中断号 */
    static irqreturn_t gpio_int_handler(int irq,void *dev_id,struct pt_regs regs) / 中断处理函数 /
    static int register_gpio_IRQ(void) /
    向内核申请注册中断 */
    4.2 在设备打开时进行中断的初始化和注册(黑体部分)。
    //---OPEN
    static int gpio_int_open(struct inode *inode, struct file *file)
    {
    int ret_v=0;
    printk("gpio_int driver open........ \n");

        //------------    init GPIO interrupt   ----------
        Init_GPIO();
        get_gpio_INT_num();
        ret_v=register_gpio_IRQ();  //---register GPIO interrup     
    

    return ret_v;
    }

    4.3 在读取设备时返回中断状态,并重使能中断(黑体部分)。
    //---READ
    static ssize_t gpio_int_read(struct file *file, char __user *buffer,
    size_t len, loff_t *pos)
    {
    int ret_v = 0;
    printk("gpio_int drive read...\n");

     copy_to_user(buffer,&INT_STATUS,4);
     INT_STATUS=0; //----reset interrupt token
     msleep(100);  //------------ deter re-enabling irq to avoid key-jitter
      if(DISABLE_IRQ_TOKEN)
         {
               enable_irq(GPIO_INT_NUM);   
              DISABLE_IRQ_TOKEN=0;
         } 
    

    ret_v=4;
    return ret_v;
    }
    4.4 在设备关闭的时候注销和清理中断资源(黑体部分)。
    //---CLOSE
    static int gpio_int_close(struct inode *inode , struct file *file)
    {
    printk("gpio_int drive close...\n");

        //------------free irq resource----------------------------
        GPIO_unmap();  //-----free GPIO map
        free_irq(GPIO_INT_NUM,NULL); //----free irq NON-SHARED**
    

    return 0;
    }

    5. 用户程序
    写一个用户程序来测试,当中断被触发时会在串口终端输出"GPIO Interrupt Triggered! "信息。

     #include <stdio.h>
     #include <sys/types.h>
     #include <sys/stat.h>
     #include <fcntl.h>
     #include <unistd.h>
     #include <sys/ioctl.h>
    
     char str_dev[]="/dev/gpio_int_dev";
    
     int main(int argc, char **argv)
     {
         int fd;
         unsigned int INT_STATUS = 0;
         //------- open driver--------
         fd = open(str_dev, O_RDWR | O_NONBLOCK);
         if (fd < 0)
          {
         printf("can't open %s\n",str_dev);
         return -1;
             }
           //------------- read INT_STATUS -------
         while(1)
         {
         read(fd, &INT_STATUS, sizeof(INT_STATUS));
             if(INT_STATUS==1)
                   printf("GPIO Interrupt Triggered!\n");
             usleep(200000);  
          }
         close(fd);
    
         return 0;
     }
    

    6. 接线试验
    由于这里用的是上升沿触发,需要将GPIO17脚通过一个几k的电阻(3.9k etc.)拉低。


    可以在这里获得完整的源码: https://github.com/midaszhou/openwrt_widora/tree/midas/package/kernel/gpio-int


  • administrators

    Cool::👍



  • 这里有个问题, GPIO mmap在模块卸载的时候释放比较好,而不是文件关闭的时候。


  • administrators

    fork后,也可以通过提交pr申请来更新到widora最新代码



  • 按照楼主的github代码进行编译,加载,运行,每次中断都会有 CALL TRACE 出现,不知道你们的是否会这样?
    我是直接用GPIO14联到GPIO17上,控制GPIO14高低电平,产生中断,不知道是不是这样引起的。谢谢!

    [ 1108.570000] irq event 41: bogus return value 86ba0000
    [ 1108.570000] CPU: 0 PID: 1334 Comm: ash Tainted: G W 3.18.29 #3
    [ 1108.570000] Stack : 00000000 00000000 00000000 00000000 803541f2 0000003e 00000000 00000000
    00000000 80310184 802b1cdc 8030a9e3 00000536 80353420 86b71f58 80310184
    80310000 802b5384 802b5398 80047628 00000006 80024378 00000000 00000000
    802b51e0 86bfdc14 00000000 00000000 00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    ...
    [ 1108.570000] Call Trace:
    [ 1108.570000] [<800141a8>] show_stack+0x50/0x84
    [ 1108.570000] [<8004a9e4>] __report_bad_irq.isra.7+0x44/0xf8
    [ 1108.570000] [<8004907c>] handle_irq_event_percpu+0x154/0x188
    [ 1108.570000] [<800490ec>] handle_irq_event+0x3c/0x60
    [ 1108.570000] [<8004b830>] handle_level_irq+0xcc/0x110
    [ 1108.570000] [<80048900>] generic_handle_irq+0x28/0x44
    [ 1108.570000] [<80162cec>] mediatek_gpio_irq_handler+0xa4/0x114
    [ 1108.570000] [<80048900>] generic_handle_irq+0x28/0x44
    [ 1108.570000] [<80048900>] generic_handle_irq+0x28/0x44
    [ 1108.570000] [<800111b0>] do_IRQ+0x1c/0x2c
    [ 1108.570000] [<80004830>] ret_from_irq+0x0/0x4
    [ 1108.570000] [<80161b20>] gpio_value_store+0x98/0xb8
    [ 1108.570000] [<800ecd80>] kernfs_fop_write+0x11c/0x19c
    [ 1108.570000] [<8009fd5c>] vfs_write+0xb8/0x1bc
    [ 1108.570000] [<800a0344>] SyS_write+0x58/0xc4
    [ 1108.570000] [<80006b5c>] handle_sys+0x11c/0x140
    [ 1108.570000]
    [ 1108.570000] handlers:
    [ 1108.570000] [<86ba0000>] 0x86ba0000
    GPIO Interrupt Triggered!



  • gpio_int_handler 函数给一个返回值就好了。

    static irqreturn_t gpio_int_handler(int irq,void *dev_id,struct pt_regs *regs)
    {
    static int i=0;

    if (i>20)
    {
    printk("-------- midas_GPIO Interrupt triggered! ------\n");
    }

    if(((*GINT_STAT_0)>>GPIO_PIN_NUM)&0x1) //-----confirm the Interrupt
    {
    //printk("GPIO Interrupt confirmed!\n");
    INT_STATUS=1; //----set intterupt token
    }

    *GINT_STAT_0 &=~(0x1<<GPIO_PIN_NUM); //----reset GPIO INT STAT register
    *GINT_REDGE_0 |=(0x1<<GPIO_PIN_NUM); //----re-enable Rising Edge interrupt

    //disable_irq_nosync(GPIO_INT_NUM);
    DISABLE_IRQ_TOKEN=1;
    return IRQ_HANDLED;
    }



  • @puzhicai 确实如此!



  • 你真是好人,给你小红花,么么哒



  • 大神,能弄个硬件I2C驱动吗,谢谢



  • @于大圣 widora-openwrt里面有i2cset和i2cget命令,可以参考 https://github.com/widora/ctest/blob/master/python/i2c-gyro.py



  • 谢谢,虽然看不大懂,我看着像用了什么工具,我想弄个底层驱动,像GPIO一样操作寄存器的