最近想在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