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

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

文章目录

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

龙芯LS2K GPIO中断配置

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 <xxx@loongson.cn>");
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;
}


  • 最终执行结果:

20200108173753416.png20200108173451250.png

能够在短时间内正常获取gpio上发送来的信号,不过发现一个问题,就是驱动中虽然设置成上升沿触发中断,但是下降沿的时候依然会触发,这个问题在中断中采用判断解决了,但是一直想不通是因为信号不稳定的原因还是因为龙芯检测触发机制的问题,龙芯边沿触发是不是不管上升还是下降呢,具体还不太清楚。


相关文章
|
7月前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
259 0
|
1月前
|
运维 JavaScript jenkins
鸿蒙5.0版开发:分析CppCrash(进程崩溃)
在HarmonyOS 5.0中,CppCrash指C/C++运行时崩溃,常见原因包括空指针、数组越界等。系统提供基于posix信号机制的异常检测能力,生成详细日志辅助定位。本文详解CppCrash分析方法,涵盖异常检测、问题定位思路及案例分析。
53 4
|
1月前
|
运维 监控 JavaScript
鸿蒙next版开发:分析JS Crash(进程崩溃)
在HarmonyOS 5.0中,JS Crash指未处理的JavaScript异常导致应用意外退出。本文详细介绍如何分析JS Crash,包括异常捕获、日志分析和典型案例,帮助开发者定位问题、修复错误,提升应用稳定性。通过DevEco Studio收集日志,结合HiChecker工具,有效解决JS Crash问题。
52 4
|
3月前
|
存储 Linux
为什么通过clear_refs可以使进程触发缺页?
为什么通过clear_refs可以使进程触发缺页?
|
5月前
|
Java 运维
开发与运维命令问题之使用jstack命令查看Java进程的线程栈如何解决
开发与运维命令问题之使用jstack命令查看Java进程的线程栈如何解决
72 2
|
4月前
|
JavaScript 开发工具
Electron 开发过程中主进程的无法看到 console.log 输出怎么办
Electron 开发过程中主进程的无法看到 console.log 输出怎么办
|
4月前
|
机器学习/深度学习 数据可视化 搜索推荐
低代码开发是一种能够加速软件研发进程的高效开发方法
【8月更文挑战第4天】低代码开发是一种能够加速软件研发进程的高效开发方法
63 0
|
5月前
|
SQL 自然语言处理 网络协议
【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)
TCP(Transmission Control Protocol)连接是互联网上最常用的一种面向连接、可靠的、基于字节流的传输层通信协议。建立TCP连接需要经过著名的“三次握手”过程: 1. SYN(同步序列编号):客户端发送一个SYN包给服务器,并进入SYN_SEND状态,等待服务器确认。 2. SYN-ACK:服务器收到SYN包后,回应一个SYN-ACK(SYN+ACKnowledgment)包,告诉客户端其接收到了请求,并同意建立连接,此时服务器进入SYN_RECV状态。 3. ACK(确认字符):客户端收到服务器的SYN-ACK包后,发送一个ACK包给服务器,确认收到了服务器的确
201 1
|
5月前
|
NoSQL Linux Redis
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决