嵌入式学习——ARM学习(2)——汇编学习

工具:Keil-uVision5

1、汇编

1.1 汇编的组成

指令:汇编语言的核心部分,表示 CPU 可以执行的操作,如数据传输、算术运算、逻辑运算等。

操作数:指令中用于指定操作对象的数据,可以是寄存器、内存地址或立即数。

标签:用于标识代码中的特定位置,通常用于跳转和循环控制。

伪指令:不直接对应于机器指令的指令,用于控制汇编过程,如 .data、.text、.global 等。

注释:用于解释代码,通常以特定符号(如 ;、@)开头和/* */,汇编器在处理时会忽略这些内容。

段:汇编程序通常分为不同的段,如代码段(.text)、数据段(.data)和堆栈段(.bss),每个段有不同的用途。

宏:一种用于简化代码的工具,可以定义一组指令并在需要时调用。

这些组成部分共同构成了汇编语言,使得程序员能够以更接近人类理解的方式编写与机器语言相对应的代码

1.2 汇编指令格式

1. **标签(可选)**:
   - 用于标识指令的位置,后面跟一个冒号(`:`)。
   - 例如:`loop_start:`

2. **操作码(Opcode)**:
   - 指令的名称,表示要执行的操作。
   - 例如:`MOV`、`ADD`、`SUB`。

3. **操作数**:
   - 指令的参数,指定操作的对象,可以是寄存器、立即数或内存地址。
   - 例如:`R0`、`R1`、`#5`。

4. **条件码(可选)**:
   - 用于指定指令的执行条件,通常放在操作码前。
   - 例如:`EQ`(相等)、`NE`(不相等)。

5. **注释(可选)**:
   - 用于解释指令的功能,通常以 `@` 开头,汇编器会忽略这些内容。
   - 例如:`@ 这是一个注释`。

### 示例格式:
```assembly
label:   opcode   operand1, operand2  @ 注释
```

### 示例指令:
```assembly
loop_start:   MOV     R0, #5          ; 将 5 移动到 R0 寄存器
               ADD     R1, R0, #10    ; 将 R0 和 10 相加,结果存储到 R1
```

在这个示例中:
- `loop_start` 是标签。
- `MOV` 和 `ADD` 是操作码。
- `R0`、`R1` 和 `#10` 是操作数。
- 注释解释了每条指令的功能。

2、汇编指令

2.1 数据搬移指令

2.1.1 MOV指令

格式:

        mov{条件码} 目标寄存器,操作数

解释:

        条件码可有可无,当存在条件码时,如果条件满足,则将操作数数值搬移到目标寄存器

主要功能:

        1、将数据从一个寄存器传送到另一个寄存器。

        2、将一个常数值传送到寄存器中。

        3、当 PC( R15)用做目的寄存器时,可以实现程序跳转。如“ MOV PC, LR”,所以这种跳转可以实现子程序调用及从子程序返回,代替指令“ B, BL”。       

        4、当 PC 作为目标寄存器且指令中 S 位被设置时,指令在执行跳转操作的同时,将当前处理器模式的 SPSR 寄存器的内容复制到 CPSR 中。这种指令“ MOVS PC LR”可以实现从某些异常中断中返回。

eg:

        MOV R0, #5      ; 将立即数 5 移动到 R0

2.1.2 MVN 指令

格式:

         mvn{条件码} 目标寄存器,操作数

解释:

        将操作数按位取反后的结果搬移到目标寄存器中

主要功能:

        1、MVN 是反相传送( Move Negative)指令。它将操作数的反码传送到目的寄存器。

        2、MVN 指令多用于向寄存器传送一个负数或生成位掩码。

        3、MVN 指令将 shifter_operand 表示的数据的反码传送到目的寄存器 Rd,并根据操作结果更新 CPSR 中相应的条件标志位。

主要作用:

        1、向寄存器中传送一个负数。

        2、生成位掩码( Bit Mask)。

        3、求一个数的反码。

eg:

        MVN R0, #0xfffffffe     ; 把0x00000001  移动到 R0

2.1.3立即数

在 ARM 汇编语言中,立即数(Immediate Value)是指在指令中直接给出的常量值,而不是存储在寄存器或内存中的值。立即数通常用于算术运算、逻辑运算和数据传输等操作。

数据的处理方式

假设存在立即数x,将x循环右移偶数位y,将会得到一个0-255内的数据z.此时,就说明x是一个立即数,当x当作指令的一部分执行时,实际保存到指令空间的时数据y和数据z,在指令空间的[7:0]保存的是数据z,[11:8]保存的是数据(32-y)/2.

立即数的特点

1. 直接使用:立即数可以直接在指令中使用,无需先加载到寄存器。

2. 范围限制:在 ARM 中,立即数的表示通常有范围限制,具体取决于指令的类型。例如,某些指令的立即数可以是 8 位、12 位或 16 位。

3. 前缀:在 ARM 汇编中,立即数通常以 # 符号开头。

立即数的判断:

1.直接写完编译,出错就不是立即数

2.判断一个数据是不是立即数时,只需要判断这个数据或者它的取反值能不能循环右移偶数位得到一个0-255内的数据,如果能吗,这个数据就是一个立即数

如果想要将一个非立即数放到寄存器中,可以使用一个伪指令LDR

格式:ldr 目标寄存器,=数据

eg: LDR R0,=0X12345678

2.2 移位运算指令

2.2.1 LSL 逻辑左移

解释:

        一个32位数据进行逻辑左移,最高位移出,最低位补0

格式:

        lsl{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        MOV R0, #0XFF

        LSL R1, R0, #0X4        @0xFF0

C语言语法也可以

        LSL R1, R0, #(0x1<<2)        @0xFF0

2.2.2 LSR 逻辑右移

解释:

        一个32位的数据进行逻辑右移,最低位移出,最高位补0

格式:

        lsr{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        MOV R0, #0XFF

        LSR R1, R0, #0X4        @0xF

2.2.3 ASR 算数右移

解释:

        一个32位的数据进行逻辑右移,最低位移出,最高位(30)补0(31符号位不变)

格式:

        asr{条件码} 目标寄存器,第一操作寄存器,第二操作数

2.2.4 ROR 循环右移

解释:

        一个32位的数据进行循环右移,最低位移出,补到最高位

格式:

        ror{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        MOV R0, #0XFF

        ROR R1, R0, #0X4        @0xF000000F

2.3 位运算指令

2.3.1 and 与运算

解释:

        将第一操作寄存器的数据和第二操作数进行与运算,得到的结果保存到目标寄存器中

格式:

        and{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        and r2,r1,r3 ;r2 = r1&r3

2.3.2 orr 或运算

解释:

        将第一操作寄存器的数据和第二操作数进行或运算,得到的结果保存到目标寄存器中

格式:

        orr{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        orr r2,r1,r3 ;r2 = r1 | r3

2.3.3 eor 异或运算

解释:

        将第一操作寄存器的数据和第二操作数进行异或运算,得到的结果保存到目标寄存器中

格式:

        eor{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        eor r2,r1,r3 ;r2 = r1 ^ r3

2.3.4 mvn取反运算

查看2.1.2

2.3.5 bic 按位清零(遇1清0)

解释:

        将第一操作寄存器的数据和第二操作数进行按位清零运算,得到的结果保存到目标寄存器

格式:

        BIC{条件码} 目标寄存器,第一操作寄存器,第二操作数

eg:

        bic r0, r0, #0x1011 ;清除r0中的位12、 4和0位,保持其余的不变

        bic r1, r2, r3 ;将 r3 和 r2 做“逻辑与”操作,结果保存到 r1 中

2.4 算数运算指令

2.4.1 add 加法

解释:

        将第一操作寄存器的数值+第二操作数,结果保存到目标寄存器中,如果+s,运算结果会影响到CPSR条件位

格式:

        add{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.4.1.1 adc 加法

解释:

        目标寄存器 = 第一操作寄存器 + 第二操作数 + cpsr的c位数值

格式:

        adc{条件码} 目标寄存器,第一操作寄存器,第二操作数

2.4.2 sub 减法

解释:

        将第一操作寄存器的数值-第二操作数,结果保存到目标寄存器中,如果+s,运算结果会影响到CPSR条件位

格式:

        sub{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.4.2.1 sbc 减法

解释:

        目标寄存器 = 第一操作寄存器 - 第二操作数 - cpsr的c位的取反值

格式:

       sbc{条件码} 目标寄存器,第一操作寄存器,第二操作数

2.4.3 rsb 翻转减

解释:

        将第二操作数-第一操作寄存器的数值,结果保存到目标寄存器中,如果+s,运算结果会影响到CPSR条件位

格式:

        rsb{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.4.4 mul 乘法

解释:

        将第二操作数*第一操作寄存器的数值,结果保存到目标寄存器中

格式:

        mul{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数

2.5 比较指令

cmp 比较

解释:

        将第一操作寄存器的数值和第二操作数进行比较 比较指令的本质是比较的两个数据进行减法运算,并且运算的结果会影响到CPSR寄存器的条件位

格式:

        cmp 第一操作寄存器,第二操作数

注意:

        比较指令经常和条件码一起使用,即先比较两个数的大小,再下面执行的指令后加上条件码,根据条件码 对应的条件是否满足决定这条指令是否执行

图表:

2.6 跳转指令

2.6.1 b

解释:

        跳转到指定的标签下执行,当程序发生跳转时lr不保存程序的返回地址

格式:

        b 标签

2.6.2 bl

解释:

        跳转到指定的标签下执行,当程序发生跳转时lr保存程序的返回地址

格式:

        bl 标签

2.7 内存读写指令

通过指令将一个寄存器的数据向内存中写或者从内存中读取数据保存到一个寄存器中

2.7.1 单寄存器内存读写指令 STR / LDR

向内存中写 str

        1、str 目标寄存器,[保存目标地址的寄存器] @将目标寄存器4字节数据写入到目标地址对应的内存中

        2、strh 目标寄存器,[保存目标地址的寄存器] @将目标寄存器2字节数据写入到目标地址对应的内存中

        3、strb 目标寄存器,[保存目标地址的寄存器] @将目标寄存器1字节数据写入到目标地址对应的内存中

从内存中读 ldr 

        1、ldr 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取4字节数据写入到目标寄存器

        2、ldrh 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取2字节数据写入到目标寄存器

        3、ldrb 目标寄存器,[保存目标地址的寄存器] @从目标地址对应的内存中读取1字节数据写入到目标寄存器

拓展:

        str 目标寄存器,[保存目标地址的寄存器,#操作数] @将目标寄存器的数据写入到目标地址+操作数为首地址的内存中

        ldr 目标寄存器,[保存目标地址的寄存器,#操作数] @从目标地址+操作数为首地址的内存中读取数据写入到目标寄存器

        str 目标寄存器,[保存目标地址的寄存器],#操作数 @将目标寄存器的数据写入到目标地址为首地址的内存中,然后保存目标地址的寄存器数值变为原来的数据+操作数大小

        ldr 目标寄存器,[保存目标地址的寄存器],#操作数 @从目标地址为首地址的内存中读取数据写入到目标寄存器,然后保存目标地址的寄存器数值变为原来的数据+操作数大小

2.7.2 批量寄存器内存读写指令  STM / LDM

写:将多个寄存器的数据写入到指定内存中

读:从指定的内存中读取数据写入到多个寄存器

2.7.2.1 stm / ldm

写:

        stm 保存目标地址的寄存器,{寄存器列表} @将寄存器列表中各个寄存器的数据写入到目标地址对应的内存中

读:

        ldm 保存目标地址的寄存器,{寄存器列表}  @从目标地址对应的内存中读取数据保存到寄存器列表中的各个寄存器中

注意:

        1.寄存器列表中寄存器编号如果连续,可以用-表示一个寄存器范围 {r1-r5} 如果寄存器编号散乱,则用 ,分隔每一个寄存器 {r1,r3,r5,r7} {r1-r3,r5,r7}

        2.无论寄存器列表中寄存器的顺序是什么样的,低内存地址始终和小编号寄存器对应

2.7.2.2 地址增长方式

当指定一个内存基地址后,我们对这个基地址内存进行读写操作,然后根据地址增长方式可以让我们操作的基地址发生变化

格式:

        stm{地址增长后缀} 保存目标地址的寄存器!,{寄存器列表}

解释:

        将寄存器列表中的寄存器数据写入到目标地址中时,保存目标地址的寄存器保存的目标地址会发生对应的改变

后缀:

        ia:先向目标地址对应的内存中写入数据,然后保存目标地址的寄存器保存的目标地址数值+4

        ib:先让保存目标地址的寄存器保存的地址+4,再向目标地址对应的内存中写入数据         da:先向目标地址对应的内存中写入数据,然后保存目标地址的寄存器保存的目标地址数值-4

        db:先让保存目标地址的寄存器保存的地址-4,再向目标地址对应的内存中写入数据

ldm同上

2.7.3 栈内存读写指令

操作栈内存需要知道栈顶的地址,而栈顶地址保存在SP寄存器中

栈类型

增栈:每次压栈结束后栈指针寄存器保存的栈顶地址往高地址方向增长

减栈:每次压栈结束后栈指针寄存器保存的栈顶地址往低地址方向增长

空栈:每次压栈结束后栈指针寄存器指向的栈顶空间没有保存的有效数据

满栈:每次压栈结束后栈指针寄存器指向的栈顶空间存在有效数据

栈可以分为空增栈(EA)、空减栈(ED)、满增栈(FA)、满减栈(FD)

ARMv7-a架构处理器默认使用满减栈实现压栈和出栈

压栈命令:

        push {寄存器列表} @将寄存器列表中每一个寄存器的数值进行压栈

出栈命令:

        pop {寄存器列表}

手动实现满减栈

方法1、db后缀压栈 ia后缀出栈

eg:

        

方法2、使用满减栈对应的后缀fd实现

eg:

        

2.8 状态寄存器传送指令 MRS / MSR

内核中存在一个寄存器CPSR,这个寄存器保存的是当前程序的状态,我们可以通过状态寄存器传送指令实现对当前程序状态的读取以及修改程序的状态

MRS 目标寄存器,CPSR 

        @读取CPSR保存的状态值到目标寄存器中

MSR CPSR,操作数

        @将操作数写入到CPSR寄存器中进而修改程序的状态

注意:

        在特权模式下可以通过修改cpsr寄存器的数值切换到USER模式

        但是USER模式下无法通过修改CPSR数值切换到特权模式

        想要在user模式下切换到其他模式,只要发生对应的事件后处理器自动进入对应的模式

2.9 软中断产生指令

2.9.1 软中断

swi 中断号

        @中断号是一个12位的立即数,不同的中断号用来标识不同的中断

2.9.2 异常模式和异常源

异常模式:当发生了异常之后处理器进入的工作模式就是异常模式

异常源:引发处理器进入异常模式的源头叫做异常源

5种异常模式和7种异常源

异常模式异常源
IRQ        

产生了一个IRQ中断

FIQ

产生了一个FIQ中断

UNDEF

处理器遇到未定义指令导致程序中止

SVC

复位

产生软中断

ABORT

取数据异常导致程序中止

取指令异常导致程序中止

2.9.3 异常向量表

在 ARM 架构中,异常向量表(Exception Vector Table)是一个特殊的内存区域,用于处理各种异常和中断。异常向量表的每个条目对应于特定类型的异常,指向处理该异常的代码(即异常处理程序)。

异常向量表的结构

在 ARM 体系结构中,异常向量表通常位于内存的起始地址(例如,地址 0x00000000)。每个异常向量的大小通常为 4 字节(32 位),因此每个异常向量表的条目可以容纳一个指向异常处理程序的地址。

地址异常进入模式
0x00000000复位管理模式
0x00000004未定义指令未定义模式
0x00000008软件中断管理模式
0x0000000C中止(预取)中止模式
0x00000010中止(数据)中止模式
0x00000014保留保留
0x00000018中断 IRQ中断模式
0x0000001C快中断 FIQ快中断模式

.section .vectors.word _reset_handler      ; 复位向量.word _undefined_handler   ; 未定义指令向量.word _swi_handler         ; 软件中断向量.word _prefetch_abort_handler ; 预取异常向量.word _data_abort_handler  ; 数据异常向量.word 0                    ; 保留.word _irq_handler         ; IRQ 向量.word _fiq_handler         ; FIQ 向量.section .text
_reset_handler:; 复位处理代码B main                    ; 跳转到主程序_undefined_handler:; 未定义指令处理代码B ._swi_handler:; 软件中断处理代码B ._prefetch_abort_handler:; 预取异常处理代码B ._data_abort_handler:; 数据异常处理代码B ._irq_handler:; IRQ 处理代码B ._fiq_handler:; FIQ 处理代码B .

2.9.4 异常处理流程(四大步,三小步)

1、保存程序当前的工作状态到对应的异常模式的SPSR寄存器中

2、修改SPSR

        1.修改程序的工作模式为对应的异常模式[4:0]

        2.修改程序的工作状态为ARM状态[5] @ARM为0,thumb为1

        3.根据当前异常的优先级设置程序合适的中断禁止[7:6] @1为忽略

3、将程序的返回地址保存到对应的异常模式的LR寄存器(L14)

4、修改PC寄存器的值为当前异常在异常向量表中的位置

注意:该步骤为处理器自动完成

2.9.5 异常返回工作

注意:该步骤为 程序员手动完成

1、恢复程序状态 SPSR => CPSR

2、返回主程序执行 LR => PC

2.9.6 测试代码

.text  
.global _start _start:
@初始化异常向量表b mainb .b do_swi  @产生软中断后跳到这里执行b .b .b .b .b .
main:
@初始化SVC模式下的栈ldr sp,=0X40000020@切换程序的工作模式到user模式MSR CPSR,#0x10@初始化user模式下的栈ldr sp,=0X40000020@主程序执行mov r1,#1mov r2,#2@产生一个软中断swi 1add r3,r1,r2b loop@软中断异常处理程序
do_swi:
@压栈保护现场stmfd sp!,{r1,r2,lr}mov r1,#3mov r2,#4mul r4,r1,r2ldmfd sp!,{r1,r2,pc}^  @异常返回loop:b loop
.end 

3、混合编程

要想实现C和汇编的混合编程必须遵循ATPCS规范。

ATPCS : ARM-Thumb Procedure Call Standard
 

int add(int i,int j) {return i+j;
}

将汇编的标签当作C语言的函数使用

将C语言的函数当作汇编的标签使用

函数参数的传递采用R0-R3进行传递,如果参数的个数大于4个通过压栈的方式进行传递

函数的返回值通过R0返回,如果函数的返回值大于4个字节通过r0-r1返回

ATPCS规范中规定ARM采用满减栈。

3.1 汇编调用C语言的函数 

#include <stdio.h>// 声明汇编函数
extern int add_func(int a, int b);int main() {int result = add_func(5, 10); // 调用汇编函数printf("Result: %d\n", result);return 0;
}
*****汇编文件**********
.text    
.global _start  _start: @ 1. 初始化栈指针,C代码运行必须有栈ldr sp, =0x40000820@ 2. 汇编调用c函数 @ 2.1 给C的函数传递实参值mov r0, #3   @ a = 3mov r1, #4   @ b = 4@ 2.2 汇编调用c的函数bl add_func@ 2.3 函数的返回通过r0返回,查看r0寄存器中的值loop:   b loop  .end

3.2 c语言调用汇编标签

.text    .globl _start  _start: @ 1. 初始化栈指针,C代码运行必须有栈ldr sp, =0x40000820@ 2. 汇编调用c,跳转到main函数b main.end
********c文件************
// 使用extern对函数进行声明
extern int add_func(int a, int b, int c, int d);int sum = 0;
int main()
{// 在c代码中调用汇编代码sum = add_func(1,2,3,4);while(1);return 0;
}
********汇编文件**********
.text 
.global add_func  @ 将add_func函数声明为全局add_func:add r0, r0, r1add r0, r0, r2add r0, r0, r3mov pc, lr
.end

3.3 c语言内联汇编

c语言中直接使用汇编的语法

通过asm关键字进行修饰

asm volatile("汇编指令\n\t"     //"\n\t"表示一条指令的结束.....:输出列表  //指令结果的输出值:输入列表  //指令的数据输入:破坏列表  //破坏列表指定我们当前被修改的资源 memory
);
********汇编启动文件*******
.text    
.globl _start  _start: @ 1. 初始化栈指针,C代码运行必须有栈ldr sp, =0x40000820@ 2. 汇编调用c,跳转到main函数b main
.end
**********c语言文件***********// 内联汇编 
int add_func2(int a, int b, int c, int d)
{int sum = 0;// 使用汇编实现求和asm volatile(  "add r0, r0, r1\n\t"  "add r0, r0, r2\n\t"  "add r0, r0, r3\n\t"  :"=r"(sum)  :"r"(a),"r"(b),"r"(c),"r"(d)  :"memory" );return sum;
}//"=r"(sum)表示输出从寄存器中放到变量sum中
// "r"(a) 指定输入从变量a中获取放到通用寄存器//"memory"声明使用内存// 使用extern对函数进行声明
extern int add_func(int a, int b, int c, int d);int sum = 0;
int main()
{// 调用内联汇编的函数 sum = add_func2(5,6,7,8);// 在c代码中调用汇编代码sum = add_func(1,2,3,4);while(1);return 0;
}*********汇编文件*************.text 
.global add_func  @ 将add_func函数声明为全局add_func:add r0, r0, r1add r0, r0, r2add r0, r0, r3mov pc, lr
.end

 

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

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

相关文章

【Material-UI】Slider 组件中的 Discrete Sliders 详解

文章目录 一、Slider 组件概述1. 组件介绍2. Discrete Sliders 的特点 二、Discrete Sliders 的基本用法1. step 属性2. marks 属性3. valueLabelDisplay 属性 三、深入理解 Discrete Sliders 的配置1. 自定义刻度标记2. 限制可选值3. 设置较小的步长4. 始终显示值标签 四、应用…

惊叹:《黑神话:悟空》所在 Steam 发行平台遭网络狂袭,威胁流量猛增两万倍!

8月24日&#xff0c;对于《黑神话&#xff1a;悟空》的玩家而言&#xff0c;本应是尽情畅玩游戏发售后第一个周六的美好时光&#xff0c;然而在当日晚间&#xff0c;众多玩家却发现该游戏的主要发行平台Steam无法登录。很快&#xff0c;“#Steam崩了#”便冲上微博热搜榜。不少玩…

搭建FTP服务器,通过浏览器访问FTP服务器,测试终端上传的音频文件。

文章目录 引言I 搭建FTP服务器II 浏览器访问FTP文件PC端浏览器访问iphone-safari浏览器访问FTP设置Mac-Safari浏览器访问FTP设置III FTP基础知识FTP客户端数据连接: 被动模式(PASV)引言 需求: 通过浏览器访问,测试终端通过FTP上传的语音文件,支持直接播放语音文件。 建议…

Spring底层机制环境搭建

文章目录 1.模块创建和依赖引入1.聚合模块&#xff0c;下面有一个myspring2.查看父模块是否管理了子模块3.myspring模块引入基本包 2.进行环境搭建1.目录概览2.UserController.java3.UserService.java4.UserDao.java5.AppMain.java6.beans.xml7.测试8.配置UserController.java为…

gptk是什么意思?Mac电脑如何在crossover里安装gptk2.0测试版?借助GPTK玩《原神》《黑神话悟空》游戏

很人多都听说使用 gptk2.0 beta 可以让《黑神话&#xff1a;悟空》等游戏的帧数提高&#xff0c;但自己并不知道如何安装&#xff0c;下面就给大家说下如何在crossover里安装 gptk2.0 beta 。安装前请先确认自己的电脑里已经安装好了crossover软件。 Game Porting Toolkit 简介…

数字化转型升级探索(二)

在数字化转型升级的探索中&#xff0c;我们计划通过整合前沿技术如人工智能、物联网和大数据&#xff0c;全面改造传统业务流程&#xff0c;打造智能化、数据驱动的业务架构&#xff0c;实现从数据采集、处理到分析的全链条数字化&#xff0c;以提升决策效率、优化运营管理&…

stm32-USB-1

1. USB简介 USB&#xff0c; 英文全称&#xff1a;Universal Serial Bus&#xff0c;即通用串行总线 USB提供适合各种应用的传输协议&#xff0c;而且协议标准向下兼容 优缺点 2. USB2.0拓扑结构 USB是一种主从结构的系统&#xff0c;数据交换只能发生在主从设备之间&#…

【STM32】写Keil程序的注意事项

看正点原子的资料使用Keil写STM32程序的时候&#xff0c;总是在不断学习&#xff0c;不断探索。后续又学到啥再更新 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目录 1 Keil设置 1.1 字体设置 1.2 快捷键设置 1.3 快速前往前一操作位置/后一操作位…

esp8266根据httpserver状态,调用网络唤醒,实现一键开机

esp8266根据httpserver状态&#xff0c;调用网络唤醒&#xff0c;实现一键开机 一.开发板程序二. 服务端三.服务端状态变更 一.开发板程序 #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <WiFiUdp.h> #include <ArduinoJson.h>/…

Autosar(Davinci) --- 创建一个OS TASK

目录 前言 一、认识OS 二、创建一个Basic Task 三、创建一个Extended Task 四、Task Mapping 五、生成代码 六、代码集成与编译 七、烧录&调试 八、Basic Task & Extended Task代码分析 前言 所有的runnable都是基于在TASK上运行的,那么我们这章就讲解,如何…

分享5款支持论文写作网站先稿后付的网站!

在当今学术研究和学术写作领域&#xff0c;AI论文写作工具已经成为不可或缺的助手。这些工具不仅能够提高写作效率&#xff0c;还能帮助研究人员生成高质量的论文内容。特别是那些提供“先稿后付”服务模式的网站&#xff0c;更是为用户提供了极大的便利和保障。以下是五款值得…

【Qt窗口】—— 状态栏

目录 1.1 状态栏的创建 1.2 在状态栏中显示实时消息 1.3 在状态栏中显示永久消息 状态栏是应用程序中输出简要信息的区域。⼀般位于主窗口的最底部&#xff0c;⼀个窗⼝中最多只能有⼀个状态栏。在Qt中&#xff0c;状态栏是通过QStatusBar类来实现的。在状态栏中可以显示的消…

2024118读书笔记|《岳阳楼记》——天高地迥,觉宇宙之无穷;兴尽悲来,识盈虚之有数

2024118读书笔记|《岳阳楼记》——天高地迥&#xff0c;觉宇宙之无穷&#xff1b;兴尽悲来&#xff0c;识盈虚之有数 爱莲说陋室铭小石潭记醉翁亭记赤壁赋桃花源记归去来兮辞木兰辞阿房宫赋滕王阁序岳阳楼记 《岳阳楼记》范仲淹&#xff0c;都是背过的古文&#xff0c;挺不错的…

并查集【算法 12】

并查集 (Union-Find) 的基础概念与实现 并查集&#xff08;Union-Find&#xff09;是一种用于处理不相交集合&#xff08;disjoint sets&#xff09;的数据结构&#xff0c;常用于解决连通性问题。典型的应用场景包括动态连通性问题&#xff08;如网络节点连通性检测&#xff0…

【STM32】FMC

FMC功能与FSMC类似&#xff0c;但比FSMC更强大&#xff0c;但仅在F4 / F7 / H7等高级一点的MCU上支持&#xff0c;F1不支持。虽然我的是F103&#xff0c;但顺便都看了。 大部分图片来源&#xff1a;正点原子HAL库课程 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目…

Axure 9 使用

一、界面初识 二、基础功能 1.菜单栏 1.1文件 新建文件&#xff1a;axure9包含四种文件.rp代表原型文件&#xff0c;.rplib代表元件库文件&#xff0c;.rpteam 团队项目文件 .html 网页文件 偏好设置&#xff1a;备份&#xff0c;需要备份文件再从备份中恢复 创建项目团…

二叉树前序,中序,后序非递归遍历(Java)

1. 思路&#xff1a; 首先创建一个栈和顺序表&#xff0c;按照根左右的前序遍历顺序去遍历这棵树&#xff0c;一直往左孩子方向遍历&#xff0c;每遍历到一个结点就入栈并且加入到顺序表里&#xff0c;如果没有左孩子了&#xff0c;就拿出栈顶元素&#xff0c;看它是否有右孩子…

智能学习辅助系统——后端部分

目录 前言 一、准备工作 1.需求&环境搭建 1.1需求说明 1.2环境搭建 2.开发规范 2.1 开发规范-REST 2.2 开发规范-统一响应结果 3.开发流程 二、部门管理 1.查询部门 &#xff08;1&#xff09;原型和需求 &#xff08;2&#xff09;接口文档 &#xff08;3&…

Unity2D游戏开发-Pak木鱼

在接下来文章里我会以Unity为主一起制作游戏 在unity 里如何制作一个简单的敲木鱼游戏&#xff1f; 创建一个2D场景&#xff08;本人使用Unity2023&#xff09; (每个一段时间要申请一个个人许可证) 点击下方蓝色按钮创建 将以下素材拖动到Assets文件夹中 这张图随意命名我…

深入理解DPO(Direct Preference Optimization)算法

目录 1. 什么是DPO&#xff1f;2. Bradley-Terry模型2.1 奖励模型的训练 3. 从PPO到DPO4. DPO的简单实现5. 梯度分析Ref 1. 什么是DPO&#xff1f; 直接偏好优化&#xff08;Direct Preference Optimization, DPO&#xff09;是一种不需要强化学习的对齐算法。由于去除了复杂的…