一、中断子系统
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);
}
}