嵌入式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.rhkb.cn/news/153799.html

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

相关文章

【leetcode】 vscode leetcode [ERROR] invalid password? 问题解决

目录 问题解决 问题 使用vscode连接leetcode出现下列问题&#xff1a; vscode leetcode [ERROR] invalid password?出现invalid password?的问题&#xff0c;首先需要检查账号密码是否出错&#xff0c;leetcode的账号可以是手机或邮箱&#xff0c;然后密码去check一下&…

如何做好sop流程图?sop流程图用什么软件做?

5.如何做好sop流程图&#xff1f;sop流程图用什么软件做&#xff1f; 建立标准作业程序sop已经成为企业进步和发展的必经之路&#xff0c;不过&#xff0c;很多刚刚开始着手搭建sop的企业并不知道要如何操作&#xff0c;对于如何做sop流程图、用什么软件做sop流程图等问题充满…

【gitlab】从其他仓库创建项目

需求描述 解决方法 以renren-fast脚手架为例 第一步 第二步 第三步 第四步 参考文章

Redis的五种常用数据类型

1.字符串 String的数据结构是简单的Key-Value模型&#xff0c;Value可以是字符串&#xff0c;也可以是数字。 String是Redis最基本的类型&#xff0c;是二进制安全的&#xff0c;意味着Redis的string可以包含任何数据&#xff0c;比如jpg图片。 一个redis中字符串value最大是…

“注释: 爱恨交织的双重标准?解析注释在代码开发中的作用。”

文章目录 每日一句正能量前言观点和故事程序员不写注释的原因是什么如何才能写出漂亮的注释后记 每日一句正能量 水与水之间有距离&#xff0c;但地心下直相牵&#xff0c;人与人之间有距离&#xff0c;但心里时刻挂念&#xff0c;发条短信道声晚安&#xff0c;梦里我们相见。 …

06-进程间通信

学习目标 熟练使用pipe进行父子进程间通信熟练使用pipe进行兄弟进程间通信熟练使用fifo进行无血缘关系的进程间通信使用mmap进行有血缘关系的进程间通信使用mmap进行无血缘关系的进程间通信 2 进程间通信相关概念 2.1 什么是进程间通信 Linux环境下&#xff0c;进程地址空间…

vscode package.json文件开头的{总是提升警告

警告如下 Problems loading reference https://json.schemastore.org/stylelintrc.json: Unable to load schema from https://json.schemastore.org/stylelintrc.json: read ECONNRESET. 解决如下 在设置&#xff08;settings.json&#xff09;里 新增一条属性 "ht…

Mock工具之Moco使用

一、什么是Mock mock英文单词有愚弄、嘲笑、模拟的意思&#xff0c;这里主要是模拟的意思 二、什么是Moco 开源的、基于java开发的一个mock框架支持http、https、socket等协议 三、Mock的特点 只需要简单的配置request、response等即可满足要求 支持在request 中设置headers、…

【附代码】使用Shapely计算多边形外扩与收缩

文章目录 相关文献效果图代码 作者&#xff1a;小猪快跑 基础数学&计算数学&#xff0c;从事优化领域5年&#xff0c;主要研究方向&#xff1a;MIP求解器、整数规划、随机规划、智能优化算法 本文档介绍如何使用 Shapely Python 包 计算多边形外扩与收缩。 如有错误&…

sface人脸相似度检测

sface人脸相似度检测&#xff0c;基于OPENCV&#xff0c;人脸检测采用yunet&#xff0c;人脸识别采用sface&#xff0c;支持PYTHON/C开发&#xff0c;图片来自网络&#xff0c;侵权请联系本人立即删除 yunet人脸检测sface人脸识别&#xff0c;检测两张图片的人脸相似度

Redis-01基本数据结构

1、String 1.1、介绍 String 是最基本的 key-value 结构&#xff0c;key 是唯一标识&#xff0c;value 是具体的值&#xff0c;value其实不仅是字符串&#xff0c; 也可以是数字&#xff08;整数或浮点数&#xff09;&#xff0c;value 最多可以容纳的数据长度是 512M 1.2、…

基于springboot实现在线动漫信息交流分享平台项目【项目源码+论文说明】计算机毕业设计

基于springboot实现在线动漫信息交流分享平台演示 摘要 随着社会互联网技术的快速发展&#xff0c;每个行业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于在线动漫信息平台当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#x…

基于拉丁超立方法的风光场景生成与削减

代码链接&#xff1a;基于拉丁超立方法的风光场景生成与削减 摘要&#xff1a;与蒙特卡洛法不同&#xff0c;拉丁超立方采样改进了采样策略能够做到较小采样规模中获得较高的采样精度&#xff0c;属于分层抽样技术&#xff0c;设定风光出力遵从正态分布normrnd&#xff0c;从而…

【LeetCode】——链式二叉树经典OJ题详解

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法头疼记&#xff1a;算法专栏…

关于 打开虚拟机出现“...由VMware产品创建,但该产品与此版VMwareWorkstateion不兼容,因此无法使用” 的解决方法

文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/133678951 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结…

网工内推 | base郑州,上市公司,最高15薪,五险一金全额缴

01 四方达 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责公司数据中心&#xff08;机房&#xff09;的管理与运维工作。 2、负责公司服务器、路由器、防火墙、交换机等设备的管理、以及网络平台的运行监控和维护&#xff1b; 3、负责公司服务器运维管理工作、…

17基于matlab卡尔曼滤波的行人跟踪算法,并给出算法估计误差结果,判断算法的跟踪精确性,程序已调通,可直接运行,基于MATLAB平台,可直接拍下。

17基于matlab卡尔曼滤波的行人跟踪算法&#xff0c;并给出算法估计误差结果&#xff0c;判断算法的跟踪精确性&#xff0c;程序已调通&#xff0c;可直接运行&#xff0c;基于MATLAB平台&#xff0c;可直接拍下。 17matlab卡尔曼滤波行人跟踪 (xiaohongshu.com)

androidStudio第一次运行报错无法运行

安卓第一次运行失败 大家好&#xff0c;我使用androidStudio新建了一个测试demo第一次运行&#xff0c;结果失败了&#xff0c;显示如下图&#xff1a; 然后查了各种方法&#xff0c;都是没有用&#xff0c;最后 历经困难&#xff0c;还是找到了&#xff0c;原来是 gradle的依…

CV计算机视觉每日开源代码Paper with code速览-2023.10.9

精华置顶墙裂推荐&#xff01;小白如何1个月系统学习CV核心知识&#xff1a;链接 点击CV51&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【基础网络架构】Entropic Score metric: Decoupling Topology and…

2023.10.8 基本 Thread 线程详解

目录 Thread 常见构造方法 Thread 常见属性 创建一个 Thread 线程 使用 jconsole 命令观察线程 中断一个 Thread 线程 等待一个 Thread 线程 休眠当前 Thread 线程 让出当前 Thread 线程的 CPU 资源 线程的状态 Thread 常见构造方法 方法说明Thread()创建线程对…