xilinx linux下中断驱动

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一ZYNQ中断底层分类详解

1ZYNQ CPU 软件中断SGISoftware generatedinterruptsZYNQ 共有两个 CPU每个 CPU 具备各自的 16 个软件中断(中断号0-15)16–26 reserved :被路由到一个或者两个CPU上通过写ICDSGIR寄存器产生SGI.

2CPU私有外设中断PPIprivate peripheralinterrupts 私有中断是固定的不能修改。这里有 2 个 PL 到 CPU 的快速中断 nFIQ中断号27-31:每个CPU都有一组PPI包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ.

3ZYNQ PS 和 PL 共享中断SPIshared peripheralinterrupts共享中断就是一些端口共用一个中断请求线中断号32-95:由PS和PL上的各种I/O控制器和存储器控制器产生这些中断信号被路由到相应的CPU PL部分有16个共享中断它们的触发方式可以设置

共享中断就是 PL 的中断可以发送给 PS 处理总共16 个 PL 的中断它们可以设置为高电平或者低电平触发。

二中断的软件设置流程

根据中断ID查找GIC中断配置——>初始化GIC中断控制器——>异常初始化——>将中断注册到异常处理——>使能异常——>将中断处理函数与中断异常连接起来——>设置中断优先级触发方式——>GIC中断使能

1中断控制器实例
init.c定义了一个XScuGic类型的结构体变量设计者需要为系统中的每个中断设备分配一个此类型的变量。其它中断相关的API需要指向此变量的指针作为传入参数。

中断初始化函数中又定义了一个指向XScuGic_Config类型的结构体变量的指针这个结构体包含了中断设备的配置信息。两个结构体的原型如下注意XScuGic_Config也是XscuGic的一个成员
typedef struct
{
u16 DeviceId;

u32 CpuBaseAddress;

u32 DistBaseAddress;

XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];// Vector table of interrupt handlers
} XScuGic_Config;
typedef struct
{
XScuGic_Config *Config; /**

u32 IsReady;

u32 UnhandledInterrupts;

} XScuGic;

2中断控制器初始化
中断初始化函数中使用XScuGic_LookupConfig()函数和XScuGic_CfgInitialize()函数完成中断控制器的初始化。其实上面这部分处理和GPIO部分的一样只是处理对象从“GPIO设备”换成了“中断设备”。

XScuGic_LookupConfig()函数的原型接口XScuGic_Config *XScuGic_LookupConfig(u16 DeviceId){}传入参数为设备ID返回的是一个XScuGic_Config类型的指针。如果存在该中断设备则返回该配置信息的列表未找到则返回NULL。

XScuGic_CfgInitialize()函数的原型接口如下。该函数用来初始化传入的XScuGic类型的变量可称作中断控制器实例初始化该结构体中的成员字段。第二个参数便是用于初始化中断控制器实例的配置信息表。第三个参数EffectiveAddr是虚拟内存地址空间中的设备基地址如果没有使用地址转换功能传入Config->BaseAddress即可。
s32 XScuGic_CfgInitialize(XScuGic *InstancePtr,
XScuGic_Config *ConfigPtr, u32 EffectiveAddr) {}3.中断异常处理
中断控制器初始化完成后使用Xil_ExceptionRegisterHandler()和Xil_ExceptionEnable()两个函数设置中断异常处理功能。
Xil_ExceptionRegisterHandler()为异常注册处理函数。处理器运行过程中遇到该异常便会调用设置的处理函数。该函数原型如下第一个为异常的ID号第二个为设置的处理函数第三个是处理函数被调用时传给它的参数。

void Xil_ExceptionRegisterHandler(u32 Exception_id,
                            Xil_ExceptionHandler Handler, void *Data)

Xil_ExceptionEnable()启用IRQ异常没有参数和返回值。异常处理机制并不是中断独有的在xil_exception.h文件中有所有异常情况的ID列表。下面贴出init.c中的代码更进一步理解
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                            (Xil_ExceptionHandler)XScuGic_InterruptHandler, &INTCInst);

XIL_EXCEPTION_ID_INT表示IRQ中断异常。XScuGic_InterruptHandler是库中提供的函数它可以解析那些中断时活跃的和启用的并调用相应的中断处理程序先服务优先级最高的中断。该函数是保证中断系统正常运行的基础其原型如下void XScuGic_InterruptHandler(XScuGic *InstancePtr)
它需要一个指向XscuGic类型的指针参数。在调用Xil_ExceptionRegisterHandler()时通过设置第三个参数便可完成参数传入。

4中断源设置
之后使用XScuGic_Connect()函数将中断源和中断处理程序联系起来当中断发生时会调用设置的中断处理程序。函数原型如下Int_ID是中断源对应的ID号Handler是中断处理程序最后一个void*类型的CallBackRef在中断处理程序被调用时会作为参数传入。
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
Xil_InterruptHandler Handler, void *CallBackRef)

再之后便是用自定义的函数完成中断敏感类型的设置主要是对中断相关寄存器的配置。最后使用XScuGic_Enable()函数启用中断源。函数原型如下Int_Id为中断源对应的ID号。
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)

5中断处理程序
static void SW_intr_Handler(void *param)
{
int sw_id = (int)param;
printf("SW%d int\n\r", sw_id); //根据中断请求源打印相关信息
}

中断处理程序只能传入void*类型的参数因此在内部使用时需要做适当的类型转换。我们通过两个按键中断传入不同的参数便可在一个中断处理程序中识别出到底是哪个中断源发出了中断请求

  三linux内核中断接口函数

linux 内核中的中断框架使用已经相当便捷一般需要做三件事申请中断、实现中断服务函数、使能中断。对应的接口函数如下

1中断申请和释放函数

1中断申请函数request_irq该函数可能会导致睡眠在申请成功后会使能中断函数原型

Int request_irq(unsignedintirq,irq_handler_t handler, unsigned long flags, const char*name,void*dev);

参数说明

irq申请的中断号是中断的唯一标识也是内核找到对应中断服务函数的依据。

handler中断服务函数中断触发后会执行的函数。

name中断名称申请中断成功后在/proc/interrupts 文件中可以找到这个名字。

devflag设置IRQF_SHARED时使用dev来区分不同的设备dev的值会传递给中断服务函数的第二个参数。

返回值0-申请成功-EBUSY-中断已被占用其他值都表示申请失败。

flags中断标志用于设置中断的触发方式和其它特性常用的标识有

/* 无触収 */
IRQF_TRIGGER_NONE
/* 上升沿触収 */
IRQF_TRIGGER_RISING
/* 下降沿触収 */
IRQF_TRIGGER_FALLING
/* 高电平触収 */
IRQF_TRIGGER_HIGH
/* 低电平触収 */
IRQF_TRIGGER_LOW
/* 单次中断 */
IRQF_ONESHOT
/* 作为定时器中断 */
IRQF_TIMER
/* 共享中断多个讴备共享一个中断号时需要此标志 */
IRQF_SHARED

2中断释放函数free_irq如果目标中断不是共享中断那举free_irq函数在释放中断后会禁止中断并删除中断服务凼数原型void free_irq(unsigned int irq, void *dev);

参数说明 irq需要释放的中断。 dev释放的中断如果是共享中断用这个参数来区分具体的中断只有共享中断下所有的dev都被释放时free_irq函数才会禁止中断并删除中断服务函数。

2实现服务申请函数irqreturn_t (*irq_handler_t) (int, void *)

第一个参数int型时中断服务函数对应的中断号。 第二个参数是通用指针需要和request_irq函数的参数 dev 一致。 返回值 irqreturn_t 为return IRQ_RETVAL(IRQ_HANDLED);

3中断使能和禁止函数

1) enable_irq(unsigned int irq),disable_irq(unsigned int irq),disable_irq_nosync(unsigned int irq) enable_irq和disable_irq 分别是中断使能和禁止函数irq是目标中断号。disable_irq会等待目标中断的中断服务函数执行结束才会禁止中断如果想禁止中断则可以使用 disable_irq_nosync()

 (2) local_irq_enable()和local_irq_disable() local_irq_enable()函数用于使能当前处理器的中断系统。 local_irq_disable()函数用于禁止当前处理器的中断系统。

3local_irq_save(flags)和local_irq_restore(flags) local_irq_save(flags)凼数也是用于禁止当前处理器中断但是会把进之前的中断状态保存在输入参数 flags 中。而 local_irq_restore(flags)函数则是把中断恢复到 flags 中记录的状态。

四linux 的底半部机制上半部机制就是顶半部机制下半部机制就是地半部机制

上半部下半部是为了尽量缩短中断服务函数的处理过程而引入的机制上半部就是中断出发后立即执行的中断服务函数而下半部就是对中断服务函数的延时处理。因为中断的优先级较高如果处理内容过多长时间占用处理器会影响其他代码的运行所以上半部要尽量的短。裸机程序里在中断处理函数中标记flag然后到主程序大循环中去轮询判断这个flag再做相应的操作就是一种上半部下半部的思想。linux 中的上半部就是指中断服务函数irq_handler_t。 至于那些任务放在上半部哪些放在下半部没有明确的界限一般我们把对时间敏感、步能被打断的任务放到上半部中其他的都可以考虑放到下半部。下面是一些针对下半部机制的

1软中断

软中断结构体定义在include/linux/interrupt.h文件中具体如下

struct softirq_action
{
    void (*action)(struct softirq_action *);
};

内核在kernel/softirq.c文件中定义了全局的软中断向量表 

static struct softirq_action softirq_vec[NR_SOFTIRQS];

NR_SOFTIRQS 为枚举类型的最大值该枚举类型定义在include/linux/interrupt.h中代表了十个软中断要使用软中断只能向内核定义的软中断向量表注册注册软中断需要使用函数

void (int nr, void (*action)(struct softirq_action *));

 enum
 {
   HI_SOFTIRQ=0,
   TIMER_SOFTIRQ,
   NET_TX_SOFTIRQ,
   NET_RX_SOFTIRQ,
   BLOCK_SOFTIRQ,
   IRQ_POLL_SOFTIRQ,
   TASKLET_SOFTIRQ,
   SCHED_SOFTIRQ,
   HRTIMER_SOFTIRQ,
   RCU_SOFTIRQ,
   NR_SOFTIRQS
};

参数说明

nr小于NR_SOFTIRQS的枚举值。 action对应的软中断服务凼数。 软中断必项在编译时静态注册注册完成就需要使用raise_softirq(unsigned int nr)触发nr 即为需要触发的软中断。 但下半部机制通常不用软中断而是使用tasklets机制。

2tasklets 机制

linux内核在softirq_int函数中初始化软中断,其中HI_SOFTIRQ和TASKLET_SOFTIRQ 是默认打开的。tasklets机制就是在这两个软中断基础上实现的。tasklets的结构体定义在头文linux/interrupt.h

  struct tasklet_struct
  {
       struct tasklet_struct *next;
       unsigned long state;
       atomic_t count;
       void (*func)(unsigned long);
       unsigned long data;
  };

 其中func就是相当于是tasklet的中断服务函数tasklet的定义初始化可以用下面的宏定义完成

DECLARE_TASKLET( name//tasklet 的名字,

                                   func//tasklet 触发时的处理函数,

                                   data//传递给func 的输入参数)

初始化完成后调用tasklet_schedule(struct tasklet_struct *t)激活tesklet。激活后 esklet的服务函数就会在合适的时间运行。用作中断的下半段时就在上半段中调用该函数。如果要用优先级较高的tasklet就用tasklet_hi_schedule(struct tasklet_struct *t)函数激活。tasklet 的下半部机制示例

   /* 定义 taselet */
   struct tasklet_struct example;
   /* tasklet 处理函数 */
   void testtasklet_func(unsigned long data)
   {
     /* tasklet 具体处理内容 */
   }
    /* 中断处理函数 */
   irqreturn_t test_handler(int irq, void *dev_id)
   {
    /* 调度 tasklet */
    tasklet_schedule(&example);
   }
    /* 驱动入口函数 */
   static int __init xxxx_init(void)
   {
    /* 初始化 tasklet */
    tasklet_init(&example, testtasklet_func, data);
    /* 注册中断处理函数 */
    request_irq(irq, test_handler, 0, "name", &dev);
   }

3工作队列

工作队列也是下半部的实现方案。与tasklet相对的工作队列是可阻塞的因此不能在中断上下文中运行。工作队列的队列实现我们可以不用去管要使用工作队列只要定一个工作即可。

工作结构体是work_struct定义在/include/linux/workqueue.h 文件中

struct work_struct 
{
   atomic_long_t data;
   struct list_head entry;
   work_func_t func;
   #ifdef CONFIG_LOCKDEP
      struct lockdep_map lockdep_map;
   #endif
};

 func 即为需要处理的函数。可使用以下宏定义来创建并初始化工作DECLARE_WORK(n, f)

n需要创建并初始化的工作结构体work_struct 的名称。 f工作队列需要处理的函数。

初始化完成后使用bool schedule_work(struct work_struct *work)函数来调用工作队列。

work需要调用的工作。 返回值0 成功1 失败。

工作队列workqueue的下半段机制使用示例

   /* 定义工作(work) */
   struct work_struct example;
   /* work 处理函数 */
   void work_func_t(struct work_struct *work);
   {
    /* work 具体处理内容 */
   }
   /* 中断处理函数 */
    irqreturn_t test_handler(int irq, void *dev_id)
    {
    /* 调度 work */
    schedule_work(&example);
    }
    /* 驱动入口函数 */
    static int __init xxxx_init(void)
    {
      ......
      /* 初始化 work */
     INIT_WORK(&example, work_func_t);
     /* 注册中断处理函数 */
     request_irq(irq, test_handler, 0, "name", &dev);
    }

五设备树的中断

设备树中通用的中断设置方法可参考文档 Documentation/devicetree/bindings/arm/arm,gi c.txt。Xilinx的设备树中断控制器的讴置不 linux 内核的通用设置稍有区别可以查看文档 Docu mentation/devicetree/bindings/arm/xilinx,intc.txt具体了解。

axi_intc_0: interrupt-controller@41800000 
{
  /*"#interrupt-cells"是中断控制器节点的属性用来描述子节点中"interrupts"属性值的数量。
   一般父节点的"#interrupt-cells"值为3则子节点的"interrupts"一个cell的三个32位整数的值
  为<中断域中断号触发方式>如果父节点的该属性是2则是<中断号出发方式>。*/
   #interrupt-cells = <2>;
   
  //属性"interrupt-controller"代表这个节点是一个中断控制器
   compatible = "xlnx,xps-intc-1.00.a";

 //"interrupt-parent"属性表明这个设备属于哪个中断控制器
    如果没有这个属性会自动依附于父节点的"interrupt-parent"。
   interrupt-controller;

//"interrupts"第一个值为0表示SPI中断1表示PPI中断。
  在zynqmp中第一个值如果是 0则中断号等于第二个值加32。
   interrupt-parent = <&ps7_scugic_0>;

//中断号
   interrupts = <0 29 4>;

//暂时不清楚
   reg = <0x41800000 0x10000>;

//"xlnx,kind-of-intr"表示为每个可能的中断指定中断类型1 表示 edge0 表示 level。
   xlnx,kind-of-intr = <0x1>;

//"xlnx,num-intr-inputs"属性指定控制器的特定实现支持的中断数范围是 1~32。
   xlnx,num-intr-inputs = <0x1>;
};

我们需要从设备树中获取设备号信息以向内核注册中断of 函数中有对应的函数

unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

要使用这个函数的话需要我们在对应的设备中设置好"interrupts"属性。

dev 是设备节点
index 是对属性"interrupts"元素的索引因为中断号的位置有可能不同。
返回值就是中断号。

如果是gpio,内核提供了更方便的函数获取中断号int gpio_to_irq(unsigned int gpio);

gpio 为需要申请中断号的 gpio 编号。gpio 为需要申请中断号的 gpio 编号。

zynqmp下gpio是共享的一个中断针对单个io去设置"interrupts"属性比较复杂gpio_to_ irq函数做了很多事下面的gpio中断实验我们就直接使用这个函数。

六中断驱动程序用 petalinux-config -c rootfs 命令选上新增的驱动程序

关于自旋锁保护的对象实际上就是 alinx_char.key_sts 这个值因为这个值在读函数中操作了在中断开启定时器回掉函数中也操作了这两个操作是有可能同时发生的因此需要保护。

#include <linux/module.h>  
#include <linux/kernel.h>
#include <linux/init.h>  
#include <linux/ide.h>  
#include <linux/types.h>  
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
  
/* 设备节点名称 */  
#define DEVICE_NAME       "interrupt_led"
/* 设备号个数 */  
#define DEVID_COUNT       1
/* 驱动个数 */  
#define DRIVE_COUNT       1
/* 主设备号 */
#define MAJOR_U
/* 次设备号 */
#define MINOR_U           0

/* 把驱动代码中会用到的数据打包进设备结构体 */
struct alinx_char_dev 
{
/** 字符设备框架 **/
    dev_t              devid;             //设备号
    struct cdev        cdev;              //字符设备
    struct class       *class;            //类
    struct device      *device;           //设备
    struct device_node *nd;               //设备树的设备节点
/** 并发处理 **/
    spinlock_t         lock;              //自旋锁变量
/** gpio **/    
    int                alinx_key_gpio;    //gpio号
    int                key_sts;           //记录按键状态, 为1时被按下
/** 中断 **/
    unsigned int       irq;               //中断号
/** 定时器 **/
    struct timer_list  timer;             //定时器
};
/* 声明设备结构体 */
static struct alinx_char_dev alinx_char = 
{
    .cdev = 
    {
        .owner = THIS_MODULE,
    },
};

/** 回掉 **/
/* 中断服务函数 */
static irqreturn_t key_handler(int irq, void *dev)
{
 //实现key_handler先是开启了一个 50ms 的 timer然后返回IRQ_RETVAL(IRQ_HANDLED)就行了。
    /* 按键按下或抬起时会进入中断 */
    /* 开启50毫秒的定时器用作防抖动 */
    mod_timer(&alinx_char.timer, jiffies + msecs_to_jiffies(50));
    return IRQ_RETVAL(IRQ_HANDLED);
}

/* 定时器服务函数 */
void timer_function(struct timer_list *timer)
{
    unsigned long flags;
    /* 获取锁 */
    spin_lock_irqsave(&alinx_char.lock, flags);

    /* value用于获取按键值 */
    unsigned char value;
    /* 获取按键值 */
    value = gpio_get_value(alinx_char.alinx_key_gpio);
    if(value == 0)
    {
        /* 按键按下, 状态置1 */
        alinx_char.key_sts = 1;
    }
    else
    {
        /* 按键抬起 */
    }
    
    /* 释放锁 */
    spin_unlock_irqrestore(&alinx_char.lock, flags);
}

/** 系统调用实现 **/
/* open函数实现, 对应到Linux系统调用函数的open函数 */  
static int char_drv_open(struct inode *inode_p, struct file *file_p)  
{  
    printk("gpio_test module open\n");  
    return 0;  
}  
  
  
/* read函数实现, 对应到Linux系统调用函数的write函数 */  
static ssize_t char_drv_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p)  
{  
    unsigned long flags;
    int ret;
    /* 获取锁 */
    spin_lock_irqsave(&alinx_char.lock, flags);
    
    /* keysts用于读取按键状态 */
    /* 返回按键状态值 */
    ret = copy_to_user(buf, &alinx_char.key_sts, sizeof(alinx_char.key_sts));
    /* 清除按键状态 */
    alinx_char.key_sts = 0;
    
    /* 释放锁 */
    spin_unlock_irqrestore(&alinx_char.lock, flags);
    return 0;  
}  
  
/* release函数实现, 对应到Linux系统调用函数的close函数 */  
static int char_drv_release(struct inode *inode_p, struct file *file_p)  
{  
    printk("gpio_test module release\n");
    return 0;  
}  
      
/* file_operations结构体声明, 是上面open、write实现函数与系统调用函数对应的关键 */  
static struct file_operations ax_char_fops = 
{  
    .owner   = THIS_MODULE,  
    .open    = char_drv_open,  
    .read   = char_drv_read,     
    .release = char_drv_release,   
};  
  
/* 模块加载时会调用的函数 */  
static int __init char_drv_init(void)  
{
    /* 用于接受返回值 */
    u32 ret = 0;
    
/** 并发处理 **/
    /* 初始化自旋锁 */
    spin_lock_init(&alinx_char.lock);
    
/** gpio框架 **/   
    /* 获取设备节点 */
    alinx_char.nd = of_find_node_by_path("/alinxkey");
    if(alinx_char.nd == NULL)
    {
        printk("alinx_char node not find\r\n");
        return -EINVAL;
    }
    else
    {
        printk("alinx_char node find\r\n");
    }
    
    /* 获取节点中gpio标号 */
    alinx_char.alinx_key_gpio = of_get_named_gpio(alinx_char.nd, "alinxkey-gpios", 0);
    if(alinx_char.alinx_key_gpio < 0)
    {
        printk("can not get alinxkey-gpios");
        return -EINVAL;
    }
    printk("alinxkey-gpio num = %d\r\n", alinx_char.alinx_key_gpio);
    
    /* 申请gpio标号对应的引脚 */
    ret = gpio_request(alinx_char.alinx_key_gpio, "alinxkey");
    if(ret != 0)
    {
        printk("can not request gpio\r\n");
        return -EINVAL;
    }
    
    /* 把这个io设置为输入 */
    ret = gpio_direction_input(alinx_char.alinx_key_gpio);
    if(ret < 0)
    {
        printk("can not set gpio\r\n");
        return -EINVAL;
    }

/** 中断 **/
    //获取中断号,初始化gpio后使用gpio_to_irq凼数通过gpio端口号来获取中断号。
    alinx_char.irq = gpio_to_irq(alinx_char.alinx_key_gpio);
    /*申请中断通过中断号向内核申请中断。上升沿戒下降沿触发命名为”alinxkey”
       中断服务凼数为key_handler。现在我们只要实现 key_handler凼数就可以了*/
    ret = request_irq(alinx_char.irq,
                      key_handler,
                      IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                      "alinxkey", 
                      NULL);
    if(ret < 0)
    {
        printk("irq %d request failed\r\n", alinx_char.irq);
        return -EFAULT;
    }
    
/** 定时器 **/
    timer_setup(&alinx_char.timer, timer_function, NULL);

/** 字符设备框架 **/    
    /* 注册设备号 */
    alloc_chrdev_region(&alinx_char.devid, MINOR_U, DEVID_COUNT, DEVICE_NAME);
    
    /* 初始化字符设备结构体 */
    cdev_init(&alinx_char.cdev, &ax_char_fops);
    
    /* 注册字符设备 */
    cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT);
    
    /* 创建类 */
    alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME);
    if(IS_ERR(alinx_char.class)) 
    {
        return PTR_ERR(alinx_char.class);
    }
    
    /* 创建设备节点 */
    alinx_char.device = device_create(alinx_char.class, NULL, 
                                      alinx_char.devid, NULL, 
                                      DEVICE_NAME);
    if (IS_ERR(alinx_char.device)) 
    {
        return PTR_ERR(alinx_char.device);
    }
    
    return 0;  
}

/* 卸载模块 */  
static void __exit char_drv_exit(void)  
{  
/** gpio **/
    /* 释放gpio */
    gpio_free(alinx_char.alinx_key_gpio);

/** 中断 **/
    /* 释放中断 */
    free_irq(alinx_char.irq, NULL);

/** 定时器 **/
    /* 删除定时器 */   
    del_timer_sync(&alinx_char.timer);

/** 字符设备框架 **/
    /* 注销字符设备 */
    cdev_del(&alinx_char.cdev);
    
    /* 注销设备号 */
    unregister_chrdev_region(alinx_char.devid, DEVID_COUNT);
    
    /* 删除设备节点 */
    device_destroy(alinx_char.class, alinx_char.devid);
    
    /* 删除类 */
    class_destroy(alinx_char.class);
    
    printk("timer_led_dev_exit_ok\n");  
}  
  
/* 标记加载、卸载函数 */  
module_init(char_drv_init);  
module_exit(char_drv_exit);  
  
/* 驱动描述信息 */  
MODULE_AUTHOR("subomb");  
MODULE_ALIAS("subomb char");  
MODULE_DESCRIPTION("INTERRUPT LED driver");  
MODULE_VERSION("v1.0");  
MODULE_LICENSE("GPL");  

七linux中断测试程序

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
    int fd, fd_l ,ret;
    char *filename, led_value = 0;
    unsigned int key_value;
    if(argc != 2)
    {
        printf("Error Usage\r\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("file %s open failed\r\n", argv[1]);
        return -1;
    }
    while(1)
    {
        ret = read(fd, &key_value, sizeof(key_value));
        if(ret < 0)
        {
            printf("read failed\r\n");
            break;
        }
        if(1 == key_value)
        {
            printf("ps_key1 press\r\n");
            led_value = !led_value;
            fd_l = open("/dev/gpio_leds", O_RDWR);
            if(fd_l < 0)
            {
                printf("file /dev/gpio_leds open failed\r\n");
                break;
            }
            ret = write(fd_l, &led_value, sizeof(led_value));
            if(ret < 0)
            {
                printf("write failed\r\n");
                break;
            }
            ret = close(fd_l);
            if(ret < 0)
            {
                printf("file /dev/gpio_leds close failed\r\n");
                break;
            }
        }
    }
    ret = close(fd);
    if(ret < 0)
    {
        printf("file %s close failed\r\n", argv[1]);
        return -1;
    }
    return 0;
}

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux

“xilinx linux下中断驱动” 的相关文章