中断子系统练习记录

中断子系统练习记录

前言:
	简单记录下理解不深刻且老是遗忘的一些点
	后面也可以直接在此博客中抄写修改成对应的中断驱动
博客状态:
	持续更新中

1.API

unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
功能:解析并映射成软中断号
参数:
    @dev:节点的指针
 @index:下标 interrupts的下标
返回值:成功返回软中断号,失败返回0
        
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
     const char *name, void *dev)
功能:注册中断
参数:
    @irq:软中断号
 @handler:中断处理函数的函数指针
        irqreturn_t (*irq_handler_t)(int, void *);
  irqreturn_t key_irq_handle(int irqno, void *dev)
        {
            //return IRQ_NONE;
   return IRQ_HANDLED; //中断被正常执行了
        }
 @flags:中断触发方式
         IRQF_TRIGGER_RISING  //上升沿
         IRQF_TRIGGER_FALLING //下降沿
         IRQF_TRIGGER_HIGH //高电平
         IRQF_TRIGGER_LOW //低电平
         IRQF_SHARED         //共享中断
 @name:中断的名字  cat /proc/interrupts
 @dev:向中断处理函数传递的参数
返回值:成功返回0,失败返回错误码
        
const void *free_irq(unsigned int irq, void *dev_id)
功能:释放中断
参数:
    @irq:软中断号
 @dev_id:向中断处理函数传递的参数
返回值:是注册中断时候传递的name

2.概念

首先,一个大前提,数据的传输分三段,cpu、主控板内部对应子系统的控制器、以及设备端;
cpu和控制器我们不用管,linux里面已经很完善了,因此,我们要做的就是调用接口和控制器对接上,让设备的数据和控制器进行流通。

3.调试过程

3.1设备树调通

原理图:
在这里插入图片描述
在这里插入图片描述

结论:

KEY1 连接gpio5_1
KEY1 连接gpio4_14
全部低电平触发

设备树:

/{
......

 psd_key_irqs {
     compatible = "psd,gpio-keys";
     user1 {
         interrupt-parent = <&gpio5>;
         interrupts = <5 0>;
     };
    
     user2 {
         interrupt-parent = <&gpio4>;
         interrupts = <14 0>;
     };
     };
    
    ......
    };
系统运行中设备树查看:
cd /proc/device-tree

镜像文件和dtb文件位置:

在这里插入图片描述
按键中断例程:
.h

#ifndef __KEY_CTRL_H__
#define __KEY_CTRL_H__

#include <linux/timer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/timer.h>

#define KEY_NAME1 "KEY_USER1"
#define KEY_NAME2 "KEY_USER2"
#define CONSUMER_LABEL1 "user1"
#define CONSUMER_LABEL2 "user2"

#define PATH_DTS_KEY_USR1 "/psd_key_irqs/user1"
#define PATH_DTS_KEY_USR2 "/psd_key_irqs/user2"

#define KMD_ERR(str) \
	printk("%s %s line: %d %s \n", __FILE__, __FUNCTION__, __LINE__, str);

typedef struct my_key {
    char *dev_name;
    struct device_node *key_node;
    unsigned int key_irq_no;
    int key_num;
    int key_status;//led开关状态
    int Level_state;//电平状态
    struct timer_list mytimer; // 分配定时器
} key_ctrl_t;

/*key1设备树控制的初始化*/
int key1_ctrl_init(key_ctrl_t * key);
/*key2设备树控制的初始化*/
int key2_ctrl_init(key_ctrl_t * key);
/*led设备树控制的卸载处理函数*/
void key_ctrl_exit(key_ctrl_t * key);

#endif

.c

#include"key_ctrl.h"
#include <linux/timer.h>


void my_timer_callback1(unsigned long para)
{
        printk("key1 down...\n");
}

void my_timer_callback2(unsigned long para)
{
        printk("key2 down...\n");
}

irqreturn_t key1_irq_handle(int irq, void* dev)
{
    key_ctrl_t *key1 = (key_ctrl_t *)dev;
    // 启动定时器
    mod_timer(&key1->mytimer, jiffies + HZ/50);
    return IRQ_HANDLED;
}

irqreturn_t key2_irq_handle(int irq, void* dev)
{
    key_ctrl_t * key2 = (key_ctrl_t *)dev;
    // 启动定时器
    mod_timer(&key2->mytimer, jiffies + HZ/50);
    return IRQ_HANDLED;
}

int key1_ctrl_init(key_ctrl_t * key)
{
    int ret;
    key->key_node = of_find_node_by_path(PATH_DTS_KEY_USR1);
    if(IS_ERR(key->key_node))
    {
        KMD_ERR("of_find_node_by_path ERR");
        ret = -ENODATA;
        goto exit;
    }
    // 2.解析得到软中断号
    key->key_irq_no = irq_of_parse_and_map(key->key_node, 0);
    if (key->key_irq_no == 0) {
        printk("irq_of_parse_and_map error\n");
        ret = -EAGAIN;
        goto exit_node;
        // 资源暂时不可用
    }

    init_timer(&key->mytimer);
    key->mytimer.expires = jiffies + HZ/50;
    // timer_setup_on_stack(&key->mytimer, my_timer_callback1, 0);
    key->mytimer.function = my_timer_callback1;  // 设置定时器到期时调用的回调函数
    add_timer(&key->mytimer);              //将定时器加入到系统定时器链表中

    ret = request_irq(key->key_irq_no, key1_irq_handle,
        IRQF_TRIGGER_LOW, KEY_NAME1, key);
    if (ret) {
        printk("request_irq key1 error\n");
        goto exit_irq;
    }
    return 0;
exit_irq:
    free_irq(key->key_irq_no, NULL);
exit_node:
    of_node_put(key->key_node);
exit:
    return ret;  // 出错返回
}

int key2_ctrl_init(key_ctrl_t * key)
{
    int ret;
    key->key_node = of_find_node_by_path(PATH_DTS_KEY_USR2);
    if(IS_ERR(key->key_node))
    {
        KMD_ERR("of_find_node_by_path ERR");
        ret = -ENODATA;
        goto exit;
    }

    // 2.解析得到软中断号
    key->key_irq_no = irq_of_parse_and_map(key->key_node, 0);
    if (key->key_irq_no == 0) {
        printk("irq_of_parse_and_map error\n");
        of_node_put(key->key_node); // 清理已获取的节点
        ret = -EAGAIN;
        goto exit_node;
        // 资源暂时不可用
    }

    init_timer(&key->mytimer);        
    key->mytimer.expires = jiffies + HZ/50;
    // timer_setup(&key->mytimer, my_timer_callback2, 0);
    key->mytimer.function = my_timer_callback2;  // 设置定时器到期时调用的回调函数
    add_timer(&key->mytimer);                //将定时器加入到系统定时器链表中

    ret = request_irq(key->key_irq_no, key2_irq_handle,
        IRQF_TRIGGER_LOW, KEY_NAME2, key);
    if (ret) {
        printk("request_irq key2 error\n");
        goto exit_irq;
    }
    return 0;
exit_irq:
    free_irq(key->key_irq_no, NULL);
exit_node:
    of_node_put(key->key_node);
exit:
    return ret;  // 出错返回
}

void key_ctrl_exit(key_ctrl_t * key)
{
    free_irq(key->key_irq_no,key);
    if (timer_pending(&key->mytimer)) {
        del_timer_sync(&key->mytimer);
    }

}

MODULE_DESCRIPTION("key_ctrl_driver");
MODULE_LICENSE("GPL");

涉及到的定时器内容

4.其他中断示例模块

sr501(人体红外中断传感器)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值