嵌入式学习——硬件(ARM内核汇编指令)——day52

ARM汇编指令

学习arm汇编的主要目的是为了编写arm启动代码,启动代码启动以后,引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有:

  1. 初始化异常向量表;
  2. 初始化各工作模式的栈指针寄存器;
  3. 开启arm内核中断允许;
  4. 将工作模式设置为user模式;
  5. 完成上述工作后,引导程序进入c语言主函数执行;

因此汇编指令的学习主要是围绕这几个目的展开,主要学习跟上述目的相关的指令。

  1. mov指令:加载12位立即数到寄存器或转移一个寄存器的值到另外一个寄存器

mov r0, #2 ;加载立即数2到寄存器r0,MOV{S}<c> <Rd>, #<const>

mov r1, r0 ;将r0寄存器的值加载到r1,MOV{S}<c> <Rd>, <Rm>

    1. 大多数指令的格式为opcode rd, rn ,rm,其中,rd是目标寄存器,rn是第一操作数寄存器。同理:
  1. add指令常用的两种方式

ADD{S}<c> <Rd>, <Rn>, #<const>

ADDS}<c> <Rd>, <Rn>, <Rm>{, <shift>}

类似的还有

  1. sub指令

SUB{S}<c> <Rd>, <Rn>, #<const>

SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}

以上四条指令都有立即数作为第二操作数的情况,那么是什么立即数呢?准确的说这里所指的是12位立即数imm12。先说怎么判断某数是不是12位立即数,12位立即数的条件是:

  1. 如果某个数的数值范围是0~255之间,那么这个数一定是立即数;
  2. 把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位;
  3. 这个数的二进制序列的右边必须为偶数个连续的 0

例如:0x234 = 0000 0000 0000 0000 0000 0010 0011 0100

最高位1至最低位1之间的二进制数序列:1000 1101没有超过8位

末尾1的右边有2个0,所以0x234是立即数

0x3f4 = 0000 0000 0000 0000 0000 0011 1111 0100

最高位1至最低位1之间的二进制数序列:1111 1101 从第一个1开始到最后一个1之间没有超过8位

末尾1的右边有2个0,所以0x3f4是立即数

0x132 = 0000 0000 0000 0000 0000 0001 0011 0010

最高位1至最低位1之间的二进制数序列:1001 1001 从第一个1开始到最后一个1之间没有超过8位

末尾1的右边有1个0,不满足第二条,所以0x132不是立即数

0x7f8 = 0000 0000 0000 0000 0000 0111 1111 1000

最高位1至最低位1之间的二进制数序列:1111 1111从第一个1开始到最后一个1之间没有超过8位

末尾1的右边有3个0,不满足第二条,所以0x7f8不是立即数

0xfab4 = 0000 0000 0000 0000 1111 1010 1011 0100

最高位1至最低位1之间的二进制数序列:0011 1110 1010 1101 从第一个1开始到最后一个1之间超过8位,不满足条件1,所以这个数不是立即数

这是因为ARM中将这 12bits 分为 8bit 常数(0~255)和 4bit 循环右移位值(0~15)

8bit 常数范围(0~255),位移的步进值是以2为单位(即实际位移 2 * rotate 位),可以表示循环有以(0~30)偶数位: 0、2、4、6、8、10、12、14、16、18、20、22、24、26、28、30。在实际存储这个数值的时候,要想办法把这个数压缩到这12位中去。压缩的方法就是找一个数,这个数必须是一个8bit数,之后循环右移2 * rotate位。如果能找打这个数,那么待保存的数就是立即数,否则就不是。

那么如果我们就是希望把一个非立即数存进rn,又该怎么做呢?

  1. ldr寄存器加载指令:

LDR{<c>}{<q>} <Rt>, <label> ;如ldr r0, =0x2FAB4

ldr指令多用于从ram中将一个32位的字数据传送到目的寄存器中

LDR<c> <Rt>, [<Rn>{, #+/-<imm12>}] 如:

LDR   R0,[R1,#8]             ;将内存地址为R1+8的字数据读入寄存器R0,这里的#8作为12位立即数是可以省略的

LDR<c> <Rt>, [<Rn>], #+/-<imm12> 如:

ldr r0, [r1], #8 ;将内存地址R1的字数据读入r0,之后r1+8

LDR<c> <Rt>, [<Rn>, #+/-<imm12>]! 如:

LDR   R0,[R1,#8] !          ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。

  1. bic指定位清零指令:

BIC{S}<c> <Rd>, <Rn>, #<const>;将rn中的字数据const为1的比特清零,把结果放入rd

  1. orr指定位置位(置1)指令:

ORR{S}<c> <Rd>, <Rn>, #<const>

  1. 汇编指令的s后缀,几乎所有的汇编指令都可以在指令后面加上s后缀,s后缀的含义是在指令执行过程中会更新cpsr寄存器的N,V,C,Z位

N:在结果是有符号的二进制补码情况下,如果结果为负数,则N=1;如果结果为非负数,则N=0

Z:如果结果为0,则Z=1;如果结果为非零,否则Z=0

C(无符号溢出):是针对无符号数最高有效位向更高位进位时C=1;减法中运算结果的最高有效位从更高位借位时C=0

V(有符号溢出):该位是针对有符号数的操作,会在下面两种情形变为1,两个最高有效位均为0的数相加,得到的结果最高有效位为1;两个最高有效位均为1的数相加,得到的结果最高有效位为0;除了这两种情况以外V位为0

       例如:

              mov r0, #0xFFFFFFFF

              adds r1, r0, #1

              上面的操作会导致Z,C置位,这是因为结果为0,并且从无符号数角度来看,已经从最高位向更高位进位了

              而

              mov r0. #0x7FFFFFFF

              adds r1, r0, #1

              会造成N位和C位置位,这是因为计算结果0x80000000最为位为1,代表负数,并且 从有符号角度来看,把一个整数加成了负数。

  1. 更新N,V,C,Z位有什么用呢?几乎所有的arm指令都可以在指令之后可选地增加执行条件

例如:movcs r0, #100;表示只有在C位置位的情况下才能把100加载入r0,这样的话就可以非常方便地实现指令的有条件执行。

例如:实现两个unsigned long long类型变量的求和,

unsigned long long l1 = 0x00000000FFFFFFFF;

unsigned long long l2 = 0x000000000000003;

很明显结果为0x0000000100000002

我们用r0装l1的高4字节,r1装l1的低四字节:

用r2装l2的高4字节,r3装l2的低四字节

用r4装结果的高4字节,r5装结果的低四字节

第6~7行:装入被加数

第8~9行:装入加数

第10~11行:清空装结果的变量

第12行:先加低四字节,这里因为考虑到进位问题,所以要更新N,V,C,Z位

第13行:之前的进位导致更高位数据的丢失,必须把这一位补回来,要不要补就看之前时候进位,进位标志是cs

练习:从r0, r1代表的两个有符号数中找到较大值放入r2寄存器

  1. CMP比较指令用于比较两个寄存器的值或者比较一个寄存器和立即数的值,其原理是对待比较的两个数求差,看结果是否为0,这个指令会无条件修改N,V,C,Z位。

如:

mov r0, #100

cmp r0, #100

会导致Z置位,从条件码表可知,只要Z置位就是两数相等。

  1. 跳转指令b

b指令类似c语言的goto语句,能够实现无条件跳转。跳转时需要一个lable,表示要跳转到什么地方去

配合之前的条件码,就可以实现一些较为复杂的操作,例如实现从0加到100的和

这就是循环实现的机制

  1. 事实上,程序跳转工作更多的是为了实现类似函数的功能,此时lable就是函数的函数名,其实lable本身代表的就是待跳转那一行指令的地址,b指令本质上就是把待跳转那行的地址装入pc寄存器,但是函数在调用完毕之后要回到调用处的下一行指令处执行,为了能够回到调用的下一行,需要使用bl指令。bl和b之间的区别就在于bl会在lr寄存器中保存回来的地址。如:

以上代码虽然实现了函数调用的程序流程,但是还存在两个问题:一是调用完毕以后r0, r1寄存器的值被max_of_two_numbers函数修改了,二是如果出现了函数的嵌套调用,那么lr寄存器的值就会被修改,而无法回到最开始的地方。要解决这个问题,就必须在每次函数调用前保护现场,在函数调用完毕以后恢复现场。而实现这个功能就需要使用栈这个数据结构。

  1. 栈的实现类型:

2440实现保护和恢复现场使用的栈是数组栈,即用一段连续的内存空间为栈提供空间。从数组栈的具体实现来看入栈的方式有四种做法:

  1. 空增:先写入数据,再让栈指针自增;
  2. 空减:先写入数据,再让栈指针自减;
  3. 满增:先让栈指针自增,再写入数据;
  4. 满减:先让栈指针自减,再写入数据。

arm体系采用的方案是满减,但是在进行操作之前,我们必须告诉2440栈底的位置,这里我们把栈底设置为0x40001000,从地址0x40000000开始的0x1000这段内存空间对应的是2440内部的一段ram,总共4k。实际能够使用的内存空间为[0x40000000~0x40000FFF],设置栈底指针寄存器: ldr sp =0x40001000

  1. 入栈保护指令stmfd(STMDB)

STMFD<c> <Rn>{!}, <registers>

其中Rn表示栈底指针寄存器,< registers >表示需要入栈保护的寄存器,!表示入栈之后sp自动自减。如:

stmfd sp!, {r0, r1, r2, r3-r12, lr}

  1. 出栈恢复指令ldmfd(LDM/LDMIA/)

LDMFD<c> <Rn>{!}, <registers>

中Rn表示栈底指针寄存器,< registers >表示需要入栈保护的寄存器,!表示出栈之后sp自动自增。如:

ldmfd sp!, {r0, r1, r2, r3-r12, lr}

  1. 在汇编中调用c语言编写的函数

设有c语言定义的函数void func_c(void);在汇编代码中调用该函数,只需用import声明函数名即可,之后就可以使用bl指令调用该函数,注意,既然是调函数,就一定要保护现场

  1. 向c函数传参

向c函数传参的方法很简单,如果参数个数小于等于4个,就直接用r0~r3传参,c函数返回值通过r0寄存器返回:

设有c函数:

int add_c(int a, int b, int c, int d)

{

return a + b + c + d;

}

如果参数个数大于4个,从第五个参数开始就需要通过栈来传参

  1. 在c语言中调用汇编编写的函数类似,不过在汇编中用export声明函数,同时需要在c语言中用extern声明函数,按照标准,调用者负责保护现场和恢复现场

传参方法于此类似

  1. 切换arm内核的工作模式

切换工作方式的思路很简单,由于内核的工作模式是由cpsr寄存器的低5位来设置的,那么就可以先把cpsr读出来,更改低5位之后再设置进去。这里读取cpsr使用mrs指令,写cpsr寄存器用msr指令,需要注意的是在keil环境下写cpsr需要写成:   msr cpsr_c r0;将r0的值写入到cpsr寄存器

学习了这些指令,最终就可以编写我们自己的启动代码了

       preserve8

       area reset, code, readonly

       code32

       entry

       b start

       nop

       nop

       nop

       nop

       nop

       b do_interrput

       nop

start

       ldr sp, =0x40001000

       mrs r0, cpsr

       bic r0, r0, #0x1F

       orr r0, r0, #0x12

       bic r0, r0, #(0x01 << 7)

       msr cpsr_c, r0

       ldr sp, =0x40001000

       sub sp, sp, #1024

       mrs r0, cpsr

       bic r0, r0, #0x1F

       orr r0, r0, #0x10

       msr cpsr_c, r0

       ldr sp, =0x40001000

       sub sp, sp, #2048

       import main

       b main

do_interrput

       sub lr, lr, #4

       stmfd sp!, {r0-r12, lr}

       import interrupt_handle

       bl interrupt_handle

       ldmfd sp!, {r0-r12, pc}^

       end

代码

1. start.s

	preserve8
	area reset, code, readonly
	code32
	entry

	;ldr r0, =0x40000fff;装立即数

	
;	mov r0, #0xffffffff
;	bic r0, r0, #1;将最后一位清零
;	bic r0, r0, #(1 << 5);将第5位清零
;	bic r0, r0, #(1 << 7);将第7位清零


;	mov r0, #0x0			
;	orr r0, r0, #1;将最后一位置1
;	orr r0, r0, #(1 << 5);将第5位置1
;	orr r0, r0, #(1 << 7);将第7位置1


;	mov r0, #10	;两个数比较大小
;	mov r1, #20
;	cmp r0, r1
;	movge r2, r0 ;大的给r2
;	movle r2, r1 ;小的给r2


;	mov r0, #10	;三个数比较大小
;	mov r1, #20
;	mov r2, #30	

;	cmp r0, r1
;	movge r3, r0
;	movle r3, r1

;	cmp r3, r2
;	movle r3, r2	


;	mov r0, #1;对无符号longlong进行相加操作判断溢出
;	mov r1, #0
;	mov r2, #0xffffffff
;	mov r3, #0
;
;	adds r4, r0, r2
;	mov r5, #0
;	addcs r5, #1;cs为判断低四字节是否溢出


;	mov r0, #1;实现b
;	mov r1, #0
;	b foo
;	mov r2, #0xffffffff
;	mov r3, #0
;foo
;	adds r4, r0, r2	  ;add后加s,影响CPSR寄存器中的NZCV位,之后指令可加不同的条件分支
;	mov r5, #0
;	addcs r5, #1;cs为判断低四字节是否溢出	

;	mov r0, #0x10;两个数字比大小
;	mov r1, #0x20
;	cmp r0, r1
;	bge great
;	ble less
 ;
;great
;	mov r2, r0
;	b finished
;less
;	mov r2, r1


;	mov r0, #0x10	;判断三个数的最大值
;	mov r1, #0x20
;	mov r2, #0x30
 ;
;	cmp r0, r1
;	bge tmpgreat
;	ble tmpless
;tmpgreat
;	mov r3, r0
;	b continue
;tmpless
;	mov r3, r1

;continue
;	cmp r3, r2
;	bge finished
;	mov r3, r2

;	mov r0, #0	;1到100求和
;	mov r1, #0
 ;
;loop
;	cmp r1, #100
;	bgt finished
;	add r0, r0, r1
;	add r1, #1
;	b loop

;	ldr sp, =0x40001000;初始化满减栈
;	mov r0, #0	 
;	mov r1, #0
;	stmfd sp!, {r0-r12};保存现场,入栈保护,防止数据被修改
;	bl foo	;bl会在b指令跳转时,将下一行要执行指令的地址放入lr
;	ldmfd sp!, {r0-r12}; 恢复现场,出栈
 ;
;loop
;	cmp r1, #100
;	bgt finished
;	add r0, r0, r1
;	add r1, #1
;	b loop
;
;foo
;	mov r0, #1
;	mov r1, #2
;	add r2, r0, r1
;	bx lr  ;(跳转回原函数)mov pc, lr



;	ldr sp, =0x40001000	 ;入栈保护,嵌套调用汇编函数
;	mov r0, #1
;	mov r1, #2
;	stmfd sp!, {r0-r12, lr}
;	bl max_of_two_numbers
;	ldmfd sp!, {r0-r12, lr}
 ;
;foo
;	mov r0, #1
;	mov r1, #2
;	add r2, r0, r1
;	bx lr  ;(跳转回原函数)mov pc, lr
 ;
;max_of_two_numbers
 ;	mov r0, #3
;	mov r1, #4
;	stmfd sp!, {r0-r12, lr};不加lr,第二次lr会将第一次lr的值覆盖
;	bl foo
;	ldmfd sp!, {r0-r12, lr}
;	cmp r0, r1
;	movge r2, r0
;	movle r2, r1
;	bx lr

;	mov r0, #1
;	mov r1, #2
;	mov r2, #3
;	stmfd sp!, {r0-r12, lr}	;汇编start.s调用main.c中的c语言函数,传参利用r0、r1、r2、r3传参 
;	import add_c			 ;汇编调c中函数用import声明c中的函数,c中调汇编函数在汇编中用export声明汇编的函数
;	bl add_c
;	ldmfd sp!, {r0-r12, lr} 


;	mov r0, #1	 ;编写函数实现求三个数中的最大值
;	mov r1, #2					  ;汇编给c中函数传参用r0-r3,其他的通过入栈出栈传参,返回值r0
;	mov r2, #3
;	stmfd sp!, {r0-r12, lr}	;汇编start.s调用main.c中的c语言函数,传参利用r0、r1、r2、r3传参 
;	import max_of_three_numbers	   ;调用函数时要入栈保护,保护现场恢复现场
;	bl max_of_three_numbers
;	ldmfd sp!, {r0-r12, lr} 


;	mov r0, #1	 ;编写函数实现传参五个数进入main.c
;	mov r1, #2
;	mov r2, #3
;	mov r3, #4
;	mov r4, #5
 ;
;	stmfd sp!, {r0-r12, lr}	;汇编start.s调用main.c中的c语言函数,传参利用r0、r1、r2、r3传参 
;	import access_five_numbers	  ;汇编调c语言函数用import
;	stmfd sp!, {r4}
;	bl access_five_numbers
;	ldmfd sp!, {r4}
;	ldmfd sp!, {r0-r12, lr} 


;	ldr sp, =0x40001000;初始化满减栈
;	import main
;	export foo
;	b main
 ;
;foo
;	add r0, r0, r1
;	bx lr  ;(跳转回原函数)mov pc, lr



;	ldr sp, =0x40001000
;	import main
;	export sum_c
;	b main
 ;
;sum_c
;	mov r2, r0	;1到100求和
;	mov r1, #0
;	mov r0, #0
 ;
;loop
;	cmp r1, r2
;	bxgt lr
;	add r0, r0, r1
;	add r1, #1
;	b loop

	ldr pc, =start
	ldr pc, =undifine_handler
	ldr pc, =swi_handler
	ldr pc, =prefetch_handler
	ldr pc, =abort_handler
	nop
	ldr pc, =IRQ_handler	 
	ldr pc, =FIQ_handler


undifine_handler
	b undifine_handler

swi_handler
	import swi_function
	stmfd sp!, {r0-r12, lr}
	ldr r0, [lr, #-4] ;取到swi中的值,当执行到swi这一行时, pc指向swi的下一行,lr放着swi下一行高四个字节的地址
	bic r0, r0, #0xff000000	;取swi中的0x80给到swi——function函数进行传参
	bl swi_function
	ldmfd sp!, {r0-r12, pc}^   ;^将spsr恢复到cpsr
	;ldmfd sp!, {r0-r12, lr}
	;bx lr
						 
prefetch_handler
	b prefetch_handler

abort_handler
	b abort_handler

IRQ_handler
	b IRQ_handler

FIQ_handler
	b FIQ_handler



start
	ldr sp, =0x40001000;初始化满减栈
	mrs r0, cpsr							;模式切换msr读取cpsr
	bic r0, r0, #0x1f;所有低五位清零
	orr r0, r0, #0x12;置位成IRQ模式10010
	msr cpsr_c, r0;转换模式为usr			;写cpsr,设置cpsr,写入m域

	ldr r0, =0x40001000;初始化满减栈
	sub r0, r0, #1024
	mov sp, r0

	mrs r0, cpsr
	bic r0, r0, #0x1f;所有低五位清零
	orr r0, r0, #0x10;置位成usr模式10000
	msr cpsr_c, r0;转换模式为usr

	ldr r0, =0x40001000
	sub r0, r0, #2048
	mov sp, r0

	swi #0x80
	import main
	b main


finished
	b finished;类似于while(1)

	end

2. main.c

//extern int add_c(int a, int b, int c);
//extern int max_of_three_numbers(int a, int b, int c);
//extern int access_five_numbers(int a, int b, int c, int d, int e);
//extern int foo(int a, int b);
//extern int sum_c(int a);
//int add_c(int a, int b, int c)
//{
//	return a + b + c;
//}
//
//int max_of_three_numbers(int a, int b, int c)
//{
//	int max = a;
//	max = a > b ? a : b;
//	max = max > c ? max : c;
//
//	return max;
//}
//
//int access_five_numbers(int a, int b, int c, int d, int e)
//{
//	return a + b + c + d + e;
//}
extern void swi_function(int n);

void swi_function(int n)
{
	while (n--)
	{
		
	}
	
}

int main(void)
{
//	int ret = foo(10, 20);
//	int sum = sum_c(100);
	while (1)
	{
		
	}
	
}


1. add(s)<c>

s : 影响CPSR寄存器中的NZCV位,之后指令可加不同的条件分支

c : 

movge >= 

movgt>

b

bl : 保存下一条指令的地址

bx

arm满减栈

函数调用者实现出栈入栈

传参:两种方法一样

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端学习 Vue 插槽如何实现组件内容分发?

目录 一、Vue.js框架介绍二、什么是Vue 插槽三、Vue 插槽的应用场景四、Vue 插槽如何实现组件内容分发 一、Vue.js框架介绍 Vue.js是一个用于构建用户界面的渐进式JavaScript框架。它设计得非常灵活&#xff0c;可以轻松地被集成到现有的项目中&#xff0c;也可以作为一个完整…

Redis-实战篇-缓存击穿问题及解决方案

文章目录 1、缓存击穿2、常见的解决方案有两种&#xff1a;2.1、互斥锁2.2、逻辑过期2.3、两种方案对比 3、利用互斥锁解决缓存击穿问题3.1、ShopServiceImpl.java3.2、使用 jmeter.bat 测试高并发 4、利用逻辑过期解决缓存击穿问题 1、缓存击穿 缓存击穿问题 也叫 热点key问题…

半个月从几十升粉到500(发红包喽)

目录 1. 背景2. 涨粉秘籍2.1 持续创作高质量内容2.1.1 保持频率2.1.2 技术文章为主2.1.3 图文并茂 2.2 积极参与社区活动2.2.1 社区分享2.2.2 发文活动 2.3 互动与建立信任2.3.1 与读者互动2.3.2 红包互动2.3.3 动态分享 2.4 标题与内容的优化2.4.1 标题吸引2.4.2 内容实用 2.5…

【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【从零开始学架构 架构基础】五 架构设计的复杂度来源:低成本、安全、规模

架构设计的复杂度来源其实就是架构设计要解决的问题&#xff0c;主要有如下几个&#xff1a;高性能、高可用、可扩展、低成本、安全、规模。复杂度的关键&#xff0c;就是新旧技术之间不是完全的替代关系&#xff0c;有交叉&#xff0c;有各自的特点&#xff0c;所以才需要具体…

解决idea中git无法管理项目中所有需要管理的文件

点击文件->设置 选择版本控制—>目录映射 点击加号 设置整个项目被Git管理

springboot助农电商系统-计算机毕业设计源码08655

摘要 近年来&#xff0c;电子商务的快速发展引起了行业和学术界的高度关注。基于移动端的助农电商系统旨在为用户提供一个简单、高效、便捷的农产品购物体验&#xff0c;它不仅要求用户清晰地查看所需信息&#xff0c;而且还要求界面设计精美&#xff0c;使得功能与页面完美融…

【GPU虚拟化到池化技术深度分析 2024】

文末有福利&#xff01; 随着大模型的兴起&#xff0c;对GPU算力的需求越来越多&#xff0c;而当前现实情况使企业往往受限于有限的GPU卡资源&#xff0c;即便进行了虚拟化&#xff0c;往往也难以充分使用GPU卡资源或持续使用资源。为解决GPU算力资源不均衡等问题&#xff0c;…

国标GB/T 28181详解:国标GBT28181-2022第三方呼叫控制的视音频文件下载流程

目录 一、定义 1、国标GB/T 28181 2、第三方呼叫控制的视音频文件下载流程 二、作用 1、提供有效的数据回顾机制 2、增强监控系统的功能性 3、保障数据传输与存储的可靠性 4、实现精细化的操作与控制 5、促进监控系统的集成与发展 6、提供清晰的信令流程和操作规范 三…

驱动开发:配置Visual Studio驱动开发环境

100编程书屋_孔夫子旧书网 配置驱动开发环境配置驱动开发模板配置驱动双机调试 在正式开始驱动开发之前&#xff0c;需要自行搭建驱动开发的必要环境&#xff0c;首先我们需要安装Visual Studio 2013这款功能强大的程序开发工具&#xff0c;在课件内请双击ISO文件并运行内部的…

基于web的产品管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于web的产品管理系统,java项目。 ecli…

5G RAN

两个entity&#xff1a;NodeB、UE entity之间传输数据的东东 entity内部的流水线岗位&#xff1a;L3/L2/L1 岗位之间是消息交互/信令交互

阿里云centos7.9 挂载数据盘到 www目录

一、让系统显示中文 参考&#xff1a;centos7 怎么让命令行显示中文&#xff08;英文-&#xff1e;中文&#xff09;_如何在命令行中显示中文-CSDN博客 1、输入命令&#xff1a;locale -a |grep "zh_CN" 可以看到已经存在了中文包 2、输入命令&#xff1a;sudo vi…

在Clion使用CubeMX Stm32的步骤

Step1 准备软件&#xff0c;安装环境&#xff1a; 1. cubemx v6.5.0&#xff08;可以兼容以前版本的project&#xff09; https://www.st.com.cn/zh/development-tools/stm32cubemx.html STM32CubeMX 默认安装目录, 6.5版本可以兼容老版本 C:\Program Files\STMicroelectroni…

Golang | Leetcode Golang题解之第199题二叉树的右视图

题目&#xff1a; 题解&#xff1a; /** 102. 二叉树的递归遍历*/ func levelOrder(root *TreeNode) [][]int {arr : [][]int{}depth : 0var order func(root *TreeNode, depth int)order func(root *TreeNode, depth int) {if root nil {return}if len(arr) depth {arr a…

3D Web轻量引擎HOOPS Web Platform赋能AEC行业数字化,高效渲染与多格式支持!

在建筑、工程和施工&#xff08;AEC&#xff09;行业&#xff0c;数字化转型和高效协作正变得越来越重要。为应对日益复杂的项目需求和不断提升的质量标准&#xff0c;AEC企业需要一种强大的工具来实现高效的3D可视化和数据管理。HOOPS Web Platform作为一款综合性3D开发平台&a…

金融科技如何多角度助力小微企业融资

一、引言 在全球化与数字化交织的时代背景下&#xff0c;金融科技&#xff08;FinTech&#xff09;作为新兴力量&#xff0c;正逐步改变传统的金融业态&#xff0c;尤其在助力小微企业融资方面&#xff0c;金融科技展现出了多元化的价值和优势。本文将从不同角度探讨金融科技如…

python操作elasticsearch

1、安装 首先&#xff0c;确保你已经安装了elasticsearch库。如果没有安装&#xff0c;可以使用pip进行安装&#xff1a; pip install elasticsearch 如下&#xff1a; 2、测试是否联通 输入 python 进入交互模式。然后依次输入一下命令。 from elasticsearch import Elas…

uniapp启动页面鉴权页面闪烁问题

在使用uni-app开发app 打包完成后如果没有token&#xff0c;那么就在onLaunch生命周期里面判断用户是否登录并跳转至登录页。 但是在app中页面会先进入首页然后再跳转至登录页&#xff0c;十分影响体验。 处理方法&#xff1a; 使用plus.navigator.closeSplashscreen() 官网…

Nginx网站服务详解(设置并发数、实现不同虚拟主机等)

一、nginx的最大并发数设置已经状态收集模块 [root192 nginx]# cat nginx.conf # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/user ngin…