龙芯2K驱动开发——使用中断触发读取GPIO电平值上传给读取进程


龙芯2K1000平台下编写GPIO驱动满足应用使用

龙芯LS2K GPIO中断配置

ls2k gpio简述
龙芯2K1000有60个GPIO引脚,GPIO 引脚与中断引脚的对应关系如下:

GPIO 引脚 中断引脚 中断号 说明
GPIO0 Gpio_int0 68 专用 GPIO 引脚,与中断引脚一一对应
GPIO1 Gpio_int1 69 专用 GPIO 引脚,与中断引脚一一对应
GPIO2 Gpio_int2 70 专用 GPIO 引脚,与中断引脚一一对应
GPIO3 Gpio_int3 71 专用 GPIO 引脚,与中断引脚一一对应
GPIO[31:04] Gpio_int_lo 66 GPIO4~31 复用中断引脚 Gpio_int_lo
GPIO[63:32] Gpio_int_hi 67 GPIO32~63 复用中断引脚 Gpio_int_hi
  • 备注:共享中断的GPIO只支持电平触发模式。GPIO的一个共享中断号里有两个以上GPIO时,此时只支持高电平触发模式

  • 表 1-2 GPIO中断相关寄存器

寄存器 地址 描述
Intpol_1 0x1fe11470 中断极性控制:1 代表低电平,0 代表高电平
Intedge_1 0x1fe11474 触发方式寄存器(1:脉冲触发;0:电平触发)
Intenset_1 0x1fe11468 设置中断使能寄存器
GPIO0_INTEN 0x1fe10530 63:0中断使能位,每一位对应一GPIO引脚
  • 寄存器Intpol_1、Intedge_1、Intenset_1 第26~31位对应GPIO中断引脚:
    • 第26、27位分别对应:Gpio_int_lo、Gpio_int_hi
    • 第28~31位分别对应:Gpio_int0、Gpio_int1、Gpio_int2、Gpio_int3

Gpio中断软件配置操作

以设置GPIO0中断为例:

  • 第一种模式:下降沿触发中断
1、gpio0设置为输入模式
2、配置gpio0触发类型为下降沿触发
	寄存器Intpol_1第28位置1
	寄存器Intedge_1第28位置1
3、使能GPIO0中断
	寄存器GPIO0_INTEN 第0位置1
  • 第二种模式:上升沿触发中断
1、gpio0设置为输入模式
2、配置gpio0触发类型为上升沿触发
	寄存器Intpol_1第28位置0
	寄存器Intedge_1第28位置1
3、使能GPIO0中断
	寄存器GPIO0_INTEN 第0位置1
  • 第三种模式:低电平触发中断
1、gpio0设置为输入模式
2、配置gpio0触发类型为低电平触发
	寄存器Intpol_1第28位置1
	寄存器Intedge_1第28位置0
3、使能GPIO0中断
	寄存器GPIO0_INTEN 第0位置1
  • 第四种模式:高电平触发中断
1、gpio0设置为输入模式
2、配置gpio0触发类型为高电平触发
	寄存器Intpol_1第28位置0
	寄存器Intedge_1第28位置0
3、使能GPIO0中断
	寄存器GPIO0_INTEN 第0位置1

示例使用

需求

  • 在指定GPIO0上升沿触发中断后读取当前指定的六个GPIO电平值,经过短暂编码转换后,将枚举变量值返还给用户空间应用程序使用。
  • 在触发中断后需要经过10ms的电平稳定时间。
  • 经过电平稳定时间后读取指定的六个GPIO管脚电平值,转换成预先设定的枚举变量。
  • 读取结果成功后唤醒当前等待获取数据的进程任务,将数据传递给用户空间的应用程序使用。

代码具体如下:

  • gpio_drv.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <ls2k.h>


#define GPIO_INT_ENABLE 0x1fe10530
#define INT_POL 0x1fe11470
#define INT_EDGE 0x1fe11474

static int gpio_0 = 55;
static int gpio_1 = 56;
static int gpio_2 = 57;
static int gpio_3 = 58;
static int gpio_4 = 59;
static int gpio_7 = 60;

module_param(gpio_0, int, S_IRUGO);
module_param(gpio_1, int, S_IRUGO);
module_param(gpio_2, int, S_IRUGO);
module_param(gpio_3, int, S_IRUGO);
module_param(gpio_4, int, S_IRUGO);
module_param(gpio_7, int, S_IRUGO);

//定义上报枚举结构体
typedef enum
{
    GPIO_VAL_DA = 1,
    GPIO_VAL_CA,
    GPIO_VAL_CDA,
    GPIO_VAL_BA,
    GPIO_VAL_BDA,
    GPIO_VAL_BCA,
    GPIO_VAL_BCDA,
    GPIO_VAL_EA,
    GPIO_VAL_EDA,
    GPIO_VAL_ECA,
    GPIO_VAL_ECDA,
    GPIO_VAL_EBA,
    GPIO_VAL_EBDA,
    GPIO_VAL_EBCA,
    GPIO_VAL_EBCDA,
    GPIO_VAL_NULL,
    GPIO_VAL_ERR,
    GPIO_VAL_UNDIFINE
} YXGpioSumType_new;

//声明龙芯GPIO硬件数据结构
struct ls2k_gpio_info
{
    int gpio;
    int irq;
    int irq_flags;
    char name[6];
};

struct ls2k_gpio_info ls2k_gpio_irq;

static int ls2k_gpio_info(struct ls2k_gpio_info *dev, int gpio, int flags)
{
    char offset;
    struct ls2k_gpio_info *date = dev;

    date->gpio = gpio;

    if (date->gpio < 4)
    {
        offset = 28 + date->gpio;
        date->irq = 68 + date->gpio;
    }
    else if (date->gpio < 32)
    {
        offset = 26;
        date->irq = 66;
    }
    else
    {
        offset = 27;
        date->irq = 67;
    }

    date->irq_flags = flags;

    sprintf(date->name, "gpio%d", date->gpio);

    //printk("text %s irq:%d\n", date->name, date->irq);

    if (IRQF_TRIGGER_RISING & date->irq_flags)
    {
        ls2k_writel(ls2k_readl(INT_EDGE) | (1 << offset), INT_EDGE); //set edge
        ls2k_writel(ls2k_readl(INT_POL) & ~(1 << offset), INT_POL);  //set falling
    }
    else if (IRQF_TRIGGER_FALLING & date->irq_flags)
    {
        ls2k_writel(ls2k_readl(INT_EDGE) | (1 << offset), INT_EDGE); //set edge
        ls2k_writel(ls2k_readl(INT_POL) | (1 << offset), INT_POL);   //set falling
    }
    else if (IRQF_TRIGGER_HIGH & date->irq_flags)
    {
        ls2k_writel(ls2k_readl(INT_EDGE) & ~(1 << offset), INT_EDGE); //set edge
        ls2k_writel(ls2k_readl(INT_POL) & ~(1 << offset), INT_POL);   //set falling
    }
    else if (IRQF_TRIGGER_LOW & date->irq_flags)
    {
        ls2k_writel(ls2k_readl(INT_EDGE) & ~(1 << offset), INT_EDGE); //set edge
        ls2k_writel(ls2k_readl(INT_POL) | (1 << offset), INT_POL);    //set falling
    }
    else
    {
        return -1;
    }
    ls2k_writel(ls2k_readl(GPIO_INT_ENABLE + date->gpio / 32 * 4) | (1 << date->gpio % 32), GPIO_INT_ENABLE + date->gpio / 32 * 4); //int enable

    return 0;
}

//声明处理信号结构体
struct yxgpio_data
{
    int g_gpio0;
    int g_gpio1;
    int g_gpio2;
    int g_gpio3;
    int g_gpio4;
    int g_gpio7;
};

//声明一个全局化的操作变量
static struct yxgpio_data m_data;


static void m_data_clean(void)
{
    m_data.g_gpio0 = 0;
    m_data.g_gpio1 = 0;
    m_data.g_gpio2 = 0;
    m_data.g_gpio3 = 0;
    m_data.g_gpio4 = 0;
    m_data.g_gpio7 = 0;
}

static void m_data_getvalue(void)
{
    m_data.g_gpio0 = gpio_get_value(gpio_0);
    m_data.g_gpio1 = gpio_get_value(gpio_1);
    m_data.g_gpio2 = gpio_get_value(gpio_2);
    m_data.g_gpio3 = gpio_get_value(gpio_3);
    m_data.g_gpio4 = gpio_get_value(gpio_4);
    m_data.g_gpio7 = gpio_get_value(gpio_7);
}

//声明反馈的内核缓冲区
static YXGpioSumType_new yxgpio_ret;

//指示是否收到GPIO信号 1-有信号 0-没信号
static int issigned;

//定义定时器对象
static struct timer_list yxgpio_timer;

//定义一个等待对了头对象
static wait_queue_head_t rwq;

//定义读取硬件操作接口
static ssize_t yxgpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    //进程进入直接休眠,等待设备信号到来
    wait_event_interruptible(rwq, issigned);
    issigned = 0;

    copy_to_user(buf, &yxgpio_ret, sizeof(yxgpio_ret));
    m_data_clean();
    return count;
}

//计算yxgpio_ret值
static void get_yxgpio_ret(void)
{
    int gpio_value_sum = 0;
    gpio_value_sum = (m_data.g_gpio4 * 8) + (m_data.g_gpio1 * 4) + (m_data.g_gpio2 * 2) + (m_data.g_gpio3 * 1);
    //printk(" %s : get yxgpio sum = %d \n", __func__, gpio_value_sum);
    switch (gpio_value_sum)
    {
        case 1: //DA
            yxgpio_ret = GPIO_VAL_DA;
            break;
        case 2: //CA
            yxgpio_ret = GPIO_VAL_CA;
            break;
        case 3: //CDA
            yxgpio_ret = GPIO_VAL_CDA;
            break;
        case 4: //BA
            yxgpio_ret = GPIO_VAL_BA;
        case 5: //BDA
            yxgpio_ret = GPIO_VAL_BDA;
            break;
        case 6: //BCA
            yxgpio_ret = GPIO_VAL_BCA;
            break;
        case 7: //BCDA
            yxgpio_ret = GPIO_VAL_BCDA;
            break;
        case 8: //EA
            yxgpio_ret = GPIO_VAL_EA;
            break;
        case 9: //EDA
            yxgpio_ret = GPIO_VAL_EDA;
            break;
        case 10: //EDA
            yxgpio_ret = GPIO_VAL_ECA;
            break;
        case 11: //EDA
            yxgpio_ret = GPIO_VAL_ECDA;
            break;
        case 12: //EDA
            yxgpio_ret = GPIO_VAL_EBA;
            break;
        case 13: //EDA
            yxgpio_ret = GPIO_VAL_EBDA;
            break;
        case 14: //EDA
            yxgpio_ret = GPIO_VAL_EBCA;
            break;
        case 15: //EDA
            yxgpio_ret = GPIO_VAL_EBCDA;
            break;
        default:
            yxgpio_ret = GPIO_VAL_UNDIFINE;
    }
}

//定时器超时处理函数
static void yxgpio_timer_function(unsigned long data)
{
    //printk("%s : timer function\n", __func__);
    //获取当前GPIO值
    
    m_data_getvalue();

    //计算得出yxgpio_ret的值
    get_yxgpio_ret();
    //唤醒进程
    issigned = 1;
    wake_up(&rwq);
}

//gpio中断触发函数

static irqreturn_t gpio_handler(int irq, void *dev)
{

    int value = 0;
    struct ls2k_gpio_info *p = (struct ls2k_gpio_info *)dev;
    
    //printk("%s : gpio handler \n", __func__);

    if ((p->gpio) >= 4)
    {
        if (p->irq_flags & IRQF_TRIGGER_RISING)
        {
            value = gpio_get_value(gpio_0);
            if(!value)
                return IRQ_NONE;
            //触发定时器启动
            mod_timer(&yxgpio_timer, jiffies + msecs_to_jiffies(5));
            return IRQ_HANDLED;
        }
        else if (p->irq_flags & IRQF_TRIGGER_LOW)
        {
            return IRQ_NONE;
        }
        else
        {
            return IRQ_NONE;
        }
    }

    //触发定时器启动
    return IRQ_HANDLED;
}

//定义初始化硬件操作接口对象
static struct file_operations yxgpio_fops = {
    .owner = THIS_MODULE,
    .read = yxgpio_read,
};

//定义初始混杂设备对象
static struct miscdevice yxgpio_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "yxgpio",
    .fops = &yxgpio_fops
};

static int __init new_gpio_init(void)
{
    int error, irq_flags;
    //注册混杂设备对象
    misc_register(&yxgpio_misc);
    //printk("init misc_register\n");
    //初始化等待队列
    init_waitqueue_head(&rwq);
    //printk("init waitqueue head.\n");

    //申请GPIO资源
    //gpio request
    gpio_request(gpio_0, "gpio_0");
    gpio_request(gpio_1, "gpio_1");
    gpio_request(gpio_2, "gpio_2");
    gpio_request(gpio_3, "gpio_3");
    gpio_request(gpio_4, "gpio_4");
    gpio_request(gpio_7, "gpio_7");

    //设置GPIO方向
    gpio_direction_input(gpio_0);
    gpio_direction_input(gpio_1);
    gpio_direction_input(gpio_2);
    gpio_direction_input(gpio_3);
    gpio_direction_input(gpio_4);
    gpio_direction_input(gpio_7);

    if (gpio_0 < 4)
    {
        irq_flags = IRQF_TRIGGER_FALLING;
    }
    else
    {
        irq_flags = IRQF_SHARED | IRQF_TRIGGER_RISING;
    }

    error = ls2k_gpio_info(&ls2k_gpio_irq, gpio_0, irq_flags);
    if (error < 0)
    {
        //printk("gpio-irq: failed to ls2k_gpio_info"
        //       " for GPIO %d, error %d\n",
        //       gpio_0, error);
        goto fail1;
    }

    error = request_irq(ls2k_gpio_irq.irq, gpio_handler, irq_flags, ls2k_gpio_irq.name, &ls2k_gpio_irq);
    if (error)
    {
        //printk("gpio-irq: Unable to claim irq %d; error %d\n", ls2k_gpio_irq.irq, error);
        goto fail2;
    }

    //初始化定时器对象
    init_timer(&yxgpio_timer);
    yxgpio_timer.function = yxgpio_timer_function;
    //printk("%s : init module.\n",__func__);
    return 0;

fail2:
    free_irq(ls2k_gpio_irq.irq, &ls2k_gpio_irq);
    ls2k_writel(ls2k_readl(GPIO_INT_ENABLE + ls2k_gpio_irq.gpio / 32 * 4) & ~(1 << ls2k_gpio_irq.gpio % 32), GPIO_INT_ENABLE + ls2k_gpio_irq.gpio / 32 * 4); //int disable
fail1:
    gpio_free(ls2k_gpio_irq.gpio);
    //printk("gpio error: %d \n", __LINE__);
    return error;
}

static void __exit new_gpio_exit(void)
{
    //卸载混杂设备对象
    misc_deregister(&yxgpio_misc);
    //删除定时器
    del_timer(&yxgpio_timer);

    free_irq(ls2k_gpio_irq.irq, &ls2k_gpio_irq);
    ls2k_writel(ls2k_readl(GPIO_INT_ENABLE + ls2k_gpio_irq.gpio / 32 * 4) & ~(1 << ls2k_gpio_irq.gpio % 32), GPIO_INT_ENABLE + ls2k_gpio_irq.gpio / 32 * 4); //int disable
    gpio_free(ls2k_gpio_irq.gpio);
    //释放剩余GPIO
    gpio_free(gpio_1);
    gpio_free(gpio_2);
    gpio_free(gpio_3);
    gpio_free(gpio_4);
    gpio_free(gpio_7);
    
    //printk("%s : exit module.\n",__func__);
}

module_init(new_gpio_init);
module_exit(new_gpio_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("loongson <[email protected]>");
MODULE_DESCRIPTION("ls2k GPIO irq");
MODULE_ALIAS("LS2K");

  • Makefile
obj-m += new_gpio_drv.o

all:
	make ARCH=mips CROSS_COMPILE=mips64el-linux- -C /home/ww/loongson/kernel/linux-3.10/ SUBDIRS=$(PWD) modules

clean:
	make ARCH=mips CROSS_COMPILE=mips64el-linux- -C /home/ww/loongson/kernel/linux-3.10/ SUBDIRS=$(PWD) clean

  • test_demo.c
/*************************************************************************
	> File Name: new_gpio_test.c
	> Author: 
	> Mail: 
	> Created Time: Tue 07 Jan 2020 11:51:27 PM PST
 ************************************************************************/

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

typedef enum
{
    GPIO_VAL_DA = 1,
    GPIO_VAL_CA,
    GPIO_VAL_CDA,
    GPIO_VAL_BA,
    GPIO_VAL_BDA,
    GPIO_VAL_BCA,
    GPIO_VAL_BCDA,
    GPIO_VAL_EA,
    GPIO_VAL_EDA,
    GPIO_VAL_ECA,
    GPIO_VAL_ECDA,
    GPIO_VAL_EBA,
    GPIO_VAL_EBDA,
    GPIO_VAL_EBCA,
    GPIO_VAL_EBCDA,
    GPIO_VAL_NULL,
    GPIO_VAL_ERR,
    GPIO_VAL_UNDIFINE
} YXGpioSumType_new;

int get_current(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (tv.tv_sec * 1000 * 1000) + tv.tv_usec;
}

int main()
{
    int fd;
    int start_time;
    int end_time;

    YXGpioSumType_new recv_type = GPIO_VAL_UNDIFINE;

    fd = open("/dev/yxgpio", O_RDWR);
    if(fd < 0)
    {
        perror("open device fail.");
        return -1;
    }

    while(1)
    {
        start_time = get_current();
        read(fd, &recv_type, sizeof(recv_type));
        switch(recv_type)
        {
        case 1: //DA
            printf("recv_type-->DA\n");
            break;
        case 2: //CA
            printf("recv_type-->CA\n");
            break;
        case 3: //CDA
            printf("recv_type-->CDA\n");
            break;
        case 4: //BA
            printf("recv_type-->BA\n");
        case 5: //BDA
            printf("recv_type-->BDA\n");
            break;
        case 6: //BCA
            printf("recv_type-->BCA\n");
            break;
        case 7: //BCDA
            printf("recv_type-->BDCA\n");
            break;
        case 8: //EA
            printf("recv_type-->EA\n");
            break;
        case 9: //EDA
            printf("recv_type-->EDA\n");
            break;
        case 10: //EDA
            printf("recv_type-->ECA\n");
            break;
        case 11: //EDA
            printf("recv_type-->ECDA\n");
            break;
        case 12: //EDA
            printf("recv_type-->EBA\n");
            break;
        case 13: //EDA
            printf("recv_type-->EBDA\n");
            break;
        case 14: //EDA
            printf("recv_type-->EBCA\n");
            break;
        case 15: //EDA
            printf("recv_type-->EBDCA\n");
            break;
        default:
            printf("recv_type-->no reginize\n");
        }
        end_time = get_current();
        printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
        printf("--->get signal time = [ %d ]\n", end_time - start_time);
        printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
    }

    close(fd);
    return 0;
}


  • 最终执行结果:
    在这里插入图片描述
    在这里插入图片描述
    能够在短时间内正常获取gpio上发送来的信号,不过发现一个问题,就是驱动中虽然设置成上升沿触发中断,但是下降沿的时候依然会触发,这个问题在中断中采用判断解决了,但是一直想不通是因为信号不稳定的原因还是因为龙芯检测触发机制的问题,龙芯边沿触发是不是不管上升还是下降呢,具体还不太清楚。
发布了78 篇原创文章 · 获赞 25 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_37596943/article/details/103873854