嵌入式Linux裸机开发(五)中断管理

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • STM32 中断系统
  • IMX6U中断控制
    • 8个中断
    • GIC中断控制器
      • GIC介绍
      • 中断ID
      • GIC逻辑分块
      • GIC协处理器
    • 中断使能
    • 中断优先级
  • 重点代码分析
    • 官方SDK函数
    • start.S文件
    • 自行编写中断驱动文件


前言

最近在学习中发现,学Linux嵌入式不仅是对Linux的学习熟悉,而且还是对Cortex-A内核的学习掌握
没怎么看懂,内容太多了,我觉得暂时先搞清楚一些原理概念以及简单的外部函数接口就行,内部可能在后续的学习中进行钻研


STM32 中断系统

STM32 的中断系统主要有以下几个关键点:
①、 中断向量表。
②、 NVIC(内嵌向量中断控制器)。
③、 中断使能。
④、 中断服务函数。

中断向量表是一个表,这个表里面存放的是中断向量。中断向量表是一系列中断服务程序入口地址组成的表。
由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。
中断向量表在整个程序的最前面,如 STM32F103 :

__Vectors       DCD     __initial_sp               ; Top of StackDCD     Reset_Handler              ; Reset HandlerDCD     NMI_Handler                ; NMI HandlerDCD     HardFault_Handler          ; Hard Fault HandlerDCD     MemManage_Handler          ; MPU Fault HandlerDCD     BusFault_Handler           ; Bus Fault HandlerDCD     UsageFault_Handler         ; Usage Fault HandlerDCD     0                          ; ReservedDCD     0                          ; ReservedDCD     0                          ; ReservedDCD     0                          ; ReservedDCD     SVC_Handler                ; SVCall HandlerDCD     DebugMon_Handler           ; Debug Monitor HandlerDCD     0                          ; ReservedDCD     PendSV_Handler             ; PendSV HandlerDCD     SysTick_Handler            ; SysTick Handler; External InterruptsDCD     WWDG_IRQHandler            ; Window WatchdogDCD     PVD_IRQHandler             ; PVD through EXTI Line detectDCD     TAMPER_IRQHandler          ; TamperDCD     RTC_IRQHandler             ; RTCDCD     FLASH_IRQHandler           ; FlashDCD     RCC_IRQHandler             ; RCCDCD     EXTI0_IRQHandler           ; EXTI Line 0DCD     EXTI1_IRQHandler           ; EXTI Line 1DCD     EXTI2_IRQHandler           ; EXTI Line 2DCD     EXTI3_IRQHandler           ; EXTI Line 3DCD     EXTI4_IRQHandler           ; EXTI Line 4DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7DCD     ADC1_2_IRQHandler          ; ADC1_2DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TXDCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1DCD     CAN1_SCE_IRQHandler        ; CAN1 SCEDCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5DCD     TIM1_BRK_IRQHandler        ; TIM1 BreakDCD     TIM1_UP_IRQHandler         ; TIM1 UpdateDCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and CommutationDCD     TIM1_CC_IRQHandler         ; TIM1 Capture CompareDCD     TIM2_IRQHandler            ; TIM2DCD     TIM3_IRQHandler            ; TIM3DCD     0                          ; ReservedDCD     I2C1_EV_IRQHandler         ; I2C1 EventDCD     I2C1_ER_IRQHandler         ; I2C1 ErrorDCD     0                          ; ReservedDCD     0                          ; ReservedDCD     SPI1_IRQHandler            ; SPI1DCD     0                          ; ReservedDCD     USART1_IRQHandler          ; USART1DCD     USART2_IRQHandler          ; USART2DCD     0                          ; ReservedDCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10DCD     RTC_Alarm_IRQHandler        ; RTC Alarm through EXTI LineDCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
__Vectors_End

中断向量表都是链接到代码的最前面,比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是从 0X00000000 开始存放的。

“__initial_sp”就是第一条中断向量,存放的是栈顶指针
复位中断复位函数 Reset_Handler 的入口地址

ARM 处理器都是从地址 0X00000000 开始运行,但是我们是下载到 0X8000000 开始的存储区域中,Cortex-M 架构引入了中断向量表偏移,中断向量表偏移配置在函数 SystemInit 中完成,通过向 SCB_VTOR 寄存器写入新的中断向量表首地址即可。

void SystemInit (void)
{RCC->CR |= (uint32_t)0x00000001;/* 省略其它代码 */#ifdef VECT_TAB_SRAMSCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;     //将中断向量表设置到 RAM 中#elseSCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;     //将中断向量表设置到 ROM 中#endif
}#define FLASH_BASE ((uint32_t)0x08000000)

对于Cortex-M来说:
中断使能:

NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);

中断服务函数:
要处理的工作就可以放到中断服务函数中去完成

/* 外部中断 2 服务程序 */
void EXTI2_IRQHandler(void)
{
/* 中断处理代码 */
}

IMX6U中断控制

Cortex-M 内核有个中断系统的管理机构—NVIC(Nested Vectored Interrupt Controller)
Cortex-A7 内核有个中断系统管理机构——GIC(general interrupt controller)

8个中断

Cortex-A内核有8个异常中断:
在这里插入图片描述
其中还包括一个未使用中断,实际只有七个中断。

对于 Cortex-M 内核来说,中断向量表列举出了一款芯片所有的中断向量,包括芯片外设的所有中断。

Cortex-A 内核 CPU 的所有外部中断都属于这个 IRQ 中断,当任意一个外部中断发生的时候都会触发 IRQ 中断。在 IRQ 中断服务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断,进而根据具体的中断做出相应的处理。
在这里插入图片描述
①、复位中断(Rest), CPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化 SP 指针、 DDR 等等。
②、未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
③、软中断(Software Interrupt,SWI),由 SWI 指令引起的中断, Linux 的系统调用会用 SWI指令来引起软中断,通过软中断来陷入到内核空间。
④、指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。
⑤、数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。
⑥、 IRQ 中断(IRQ Interrupt),外部中断,前面已经说了,芯片内部的外设中断都会引起此中断的发生。
⑦、 FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中断。

常用复位中断和IRQ中断,实际需要编写的只有复位中断服务函数 Reset_Handler 和 IRQ 中断服务函数 IRQ_Handler,其它的中断本暂时没用到,所以都是死循环。

GIC中断控制器

GIC介绍

GIC目前有V1-V4,V1太老淘汰了,GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A7、 Cortex-A9、 Cortex-A15 等,V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。ARM 会根据 GIC 版本的不同研发出不同的 IP 核,那些半导体厂商直接购买对应的 IP 核即可,ARM 针对 GIC V2 就开发出了 GIC400 这个中断控制器 IP 核。当 GIC 接收到外部中断信号以后就会报给 ARM 内核,但是ARM 内核只提供了四个信号给 GIC 来汇报中断情况: VFIQ、 VIRQ、 FIQ 和 IRQ。

VFIQ:虚拟快速 FIQ。VIRQ:虚拟外部 IRQ。FIQ:快速中断 IRQ。IRQ:外部中断 IRQ。
VFIQ 和 VIRQ 是针对虚拟化的,不管,只看IRQ
在这里插入图片描述
①、 SPI(Shared Peripheral Interrupt),共享中断,所有 Core 共享的中断,那些外部中断都属于 SPI 中断。比如按键中断、串口中断等等,这些中断所有的 Core 都可以处理,不限定特定 Core。
②、 PPI(Private Peripheral Interrupt),私有中断, GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理。
③、 SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。

中断ID

每一个 CPU 最多支持 1020 个中断 ID,中断 ID 号为 ID0~ID1019。这 1020 个 ID 包含了 PPI、 SPI 和 SGI。

ID0~ID15:这 16 个 ID 分配给 SGI。
ID16~ID31:这 16 个 ID 分配给 PPI。
ID32~ID1019:这 988 个 ID 分配给 SPI,像 GPIO 中断、串口中断等这些外部中断

I.MX6U 的总共使用了 128 个SPI中断,以及 PPI 和 SGI 的 32 个 ID,一共160个

中断ID全部都在参考手册的第三章:
在这里插入图片描述
移植的官方SDK(Software Development Kit)中就有所有中断ID的枚举
在这里插入图片描述

GIC逻辑分块

GIC 架构分为了两个逻辑块: Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端

Distributor(分发器端):中断事件应该发送到哪个 CPU 接口,将优先级最高的中断事件发送到 CPU 接口端
全局中断使能控制。控制每一个中断的使能或者关闭。设置每个中断的优先级。设置每个中断的目标处理器列表。设置每个外部中断的触发模式:电平触发或边沿触发。设置每个中断属于组 0 还是组 1。

CPU Interface(CPU 接口端):每个 CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface
使能或者关闭发送到 CPU Core 的中断请求信号。应答中断。通知中断处理完成。设置优先级掩码,通过掩码来设置哪些中断不需要上报给 CPU Core。定义抢占策略。当多个中断到来的时候,选择优先级最高的中断通知给 CPU Core。

在我们移植的 core_ca7.h 文件里面有定义结构体 GIC_Type,分发器端和 CPU 接口端:
在这里插入图片描述

GIC协处理器

通过 c0 寄存器可以获取到处理器内核信息;通过 c1 寄存器可以使能或禁止 MMU、 I/D Cache 等;通过 c12 寄存器可以设置中断向量偏移;通过 c15 寄存器可以获取 GIC 基地址。(暂时没太理解,后续补充)

中断使能

使用 I.MX6U 上的外设中断就必须先打开 IRQ 中断
寄存器 CPSR 的 I=1 禁止 IRQ,当 I=0 使能 IRQ; F=1 禁止 FIQ, F=0 使能 FIQ。
还可以使用更简单的指令:
在这里插入图片描述
GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用来完成外部中断的使能和禁止,Cortex-A7 内核中断 ID 只使用了 512 个。一个 bit 控制一个中断 ID 的使能,那么就需要 512/32=16 个 GICD_ISENABLER 寄存器和16个GICD_ICENABLER来完成中断的使能与禁止。
GICD_ISENABLER0 的 bit[15:0]对应ID15 ~ 0 的 SGI 中断, GICD_ISENABLER0 的 bit[31:16]对应 ID31 ~ 16 的 PPI 中断。剩下的GICD_ISENABLER1~GICD_ISENABLER15 就是控制 SPI 中断的。

中断优先级

Cortex-A7 的中断优先级也可以分为抢占优先级和子优先级,两者同样是可以配置的。 GIC 控制器最多可以支持 256 个优先级,数字越小,优先级越高! Cortex-A7 选择了 32 个优先级。

在使用中断的时候需要初始化 GICC_PMR 寄存器:(设置优先级数)
在这里插入图片描述
Cortex-A7内核支持 32 个优先级。

抢占优先级和子优先级位数设置由寄存器 GICC_BPR决定:
在这里插入图片描述
寄存器 GICC_BPR 只有低 3 位有效,其值不同,抢占优先级和子优先级占用的位数也不同:
在这里插入图片描述
有32个抢占优先级,Cortex-A7 使用了 512 个中断 ID
如要设置ID40 中断的优先级为 5:

GICD_IPRIORITYR[40] = 5 << 3

重点代码分析

官方SDK函数

先了解官方SDK包的API函数:

GIC_Init     				//初始化 GIC。
GIC_EnableIRQ 				//使能指定的外设中断。
GIC_DisableIRQ 				//关闭指定的外设中断。
GIC_AcknowledgeIRQ 			//返回中断号。
GIC_DeactivateIRQ 			//无效化指定中断。
GIC_GetRunningPriority 		//获取当前正在运行的中断优先级。
GIC_SetPriorityGrouping 	//设置抢占优先级位数。
GIC_GetPriorityGrouping 	//获取抢占优先级位数。
GIC_SetPriority 			//设置指定中断的优先级。
GIC_GetPriority 			//获取指定中断的优先级。

这些函数都在文件末尾:
在这里插入图片描述

start.S文件

.global _start  				/* 全局标号 *//** 描述:	_start函数,首先是中断向量表的创建* 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42,3 ARM Processor Modes and Registers(ARM处理器模型和寄存器)* 		 	ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)*/
_start:ldr pc, =Reset_Handler		/* 复位中断 					*/	ldr pc, =Undefined_Handler	/* 未定义中断 					*/ldr pc, =SVC_Handler		/* SVC(Supervisor)中断 		*/ldr pc, =PrefAbort_Handler	/* 预取终止中断 					*/ldr pc, =DataAbort_Handler	/* 数据终止中断 					*/ldr	pc, =NotUsed_Handler	/* 未使用中断					*/ldr pc, =IRQ_Handler		/* IRQ中断 					*/ldr pc, =FIQ_Handler		/* FIQ(快速中断)未定义中断 			*//* 复位中断 */	
Reset_Handler:cpsid i						/* 关闭全局中断 *//* 关闭I,DCache和MMU    关闭 I/D Cache、 MMU、对齐检测和分支预测* 采取读-改-写的方式。*/mrc     p15, 0, r0, c1, c0, 0     /* 读取CP15的C1寄存器到R0中       		        	*/bic     r0,  r0, #(0x1 << 12)     /* 清除C1寄存器的bit12位(I位),关闭I Cache            	*/bic     r0,  r0, #(0x1 <<  2)     /* 清除C1寄存器的bit2(C位),关闭D Cache    				*/bic     r0,  r0, #0x2             /* 清除C1寄存器的bit1(A位),关闭对齐						*/bic     r0,  r0, #(0x1 << 11)     /* 清除C1寄存器的bit11(Z位),关闭分支预测					*/bic     r0,  r0, #0x1             /* 清除C1寄存器的bit0(M位),关闭MMU				       	*/mcr     p15, 0, r0, c1, c0, 0     /* 将r0寄存器中的值写入到CP15的C1寄存器中	 				*/#if 0/* 汇编版本设置中断向量表偏移 */ldr r0, =0X87800000dsbisbmcr p15, 0, r0, c12, c0, 0dsbisb
#endif/* 设置各个模式下的栈指针,* 注意:IMX6UL的堆栈是向下增长的!* 堆栈指针地址一定要是4字节地址对齐的!!!* DDR范围:0X80000000~0X9FFFFFFF*//* 进入IRQ模式 */mrs r0, cpsrbic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/orr r0, r0, #0x12 	/* r0或上0x13,表示使用IRQ模式					*/msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/ldr sp, =0x80600000	/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB *//* 进入SYS模式 */mrs r0, cpsrbic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/orr r0, r0, #0x1f 	/* r0或上0x13,表示使用SYS模式					*/msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/ldr sp, =0x80400000	/* 设置SYS模式下的栈首地址为0X80400000,大小为2MB *//* 进入SVC模式 */mrs r0, cpsrbic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式					*/msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/ldr sp, =0X80200000	/* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */cpsie i				/* 打开全局中断 */
#if 0/* 使能IRQ中断 */mrs r0, cpsr		/* 读取cpsr寄存器值到r0中 			*/bic r0, r0, #0x80	/* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */msr cpsr, r0		/* 将r0重新写入到cpsr中 			*/
#endifb main				/* 跳转到main函数 			 	*//* 未定义中断 */
Undefined_Handler:ldr r0, =Undefined_Handlerbx r0/* SVC中断 */
SVC_Handler:ldr r0, =SVC_Handlerbx r0/* 预取终止中断 */
PrefAbort_Handler:ldr r0, =PrefAbort_Handler	bx r0/* 数据终止中断 */
DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0/* 未使用的中断 */
NotUsed_Handler:ldr r0, =NotUsed_Handlerbx r0/* IRQ中断!重点!!!!! */
IRQ_Handler:push {lr}					/* 保存lr地址 */push {r0-r3, r12}			/* 保存r0-r3,r12寄存器 */mrs r0, spsr				/* 读取spsr寄存器 */push {r0}					/* 保存spsr寄存器 */mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49* Cortex-A7 Technical ReferenceManua.pdf P68 P138*/							add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据* 这个中断号来绝对调用哪个中断服务函数*/push {r0, r1}				/* 保存r0,r1 */cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */push {lr}					/* 保存SVC模式的lr寄存器 */ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */pop {lr}					/* 执行完C语言中断服务函数,lr出栈 */cps #0x12					/* 进入IRQ模式 */pop {r0, r1}				str r0, [r1, #0X10]			/* 中断执行完成,写EOIR */pop {r0}						msr spsr_cxsf, r0			/* 恢复spsr */pop {r0-r3, r12}			/* r0-r3,r12出栈 */pop {lr}					/* lr出栈 */subs pc, lr, #4				/* 将lr-4赋给pc *//* FIQ中断 */
FIQ_Handler:ldr r0, =FIQ_Handler	bx r0									

重点在复位中断和IRQ中断(目前学的比较浅,还不太理解),但是关于下图:
在这里插入图片描述
之前学习过ARM的三级流水过程:(类似下图)
在这里插入图片描述
中断发生保存的是0x2008,所以要跳回到0x2004处执行这条指令,不然漏了这条指令。

自行编写中断驱动文件

irqNesting:中断嵌套计数器
irqTable:中断服务函数数组,大小为 I.MX6U 的中断源个数:160 个
int_init:中断初始化函数,初始化了 GIC,然后初始化了中断服务函数表,最终设置了中断向量表偏移
system_irqtable_init:中断服务函数表初始化函数,初始化 irqTable,给其赋初值
start.S 中调用的 system_irqhandler 函数:根据中断号在中断处理函数表 irqTable 中取出对应的中断处理函数并执行
default_irqhandler: 默认中断处理函数

#include "bsp_int.h"/* 中断嵌套计数器 */
static unsigned int irqNesting;/* 中断服务函数表 */
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];/** @description	: 中断初始化函数* @param		: 无* @return 		: 无*/
void int_init(void)
{GIC_Init(); 						/* 初始化GIC 							*/system_irqtable_init();				/* 初始化中断表 							*/__set_VBAR((uint32_t)0x87800000); 	/* 中断向量表偏移,偏移到起始地址   				*/
}/** @description	: 初始化中断服务函数表 * @param		: 无* @return 		: 无*/
void system_irqtable_init(void)
{unsigned int i = 0;irqNesting = 0;/* 先将所有的中断服务函数设置为默认值 */for(i = 0; i < NUMBER_OF_INT_VECTORS; i++){system_register_irqhandler((IRQn_Type)i,default_irqhandler, NULL);}
}/** @description			: 给指定的中断号注册中断服务函数 * @param - irq			: 要注册的中断号* @param - handler		: 要注册的中断处理函数* @param - usrParam	: 中断服务处理函数参数* @return 				: 无*/
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam) 
{irqTable[irq].irqHandler = handler;irqTable[irq].userParam = userParam;
}/** @description			: C语言中断服务函数,irq汇编中断服务函数会调用此函数,此函数通过在中断服务列表中查找指定中断号所对应的中断处理函数并执行。* @param - giccIar		: 中断号* @return 				: 无*/
void system_irqhandler(unsigned int giccIar) 
{uint32_t intNum = giccIar & 0x3FFUL;/* 检查中断号是否符合要求 */if ((intNum == 1023) || (intNum >= NUMBER_OF_INT_VECTORS)){return;}irqNesting++;	/* 中断嵌套计数器加一 *//* 根据传递进来的中断号,在irqTable中调用确定的中断服务函数*/irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);irqNesting--;	/* 中断执行完成,中断嵌套寄存器减一 */}/** @description			: 默认中断服务函数* @param - giccIar		: 中断号* @param - usrParam	: 中断服务处理函数参数* @return 				: 无*/
void default_irqhandler(unsigned int giccIar, void *userParam) 
{while(1) {}
}

还是没怎么看懂,内容太多了,我觉得暂时先搞清楚一些原理概念以及简单的外部函数接口就行,内部可能在后续的学习中进行钻研

gpio内部有中断的设置:
在这里插入图片描述

中断函数写在这里:
在这里插入图片描述
主函数进行初始化:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/100425.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为Yolov7环境安装Cuba匹配的Pytorch

1. 查看Cuba版本 方法一 nvidia-smi 找到CUDA Version 方法二 Nvidia Control Panel > 系统信息 > 组件 > 2. 安装Cuba匹配版本的PyTorch https://pytorch.org/get-started/locally/这里使用conda安装 conda install pytorch torchvision torchaudio pytorch-cu…

JDK、JRE、JVM三者之间的关系

1.JDK 基本介绍 1) JDK 的全称 (Java Development Kit Java 开发工具包 ) JDK JRE java 的开发工具 [java, javac,javadoc,javap 等 ] 2) JDK 是提供给 Java 开发人员使用的&#xff0c;其中包含了 java 的开发工具&#xff0c;也包括了 JRE 。所以安装了 JDK &#xff0c;就…

论文研读|Protecting Intellectual Property of Deep Neural Networks with Watermarking

目录 论文信息文章简介研究动机研究方法水印生成水印嵌入版权验证 实验结果有效性&#xff08;Effectiveness&#xff09;高效性&#xff08;Converge Speed&#xff09;保真度&#xff08;Functionality&#xff09;鲁棒性&#xff08;Robustness&#xff09;Anti-剪枝攻击&am…

SpringBoot源码分析-自动装配-实现原理

文章目录 SpringBoot自动装配前言介绍实现原理SpringBootApplicationEnableAutoConfigurationselectImports方法没有走&#xff1f;DeferredImportSelector源码分析设计目的 总结 SpringBoot自动装配 前言 什么是自动装配&#xff1f;用过Spring的应该都知道&#xff0c;虽然…

MySQL总结练习题

目录 1.准备数据表 2.表之间的关系 3.题目 3.1 取得每个部门最高薪水的人员名称 3.2 哪些人的薪水在部门的平均薪水之上 3.3 取得部门中&#xff08;所有人的&#xff09;平均的薪水等级 3.4 不准用组函数&#xff08;Max &#xff09;&#xff0c;取得最高薪水 3.5 取…

【数据结构】归并排序和计数排序(排序的总结)

目录 一&#xff0c;归并排序的递归 二&#xff0c;归并排序的非递归 三&#xff0c;计数排序 四&#xff0c;排序算法的综合分析 一&#xff0c;归并排序的递归 基本思想&#xff1a; 归并采用的是分治思想&#xff0c;是分治法的一个经典的运用。该算法先将原数据进行拆…

BUUCTF SimpleRev

分析 该文件为64位的ELF文件&#xff0c;运行在linux平台 使用IDA64打开 进入Decry函数 输入flag和成功的提示 看看如何才能成功拿到flag 这里比较text和str2&#xff0c;text是源代码就有的 那么str2应该就是我们输入的内容 先分析text的内容是什么 进入join函数 该函数…

SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池

前言 之前搭建SpringBoot项目工程&#xff0c;所使用的持久层框架不是Mybatis就是JPA&#xff0c;还没试过整合MybatisPlus框架并使用&#xff0c;原来也如此简单。在此简单记录一下在SpringBoot项目中&#xff0c;整合MybatisPlus持久层框架、Druid数据库连接池的过程。 一、…

Eclipse iceoryx(千字自传)

1 在固定时间内实现无任何限制的数据传输 在汽车automotive、机器人robotics和游戏gaming等领域,必须在系统的不同部分之间传输大量数据。使用Linux等操作系统时,必须使用进程间通信(IPC)机制传输数据。Eclipse iceoryx是一种中间件,它使用零拷贝Zero-Copy、共享内存Share…

【OSPF宣告——network命令与多区域配置实验案例】

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;喜欢编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc…

win10电脑插入耳机,右边耳机声音比左边小很多

最近使用笔记本看视频&#xff0c;发现插入耳机&#xff08;插入式和头戴式&#xff09;后&#xff0c;右边耳机声音比左边耳机声音小很多很多&#xff0c;几乎是一边很清晰&#xff0c;另一边什么都听不到。 将耳机插到别人电脑上测试耳机正常&#xff0c;那就是电脑的问题。试…

自然语言处理(NLP)的开发框架

自然语言处理&#xff08;NLP&#xff09;领域有许多开源的框架和库&#xff0c;用于处理文本数据和构建NLP应用程序。以下是一些常见的NLP开源框架及其特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合…

Outlook屏蔽Jira AI提醒

前言&#xff1a;最近不知道为什么jira上的ai小助手抽风&#xff0c;一周发个几千封邮件…导致我现在都不想在邮箱里面跟找垃圾一样找消息了。实在忍无可忍&#xff0c;决定屏蔽AI小助手&#xff0c;方法很简单&#xff0c;follow me~~ 第一步&#xff1a;双击打开电脑版Outloo…

springboot家乡特色推荐系统springboot28

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

leetCode 1143.最长公共子序列 动态规划 + 滚动数组

1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串…

Oracle Database Express Edition (XE)配置与部署

获取下载安装包 https://www.oracle.com/cn/database/technologies/xe-downloads.htmlhttps://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/index.html安装.rpm安装包 cd /usr/local/src wget https://download.oracle.com/otn-pub/otn_software/db-express/oracle-d…

Linux下kibana的安装与配置

1. 环境配置 确保Linux服务器上已安装Java 8或更高版本。可以通过运行 java -version 来验证Java的版本。 下载Kibana 7.17.11的压缩文件&#xff0c;可以从Kibana 7.17.11下载 上传服务器&#xff0c;并解压Kibana压缩文件。 2. Kibana配置 编辑Kibana的配置文件 config/k…

Idea下面git的使用:变基、合并、优选、还原提交、重置、回滚、补丁

多分支和分支切换 变基和合并 变基是把本项目的所有提交都列出来按顺序一个个提交到目标分支上去 而合并是把两个分支合并起来&#xff0c;但是旧的分支还是可以启动其他分支&#xff0c;在旧的分支上继续开发 master: A -- B -- C -- M/ feature: D -- Emaster: A -…

粘性文本整页滚动效果

效果展示 CSS 知识点 background 相关属性综合运用position 属性的 sticky 值运用scroll-behavior 属性运用scroll-snap-type 属性运用scroll-snap-align 属性运用 整体页面效果实现 <div class"container"><!-- 第一屏 --><div class"sec&qu…

JAVA编程题-求矩阵螺旋值

螺旋类 package entity; /*** 打印数组螺旋值类*/ public class Spiral { // 数组行private int row; // 数组列private int col; // 行列数private int size; // 当前行索引private int rowIndex; // 当前列索引private int colIndex; // 行开始索引private int rowStart; //…