STM32MP157 | 基于 Linux 中断子系统的按键检测

本文详细介绍了STM32MP1中断子系统的处理流程,包括从保存状态、设置处理器模式到保存LR和设置PC等步骤。同时,展示了如何在设备树中添加中断控制器、EXTI和GPIO节点,以及如何根据内核文档编写设备树。接着,讲解了编写按键设备驱动的过程,包括初始化、映射中断号和注册中断处理函数。最后,提到了测试驱动模块的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、中断子系统

1.中断简介

中断是指程序执行过程中,遇到急需处理的事件时,暂时中止CPU上现行程序的运行,转去执行相应的事件处理程序,待处理完成后再返回原程序被中断处或调度其他程序执行的过程,中断是由外部硬件设备产生,又称为外部中断。

2.中断的处理流程

异常处理流程(4大步,3小步):

1.将cpsr保存到spsr中

2.设置cpsr

​ 2.1设置为arm核为svc模型

​ 2.2如果有必要进制相应的中断

​ 2.3设置为对应的异常模式

3.保存LR

4.设置PC到对应的执行的位置

3. 中断子系统执行的流程图

二、 添加设备树的节点

1.开发板按键引脚连接的原理图

在这里插入图片描述

2. 找出控制器的设备树节点

stm32mp151.dtsi

//1.gic控制器的设备树的节点
intc: interrupt-controller@a0021000 {                                                                         
    compatible = "arm,cortex-a7-gic";//厂商和设备名
    #interrupt-cells = <3>; //看到#号修饰子节点引用这个节点成员的个数
    interrupt-controller;//空属性,只起标识作用,具有中断的功能
    reg = <0xa0021000 0x1000>,//寄存器地址
          <0xa0022000 0x2000>;//两个核
};   
//2.exti的设备树节点//gic的子节点
soc {                                                                                                    
    compatible = "simple-bus";
    #address-cells = <1>;//修饰子节点地址的个数
    #size-cells = <1>;//子节点长度的个数
    interrupt-parent = <&intc>; //exit的父节点是intc
    exti: interrupt-controller@5000d000 {                                                                     
        compatible = "st,stm32mp1-exti", "syscon";
        interrupt-controller;
        #interrupt-cells = <2>;
        reg = <0x5000d000 0x400>;
    };
};
//3.gpio控制的设备树节点
pinctrl: pin-controller@50002000 {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "st,stm32mp157-pinctrl";                                                                      
        interrupt-parent = <&exti>;
 
        gpiof: gpio@50007000 {                                                                                
            interrupt-controller;
            //修饰的是interrupts
            #interrupt-cells = <2>;//写二表示第二个是  中断控制器的索引号 第二个2代表触发方式标志位flge,1上升沿 2,下降沿 3,高电平 4,低电平。写3表示第一个参数是中断的的类型,第二个参数是中断的编号,第三个是触发方式
            reg = <0x5000 0x400>;
            clocks = <&rcc GPIOF>;
            st,bank-name = "GPIOF";
            status = "disabled";  //中断控制器没有使能
        };
};
//4.gpiof的使能
&pinctrl {
    gpiof: gpio@50007000 {
		status = "okay";  
    }}

3. 根据内核帮助文档编写自己的设备树

/home/linux/linux-5.10.61/Documentation/devicetree/bindings/i2c/

//在stm32mp175a-fsmp1a.dts中添加如下节点:
myirqs{
     interrupt-parent = <&gpiof>;
   	 interrupts = <9 0>,<7 0>,<8 0>;
};

5. 重新编译设备树

make dtbs

重启开发板

安装驱动

三. 编写按键设备驱动

1.先写个模块

#include <linux/init.h>
#include <linux/module.h>
static int __init mycdev_init(void)
{
    return 0;
}
static void __exit mycdev_exit(void)
{
    return 0;
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

2. 写个Makefile编译一下:

ifeq ($(arch),arm)
KERNELDIR :=/home/linux/linux-5.10.61
CROSS_COMPILE ?=arm-linux-gnueabihf-
else 
KERNELDIR :=/lib/modules/$(shell uname -r)/build
CROSS_COMPILE ?=
endif 

modname ?=
PWD :=$(shell pwd)

CC :=$(CROSS_COMPILE)gcc

all:
	make -C $(KERNELDIR) M=$(PWD) modules
	# $(CC) test.c -o test
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
	# rm test

install:
	cp *.ko ~/nfs/rootfs/
	# cp test ~/nfs/rootfs/

help:
	echo "make arch = arm or x86 modname= dirvers file name"

obj-m:=$(modname).o
linux@ubuntu:~/linu/driver/csdn/key$ make arch=arm modname=key

3. 编写中断子系统流程

添加头文件

#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>

(1)mycdev_init

struct device_node* node;
unsigned int irqno[3];
const char* irqname[] = { "key1", "key2", "key3" };

static int __init mycdev_init(void)
{
    int ret, i;
    // 1.获取节点
    node = of_find_node_by_path("/myirqs");
    if (node == NULL) {
        printk("find node error\n");
        return -EAGAIN;
    }
    // 2.映射软中断号
    for (i = 0; i < ARRAY_SIZE(irqno); i++) {
        irqno[i] = irq_of_parse_and_map(node, i);
        if (!irqno[i]) {
            printk("get irq number error\n");
            return -EAGAIN;
        }

        // 3.注册中断
        ret = request_irq(irqno[i], key_irq_handle,
            IRQF_TRIGGER_FALLING, irqname[i], (void*)i);
        if (ret) {
            printk("request irq error\n");
            return ret;
        }
    }
    return 0;
}

(2)中断处理函数

irqreturn_t key_irq_handle(int irq, void* dev)
{
    int i = (int)dev;
    switch (i) {
    case 0:
        printk("key1 !!!!!!!!!!!!!!!!\n");
        break;
    case 1:
        printk("key2 *****************\n");
        break;
    case 2:
        printk("key3 %%%%%%%%%%%%%%%%%%%%%%\n");
        break;
    }
    return IRQ_HANDLED;
}

(3)mycdev_exit

static void __exit mycdev_exit(void)
{
    int i = 0;
    for (i = 0; i < ARRAY_SIZE(irqno); i++) {
        free_irq(irqno[i], (void *)i);
    }
}

四、测试驱动模块

1.加载驱动,查看驱动,并测试

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值