精华内容
下载资源
问答
  • 在处理器中,中断是一个过程。即CPU在正常执行程序过程中,遇到外部或内部紧急事件需要处理,暂时中断(中止)当前程序执行,而转去完成事件服务程序,待事件完毕后,再返回到暂停处(断点)继续执行原来程序。...

    中断

    中断

    1) 硬中断
    在处理器中,中断是一个过程。即CPU在正常执行程序的过程中,遇到外部或内部的紧急事件需要处理,暂时中断(中止)当前程序的执行,而转去完成事件服务程序,待事件完毕后,再返回到暂停处(断点)继续执行原来的程序。事件服务程序又称中断处理程序或中断服务程序。严格意义上来说,上面的描述是针对硬件中断而言的。
    2) 软中断
    用软件方法引起的中断,即事先在程序中安排特殊的指令,CPU执行到该类指令的时候,会跳转去执行相应的一段预先安排好的程序,待程序执行完,再返回原来程序处。这种通过软件方法实现的中断叫软中断。

    3) 软、硬中断和信号的区别

    • 硬中断的出现过程是不可预测的,是随机的;而软中断是预先安排好的指令中断方式。
    • “硬中断是外部设备对CPU的中断”,“软中断通常是硬中断服务程序对内核的中断”,“信号则是由内核(或其他进程)对某个进程的中断”。

    中断源

    把引起中断的原因,或者能够发出中断请求信号的来源统称为中断源。

    中断/异常优先级

    ARM处理器五种模式对应的7种异常源优先级顺序(从高到低):

    1 复位异常(reset)
    2 数据异常(Data Abord)
    3 快速中断异常(FIQ)
    4 外部中断异常(IRQ)
    5 预取异常(prefetch Abord)
    6 软中断异常(SWI)
    7 未定义指令异常(Undefined)

    异常和中断的区别

    异常主要是从处理器被动接受异常的角度来考虑;中断带有主动请求处理器处理中断的意味。
    共同点:都是打断当前程序执行,进入特定的程序处理事件的一种机制。

    中断的状态

    • 挂起态:中断已发生,但是中断没有被处理执行
    • 激活态:中断发生,正在执行对应的中断处理函数,但是还没有执行结束
    • 未激活态:中断没有发生。

    ARM-7种异常类型

    1) 复位异常(reset)
    两种情况引起异常:系统上电/系统复位
    复位异常中断处理程序完成的功能:

    • 设置异常向量表
    • 初始化栈和寄存器
    • 初始化MMU,Cache
    • 初始化关键的I/O设备
    • 使能中断
    • 切换到SVC模式
    • 初始化C程序中的变量,跳转到相应程序的执行

    2)数据异常(Data Abord)
    存储器访问指令load/store访问外部数据时,出现地址不存在或不允许访问等时产生:取数据中止异常
    3)快速中断异常(FIQ)
    4)外部中断异常(IRQ)
    5)预取异常(prefetch Abord)
    取一条被标记无效的指令时:取指令中止异常
    6)软中断异常(SWI)
    软中断发生,系统进入SVC模式,跳转到处理程序
    7)未定义指令异常(Undefined)
    :未定义指令的异常,指令取出后通过译码器后未定义指令导致译码出错
    在这里插入图片描述

    ARM-异常中断的响应流程(四大步三小步)

    注意:

    • User和System模式是不可通过异常进入的两种模式,即要进入这两种模式,必须要编程设置CPSR的模式状态。
    • ARM处理器中cortex-A不支持中断嵌套,因为ARM处理器的处理中断处理速度较快;
    • cortex-M系列支持中断嵌套,eg:stm32。

    ARM异常处理:处理器对特定的异常事件进行的处理流程(CPU指导硬件自动完成:四大步三小步)。

    一、保存现场(四大步)
    1)保存CPSR到SPSR_mode
    2)适当设置 CPSR 对应功能位(三小步):

    • 1、切换处理器进入ARM状态:T[5]
    • 2、根据需要,禁止中断位:F[6] / I[7]
    • 3、根据异常切换到对应的异常模式:M[4:0]

    3)保存返回地址:把当前 PC 保存到 lr_mode
    4)设置PC = 存放跳转到对应的异常向量表的固定首地址。
    在这里插入图片描述

    二、恢复现场(必须手动恢复)
    1)恢复 SPSR_mode 到 CPSR
    2)恢复 lr_mode 到 PC
    3)恢复普通寄存器:R0-R7,操作sp将栈中将数据恢复。

    软中断 汇编测试代码

    .text
    .globl _start
    
    _start:
    	b reset
    	b undefined_instruction
    	b software_interrupt
    	b prefetch_abord
    	b data_abord
    	b reserved
    	b irq
    	b fiq
    	
    reset:
    	@初始化SVC下的栈内存空间
    	ldr sp, =0x40000100
    	
    	@切换到User模式下0xD3:SVC;0xD0:User
    	msr CPSR, #0xD0
    	
    	@初始化User下的栈内存空间
    	ldr sp, =0x40000200
    	
    	@执行用户代码
    	mov r0, #0x1
    	mov r1, #0x2
    	
    	@swi软中断:swi <软中断号,取值范围:0-(2^24-1)>
    	swi 2
    	
    	@执行用户代码:r2 = 1+2 = 3
    	add r2, r0,r1
    	
    undefined_instruction:
    	
    software_interrupt:
    	@保存现场
    	stmfd sp!,{r0-r1, lr}
    	
    	@执行用户代码
    	mov r0, #0x5
    	mov r1, #0x6
    	add r3, r0,r1
    	
    	@恢复现场:{r0-r1,pc}将lr恢复给pc,"^"是表示将SPSR恢复到CPSR
    	ldmfd sp!, {r0-r1,pc}^
    	
    prefetch_abord:
    	
    data_abord:
    	
    reserved:
    	
    irq:
    	
    fiq:
    	
    loop:
    	b loop
    	
    .end
    	
    
    展开全文
  • 中断与异常

    2020-10-06 16:43:20
    中断向量 :中断编号,每个中断源都被分配一8位无符号整数作为类型码 异常又叫同步中断,是当指令执行时由cpu控制单元产生 中断又叫异步中断,是由其他硬件设备依照cpu时钟信号随机产生 3、与中断相关...

    1、 中断控制的主要优点:
    CPU只有在I/O需要服务时才响应

    2、什么是外部中断、内部中断、中断向量、异常?
    外部中断:外部设备所发出的I/O请求
    内部中断:也称之为“异常”,是为解决机器运行时所出现的某些随机事件及编程方便而出现的
    中断向量 :中断源的编号,每个中断源都被分配一个8位无符号整数作为类型码
    异常又叫同步中断,是当指令执行时由cpu控制单元产生的
    中断又叫异步中断,是由其他硬件设备依照cpu时钟信号随机产生的

    3、与中断相关的汇编指令及其作用
    • 调用过程指令CALL :
    – CALL 过程名
    – 说明:在取出CALL指令之后及执行CALL指令之前,使指令指针寄存器EIP指向紧接CALL指令的下一条指令。CALL指令先将EIP值压入栈内,再进行控制转移。当遇到RET指令时,栈内信息可使控制权直接回到CALL的下一条指令
    • 调用中断过程的指令INT
    – INT 中断向量
    – 说明:EFLAG、CS及EIP寄存器被压入栈内。控制权被转移到由中断向量指定的中断处理程序。在中断处理程序结束时,IRET指令又把控制权送回到刚才执行被中断的地方。

    4、 IDTR寄存器的作用,中断描述符表的初始化过程
    作用:CPU中增设了一个中断描述符表寄存器IDTR,用来存放中断描述符表在内存的起始地址。
    • 初始化过程:初始化可编程控制器8259A;将中断描述符表的起始地址装入IDTR寄存器,并初始化表中的每一项,
     中断门或陷阱门DPL=0,CPL=3,通用保护异常
     中断门或陷阱门DPL=3,CPL=3,允许进入内核态
    • 当计算机运行在实模式时,中断描述符表被初始化,并由BIOS使用 。
    • 真正进入了Linux内核,中断描述符表就被移到内存的另一个区域,并为进入保护模式进行预初始化
    • 用汇编指令LIDT对中断向量表寄存器IDTR进行初始化,即把IDTR置为0。把中断描述符表IDT的起始地址装入IDTR
     用setup_idt()函数填充中断描述表中的256个表项。在对这个表进行填充时,使用了一个空的中断处理程序。因为现在处于初始化阶段,还没有任何中断处理程序,因此,用这个空的中断处理程序填充每个表项。
     在对中断描述符表进行预初始化后, 内核将在启用分页功能后对IDT进行第二遍初始化,也就是说,用实际的陷阱和中断处理程序替换这个空的处理程序。一旦这个过程完成,对于每个异常,IDT都由一个专门的陷阱门或系统门,而对每个外部中断,IDT都包含专门的中断门。

    5、中断执行的过程
    • 中断和异常的硬件处理 :
    – 从硬件的角度看CPU如何处理中断和异常
    • 中断请求队列的建立:
    – 方便外设共享中断线
    • 中断处理程序的执行
    • 从中断返回:
    – 调用恢复中断现场的宏RESTORE_ALL,彻底从中断返回

    6、中断和异常处理时涉及的堆栈切换,如何判断是否要切换(不出大题)

    在这里插入图片描述
    7、什么是中断服务例程、中断处理程序
    • 中断服务例程(Interrupt Service Routine ):每个中断请求都有自己单独的中断服务例程
    • 中断处理程序:共享同一条中断线的所有中断请求有一个总的中断处理程序
    • 在Linux中,15条中断线对应15个中断处理程序

    8、进程上下文和中断上下文的概念
    • 进程上下文
    • 进程上文:其是指进程由用户态切换到内核态是需要保存用户态时cpu寄存器中的值,进程状态以及堆栈上的内容,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行
    • 进程下文:其是指切换到内核态后执行的程序,即进程运行在内核空间的部分
    • 中断上下文
    • 中断上文:硬件通过中断触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。中断上文可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被中断的进程环境。)
    • 中断下文:执行在内核空间的中断服务程序。
    进程上下文和中断上下文比较:
    • 进程上下文是一种内核所处的操作模式,此时内核代表进程执行
    – 执行系统调用
    – 运行内核线程
    – 进程是以进程上下文的形式连接到内核中的,可以睡眠,也可以调用调度程序
    • 中断上下文和进程上下文没有关系
    – 没有后备进程,所以不可以睡眠
    – 如果一个函数会睡眠,就不能在中断处理程序中使用
    中断上下文代码的注意事项:
    • 运行于进程上下文的内核代码是可抢占的,但中断上下文则会一直运行至结束,不会被抢占。所以中断处理程序代码要受到一些限制,在中断代码中不能出现实现下面功能的代码:
    • (1)睡眠或者放弃CPU。
    因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时内核无法调度别的进程来执行,系统就会死掉。牢记:中断服务子程序一定不能睡眠(或者阻塞)
    • (2)尝试获得信号量
    如果获得不到信号量,代码就会睡眠,导致(1)中的结果。
    • (3)执行耗时的任务
    中断处理应该尽可能快,因为如果一个处理程序是IRQF_DISABLED类型,他执行的时候会禁止所有本地中断线,而内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。中断处理程序的任务尽可能放在中断下半部执行。
    • (4)访问用户空间的虚拟地址
    因为中断运行在内核空间。

    9、为什么要将中断分为上半部与下半部?
    上半部:完成尽可能少的比较紧急的功能,它往往只是简单的读取寄存器中的中断状态并清除中断标志后就进行“登记中断”(也就是将底半部处理程序挂在到设备的底半部执行队列中的工作)
    特点:响应速度快
    下半部:中断处理的大部分工作都在底半部,它几乎做中断处理程序的所有事情。
    特点:处理相对来说不是非常紧急的事件

    10、什么是小任务、什么是工作队列?它们有何区别,在使用时如何选择?
    • 小任务是指对要推迟执行的函数进行组织的一种方式。其数据结构为tasklet_struct,每个结构代表一个独立的小任务。
    • 小任务既可以静态地创建,也可以动态地创建
    • 工作队列是另外一种将工作推后执行的形式。
    • 工作队列可以把工作推后,交由一个内核线程执行,即这个下半部分可以在进程上下文执行。
    • 最重要的是工作队列允许被重新调度甚至睡眠。
    • 如果推后执行的任务需要睡眠,就选择工作队列。
    若推后执行的任务不需睡眠,就选择小任务。
    • 若需要用一个可以重新调度的实体来执行下半部的处理,也应使用工作队列。它是唯一能在进程上下文运行下半部实现的机制,也只有它可以睡眠。

    展开全文
  • 盛科技术人员说法是该芯片支持GPIO管脚中断和PCIE MSI中断,使用过程中二选一即可。目前PCIE MSI中断已经解决,需要调试GPIO管脚中断方式,ZYNQ连接示意图如下。如上图所示,根线之间连入一concat,再加上PCIE...

    1、  背景介绍

    最近项目中使用了盛科的交换芯片8086,该交换芯片除了使用PCIE连接到zynq外,还提供了四根GPIO引脚连入zynq。盛科技术人员的说法是该芯片支持GPIO管脚中断和PCIE MSI中断,使用过程中二选一即可。目前PCIE MSI中断已经解决,需要调试GPIO管脚中断方式,ZYNQ连接示意图如下。

    如上图所示,四根线之间连入一个concat,再加上PCIE的引脚,组成一个向量连入zynq的IRQ管脚。Zynq中启用了PL-PS的中断,分配的中断号为61-65.

     

    2、  UIO机制引入

    通常来说,zynq上挂接的中断都需要与一个控制器或IP核相对应,比如i2c,网络等,可以在devicetree中看到中断号,如下图

    该中断号与UG585中中断描述的章节相一致,下表中的IRQ ID为对应设备的中断号+32的值(0x19+32=57,正好是i2c0的IRQ ID)

    对于这种四根线直接接入的,devicetree中没有对应的设备,导致在操作系统中看不到中断。幸运的是Linux内核中提供了UIO机制,详细介绍见:https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html

    对我而言,UIO就是处理没有具体设备只有引脚的一种机制。

     

    3、  devicetree设置

    利用UIO,可以在devicetree中为四根GPIO线设置对应的设备,如下图所示。

    四根线对应的中断号为0x1e-0x21,正好是62-65号中断。同时,需要修改devicetree启动项。

    让操作系统在加载过程中执行uio驱动程序。

     devicetree.dts全部代码如下:

    /dts-v1/;
    
    / {
    	#address-cells = <0x1>;
    	#size-cells = <0x1>;
    	compatible = "xlnx,zynq-7000";
    
    	cpus {
    		#address-cells = <0x1>;
    		#size-cells = <0x0>;
    
    		cpu@0 {
    			compatible = "arm,cortex-a9";
    			device_type = "cpu";
    			reg = <0x0>;
    			clocks = <0x1 0x3>;
    			clock-latency = <0x3e8>;
    			cpu0-supply = <0x2>;
    			operating-points = <0xa4cb8 0xf4240 0x5265c 0xf4240>;
    		};
    
    		cpu@1 {
    			compatible = "arm,cortex-a9";
    			device_type = "cpu";
    			reg = <0x1>;
    			clocks = <0x1 0x3>;
    		};
    	};
    
    	fpga-full {
    		compatible = "fpga-region";
    		fpga-mgr = <0x3>;
    		#address-cells = <0x1>;
    		#size-cells = <0x1>;
    		ranges;
    	};
    
    	pmu@f8891000 {
    		compatible = "arm,cortex-a9-pmu";
    		interrupts = <0x0 0x5 0x4 0x0 0x6 0x4>;
    		interrupt-parent = <0x4>;
    		reg = <0xf8891000 0x1000 0xf8893000 0x1000>;
    	};
    
    	fixedregulator {
    		compatible = "regulator-fixed";
    		regulator-name = "VCCPINT";
    		regulator-min-microvolt = <0xf4240>;
    		regulator-max-microvolt = <0xf4240>;
    		regulator-boot-on;
    		regulator-always-on;
    		linux,phandle = <0x2>;
    		phandle = <0x2>;
    	};
    
    	amba {
    		u-boot,dm-pre-reloc;
    		compatible = "simple-bus";
    		#address-cells = <0x1>;
    		#size-cells = <0x1>;
    		interrupt-parent = <0x4>;
    		ranges;
    
    		adc@f8007100 {
    			compatible = "xlnx,zynq-xadc-1.00.a";
    			reg = <0xf8007100 0x20>;
    			interrupts = <0x0 0x7 0x4>;
    			interrupt-parent = <0x4>;
    			clocks = <0x1 0xc>;
    		};
    
    		can@e0008000 {
    			compatible = "xlnx,zynq-can-1.0";
    			status = "disabled";
    			clocks = <0x1 0x13 0x1 0x24>;
    			clock-names = "can_clk", "pclk";
    			reg = <0xe0008000 0x1000>;
    			interrupts = <0x0 0x1c 0x4>;
    			interrupt-parent = <0x4>;
    			tx-fifo-depth = <0x40>;
    			rx-fifo-depth = <0x40>;
    		};
    
    		can@e0009000 {
    			compatible = "xlnx,zynq-can-1.0";
    			status = "disabled";
    			clocks = <0x1 0x14 0x1 0x25>;
    			clock-names = "can_clk", "pclk";
    			reg = <0xe0009000 0x1000>;
    			interrupts = <0x0 0x33 0x4>;
    			interrupt-parent = <0x4>;
    			tx-fifo-depth = <0x40>;
    			rx-fifo-depth = <0x40>;
    		};
    
    		gpio@e000a000 {
    			compatible = "xlnx,zynq-gpio-1.0";
    			#gpio-cells = <0x2>;
    			clocks = <0x1 0x2a>;
    			gpio-controller;
    			interrupt-controller;
    			#interrupt-cells = <0x2>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x14 0x4>;
    			reg = <0xe000a000 0x1000>;
    		};
    
    		i2c@e0004000 {
    			compatible = "cdns,i2c-r1p10";
    			status = "okay";
    			clocks = <0x1 0x26>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x19 0x4>;
    			reg = <0xe0004000 0x1000>;
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    			clock-frequency = <0x61a80>;
    		};
    
    		i2c@e0005000 {
    			compatible = "cdns,i2c-r1p10";
    			status = "okay";
    			clocks = <0x1 0x27>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x30 0x4>;
    			reg = <0xe0005000 0x1000>;
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    			clock-frequency = <0x61a80>;
    		};
    
    		interrupt-controller@f8f01000 {
    			compatible = "arm,cortex-a9-gic";
    			#interrupt-cells = <0x3>;
    			interrupt-controller;
    			reg = <0xf8f01000 0x1000 0xf8f00100 0x100>;
    			num_cpus = <0x2>;
    			num_interrupts = <0x60>;
    			linux,phandle = <0x4>;
    			phandle = <0x4>;
    		};
    
    		cache-controller@f8f02000 {
    			compatible = "arm,pl310-cache";
    			reg = <0xf8f02000 0x1000>;
    			interrupts = <0x0 0x2 0x4>;
    			arm,data-latency = <0x3 0x2 0x2>;
    			arm,tag-latency = <0x2 0x2 0x2>;
    			cache-unified;
    			cache-level = <0x2>;
    		};
    
    		memory-controller@f8006000 {
    			compatible = "xlnx,zynq-ddrc-a05";
    			reg = <0xf8006000 0x1000>;
    		};
    
    		ocmc@f800c000 {
    			compatible = "xlnx,zynq-ocmc-1.0";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x3 0x4>;
    			reg = <0xf800c000 0x1000>;
    		};
    
    		serial@e0000000 {
    			compatible = "xlnx,xuartps", "cdns,uart-r1p8";
    			status = "okay";
    			clocks = <0x1 0x17 0x1 0x28>;
    			clock-names = "uart_clk", "pclk";
    			reg = <0xe0000000 0x1000>;
    			interrupts = <0x0 0x1b 0x4>;
    			device_type = "serial";
    			port-number = <0x0>;
    		};
    
    		serial@e0001000 {
    			compatible = "xlnx,xuartps", "cdns,uart-r1p8";
    			status = "disabled";
    			clocks = <0x1 0x18 0x1 0x29>;
    			clock-names = "uart_clk", "pclk";
    			reg = <0xe0001000 0x1000>;
    			interrupts = <0x0 0x32 0x4>;
    		};
    
    		spi@e0006000 {
    			compatible = "xlnx,zynq-spi-r1p6";
    			reg = <0xe0006000 0x1000>;
    			status = "okay";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x1a 0x4>;
    			clocks = <0x1 0x19 0x1 0x22>;
    			clock-names = "ref_clk", "pclk";
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    			is-decoded-cs = <0x0>;
    			num-cs = <0x3>;
    		};
    
    		spi@e0007000 {
    			compatible = "xlnx,zynq-spi-r1p6";
    			reg = <0xe0007000 0x1000>;
    			status = "disabled";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x31 0x4>;
    			clocks = <0x1 0x1a 0x1 0x23>;
    			clock-names = "ref_clk", "pclk";
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    		};
    
    		spi@e000d000 {
    			clock-names = "ref_clk", "pclk";
    			clocks = <0x1 0xa 0x1 0x2b>;
    			compatible = "xlnx,zynq-qspi-1.0";
    			status = "okay";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x13 0x4>;
    			reg = <0xe000d000 0x1000>;
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    			is-dual = <0x0>;
    			num-cs = <0x1>;
    		};
    
    		memory-controller@e000e000 {
    			#address-cells = <0x1>;
    			#size-cells = <0x1>;
    			status = "disabled";
    			clock-names = "memclk", "aclk";
    			clocks = <0x1 0xb 0x1 0x2c>;
    			compatible = "arm,pl353-smc-r2p1";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x12 0x4>;
    			ranges;
    			reg = <0xe000e000 0x1000>;
    
    			flash@e1000000 {
    				status = "disabled";
    				compatible = "arm,pl353-nand-r2p1";
    				reg = <0xe1000000 0x1000000>;
    				#address-cells = <0x1>;
    				#size-cells = <0x1>;
    			};
    
    			flash@e2000000 {
    				status = "disabled";
    				compatible = "cfi-flash";
    				reg = <0xe2000000 0x2000000>;
    				#address-cells = <0x1>;
    				#size-cells = <0x1>;
    			};
    		};
    
    		ethernet@e000b000 {
    			compatible = "xlnx,ps7-ethernet-1.00.a";
    			reg = <0xe000b000 0x1000>;
    			status = "okay";
    			interrupts = <0x0 0x16 0x4>;
    			clocks = <0x1 0xd 0x1 0x1e>;
    			clock-names = "ref_clk", "aper_clk";
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    			enet-reset = <0x4 0x2f 0x0>;
    			local-mac-address = [00 0a 35 00 00 00];
                                     phy-mode = "rgmii";
    			phy-handle = <0x7>;
    			xlnx,eth-mode = <0x1>;
    			xlnx,has-mdio = <0x1>;
    			xlnx,ptp-enet-clock = <0x69f6bcb>;
    
    			mdio {
    				#address-cells = <0x1>;
    				#size-cells = <0x0>;
    
    				phy@0 {
    					compatible = "marvell,88e1111";
    					device_type = "ethernet-phy";
    					reg = <0x0>;
    					linux,phandle = <0x7>;
    					phandle = <0x7>;
    				};
    			};
    		};
    
    		ethernet@e000c000 {
    			compatible = "cdns,zynq-gem", "cdns,gem";
    			reg = <0xe000c000 0x1000>;
    			status = "disabled";
    			interrupts = <0x0 0x2d 0x4>;
    			clocks = <0x1 0x1f 0x1 0x1f 0x1 0xe>;
    			clock-names = "pclk", "hclk", "tx_clk";
    			#address-cells = <0x1>;
    			#size-cells = <0x0>;
    		};
    
    		sdhci@e0100000 {
    			compatible = "arasan,sdhci-8.9a";
    			status = "disabled";
    			clock-names = "clk_xin", "clk_ahb";
    			clocks = <0x1 0x15 0x1 0x20>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x18 0x4>;
    			reg = <0xe0100000 0x1000>;
    		};
    
    		sdhci@e0101000 {
    			compatible = "arasan,sdhci-8.9a";
    			status = "disabled";
    			clock-names = "clk_xin", "clk_ahb";
    			clocks = <0x1 0x16 0x1 0x21>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x2f 0x4>;
    			reg = <0xe0101000 0x1000>;
    		};
    
    		slcr@f8000000 {
    			#address-cells = <0x1>;
    			#size-cells = <0x1>;
    			compatible = "xlnx,zynq-slcr", "syscon", "simple-mfd";
    			reg = <0xf8000000 0x1000>;
    			ranges;
    			linux,phandle = <0x5>;
    			phandle = <0x5>;
    
    			clkc@100 {
    				#clock-cells = <0x1>;
    				compatible = "xlnx,ps7-clkc";
    				fclk-enable = <0x1>;
    				clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci", "lqspi", "smc", "pcap", "gem0", "gem1", "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1", "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1", "dma", "usb0_aper", "usb1_aper", "gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper", "spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper", "swdt", "dbg_trc", "dbg_apb";
    				reg = <0x100 0x100>;
    				ps-clk-frequency = <0x2faf080>;
    				linux,phandle = <0x1>;
    				phandle = <0x1>;
    			};
    
    			rstc@200 {
    				compatible = "xlnx,zynq-reset";
    				reg = <0x200 0x48>;
    				#reset-cells = <0x1>;
    				syscon = <0x5>;
    			};
    
    			pinctrl@700 {
    				compatible = "xlnx,pinctrl-zynq";
    				reg = <0x700 0x200>;
    				syscon = <0x5>;
    			};
    		};
    
    		dmac@f8003000 {
    			compatible = "arm,pl330", "arm,primecell";
    			reg = <0xf8003000 0x1000>;
    			interrupt-parent = <0x4>;
    			interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6", "dma7";
    			interrupts = <0x0 0xd 0x4 0x0 0xe 0x4 0x0 0xf 0x4 0x0 0x10 0x4 0x0 0x11 0x4 0x0 0x28 0x4 0x0 0x29 0x4 0x0 0x2a 0x4 0x0 0x2b 0x4>;
    			#dma-cells = <0x1>;
    			#dma-channels = <0x8>;
    			#dma-requests = <0x4>;
    			clocks = <0x1 0x1b>;
    			clock-names = "apb_pclk";
    		};
    
    		devcfg@f8007000 {
    			compatible = "xlnx,zynq-devcfg-1.0";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x8 0x4>;
    			reg = <0xf8007000 0x100>;
    			clocks = <0x1 0xc 0x1 0xf 0x1 0x10 0x1 0x11 0x1 0x12>;
    			clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
    			syscon = <0x5>;
    			linux,phandle = <0x3>;
    			phandle = <0x3>;
    		};
    
    		efuse@f800d000 {
    			compatible = "xlnx,zynq-efuse";
    			reg = <0xf800d000 0x20>;
    		};
    
    		timer@f8f00200 {
    			compatible = "arm,cortex-a9-global-timer";
    			reg = <0xf8f00200 0x20>;
    			interrupts = <0x1 0xb 0x301>;
    			interrupt-parent = <0x4>;
    			clocks = <0x1 0x4>;
    		};
    
    		timer@f8001000 {
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0xa 0x4 0x0 0xb 0x4 0x0 0xc 0x4>;
    			compatible = "cdns,ttc";
    			clocks = <0x1 0x6>;
    			reg = <0xf8001000 0x1000>;
    		};
    
    		timer@f8002000 {
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x25 0x4 0x0 0x26 0x4 0x0 0x27 0x4>;
    			compatible = "cdns,ttc";
    			clocks = <0x1 0x6>;
    			reg = <0xf8002000 0x1000>;
    		};
    
    		timer@f8f00600 {
    			interrupt-parent = <0x4>;
    			interrupts = <0x1 0xd 0x301>;
    			compatible = "arm,cortex-a9-twd-timer";
    			reg = <0xf8f00600 0x20>;
    			clocks = <0x1 0x4>;
    		};
    
    		usb@e0002000 {
    			compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";
    			status = "disabled";
    			clocks = <0x1 0x1c>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x15 0x4>;
    			reg = <0xe0002000 0x1000>;
    			phy_type = "ulpi";
    		};
    
    		usb@e0003000 {
    			compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";
    			status = "disabled";
    			clocks = <0x1 0x1d>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x2c 0x4>;
    			reg = <0xe0003000 0x1000>;
    			phy_type = "ulpi";
    		};
    
    		watchdog@f8005000 {
    			clocks = <0x1 0x2d>;
    			compatible = "cdns,wdt-r1p2";
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x9 0x1>;
    			reg = <0xf8005000 0x1000>;
    			timeout-sec = <0xa>;
    		};
    	};
    
    	amba_pl {
    		#address-cells = <0x1>;
    		#size-cells = <0x1>;
    		compatible = "simple-bus";
    		ranges;
    
    		gpio@41200000 {
    			#gpio-cells = <0x2>;
    			compatible = "xlnx,xps-gpio-1.00.a";
    			gpio-controller;
    			reg = <0x41200000 0x10000>;
    			xlnx,all-inputs = <0x0>;
    			xlnx,all-inputs-2 = <0x0>;
    			xlnx,all-outputs = <0x1>;
    			xlnx,all-outputs-2 = <0x0>;
    			xlnx,dout-default = <0x0>;
    			xlnx,dout-default-2 = <0x0>;
    			xlnx,gpio-width = <0x8>;
    			xlnx,gpio2-width = <0x20>;
    			xlnx,interrupt-present = <0x0>;
    			xlnx,is-dual = <0x0>;
    			xlnx,tri-default = <0xffffffff>;
    			xlnx,tri-default-2 = <0xffffffff>;
    		};
    		
    		uio@0{
    			compatible="generic-uio";
    			status="okay";
    			interrupt-controller;
    			interrupt-parent=<0x4>;
    			interrupts=<0x0 0x1e 0x4>;
    		};
    		
    		uio@1{
    			compatible="generic-uio";
    			status="okay";
    			interrupt-controller;
    			interrupt-parent=<0x4>;
    			interrupts=<0x0 0x1f 0x4>;
    		};
    		
    		uio@2{
    			compatible="generic-uio";
    			status="okay";
    			interrupt-controller;
    			interrupt-parent=<0x4>;
    			interrupts=<0x0 0x20 0x4>;
    		};
    		
    		uio@3{
    			compatible="generic-uio";
    			status="okay";
    			interrupt-controller;
    			interrupt-parent=<0x4>;
    			interrupts=<0x0 0x21 0x4>;
    		};
    
    		axi-pcie@90000000 {
    			#address-cells = <0x3>;
    			#interrupt-cells = <0x1>;
    			#size-cells = <0x2>;
    			compatible = "xlnx,axi-pcie-host-1.00.a";
    			device_type = "pci";
    			interrupt-map = <0x0 0x0 0x0 0x1 0x6 0x1 0x0 0x0 0x0 0x2 0x6 0x2 0x0 0x0 0x0 0x3 0x6 0x3 0x0 0x0 0x0 0x4 0x6 0x4>;
    			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
    			interrupt-parent = <0x4>;
    			interrupts = <0x0 0x1d 0x4>;
    			ranges = <0x2000000 0x0 0xa0000000 0xa0000000 0x0 0x20000000>;
    			reg = <0x90000000 0x4000000>;
    
    			interrupt-controller {
    				#address-cells = <0x0>;
    				#interrupt-cells = <0x1>;
    				interrupt-controller;
    				linux,phandle = <0x6>;
    				phandle = <0x6>;
    			};
    		};
    	};
    
    	chosen {
    		bootargs = "earlycon uio_pdrv_genirq.of_id=generic-uio";
    		stdout-path = "serial0:115200n8";
    	};
    
    	aliases {
    		ethernet0 = "/amba/ethernet@e000b000";
    		serial0 = "/amba/serial@e0000000";
    		spi0 = "/amba/spi@e000d000";
    		spi1 = "/amba/spi@e0006000";
    	};
    
    	memory {
    		device_type = "memory";
    		reg = <0x0 0x40000000>;
    	};
    };
    

    4、  kernel配置

    在kernel中需要把UIO驱动编译进内核,这里使用的版本是XILINX 2017.4(内核版本4.9)。

     

    5、  UIO测试

    修改完devicetree和kernel,就可以启动linux对UIO进行测试了。这里通过cat /proc/interrupts看到的信息如下。

    为了测试中断,需要在vivado中引入VIO机制,模拟四根线电平拉高拉低。示例代码如下:

    使用vio修改value值即可模拟

    此时能发现确实收到了中断

    不过UIO存在一个问题,当中断到来时,驱动处理函数中将该中断disable,导致每次只能响应一次。需要用户启用该中断shell中输入echo 0x1 > /dev/uioX 或者调用write()函数。


    再次启用中断后,计数值增加了。

     

    6、  用户自定义中断

    UIO机制中提供了中断响应函数,不过该处理函数只是禁中断,比较简单,用户完全可以对该函数进行修改,增加自己的处理过程,比如像下面这样

    其中intrX_handler函数定义如下

    这样在中断来临时,就能触发自定义函数的执行。

    注意,在执行完自定义中断处理函数结束后enable对应的irq,否则就要像上面那样手动打开了。


    uio驱动代码如下:

    /*
     * drivers/uio/uio_pdrv_genirq.c
     *
     * Userspace I/O platform driver with generic IRQ handling code.
     *
     * Copyright (C) 2008 Magnus Damm
     *
     * Based on uio_pdrv.c by Uwe Kleine-Koenig,
     * Copyright (C) 2008 by Digi International Inc.
     * All rights reserved.
     *
     * This program is free software; you can redistribute it and/or modify it
     * under the terms of the GNU General Public License version 2 as published by
     * the Free Software Foundation.
     */
    
    #include <linux/platform_device.h>
    #include <linux/uio_driver.h>
    #include <linux/spinlock.h>
    #include <linux/bitops.h>
    #include <linux/module.h>
    #include <linux/interrupt.h>
    #include <linux/stringify.h>
    #include <linux/pm_runtime.h>
    #include <linux/slab.h>
    
    #include <linux/of.h>
    #include <linux/of_platform.h>
    #include <linux/of_address.h>
    
    #include "dal_kernel.h"
    
    //extern static irqreturn_t intr0_handler(int irq,void* dev_id);
    //extern static irqreturn_t intr1_handler(int irq,void* dev_id);
    //extern static irqreturn_t intr2_handler(int irq,void* dev_id);
    //extern static irqreturn_t intr3_handler(int irq,void* dev_id);
    
    #define DRIVER_NAME "uio_pdrv_genirq"
    
    struct uio_pdrv_genirq_platdata {
    	struct uio_info *uioinfo;
    	spinlock_t lock;
    	unsigned long flags;
    	struct platform_device *pdev;
    };
    
    /* Bits in uio_pdrv_genirq_platdata.flags */
    enum {
    	UIO_IRQ_DISABLED = 0,
    };
    
    static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode)
    {
    	struct uio_pdrv_genirq_platdata *priv = info->priv;
    
    	/* Wait until the Runtime PM code has woken up the device */
    	pm_runtime_get_sync(&priv->pdev->dev);
    	return 0;
    }
    
    static int uio_pdrv_genirq_release(struct uio_info *info, struct inode *inode)
    {
    	struct uio_pdrv_genirq_platdata *priv = info->priv;
    
    	/* Tell the Runtime PM code that the device has become idle */
    	pm_runtime_put_sync(&priv->pdev->dev);
    	return 0;
    }
    
    static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
    {
    	struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
    	/* Just disable the interrupt in the interrupt controller, and
    	 * remember the state so we can allow user space to enable it later.
    	 */
    #if 0
    	spin_lock(&priv->lock);
    	if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
    	{	
    		disable_irq_nosync(irq);
    	}
    	spin_unlock(&priv->lock);
    #endif
    	printk(">>>>>>handle UIO Interrupt now,irq number is %d\n",irq);
    #if 1
    	//get irq related handler
    	if(irq==47)
    	{
    		printk("Trigger interrupts 47\n");
    		intr0_handler(47,NULL);
    	}
    	if(irq==48)
    	{
    		printk("Trigger interrupts 48\n");
    		intr1_handler(48,NULL);
    	}
    	if(irq==49)
    	{
    		printk("Trigger interrupts 49\n");
    		intr2_handler(49,NULL);
    	}
    	if(irq==50)
    	{
    		printk("Trigger interrupts 50\n");
    		intr3_handler(50,NULL);
    	}
    #endif
    	return IRQ_HANDLED;
    }
    
    static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
    {
    	struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
    	unsigned long flags;
    
    	/* Allow user space to enable and disable the interrupt
    	 * in the interrupt controller, but keep track of the
    	 * state to prevent per-irq depth damage.
    	 *
    	 * Serialize this operation to support multiple tasks and concurrency
    	 * with irq handler on SMP systems.
    	 */
    
    	spin_lock_irqsave(&priv->lock, flags);
    	if (irq_on) {
    		if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
    			enable_irq(dev_info->irq);
    	} else {
    		if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
    			disable_irq_nosync(dev_info->irq);
    	}
    	spin_unlock_irqrestore(&priv->lock, flags);
    
    	return 0;
    }
    
    static int uio_pdrv_genirq_probe(struct platform_device *pdev)
    {
    	struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
    	struct uio_pdrv_genirq_platdata *priv;
    	struct uio_mem *uiomem;
    	int ret = -EINVAL;
    	int i;
    
    	if (pdev->dev.of_node) {
    		/* alloc uioinfo for one device */
    		uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
    				       GFP_KERNEL);
    		if (!uioinfo) {
    			dev_err(&pdev->dev, "unable to kmalloc\n");
    			return -ENOMEM;
    		}
    		uioinfo->name = pdev->dev.of_node->name;
    		uioinfo->version = "devicetree";
    		/* Multiple IRQs are not supported */
    	}
    
    	if (!uioinfo || !uioinfo->name || !uioinfo->version) {
    		dev_err(&pdev->dev, "missing platform_data\n");
    		return ret;
    	}
    
    	if (uioinfo->handler || uioinfo->irqcontrol ||
    	    uioinfo->irq_flags & IRQF_SHARED) {
    		dev_err(&pdev->dev, "interrupt configuration error\n");
    		return ret;
    	}
    
    	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    	if (!priv) {
    		dev_err(&pdev->dev, "unable to kmalloc\n");
    		return -ENOMEM;
    	}
    
    	priv->uioinfo = uioinfo;
    	spin_lock_init(&priv->lock);
    	priv->flags = 0; /* interrupt is enabled to begin with */
    	priv->pdev = pdev;
    
    	if (!uioinfo->irq) {
    		ret = platform_get_irq(pdev, 0);
    		uioinfo->irq = ret;
    		if (ret == -ENXIO && pdev->dev.of_node)
    			uioinfo->irq = UIO_IRQ_NONE;
    		else if (ret < 0) {
    			dev_err(&pdev->dev, "failed to get IRQ\n");
    			return ret;
    		}
    	}
    
    	uiomem = &uioinfo->mem[0];
    
    	for (i = 0; i < pdev->num_resources; ++i) {
    		struct resource *r = &pdev->resource[i];
    
    		if (r->flags != IORESOURCE_MEM)
    			continue;
    
    		if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
    			dev_warn(&pdev->dev, "device has more than "
    					__stringify(MAX_UIO_MAPS)
    					" I/O memory resources.\n");
    			break;
    		}
    
    		uiomem->memtype = UIO_MEM_PHYS;
    		uiomem->addr = r->start;
    		uiomem->size = resource_size(r);
    		uiomem->name = r->name;
    		++uiomem;
    	}
    
    	while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
    		uiomem->size = 0;
    		++uiomem;
    	}
    
    	/* This driver requires no hardware specific kernel code to handle
    	 * interrupts. Instead, the interrupt handler simply disables the
    	 * interrupt in the interrupt controller. User space is responsible
    	 * for performing hardware specific acknowledge and re-enabling of
    	 * the interrupt in the interrupt controller.
    	 *
    	 * Interrupt sharing is not supported.
    	 */
    
    	uioinfo->handler = uio_pdrv_genirq_handler;
    	uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol;
    	uioinfo->open = uio_pdrv_genirq_open;
    	uioinfo->release = uio_pdrv_genirq_release;
    	uioinfo->priv = priv;
    
    	/* Enable Runtime PM for this device:
    	 * The device starts in suspended state to allow the hardware to be
    	 * turned off by default. The Runtime PM bus code should power on the
    	 * hardware and enable clocks at open().
    	 */
    	pm_runtime_enable(&pdev->dev);
    
    	ret = uio_register_device(&pdev->dev, priv->uioinfo);
    	if (ret) {
    		dev_err(&pdev->dev, "unable to register uio device\n");
    		pm_runtime_disable(&pdev->dev);
    		return ret;
    	}
    
    	platform_set_drvdata(pdev, priv);
    	return 0;
    }
    
    static int uio_pdrv_genirq_remove(struct platform_device *pdev)
    {
    	struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);
    
    	uio_unregister_device(priv->uioinfo);
    	pm_runtime_disable(&pdev->dev);
    
    	priv->uioinfo->handler = NULL;
    	priv->uioinfo->irqcontrol = NULL;
    
    	return 0;
    }
    
    static int uio_pdrv_genirq_runtime_nop(struct device *dev)
    {
    	/* Runtime PM callback shared between ->runtime_suspend()
    	 * and ->runtime_resume(). Simply returns success.
    	 *
    	 * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
    	 * are used at open() and release() time. This allows the
    	 * Runtime PM code to turn off power to the device while the
    	 * device is unused, ie before open() and after release().
    	 *
    	 * This Runtime PM callback does not need to save or restore
    	 * any registers since user space is responsbile for hardware
    	 * register reinitialization after open().
    	 */
    	return 0;
    }
    
    static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
    	.runtime_suspend = uio_pdrv_genirq_runtime_nop,
    	.runtime_resume = uio_pdrv_genirq_runtime_nop,
    };
    
    #ifdef CONFIG_OF
    static struct of_device_id uio_of_genirq_match[] = {
    	{ /* This is filled with module_parm */ },
    	{ /* Sentinel */ },
    };
    MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
    module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0);
    MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
    #endif
    
    static struct platform_driver uio_pdrv_genirq = {
    	.probe = uio_pdrv_genirq_probe,
    	.remove = uio_pdrv_genirq_remove,
    	.driver = {
    		.name = DRIVER_NAME,
    		.pm = &uio_pdrv_genirq_dev_pm_ops,
    		.of_match_table = of_match_ptr(uio_of_genirq_match),
    	},
    };
    
    module_platform_driver(uio_pdrv_genirq);
    
    MODULE_AUTHOR("Magnus Damm");
    MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
    MODULE_LICENSE("GPL v2");
    MODULE_ALIAS("platform:" DRIVER_NAME);

     

    7、  一点补充

    a)UIO机制中虽然包含四种中断方式(高电平、低电平、上升沿、下降沿),但在zynq中只支持2种(高电平、上升沿),中断方式的配置位于devicetree中,示例如下

    Devicetree中大多数设备的interrupts最后参数都是0x4,从cat/proc/interrupts中能看到只有watchdog几个为上升沿触发。

     

    b)使用request_irq申请中断的时候irq的值不是devicetree中设定的值,而是操作系统启动后分配的值,所以在UIO中打印出来irq值不是62-65,而是47-50。通过在irq-gic.c中和uio_pdrv_genirq.c中增加打印信息,发现中断触发时过程如下。

    需要注意的是,在gic中该值为62,这和devicetree中设置的一样。而从打印信息能够看到,中断是先送到gic,然后再送给内核,内核再调用uio的中断处理函数。所以如果需要在uio中增加自己的代码处理相应的中断,需要先启动操作系统,确认好每根线对应的中断号才行。

    注意:在这个例子中,由于UIO已经注册了47-50号中断,且UIO不支持中断共享,所以其他程序再次注册47-50号中断时会出错。


    展开全文
  • 51单片机的中断系统

    2021-03-21 14:43:50
    CPU暂时停止当前工作(中断响应),转去处理事件B(中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断地方继续去处理事件A(中断返回)。(就是我在打麻将时候来了一快递,我就停止打麻将去拿快递...

    一、中断的概念
    中断是指:CPU在处理某一事件A时,发生了另一事件B,请求CPU迅速去处理(中断发生);CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续去处理事件A(中断返回)。(就是我在打麻将的时候来了一个快递,我就停止打麻将去拿快递,拿完快递又接着打麻将了。拿快递的过程就是中断)
    在这里插入图片描述
    二、中断系统
    80C51的中断系统有5个中断源(80C52有 6个) ,2个优先级,可实现二级中断嵌套 。(而80C52单片机有四个中断优先级,即可实现四级中断服务嵌套)下图可以看出EA是中断的总开关。如果此开关取0,就会强行屏蔽所有的中断,因此,只要用到中断,此开关必须取1。ET0是专门针对定时器0中断的局部开关。如果此开关取0,则会屏蔽定时器0的中断,如果取1则允许定时器0中断。如果要定时器0能产生中断,那么总开关EA与ET0必须同时都打开(都取1),两者缺一不可。图上几个寄存器在下面介绍,介绍的时候对比着这张图更容易理解。
    在这里插入图片描述
    在使用单片机时,要设置两个与单片机有关的寄存器:
    1、中断允许寄存器IE
    用来设定各中断源的打开和关闭,控制所有中断以及某个中断源的开放和屏蔽。可进行位寻址,单片机复位时IE被全部清0。(52单片机第五位是ET2,第六位是无效位)
    在这里插入图片描述
    EA:全局中断允许位。EA=1,打开全局中断;EA=0,关闭全部中断。
    ES:串行口中断允许位。ES=1,打开串行口中断;ES=0,关闭串行口中断。
    ET1(0):定时器/计数器1(0)中断允许位。ET1(0)=1,打开T1(0)中断;ET1(0)=0,关闭T1(0)中断。
    EX1(0):外部中断1(0)中断允许位。EX1(0)=1,打开外部中断1(0)中断;EX1(0)=0,关闭外部中断1(0)中断。
    2、中断优先级寄存器IP
    用来设定各中断源中属于两(四)级中断中的哪一级,可进行位寻址,单片机复位时IP被全部清0。80C51单片机有两个中断优先级,即可实现二级中断服务嵌套。每个中断源的中断优先级都是由中断优先级寄存器IP中的相应位的状态来规定的 。而80C52单片机有四个中断优先级,即可实现四级中断服务嵌套。每个中断源的中断优先级由中断优先级寄存器IP和IPH中的相应位的状态来规定的 。
    在这里插入图片描述
    PS:串行口中断优先级控制位。PS=1,串行口中断定义为高优先级中断;PS=0,串行口中断定义为低优先级中断。
    PT1(0):定时器/ 计数器1(0)中断优先级控制位。PT1(0)=1,定时器/ 计数器1(0)中断定义为高优先级中断;PT1(0)=0,定时器/ 计数器1(0)中断定义为低优先级中断。
    PX1(0):外部中断1(0)中断优先级控制位。PX1(0)=1,外部中断1(0)中断定义为高优先级中断;PX1(0)=0,外部中断1(0)中断定义为低优先级中断。
    中断优先级的三个原则:
    CPU同时接收到几个中断时,首先响应优先级别最高的中断请求。若是同级别的,在没有设置中断优先级的情况下,按照默认中断级别响应中断;在设置中断优先级后,则按设置顺序确定响应的先后顺序。
    正在进行的中断过程不能被新的同级或低优先级的中断请求所中断。
    正在进行的低优先级中断服务,能被高优先级中断请求所中断。
    52单片机的中断级别:
    在这里插入图片描述
    3、SCON的中断标志
    在这里插入图片描述
    RI(SCON.0):串行口接收中断标志位。当允许串行口接收数据时,每接收完一个串行帧,由硬件置位RI。注意,RI必须由软件清除。
    TI(SCON.1):串行口发送中断标志位。当CPU将一个发送数据写入串行口发送缓冲器时,就启动了发送过程。每发送完一个串行帧,由硬件置位TI。CPU响应中断时,不能自动清除TI,TI必须由软件清除。
    三、单片机的定时器/计数器中断
    定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器组成。TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0(定时器0)、T1(定时器1)的启动和停止(TR0,TR1)及设置溢出标志(TF0,TF1)。
    在这里插入图片描述
    加1计数器输入的计数脉冲有两个来源,一个是由系统的时钟振荡器输出脉冲经12分频后送来;一个是T0或T1引脚输入的外部脉冲源。每来一个脉冲计数器加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使TCON中TF0或TF1置1,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值。
    1、工作方式寄存器TMOD
    工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。不能进行位寻址,如果设置定时器0为工作方式1的话就是0x01,即0000 0001高四位(定时器1可以理解为没有设置,其实0000是定时器1的方式0)是0,低四位0001是GATE=0,C/T=0选择为定时模式,M1M0=01是16位定时器,其格式如下:
    在这里插入图片描述
    GATE:门控制位。GATE=0时,只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚也为高电平时,才能启动定时/计数器工作,即此时定时器的启动多了一条件。
    C/T:定时/计数模式选择位。C/T=0为定时模式;C/T=1为计数模式。
    M1M0:工作方式选择位。定时/计数器有四种工作方式,由M1M0进行设置。
    在这里插入图片描述
    2、控制寄存器TCON
    在这里插入图片描述
    TF1(TCON.7):定时器1(T1)溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
    TR1(TCON.6):T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
    TF0(TCON.5):定时器0(T0)溢出中断请求标志位,其功能与TF1类同。
    TR0(TCON.4):T0运行控制位,其功能与TR1类同。
    IE1(TCON.3),外部中断1中断请求标志位。
    IT1(TCON.2),外部中断1触发方式控制位。
    IE0(TCON.1),外部中断0中断请求标志位。(在IT0下当INT0(外部中断0)为低电平或从高到低的负跳变时,IE0置1)
    IT0(TCON.0),外部中断0触发方式控制位。当IT0=0时,为电平触发方式。当IT0=1时,为跳边沿触发方式(下降沿有效)。
    3、定时器0方式1逻辑结构
    在这里插入图片描述
    注意:TR0是定时器的“自身原配开关”。EA,ET0,TR0三者容易搞不清。定时器可以工作在“查询标志位”和“中断”这两种状态,也就是说在没有中断的情况下定时器也可以单独使用的。TR0是定时器0自身的发动引擎,要不要把这个发动引擎所产生的能量传输到中断的渠道,则取决于中断开关EA和ET0。TR0是源头开关,EA是中断总渠道开关,ET0是中断分支渠道的定时器0开关。TR0取1表示启动定时器0,取0表示关闭定时器0。
    看到定时器的结构图可知定时器0在GATE=0,TR0=0时,TL0就在机器周期的作用下开始计数,直到TL0和TH0计满溢出,由硬件将TF0置为1,接着向CPU申请中断。只要TR0=1,计数就不会停止。
    四、中断函数
    1、计算初值
    假设时钟频率是12MHz,那么时钟周期是时钟频率的倒数(外接晶振的倒数)就是1/12微秒,则机器周期就是1微秒。在TL0和TH0计满时也就是2^16-1,再加上一个机器周期溢出,TF0置1,触发中断的话就需要2……16个个数,也就是65536微秒。
    如果要定时50ms,就是要计50000个数,那么TL0和TH0要装的初值就是65536-50000=15536。则TH0=15536/256,TL0=15536%256。当晶振为11.0592MHz时,机器周期就是12*(1/11.0592)=1.09微秒,定时50ms时装初值就是50000/1.09=45872。在用定时器方式1时,计算TLX和THX要装的初值:THX=(65536-N)/256;TLX=(65536-N)%256。
    2、中断服务程序

    void main()
    {
       配置中断;
       while(1)
       {  
           处理常事;
       }
    }
    
    void 中断函数() interrupt 中断号    //中断函数后缀带“interrupt 序号”特别修饰
    {
       重装初值;
       中断服务程序内容;
    }
    

    可以看出“main函数”与“中断函数”在软件上看不到任何关联,既不存在“main函数”调用“中断函数”,也不存在“中断函数”调用“main函数”的情况,在观感上,“main函数”与“中断函数”仿佛是隔离的毫无“物理连接”的,为什么单片机还能在“main函数”与“中断函数”两者中切换自如?没错,“main函数”与“中断函数”在书写上是隔离的毫无关联的,但是它们之间之所以能相互切换,是因为背后有一只无形的手在自动操控这一切,这只手就是单片机硬件自身,这是一种特殊机制,也可以理解成一种特殊的游戏规则,我们只要遵守就好了,除了普通函数,其它凡是中断函数的,都不用跟main函数发生软件上的关联调用,它们之间的切换都是硬件自动完成的,这就是main函数与中断函数的特殊跳转机制(或者称为游戏规则也可以)
    3、定时器初始化步骤
    ①初始化TMOD寄存器,确定定时器1(T1)和定时器0(T0)的工作方式,如TMOD=0x01,就是设置定时器0为工作方式1;
    ②计算初值,THX=(65536-50000)/256;TLX=(65536-50000)%256;就是将定时器定时为50ms(12MHz的晶振);
    ③开放中断,EA=1;ET0=1;开总中断和定时器0中断;
    ④启动定时器,TR0=1;启动定时器0。
    五、实例
    1、用定时器0实现第一个二极管间隔0.5S闪烁,在主程序里实现用延时函数让第八个二极管间隔300ms闪烁,两个互不干涉。

    #include <reg52.h>
    #define uint unsigned int
    sbit led1=P1^0;
    sbit led8=P1^7;
    void delayms(uint xms)
    {
    	uint i,j;
    	for(i=xms;i>0;i--)
    		for(j=110;j>0;j--);
    }
    char num=0;
    void main()
    {
    	TMOD=0x01;//定时器0方式1,16位计数器
    	TH0=(65536-45872)/256;//高四位装初值,11.0592MHz
    	TL0=(65536-45872)%256;//低四位装初值
    	EA=1;
    	ET0=1;
    	TR0=1;
    	while(1)
    	{
    		led8=~led8;//每次取反
    		delayms(300);
    	}
    }
    void Timer0() interrupt 1//注意:我刚开始时把函数名写为T0,会出现重定义错误,因为在头文件中定义了
    {
    	TH0=(65536-45872)/256;//一定要记住重装初值
    	TL0=(65536-45872)%256;
    	num++;//每次进入中断就累加一次,到10次置0
    	if(num==10)//num到10时也就是500ms,二极管进行闪烁
    	{
    		num=0;
    		led1=~led1;
    	}
    
    }
    

    **注意:**一般情况下在中断服务程序中不要写太多语句,如果中断服务程序中写太多语句的话就可能会出现中断服务程序中的代码还没有执行完,而下一次中断又来临了,那么就丢失剩下的代码又重头开始执行,累计出现这种情况程序就有可能乱套。一般原则是能不在中断里面写就不在里面写。但是上面的程序就只能在中断里面写,因为我刚开始在主函数中写,放在delayms(300)语句后,会出现第一个二极管过了很长时间才亮时间(可能是127*50ms)很长时间才熄灭。原因就是主函数延时大概300ms,而num累积十次是500ms,在500ms时主函数刚好应该在第二次delayms函数600ms中,再进行一次中断50ms仍然在delayms函数中,但是num就累加到11了,当delayms函数执行完之后刚好就错过if语句的判断,而当num一直累加到127之后(char型变量)才回到0,在某一时刻刚好可以在主函数中判断到num=10,所以会出现这种情况。在有些程序中就有可能永远检测不到了。
    2、在实验板上实现:用定时器0的方式1实现前两位数码管59s的循环计时,用定时器1的方式1实现前四个二极管和后四个二极管间隔亮300ms。

    #include <reg52.h>
    sbit dula=P2^6;
    sbit wela=P2^7;
    void delayms(unsigned int xms)
    {
    	unsigned int i,j;
    	for(i=xms;i>0;i--)
    		for(j=110;j>0;j--);
    }
    
    unsigned char code table[]={0x3f,0x06,0x5b,0x4f,
    0x66,0x6d,0x7d,0x07,
    0x7f,0x6f,0x77,0x7c,
    0x39,0x5e,0x79,0x71};
    unsigned char num1=0,num=0,num2=0;
    void main()
    {
    	TMOD=0x11;//设置定时器1和定时器0为方式一
    	TH0=(65536-45872)/256;
    	TL0=(65536-45872)%256;//装初值,50ms
    	TH1=(65536-45872)/256;
    	TL1=(65536-45872)%256;
    	EA=1;
    	ET1=1;
    	ET0=1;
    	TR1=1;
    	TR0=1;
    	P1=0xf0;
    	dula=0;
    	wela=0;
    	while(1)
    	{
    		dula=1;
    		P0=table[num/10];//显示首位数码管数字,取整,可以理解为几个10
    		dula=0;
    		P0=0xff;
    		wela=1;
    		P0=0xfe;//打开第一个数码管
    		wela=0;
    		delayms(1);//延时最好要,如果不加会看到亮度比较暗,应该是点亮的速度比较快导致,时间长亮度会更亮一点
    		wela=1;//最好一定要加上位选,不然会看到数字以外的部分有微弱亮度,
    		P0=0xff;//原因在于如果不加,下面刚打开段选显示的数字就会先送到第一个数码管
    		wela=0;//由于速度很快,又到第二个数码管了,时间短所以就出现数字以外的部分有微弱亮度
    
    		dula=1;
    		P0=table[num%10];//显示第二位数码管,取余
    		dula=0;
    		P0=0xff;
    		wela=1;
    		P0=0xfd;//打开第二个数码管
    		wela=0;
    		delayms(1);
    		wela=1;
    		P0=0xff;
    		wela=0;
    	}
    }
    void timer0() interrupt 1
    {
    	TH0=(65536-45872)/256;
    	TL0=(65536-45872)%256;
    	num1++;
    	if(num1==20)//1s
    	{
    		num1=0;
    		num++;
    		if(num==60)//计时1分钟,然后清零
    			num=0;
    	}
    }
    void timer1() interrupt 3//用序号3,单片机认识中断的唯一标识
    {
    	TH0=(65536-45872)/256;
    	TL0=(65536-45872)%256;
    	num2++;
    	if(num2==15)//300ms
    	{
    		num2=0;
    		P1=~P1;
    	}
    }
    

    在这里插入图片描述

    展开全文
  • 完成一条指令最多需要四个工作周期:取指周期,间指周期,执行周期,中断周期。 取指周期 指令周期:取出并执行一条指令所需要时间(解释一条指令所需要时间) 带有间址寻址指令周期 因为寻址方式不同,...
  • 1、 熟悉中断概念、中断响应过程、中断向量表以及系统是如何根据中断类型号在中断向量表中得到该中断对应中断服务程序入口地址等内容。 2、 掌握中断服务程序设计方法。 2.7.2 实验预习要求 1、 复习中断...
  • 起初,我觉得这是一非常单调乏味的过程,只是在没有替代方法来获取信息情况下才不得已使用它。后来,一霎那间我破除了某个思维障碍,我发现自己迅速地“驰骋”于无正式文献记录机器码中,快速地破译了代码...
  • 当一个中断发生时需要响应中断,那么就要保存现场: 1、保存cpsr到spsr_<mode> 2、修改cpsr  1.切换到ARM状态  2.切换到对应异常模式(在这里是SVC)  3.禁止相应的中断位(根据需要) 3、保存返回地址到...
  • ucos-II(

    2016-10-02 20:23:56
    uC/OS-II系统响应中断的过程是:系统接收到中断请求后,如果这是CPU处于中断允许状态(即中断时开放),系统就会中止正在运行当前任务,而按照中断向量指向转而去运行中断服务子程序;当中断服务子程序运行...
  • 9. 8086/8088在当前指令执行完且IF=1情况下可以响应外部INTR中断请求。( ) 10. 8086被复位后,寄存器Flag、IP、CS内容分别是0000 、0001 、FFFFH。 ( ) 、汇编程序(共20分) 1. 试分析下面程序段完成...
  • 答: 计算机网络发展过程大致经历了四个阶段。 第一阶段:(20世纪60年代)以单个计算机为中心面向终端计算机网络系统。这种网络系统是以批处理信息为主要目的。它缺点是:如果计算机负荷较重,会导致系统...
  • 答: 计算机网络发展过程大致经历了四个阶段。 第一阶段:(20世纪60年代)以单个计算机为中心面向终端计算机网络系统。这种网络系统是以批处理信息为主要目的。它缺点是:如果计算机负荷较重,会导致系统...
  • 本文研究并设计了基于Android+HTML5在线认证检测系统,主要工作包括以下四个方面: (1)针对多窗口类浏览器模式问题,指出并分析了该问题存在原因,利用Activity运行机制,通过Fragment栈对主要模块Webview...
  • 微机课程设计

    2004-10-15 18:43:28
    7 叙述可屏蔽中断的响应过程,一个可屏蔽中断或者非屏蔽中断响应后,堆栈顶部的四个单元的内容是什么? 答:可屏蔽中断的响应过程如下: (1) 从数据总线上读取中断类型码,将其存入内部存器中 (2) 将标志寄存器...
  • 这三个过程是在一个时钟周期内完成,构成一个完整ALU操作。 在32个通用工作寄存器中,有6个寄存器可以合并成为3个16位,用于对数据存储器空间进行间接寻址间接地址寄存器(存放地址指针),以实现高效地址...
  • 遥控器在运行过程中,主要靠如下3 种状态维持他正常运行。 2.2.1. 触发遥控器进去空闲状态事件: > 广播状态,没有连接成功,出现超时事件,进入idle > 连接状态,断开连接,如无操作主动断开连接,然后进入idle ...
  • 第*套获取数字图像的过程大体分为步:扫描、分色、取样、量化基于客户机/服务器体系结构 不属于单内核系统特点,单内核系统特点:执行效率高、性能好、占用资源较多用户通过接入互联网需要配置一 ADSL MODEM...
  • USB 学习

    2016-05-07 16:32:17
    1、包的结构和传输控制:同步域、PID(包的类型)、CRC,EOP(结束包),数据的基本单元是包,包分成域。...2、USB的四种通信模式:批量、同步、中断、控制传输 USB设备枚举过程用的是控制传输模式。 3、描述符
  • 一般有死锁的预防,即破坏产生死锁的四个必要条件中的一个或多个,使系统绝不会进入死锁状态;死锁的避免,即在资源动态分配的过程中使用某种办法防止系统进人死锁状态;和允许系统产生死锁,然后使用检测算法...
  • 过滤器七:过滤链

    2021-03-02 17:08:45
    三:第一需要注意问题:chain.dofilter()书写位置 与 请求和响应过程中执行关系 补:控制过滤链中断:阻挡请求(一种简单防火墙) :第二需要注意问题:注解形式时,过滤器执行顺序(仅供了解,...
  • 40 为使工作在一般完全嵌套方式8259A中断控制器能接受下一个中断请求,在中断服务程序结束处应( )。 A、直接执行IRET指令 B、先执行POP指令 C、发送普通EOI指令 D、不用任何处理,8259会自动进行有关处理 ...
  • 比如:当客户端发送请求给服务端,在等待服务端响应的请求时,客户端不做其他的事情。当服务端做完了才返回到客户端。这样的话客户端需要一直等待。用户使用起来会有不友好。 异步的概念和同步时相对的。当一异步...
  • 软件工程教程

    热门讨论 2012-07-06 23:10:29
    删除操作一旦执行,立即被监听器捕获到,进而在执行 删除操作前执行自定义函数体,即判断实体有无undeletable标签,有则中断删除操作,无则正常删除。 用例图 关系 关联关系 ;依赖关系 ;泛化关系;关系...
  • 2、802.1X是基于端口认证策略(这里端口可以是一实实在在物理端口也可以是一就像VLAN一样逻辑端口,对于无线局域网来说“端口”就是一条信道) 3、802.1X认证最终目的就是确定一端口是否可用。...
  • 答: 计算机网络发展过程大致经历了四个阶段。 第一阶段:(20世纪60年代)以单个计算机为中心面向终端计算机网络系统。这种网络系统是以批处理信息为主要目的。它缺点是:如果计算机负荷较重,会导致系统...
  • c语言编写单片机技巧

    2009-04-19 12:15:17
    答:大学过程是理论过程,实践机会比较少,往往会造成理论与实践相脱节,这是国内大学教育系统通病,不过对于学生来说切不可好高骛远。一般从大三会开始接触到一些专业课程,电子相关专业会开设相关...
  • 还需要在文件末尾增加 CPU 自带串口服务程序,将上表中发送及接收中断程序接口,放到对应的中断服务程序中去即可。 2.2.2、定时器 涉及到定时器移植文件位于FreeModbus\port\porttimer_m.c,在这文件中...
  • 2.使用高速缓冲存储器是为了解决 问题,在执行程序时,必须把 映射到主存储器 空间上,这个过程称为 。 3.某计算机存储容量是64 KB,若按字节寻址,则寻址范围为 ,需要地址线 根,数据线 根,若字长为32位...
  • 11、纠正有时在FAT表损坏的情况下加载FAT分区时程序长时间停止响应的BUG。 12、纠正某些情况下搜索不到NTFS分区的问题。 13、纠正解析不是按文件备份的PMF文件时,错误提示不正确的BUG。 14、纠正在备份分区的高级...

空空如也

空空如也

1 2 3
收藏数 58
精华内容 23
关键字:

中断响应的四个过程是