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