LV.12 D18 中断处理 学习笔记

一、ARM的异常处理机制及工程代码结构

1.1异常概念

        处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生     这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件     异常事件处理完成之后再返回到被异常打断的点继续执行程序。

1.2异常处理机制

        不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制.

1.3ARM异常源

概念:导致异常产生的事件称为异常源

ARM异常源

FIQ                                快速中断请求引脚有效            

IRQ                                外部中断请求引脚有效    

Reset                             复位电平有效    

Software Interrupt          执行swi指令    

Data Abort                      数据终止    

Prefetch Abort                指令预取终止    

Undefined Instruction    遇到不能处理的指令

1.4异常模式

        在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切 换成对应的异常模式

1.5ARM异常响应

ARM产生异常后的动作(自动完成)
        1.拷贝CPSR中的内容到对应异常模式下的SPSR_<mode>

        2.修改CPSR的值

                2.1.修改中断禁止位禁止相应的中断

                2.2.修改模式位进入相应的异常模式   

                2.3.修改状态位进入ARM状态

        3.保存返回地址到对应异常模式下的LR_<mode>

        4.设置PC为相应的异常向量(异常向量表对应的地址)

1.6异常向量表

异常向量表
    > 异常向量表的本质是内存中的一段代码

    > 表中为每个异常源分配了四个字节的存储空间

    > 遇到异常后处理器自动将PC修改为对应的地址

    > 因为异常向量表空间有限一般我们不会再这里写异常处理程序,而是在对应的位置写一条跳

      转指令使其跳转到指定的异常处理程序的入口

    注:ARM的异常向量表的基地址默认在0x00地址但可以通过配置协处理器来修改其地址 
    

1.7异常返回

ARM异常返回的动作(自己编写)

        1.将SPSR_<mode>的值复制给CPSR使处理器恢复之前的状态

        2.将LR_<mode>的值复制给PC使程序跳转回被打断的地址继续执行 

1.8 IRQ异常举例

 注:整个过程CPSR保存的永远是当前程序运行状态,SPSR只是异常时对原来的CPSR进行备份

二、工程模板代码结构分析 

common:老师写好的库函数文件,里面实现了很多功能,比如把所有的寄存器封装,比如手搓了一个printf。

interface.c:  我们自己要实现的c源文件

start:  启动文件,任何芯片一上电执行的第一个程序一定是汇编程序,要初始化栈,初始化芯片,将异常向量表基地址位置改变,打开FIQ、IRQ然后跳转到C程序。

makefile:  编译规则

map.lds:   链接脚本,这个工程模板内有很多的C文件S文件H文件,他们编译链接后只生成一个.bin文件写入开发板,哪个文件放入哪个位置,我们写好的文件在内存中的位置都由它决定
 

三、中断处理框架搭建

LR保存的是被打断的下一条指令的地址(指的是汇编指令,一条c语言可能会编译成很多条汇编指令)

在遇到IRQ时会跳转到以_start:为基地址偏移0x18。

 

然后我们在 b main 后面来写这个中断服务程序,因为b main之前的代码都是启动代码,一开始就会执行。而 irq_handler是异常处理程序,芯片刚启动时我们不希望它执行,我们希望它遇到异常的时候再执行。

但是我们不能直接在这里写,因为IRQ模式下有很多寄存器都是和USER模式共用的如果在这里写,可定会用到一些寄存器,这样就会覆盖掉寄存器中本来的内容,返回主程序就不能正确返回这个状态了

所以我们需要先压栈保护现场

这个是时候就可以写了吗,还是不能,我们来复习一下LR寄存器 

R14(LR,Link Register)
    链接寄存器,一般有以下两种用途:

    > 执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址程序需要返回时将LR的值复制到PC即可实现。

    > 产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回。

原理

    当执行跳转指令或产生异常时,LR寄存器中不会凭空产生一个返回地址。其原理是当执行跳转指令或产生异常时,处理器内部会将PC寄存器中的值拷贝到LR寄存器中,然后再将LR寄存器中的值自减4。

BL

    当执行BL指令时,指令执行过程中处理器内部就会将PC寄存器的值拷贝到LR寄存器,然后再将LR寄存器中的值自减4, 所以LR寄存器中保存的就是BL指令下一条指令的地址。

该时刻PC=N+8 LR=N+4

 

 IRQ中断

    当执行一条指令时产生了一个IRQ中断,执行这条指令过程中处理器不会保存返回地址,而是执行完成后才会保存,但执行完成后PC的值又会自动增4,所以对于IRQ来说LR中保存的是被中断打断的指令的下下条指令的地址。

该时刻PC=N+12 LR=N+8

 

因为产生IRQ异常后自动保存到LR寄存器中的返回地址是被IRQ打断的指令下一条在下一条指令,所以需要我们人为的修复一下。

那么为什么不直接让他返回一个正确的呢,因为ARM是精简指令集,要想直接返回正确的必须要再加一个电路,这样会增加硬件成本,所以不如软件修复一条指令就解决了。

由于这是一个非叶子函数,在这段程序中可能还会有跳转,所以我们干脆把LR也压栈保护一下。
 

 异常处理程序既可以用汇编写,也可以通过混合编程用C语言写,但前面修改LR和压栈两条指令,只能用汇编写。

四、中断处理程序编程

interface.c

#include "exynos_4412.h"//异常处理程序
void do_irq(void)
{printf("Key2 pressed\n");
}void Delay(unsigned int Time)
{while(Time--);
}int main()
{/*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*//*将GPX1_1设置成中断功能*/GPX1.CON = GPX1.CON | (0xF << 4);/*设置GPX1_1中断触发方式:下降沿触发*/EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);/*使能GPX1_1的中断功能*/EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));	/*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*//*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/ICDDCR = ICDDCR | 1;/*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);/*选择CPU0来处理57号中断*/ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);/*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/CPU0.ICCICR = CPU0.ICCICR | 1;GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);while(1){/*点亮LED2*/GPX2.DAT = GPX2.DAT | (1 << 7);/*延时*/Delay(1000000);/*熄灭LED2*/GPX2.DAT = GPX2.DAT & (~(1 << 7));/*延时*/Delay(1000000);}return 0;
}

 start.S

.text
.global _start
_start:/** Vector table*/ b resetb .b .b .b .b .//从异常向量表再跳转到IRQ的异常处理程序b irq_handlerb .reset:/** Set vector address in CP15 VBAR register*/ ldr	r0, =_startmcr	p15, 0, r0, c12, c0, 0	@Set VBAR/** Set the cpu to SVC32 mode, Disable FIQ/IRQ*/  mrs r0, cpsrbic r0, r0, #0x1forr	r0, r0, #0xd3msr	cpsr ,r0/** Defines access permissions for each coprocessor*/  mov	r0, #0xfffffffmcr	p15, 0, r0, c1, c0, 2  	/** Invalidate L1 I/D                                                                                                                   */mov	r0, #0					@Set up for MCRmcr	p15, 0, r0, c8, c7, 0	@Invalidate TLBsmcr	p15, 0, r0, c7, c5, 0	@Invalidate icache/** Set the FPEXC EN bit to enable the FPU*/ mov r3, #0x40000000fmxr FPEXC, r3/** Disable MMU stuff and caches*/mrc	p15, 0, r0, c1, c0, 0bic	r0, r0, #0x00002000		@Clear bits 13 (--V-)bic	r0, r0, #0x00000007		@Clear bits 2:0 (-CAM)orr	r0, r0, #0x00001000		@Set bit 12 (---I) Icacheorr	r0, r0, #0x00000002		@Set bit 1 (--A-) Alignorr	r0, r0, #0x00000800		@Set bit 11 (Z---) BTBmcr	p15, 0, r0, c1, c0, 0/** Initialize stacks                                                                                                                  */
init_stack:     /*svc mode stack*/msr cpsr, #0xd3ldr sp, _stack_svc_end/*undef mode stack*/msr cpsr, #0xdbldr sp, _stack_und_end/*abort mode stack*/	msr cpsr,#0xd7ldr sp,_stack_abt_end/*irq mode stack*/	msr cpsr,#0xd2ldr sp, _stack_irq_end/*fiq mode stack*/msr cpsr,#0xd1ldr sp, _stack_fiq_end/*user mode stack, enable FIQ/IRQ*/msr cpsr,#0x10ldr sp, _stack_usr_end/*Call main*/b main//IRQ的异常处理程序
irq_handler://因为产生IRQ异常后自动保存到LR中的返回地址是被IRQ打断指令的//下一条再下一条指令的地址,所以我们需要人为的去修复一下sub lr, lr, #4//因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组//所以在处理异常之前需要先将之前USER模式下寄存器的值压栈保护stmfd sp!,{r0-r12}//处理异常bl do_irq//异常返回//1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值//2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前的状态//3.将栈中LR寄存器中的值出栈给PC,实现程序的返回ldmfd sp!,{r0-r12,pc}^_stack_svc_end:      .word stack_svc + 512
_stack_und_end:      .word stack_und + 512
_stack_abt_end:      .word stack_abt + 512
_stack_irq_end:      .word stack_irq + 512
_stack_fiq_end:.word stack_fiq + 512
_stack_usr_end:      .word stack_usr + 512.data
stack_svc:      .space 512
stack_und:.space 512
stack_abt:      .space 512
stack_irq:      .space 512
stack_fiq:      .space 512
stack_usr:      .space 512

 只按了一次按键,就会一直打印Key2 pressed。

中断挂起寄存器, EXT_INT41_PEND[1]对应GPX1_1引脚,会自动置1,置1就会把这个中断挂。当你处理完中断,返回到main函数,此时EXT_INT41_PEND[1]依旧是1,不会自动清零,所以还会给中断控制器发送中断信号,中断控制器还会将中断信号转发给CPU,还会触发中断。所以我们需要在CPU处理完中断后,把挂起位清零。

中断挂起寄存器比较特殊,写1才会清零,写0则保持不变。

我们在异常处理程序中对中断挂起寄存器的第一位进行修改,则按一次按键,只会产生一次中断。

但是有个新问题所有的IRQ异常都会跳到这里,那么我们需要区分一下,但是CPU不知道是谁发来的,所以需要询问中断控制器。

ICCIAR寄存器,后面[31:10]位与本次实验无关,我们只看[9:0]位,中断控制器把几号中断转给CPU,就会往这个寄存器的[9:0]位写几。所以我们可以让CPU在处理中断之前先读取这个寄存器的值,来写不同的中断处理程序。

#include "exynos_4412.h"//异常处理程序
void do_irq(void)
{unsigned int IrqNum = 0;/*从中断控制器中获取当前中断的中断号*/IrqNum = CPU0.ICCIAR & 0x3FF;switch(IrqNum){case 0://0号中断的处理程序break;case 1://1号中断的处理程序break;/** ......*/case 57:printf("Key2 pressed\n");/*清除GPIO控制器中的中断挂起位*/EXT_INT41_PEND = (1 << 1);break;/** ......*/case 159://159号中断的处理程序break;default:break;}}void Delay(unsigned int Time)
{while(Time--);
}int main()
{/*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*//*将GPX1_1设置成中断功能*/GPX1.CON = GPX1.CON | (0xF << 4);/*设置GPX1_1中断触发方式:下降沿触发*/EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);/*使能GPX1_1的中断功能*/EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));	/*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*//*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/ICDDCR = ICDDCR | 1;/*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);/*选择CPU0来处理57号中断*/ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);/*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/CPU0.ICCICR = CPU0.ICCICR | 1;GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);while(1){/*点亮LED2*/GPX2.DAT = GPX2.DAT | (1 << 7);/*延时*/Delay(1000000);/*熄灭LED2*/GPX2.DAT = GPX2.DAT & (~(1 << 7));/*延时*/Delay(1000000);}return 0;
}

此时,我们又发现按键只有第一次有效,之后再按按键就没有反应。

因为中断控制器不知道CPU0已经处理完中断处理程序了,所以并没有将新的中断信号发送给CPU0。

 ICCEOIR寄存器,本次实验只看[9:0]位,CPU处理完中断后,会把中断号写入该寄存器。

将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断

#include "exynos_4412.h"//异常处理程序
void do_irq(void)
{unsigned int IrqNum = 0;/*从中断控制器中获取当前中断的中断号*/IrqNum = CPU0.ICCIAR & 0x3FF;switch(IrqNum){case 0://0号中断的处理程序break;case 1://1号中断的处理程序break;/** ......*/case 57:printf("Key2 pressed\n");/*清除GPIO控制器中的中断挂起位*/EXT_INT41_PEND = (1 << 1);/*将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断*/CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | 57;break;/** ......*/case 159://159号中断的处理程序break;default:break;}}void Delay(unsigned int Time)
{while(Time--);
}int main()
{/*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*//*将GPX1_1设置成中断功能*/GPX1.CON = GPX1.CON | (0xF << 4);/*设置GPX1_1中断触发方式:下降沿触发*/EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);/*使能GPX1_1的中断功能*/EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));	/*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*//*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/ICDDCR = ICDDCR | 1;/*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);/*选择CPU0来处理57号中断*/ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);/*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/CPU0.ICCICR = CPU0.ICCICR | 1;GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);while(1){/*点亮LED2*/GPX2.DAT = GPX2.DAT | (1 << 7);/*延时*/Delay(1000000);/*熄灭LED2*/GPX2.DAT = GPX2.DAT & (~(1 << 7));/*延时*/Delay(1000000);}return 0;
}

这次代码我们需要实现按一次按键就产生一次中断,但个人原因,没有实现,也没找出问题,后续再解决。

五、中断编程补充

中断和轮询:轮询是CPU主动去查看硬件有没有异常产生,而中断是硬件主动通知CPU。中断的效率更高一些,用的较多。

真正开发时有操作系统,我们其实只需要写中断程序,然后打开对应中断就可以

FIQ为什么比IRQ快:

1)FIQ的优先级比IRQ高

2)FIQ可以打断IRQ

3)FIQ在异常向量表的最末,别的中断处理程序需要跳转,而FIQ可以直接往后写

4)FIQ有四组直接独有的寄存器,如果只需要r8-r12的话,他不需要压栈保护现场。但是如果用到了r0-r7还是要压栈保护现场的。


作业

1.使用中断的方式检测Key3按键的状态,实现按一次按键,LED2点亮,再次按下,LED2熄灭

 

 

  

  EINT[10]的中断号是58。

 本次实验依旧采用下降沿触发方式,GPX1_2对应EXT_INT41_CON[2]。

EXT_INT41_MASK[1]对应GPX1_1的开和关。0x0打开中断,0x1关闭中断。我们把它打开。

我们把ICDDCR寄存器写1 ,监控所有的外部中断,并将挂起的中断转发到CPU的接口

ICCDCR寄存器相当于GIC(中断控制器)的总开关。

 ICDISER_CPU寄存器的作用:寄存器接收到中断信号,通过配置该寄存器对应的位,控制该中断信号发送或不发送给CPU。 

 下面这个寄存器是ICDIPTR_CPU,它的作用是为每一个中断选择处理他的CPU。 

哪一位置1,中断信号就发给哪个CPU处理。但4412是一个四核的CPU,所以高四位是没有用的。

 

一共需要40个寄存器来管理这160个中断归属于哪一个CPU处理

想把58号中断交给CPU0处理,则把偏移地址为0x838的寄存器的[23:16]写为00000001即可。

 ICCICR_CPUn寄存器是中断控制器和CPU之间的接口,他就像一个开关,用哪个CPU就要打开哪个。

EXT_INT41_PEND[2]对应GPX1_2引脚,会自动置1,置1就会把这个中断挂。当你处理完中断,返回到main函数,此时EXT_INT41_PEND[2]依旧是1,不会自动清零,所以还会给中断控制器发送中断信号,中断控制器还会将中断信号转发给CPU,还会触发中断。所以我们需要在CPU处理完中断后,把挂起位清零。

中断挂起寄存器比较特殊,写1才会清零,写0则保持不变。

ICCIAR寄存器,后面[31:10]位与本次实验无关,我们只看[9:0]位,中断控制器把几号中断转给CPU,就会往这个寄存器的[9:0]位写几。所以我们可以让CPU在处理中断之前先读取这个寄存器的值,来写不同的中断处理程序。

 ICCEOIR寄存器,本次实验只看[9:0]位,CPU处理完中断后,会把中断号写入该寄存器。

将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断

#include "exynos_4412.h"unsigned int flag = 1;//异常处理程序
void do_irq(void)
{unsigned int IrqNum;/*从中断控制器中获取当前中断的中断号*/IrqNum = CPU0.ICCIAR & 0x3FF;switch(IrqNum){case 0://0号中断的处理程序break;case 1://1号中断的处理程序break;/** ......*/case 58:printf("Key3 pressed\n");if(flag == 1){GPX2.DAT = GPX2.DAT | (1 << 7);flag = 0;}else{GPX2.DAT = GPX2.DAT & (~(1 << 7));flag = 1;}/*清除GPIO控制器中的中断挂起位*/EXT_INT41_PEND = (1 << 2);/*将当前中断的中断号写回到中断控制器,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其他中断*/CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | 58;break;/** ......*/case 159://159号中断的处理程序break;default:break;}}void Delay(unsigned int Time)
{while(Time--);
}int main()
{/*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*//*将GPX1_2设置成中断功能*/GPX1.CON = GPX1.CON | (0xF << 8);/*设置GPX1_2中断触发方式:下降沿触发*/EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);/*使能GPX1_2的中断功能*/EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));	/*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*//*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/ICDDCR = ICDDCR | 1;/*在中断控制器中使能58号中断,使中断控制器在接收到58号中断后,能将其进一步转发到CPU接口*/ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);/*选择CPU0来处理58号中断*/ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0x1 << 16);/*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/CPU0.ICCICR = CPU0.ICCICR | 1;GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);//LED2while(1){/*点亮LED2*/GPX2.DAT = GPX2.DAT | (1 << 7);/*延时*/Delay(1000000);/*熄灭LED2*/GPX2.DAT = GPX2.DAT & (~(1 << 7));/*延时*/Delay(1000000);}return 0;
}

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

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

相关文章

【Python】解析CPP类定义代码,获取UML类图信息

参考 & 鸣谢 CppHeaderParser - 官方文档Python解析C头文件win10直接获得文件绝对路径的方法总结 目的 解析CPP头文件中的类定义&#xff0c;获取UML中的属性。用于画UML类图。如下所示格式&#xff0c;图片来源-链接 即获取&#xff0c;类名&#xff0c;成员函数&#x…

H110主板搭配魔改QNCW升级小记

最近搬家完毕&#xff0c;翻出来一块闲置已久的qncw&#xff0c;隐约记得是买的主板套装&#xff0c;现在主板早已不知踪影&#xff0c;剩下孤零零一个CPU&#xff0c;一起翻出来一个G3900T亮机CPU&#xff0c;应该是同时代的产物。 qncw百度上一搜&#xff0c;发现参数还行&am…

【ES6标准入门】JavaScript中的模块Module语法的使用细节:export命令和imprt命令详细使用,超级详细!!!

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript进阶指南 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继…

如何将vscode和Linux远程链接:

如何将vscode和Linux远程链接&#xff1a; Remote - SSH - 远程登录Linux 安装Remote - SSH 我们下载完后&#xff0c;就会出现这些图标 这里点一下号 查看一下我们的主机名&#xff0c;并复制 输入ssh 用户名主机名 这里是要将ssh这个文件要放在主机下的哪个路径下&#xff…

Android 10.0 系统修改usb连接电脑mtp和PTP的显示名称

1.前言 在10.0的产品定制化开发中,在usb模块otg连接电脑,调整为mtp文件传输模式的时候,这时可以在电脑看到手机的内部存储 显示在电脑的盘符中,会有一个mtp名称做盘符,所以为了统一这个名称,就需要修改这个名称,接下来分析下处理的 方法来解决这个问题 2.系统修改usb连…

gRPC 四模式之 双向流RPC模式

双向流RPC模式 在双向流 RPC 模式中&#xff0c;客户端以消息流的形式发送请求到服务器端&#xff0c;服务器端也以消息流的形式进行响应。调用必须由客户端发起&#xff0c;但在此之后&#xff0c;通信完全基于 gRPC 客户端和服务器端的应用程序逻辑。 为什么有了双向流模式…

网页视频下载工具 iTubeGo mac中文版软件特色

iTubeGo YouTube Downloader mac是一款功能强大的YouTube视频下载工具。 iTubeGo YouTube Downloader mac软件特色 多种格式支持&#xff1a;iTubeGo YouTube Downloader可以将YouTube视频下载为多种常见的视频和音频格式&#xff0c;包括MP4、MP3、AVI、FLV、MOV、WMV等&…

我又开始贩卖焦虑了,机器视觉兄弟们,打工这生意盘不活了?让人逃离北上广深,是毒鸡汤吗?

我想大多数人和我想的一样&#xff0c;不要质疑自己的出身&#xff0c;也不必用一生去改变出身而获得融入感&#xff0c;思想富足这是我们留给自己一生最珍贵的礼物。也许一线城市容不下肉身&#xff0c;二三线城市容不下灵魂。那我回到生我养我的十八线小县城&#xff0c;这不…

centos7安装mongodb

1、下载mongodb https://www.mongodb.com/try/download/community 2、解压 3、重命名 4、创建mongodb的data、logs目录 5、启动mongodb, bin/mongod --port27017 --dbpath/data/program/mongodb/data --logpath/data/program/mongodb/logs/mongodb.log --bind_ip0.0.0.0 --f…

WSL 2 更改默认安装的 Linux 发行版

目录 什么是 WSL 2&#xff1f;更改默认安装的 Linux 发行版更改发行版的 WSL 版本 什么是 WSL 2&#xff1f; WSL 2 是适用于 Linux 的 Windows 子系统体系结构的一个新版本&#xff0c;它支持适用于 Linux 的 Windows 子系统在 Windows 上运行 ELF64 Linux 二进制文件。 它的…

斯坦福机器学习 Lecture1 (机器学习,监督学习、回归问题、分类问题定义)

https://www.bilibili.com/video/BV1JE411w7Ub?p1&vd_source7a1a0bc74158c6993c7355c5490fc600 笔记如下 机器学习的定义&#xff1a;不需要明确编程就能让计算机去学习做某件事情 另一个定义 什么是监督学习&#xff1f; 给定一组 (x,y) 样本&#xff0c;学习一个 x-&g…

CentOS 7 安装CMake指定版本3.21.2

背景&#xff1a;今天在CentOS 7 电脑上安装C 日志框架SpdLog-1.12.0&#xff0c;提示如下错误信息&#xff1a; [rootlocalhost build]# cmake .. && make -j CMake Error at CMakeLists.txt:3 (cmake_minimum_required):CMake 3.10...3.21 or higher is required. …

本地Git项目同时推送至GitHub和Gitee

分别在gitee和github新建一个仓库 github: gitee: 添加远程仓库 git remote add origin1 [你的GitHub仓库URL] git remote add origin2 [你的Gitee仓库URL] 在本地中初始化创建一个git本地分支 git init 进入.git目录下修改config文件 [remote "origin"] url g…

VUE(一)

1.vue简介 英文官网: Vue.js - The Progressive JavaScript Framework | Vue.js 中文官网: Vue.js - 渐进式 JavaScript 框架 | Vue.js 2.Vue的特点 3.初识VUE 在官网下载VUE.js,有两个版本&#xff0c;一个开发一个生产 <!DOCTYPE html> <html lang"en"…

重磅 | 进一步夯实生态建设,朗思科技与阿里龙蜥完成兼容性认证

近日&#xff0c;北京朗思智能科技有限公司&#xff08;以下简称“朗思科技”&#xff09;自主研发的数字员工产品与OpenAnolis龙蜥社区龙蜥操作系统&#xff08;Anolis OS&#xff09;8完成兼容性认证。测试结果显示&#xff0c;双方产品相互兼容&#xff0c;功能正常&#xf…

电子学会C/C++编程等级考试2022年03月(一级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…

Codeforces Round 910 (Div. 2)(D~F)

1898D - Absolute Beauty 题意&#xff1a;给定长度为n的数组a和b&#xff0c;定义b数组的价值为&#xff0c;现可以交换一次b数组中的任意两个元素&#xff0c;求b数组的价值最大值。 思路&#xff1a;绝对值问题可以放在数轴上去解决。绝对值即为区间长度 观察上述三种情况&…

计算机毕业设计选题推荐-点餐微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

【c++随笔13】多态

【c随笔13】多态 多态性&#xff08;Polymorphism&#xff09;在面向对象编程中是一个重要概念&#xff0c;它允许以统一的方式处理不同类型的对象&#xff0c;并在运行时动态确定实际执行的方法或函数。一、什么是多态性&#xff1f;1、关键概念&#xff1a;C的多态性2、多态定…

SpringCloud微服务注册中心:Nacos介绍,微服务注册,Ribbon通信,Ribbon负载均衡,Nacos配置管理详细介绍

微服务注册中心 注册中心可以说是微服务架构中的”通讯录“&#xff0c;它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c;服务会注册到这里&#xff0c;当服务需要调用其它服务时&#xff0c;就这里找到服务的地址&#xff0c;进行调用。 微服务注册中心 服务注…