23、汇编编程中的浮点运算与栈初始状态

汇编编程中的浮点运算与栈初始状态

常见x86 - 64指令

在汇编编程里,有不少常见的x86 - 64指令,下面为大家介绍一些指令及其含义:
| 指令 | 含义 |
| — | — |
| pdivX SRC, DST | 对DST与SRC进行并行除法运算,并将结果存储在DST中(仅适用于整数值) |
| cvtXX2YY SRC, DST | 将SRC的低阶字节从XX指定的类型转换为YY指定类型的DST低阶字节,并非所有类型和组合都适用 |
| lock | 这是一个前缀,可与面向内存的指令结合使用,确保对该地址的缓存具有独占访问权 |
| nop | 执行“无操作”操作 |

浮点运算

计算机在处理数字时,此前我们接触的大多是整数,也就是没有小数点的数。而对于有小数点的十进制数,计算机处理起来存在普遍问题,因为计算机只能存储固定大小、有限的值,可十进制数的长度是任意的,甚至可能是无限长,像1/3的结果就是无限循环小数。

计算机处理小数的方式是按固定精度(有效位数)存储。计算机将十进制数分为三部分存储:符号位、指数和尾数。尾数包含实际要使用的数字,指数表示数字的大小。例如,12345.2可表示为1.23452 * 10^4,这里尾数是1.23452,指数是4,底数为10。但计算机采用的底数是2,所有数字都以X.XXXXX * 2^XXXX的形式存储,比如数字1存储为1.00000 * 2^0。这种存储数字的方式被称为浮点表示法,因为有效数字相对于小数点的位置会随指数变化。

不过,尾数和指数的长度是有限的,这就引发了一些有趣的问题。例如,计算机存储整数时,给它加1,结果就大1;但对于浮点数,情况并非如此。如果数字足够大,加1可能在尾数中根本体现不出来(要知道,尾数和指数部分的长度是有限的)。这会影响很多方面,尤其是运算顺序。给一个浮点数加1.0,如果这个数足够大,可能根本不会对其产生影响。在x86平台上,一个4字节的浮点数,虽然能表示很大的数,但超过16777216.0后,加1.0就不再有意义,数字不会改变。所以,在进行两个数先相加再相乘的运算时,先做加法和先分配乘法可能会得到不同结果。比如,1000.0 * (16777216.0 + 1.0)和1000.0 * 16777216.0 + 1000.0 * 1.0的结果就不同,前者为16777216000.0,后者为16777217024.0(后者也并非完全准确)。

浮点数在内存中的存储格式是IEEE 754。一般来说,双精度(64位)值中,1位用于符号位,11位用于指数,52位用于尾数;单精度(32位)值中,1位用于符号位,8位用于指数,23位用于尾数。需要注意的是,大多数计算机进行浮点运算比整数运算耗时长得多。所以,对速度要求高的程序大多使用整数。

浮点运算单元的发展历程

x86架构的浮点运算单元经历了多次迭代:
1. 最初阶段 :最初的8086芯片不支持浮点运算,一切都得手动完成。
2. 协处理器阶段 :后来通过协处理器8087添加了浮点支持,处理器会将相关指令交给协处理器处理。8087有一个80位的浮点值,有八个寄存器按栈的形式排列,称为FPU(浮点运算单元)栈,操作大多围绕栈的“顶部”值进行。
3. 集成阶段 :到了80486,浮点运算单元被集成到芯片中。
4. 扩展阶段 :为了扩展浮点支持并添加并行操作,Intel和AMD分别添加了额外指令。Intel的是“MMX”,添加了八个特殊的浮点寄存器,名为%mm0到%mm7,这些寄存器可直接访问;AMD的是“3DNow!”。
5. SSE系列 :Pentium III时,Intel添加了类似于3DNow!的一组指令,即SSE(流式SIMD扩展),使用一组称为XMM寄存器(%xmm0到%xmm7)的八个寄存器,这些寄存器宽128位,但只能同时存储四个32位的值。后续的SSE2在Pentium IV上推出,将寄存器数量扩展到16个(%xmm0到%xmm15),并且可以以多种方式访问(作为不同大小的整数或浮点值),x86 - 64指令集要求必须具备SSE2。

SSE2寄存器的使用

SSE2寄存器长128位,但不使用128位的值,它可以当作两个64位值、四个32位值、八个16位值或十六个8位值来处理。对这些寄存器的操作,除非另有说明,否则会同时对所有值进行。如果只关注一个值,就考虑最低阶的位(虽然指令会对所有值操作,但可以忽略其他值)。

可以使用标准的movq指令将四字(quadword)移入或移出XMM寄存器。若目标寄存器是XMM寄存器,高阶四字会被清零,这在一次只处理一个值时通常没问题,是在内存和通用寄存器之间来回移动数据的简便方法。

加载值有两种方式:
1. 编码为浮点值 :在数据段,就像有用于64位整数的.quad指令一样,有用于64位浮点值的.double指令。可以对其进行编码并正常加载,示例代码如下:

myval:
    .double 57.2
.section .text
_start:
    movq myval, %xmm0

需要注意的是,没有用于加载值的立即模式指令。
2. 整数转换 :若从整数开始并想将其转换为浮点数,可以使用cvtsi2sd指令,意思是将一个整数值转换为标量双精度浮点值。它需要一个源寄存器(不能是XMM寄存器)和一个目标寄存器(必须是XMM寄存器)。例如,要将数字5作为浮点数加载到%xmm0中,代码如下:

movq $5, %rax          # 将整数5放入%rax
cvtsi2sd %rax, %xmm0    # 将其转换为双精度浮点数存入%xmm0
移动整个寄存器

要移动整个128位,有多种指令可供选择,主要分为两类:对齐指令和未对齐指令。未对齐指令速度较慢,但可在任何寄存器或内存位置使用;对齐指令速度快,但如果使用的值未按16字节对齐,会触发异常。而且在某些处理器上,这些指令处理整数或浮点值时速度会有细微差别。具体指令如下:
| 指令 | 适用情况 |
| — | — |
| movdqu | 用于针对整数优化的未对齐移动 |
| movdqa | 用于针对整数优化的对齐移动 |
| movups | 用于针对浮点值优化的未对齐移动 |
| movaps | 用于针对整数优化的对齐移动 |

如果使用寄存器或知道要加载的内存是对齐的,就使用对齐版本的指令;否则,使用未对齐版本。

浮点运算与函数调用

进行使用浮点数的函数调用时,有几点需要注意:
- 参数传递 :对于浮点值参数,使用%xmm0到%xmm7寄存器传递。如果函数同时接收浮点和非浮点值,非浮点值按常规方式传递(使用%rdi、%rsi、%rdx、%r8和%r9),浮点值使用XMM寄存器传递。
- 可变参数函数 :对于像fprintf这样的可变参数函数,要将使用的XMM寄存器数量放入%rax,这样被调用的函数就能知道需要保存多少个向量寄存器。
- 返回值 :如果返回值是浮点数,会在%xmm0中返回。

例如,对于函数签名为double myfunc(int a, double b, int c, double d)的函数,a通过%rdi传递,b通过%xmm0传递,c通过%rsi传递,d通过%xmm1传递,返回值在%xmm0中。如果这是一个可变参数函数,需要将%rax设置为2,表示只有两个XMM寄存器有需要关注的内容。

浮点算术运算

值存入寄存器后,可使用稍有不同的指令进行标准运算。在算术指令末尾添加sd后缀,可使其对标量(单值)双精度值进行操作。例如,若%xmm0和%xmm1都加载了双精度值,可使用addsd %xmm0, %xmm1将它们相加,并将结果存储在%xmm1中。

乘法(mulsd)和除法(divsd)需要源操作数和目标操作数。除法中,第一个操作数是除数,第二个操作数是被除数,也是结果商的存储位置。

完成浮点运算后,有以下几种选择:
- 使用cvtsd2si将其转换回整数。
- 使用movq将其移回%xmm0,作为函数的返回值。
- 使用movq将其移到寄存器作为参数传递。

下面是一个将1除以3并使用fprintf输出结果的程序示例:

fpdiv.s
.globl main
output:
    .ascii "The result is %f\n\0"
.section .text
main:
    enter $0, $0
    movq $1, %rax
    movq $3, %rbx
    cvtsi2sd %rax, %xmm0
    cvtsi2sd %rbx, %xmm1
    divsd %xmm1, %xmm0
    movq stdout, %rdi
    movq $output, %rsi
    call fprintf
    leave
    ret

所有这些操作也可使用单精度(32位)浮点值进行,只需将sd后缀改为ss即可,会得到addss、subss、mulss、divss、cvtss2si和cvtsi2ss等指令。但将单精度浮点值传递给需要双精度值的函数(如fprintf)时,需要改变值的“大小”来匹配。可以使用cvtss2sd将单精度浮点值转换为双精度浮点值,也可使用cvtsd2ss进行反向转换。

向量操作

SSE2不仅能处理单个值,还能同时处理多个值。XMM寄存器长128位,可用于存储两个双精度值或四个单精度值,这种存储多个值的方式称为“打包”值。

要将值移动到寄存器的不同部分,可以直接加载到寄存器的低阶字节,然后将寄存器左移为下一个值腾出空间。不过,典型的movq指令在移动64位值时会将高阶字节清零,所以要用movsd指令,它能移动四字并保持寄存器的高阶字节不变。

要将XMM寄存器左移,需使用SSE特定的指令pslldq,它能将XMM寄存器左移指定的字节数。例如,pslldq $8, %xmm5会将%xmm5寄存器的内容左移8个字节,以便插入另一个四字。

下面是将内存中的双精度值复制到XMM寄存器两个四字的代码片段:

mydata:
    .double 1.5
.section .text
    # ... 之前的代码 ...
    movq mydata, %xmm6   # movq会将高阶字节清零
    pslldq $8, %xmm6     # 将该值移到高阶字节
    movsd mydata, %xmm6  # 将值加载到低阶字节,不影响高阶字节

提取值时,可使用普通的movq将数据从寄存器中移出,然后使用psrldq将源寄存器右移以获取下一个值。也可以用同样的技术加载较小的值,但需要使用movss指令移动4个字节(而不是8个)且不影响其他值。虽然SSE也能处理字大小和字节大小的值,但最好一次加载4或8个字节。

以这种方式加载值可以并行执行操作。对于浮点值,指令看起来和普通算术指令类似,但会添加p(表示打包),然后是d表示双精度,s表示单精度。例如,mulpd %xmm0, %xmm1会将%xmm0和%xmm1的低阶双精度值相乘,并将结果存储在%xmm1的低阶双精度位置,同时也会对高阶双精度值进行相同操作,即使用一条指令进行两次并行乘法。如果是单精度浮点运算,一条指令可以进行四次并行乘法。

下面是一个将2.1分别与5.0、6.0、7.0和8.0同时相乘,然后使用fprintf显示结果的程序:

vectormultiply.s
.globl main
.balign 16
starting_value:
    .single 2.1, 2.1, 2.1, 2.1
multiply_by:
    .single 5.0, 6.0, 7.0, 8.0
output:
    .ascii "Results: %f, %f, %f, %f\n\0"
.section .text
main:
    enter $0, $0
    # 一次加载整个128位的值
    movaps starting_value, %xmm4
    movaps multiply_by, %xmm5
    # 乘法运算
    mulps %xmm4, %xmm5
    # 提取值作为fprintf的参数
    movss %xmm5, %xmm0      # 提取第一个值到%xmm0
    cvtss2sd %xmm0, %xmm0   # 从单精度升级为双精度
    psrldq $4, %xmm5        # 移动下一个值到合适位置
    movss %xmm5, %xmm1      # 提取下一个值到%xmm1
    cvtss2sd %xmm1, %xmm1   # 从单精度升级为双精度
    psrldq $4, %xmm5        # 移动下一个值到合适位置
    movss %xmm5, %xmm2      # 提取下一个值到%xmm2
    cvtss2sd %xmm2, %xmm2   # 从单精度升级为双精度
    psrldq $4, %xmm5        # 移动下一个值到合适位置
    movss %xmm5, %xmm3      # 提取下一个值到%xmm3
    cvtss2sd %xmm3, %xmm3   # 从单精度升级为双精度
    movq $4, %rax # 保护4个XMM寄存器
    # 调用函数
    movq stdout, %rdi
    movq $output, %rsi
    call fprintf
    leave
    ret

可以看到,虽然将值整理成参数花了些功夫,但实际乘法只用了一条指令。如果有大量数学运算,这是一种快速完成的方法。SSE也能对整数进行向量操作,相关指令以p(表示打包)为前缀,后面跟着操作名称和操作数的大小(b表示字节,w表示字,d表示双字,q表示四字),例如paddw %xmm0, %xmm1会将%xmm0的每个字节与%xmm1相加。

栈的初始状态

栈初始时已经有以下值(从高内存到低内存):
- 空指针
- 第n个环境变量的指针(如果存在)
- 第二个环境变量的指针(如果存在)
- 第一个环境变量的指针(如果存在)
- 空指针
- 第n个程序参数的指针(如果存在)
- 第二个程序参数的指针(如果存在)
- 第一个程序参数的指针(如果存在)
- 程序文件名的指针
- 命令行参数计数(包括命令本身)

因此,在_start入口点,可以通过栈偏移量将这些信息加载到程序中。环境变量以单个字符串形式存储,如MYVAR = MYVAL,如果需要,要将其拆分为键(MYVAR)和值(MYVAL)。

下面的代码将参数计数加载到%rax,将文件名的指针加载到%rbx:

_start:
    movq 0(%rsp), %rax
    movq 8(%rsp), %rbx

如果使用C库(从main开始而不是_start),C库会帮我们完成这些操作。main的第一个参数是命令行参数计数,会在%rdi中可用;第二个参数是指向命令行参数字符串指针数组的指针,该指针会在%rsi中。还可以通过调用getenv函数获取单个环境变量,或调用environ函数获取所有环境变量。

需要注意的是,栈的起始位置(在内存中向下增长)接近0x00007fffffffffff,但Linux会对其进行一些随机化处理,防止黑客猜出栈在内存中的实际位置。

汇编编程中的浮点运算与栈初始状态

指令总结与对比

为了更清晰地理解各种指令的用途和特点,我们将前面介绍的一些关键指令进行总结和对比:
| 指令类型 | 指令示例 | 功能描述 | 适用场景 |
| — | — | — | — |
| 基本运算 | pdivX SRC, DST | 对DST与SRC进行并行除法运算,结果存于DST(仅整数) | 整数并行除法场景 |
| 类型转换 | cvtXX2YY SRC, DST | 将SRC低阶字节从XX类型转换为YY类型的DST低阶字节 | 不同类型数据转换 |
| 内存操作 | lock | 作为前缀与面向内存指令结合,确保对地址缓存的独占访问 | 多线程或并发环境下内存操作 |
| 无操作 | nop | 执行“无操作”操作 | 调试或填充指令序列 |
| 浮点加载 | cvtsi2sd | 将整数转换为双精度浮点数 | 从整数转换为浮点数场景 |
| 浮点运算 | addsd, mulsd, divsd | 对双精度浮点数进行加、乘、除运算 | 双精度浮点运算场景 |
| 寄存器移动 | movdqu, movdqa, movups, movaps | 移动128位数据,有对齐和未对齐之分,针对整数或浮点优化 | 不同对齐要求和数据类型的寄存器移动 |
| 向量运算 | mulpd, mulps | 对打包的双精度或单精度浮点数进行并行乘法运算 | 多数据并行运算场景 |

通过这个表格,我们可以更直观地看到不同指令在功能和适用场景上的差异,方便在实际编程中根据需求选择合适的指令。

浮点运算的注意事项

在进行浮点运算时,除了前面提到的精度和速度问题,还有一些其他的注意事项:
- 精度损失 :由于浮点数的尾数和指数长度有限,在进行复杂运算时,可能会累积精度损失。例如,多次进行加法和乘法运算后,结果可能与理论值有较大偏差。在对精度要求较高的场景中,需要谨慎处理。
- 异常处理 :浮点运算可能会产生一些异常情况,如除零错误、溢出等。在编程时,需要考虑如何处理这些异常,以确保程序的稳定性。可以通过检查状态寄存器或使用特定的异常处理机制来实现。
- 代码优化 :虽然浮点运算相对较慢,但可以通过合理的代码优化来提高性能。例如,尽量减少不必要的浮点运算,合理安排运算顺序,利用向量运算的并行性等。

栈操作的实际应用

栈的初始状态和操作在实际编程中有很多应用场景,下面我们通过一个简单的示例来展示如何利用栈的信息:

; 示例代码:打印命令行参数和环境变量
.globl _start

.section .text
_start:
    ; 加载参数计数到 %rax
    movq 0(%rsp), %rax
    ; 加载文件名指针到 %rbx
    movq 8(%rsp), %rbx

    ; 打印文件名
    movq %rbx, %rdi
    call print_string

    ; 打印参数计数
    movq %rax, %rdi
    call print_number

    ; 遍历环境变量
    movq %rsp, %r10
    addq $16, %r10 ; 跳过参数计数和文件名
    loop_env:
        movq (%r10), %r11
        cmpq $0, %r11
        je end_loop_env
        movq %r11, %rdi
        call print_string
        addq $8, %r10
        jmp loop_env
    end_loop_env:

    ; 退出程序
    movq $60, %rax
    xorq %rdi, %rdi
    syscall

; 打印字符串函数
print_string:
    movq %rdi, %rsi
    movq $1, %rax
    movq $1, %rdi
    call strlen
    movq %rax, %rdx
    syscall
    ret

; 打印数字函数
print_number:
    ; 这里可以实现将数字转换为字符串并打印的逻辑
    ret

; 计算字符串长度函数
strlen:
    xorq %rax, %rax
    loop_strlen:
        cmpb $0, (%rsi, %rax, 1)
        je end_strlen
        incq %rax
        jmp loop_strlen
    end_strlen:
    ret

这个示例代码展示了如何从栈中获取命令行参数计数、文件名和环境变量,并将它们打印出来。通过栈偏移量,我们可以方便地访问这些信息,为程序的进一步处理提供基础。

总结与展望

汇编编程中的浮点运算和栈操作是非常重要的内容,它们涉及到计算机底层的数值处理和内存管理。浮点运算虽然存在精度和速度等问题,但通过合理的使用和优化,可以在科学计算、图形处理等领域发挥重要作用。栈操作则为程序的参数传递、环境变量管理等提供了基础支持。

在未来的编程中,随着计算机硬件的不断发展,浮点运算和向量操作的性能将不断提升,同时也会有更多的指令和技术出现。我们需要不断学习和掌握这些新的知识,以更好地应对各种复杂的编程需求。同时,对于汇编编程这样的底层技术,深入理解其原理和机制,将有助于我们编写更高效、更稳定的程序。

总之,汇编编程中的浮点运算和栈操作是一个广阔而深入的领域,值得我们不断探索和研究。希望通过本文的介绍,能帮助大家更好地理解和应用这些知识。

以上就是关于汇编编程中浮点运算和栈操作的相关内容,希望对大家有所帮助。在实际编程中,要根据具体需求灵活运用这些知识,不断积累经验,提高编程能力。

玄铁RISC-V为何成为x86与ARM之外的第三极 RISC-V是一种开源指令集架构(ISA),其核心价值在于开放性、模块化与可扩展性,区别于ARM的授权壁垒和x86的封闭生态。其技术原理基于精简指令设计、标准化扩展机制(如Zicsr、Zam)及可验证的硬件抽象层,赋予开发者对CPU微架构的深度定制权。这一确定性带来显著技术价值:缩短芯片开发周期、降低BOM成本、规避IP授权风险,并支撑功能安全认证(如ISO 26262、IEC 61850)。典型应用场景覆盖嵌入式实时控制、边缘AI协处理、车规级网关及服务器异构加速等对可控性、低延迟与高可信有严苛要求的领域 阅读详情

相关推荐

蓝桥杯单片机国赛P3口硬件初始化与按键消抖实战指南

在嵌入式开发中,IO口初始化是连接软件逻辑与物理硬件的关键环节。51单片机的P3口作为准双向口,其电平状态受寄存器配置、外部上拉电路和RC建立时序三重约束,稍有疏漏即导致按键误判、LED响应异常等‘看似正确却跑不通’问题。尤其在蓝桥杯单片机国赛这类全自动评测场景下,客观题要求毫秒级时序精度与寄存器级行为确定性——例如CT107D开发板P3口必须在10μs内完成上拉稳定,消抖延时需严格匹配12.5ms黄金窗口。本文聚焦硬件底层真相,解析P3口初始化三重门、动态消抖实现及评测机零容错机制,助力考生从‘代码能编译

weixin_30698297的博客 351

RL78 Timer RDe寄存器配置实战:从原理到PWM生成与调试

定时器是嵌入式系统的核心外设,负责精确的时间基准生成、事件捕获与波形输出。其工作原理基于计数器在时钟驱动下递增,通过比较匹配或输入捕获实现精准控制。掌握定时器配置对于提升系统实时性、实现电机驱动、数字电源等复杂控制至关重要。本文聚焦瑞萨RL78系列微控制器的Timer RDe模块,深入剖析其多模式运作机制,特别是PWM生成与互补输出带死区等高级功能。通过详解TRDCRi、TRDIORAi等关键寄存器的配置逻辑与常见陷阱,并结合输入捕获测频、PWM生成等实战案例,为工程师提供从寄存器位域理解到代码实现的完整指

weixin_33816300的博客 440

第05章 内核代码(12):调度中枢的灵魂剖析——逐行拆解 sched.c 的进程管理、睡眠唤醒与时间脉搏

通过这次逐行拆解,我们不难发现,sched.cjiffies是钟表的秒针,每隔 10 毫秒响一次。schedule()是钟表的摆轮,在“公平”与“效率”之间寻找平衡,决定谁先走下一步。sleep_on()是钟表的“暂停键”,当任务在等待时,它让时间继续流动,却不消耗 CPU。do_timer()是钟表的发条,每次滴答都推动着整个系统前进。是钟表的指针,瞬间从一个任务跳到另一个任务。

weixin_42108533的博客 17

【信息科学与工程学】计算机科学与自动化——第十篇 芯片设计33 芯片中的后端设计20

编号子编号类型微电子领域领域关联子领域模型名称20​20.1​后端设计标准单元时序与功耗库NLDM/CCS时序模型​2020.2后端设计标准单元时序模型非线性延迟模型(NLDM)2020.3后端设计标准单元时序模型复合电流源模型(CCS)2020.4后端设计标准单元功耗模型开关功耗模型2020.5后端设计标准单元功耗模型内部功耗模型2020.6后端设计标准单元功耗模型漏电功耗模型2020.7后端设计标准单元噪声模型噪声容限模型2020.8后端设计标准单元噪声模型噪声传播模型2020.9后端设计标准单元PVT

weixin_49199313的博客 407

【信息科学与工程学】【数据科学】数据科学领域 第六篇 算法设计 01

算法设计

weixin_49199313的博客 1503

51单片机上实现卡尔曼滤波的工程重构方法

卡尔曼滤波是一种基于概率统计的最优估计算法,其核心原理是通过预测-更新循环融合动态模型与观测数据,实现对系统状态的递推最优估计。在嵌入式系统中,该算法的技术价值在于提升传感器数据融合精度与实时鲁棒性,广泛应用于姿态解算、导航定位和运动控制等场景。然而,在8位51单片机等资源受限平台,直接移植浮点版本会导致严重性能瓶颈。本文聚焦于51单片机与卡尔曼滤波这一典型矛盾组合,系统阐述如何通过状态降维、Q15定点运算、协方差静态化及查表替代三角函数等工程手段,完成算法在物理约束下的可部署重构,同时深度结合MPU605

chuange6363的博客 313

手把手调试:如何为高通Hypervisor上的GuestOS添加一个自定义的HAB服务通道

本文详细介绍了如何在高通Hypervisor环境下为GuestOS添加自定义HAB服务通道的全过程。从HAB通信框架的核心概念解析到设备树节点的动态注入机制,再到物理通道的内核层实现和用户层测试,提供了完整的开发案例和调试技巧,帮助开发者高效构建定制化通信服务。

weixin_27066963的博客 372

手把手教你用X-AnyLabeling实现医学图像标注:从SAM模型选择到多边形优化全流程

本文详细介绍了如何使用X-AnyLabeling平台结合Med-SAM等专业模型实现医学图像的高效标注。从环境配置、模型部署到实战工作流,涵盖智能辅助标注、低对比度解决方案及团队协作方案,帮助医疗从业者大幅提升标注效率与质量。特别适合处理CT/MRI等医学影像的复杂标注需求。

weixin_29251131的博客 457

TL431过压保护电路实战:5.5V阈值设计全流程(附MOSFET选型建议)

本文详细介绍了基于TL431和MOSFET构建5.5V阈值过压保护电路的完整设计流程。内容涵盖核心原理、阈值计算、驱动电路设计,并重点针对大电流应用场景,提供了MOSFET选型与散热处理的实战建议,帮助工程师打造可靠高效的电源保护方案。

weixin_28327051的博客 542

从零到一:在RK3588 Android12上实战RTL8723DU WiFi蓝牙双模驱动移植

本文详细介绍了在RK3588 Android12系统上实战RTL8723DU WiFi蓝牙双模驱动移植的全过程。从驱动源码获取与修改、系统级集成与编译,到调试与性能优化,提供了完整的解决方案。特别针对USB接口适配、电源管理和蓝牙共存等关键问题给出了具体代码示例,帮助开发者快速完成驱动适配并提升设备稳定性。

weixin_28703489的博客 326

REST-meta-MDD Data总结

sMRI数据使用:nwc1、nwc2、nwc3(同样数据集相关论坛中有相关信息指向需要使用wc1、wc2、wc3)多方咨询与讨论后决定使用nwc数据,这里说明wc数据相对wc数据要大的多如wc1的大小为1.33GB,而nwc1的数据却有6.90GB。VBM文件:/REST-meta-MDD/Sharing/REST-meta-MDD-VBM- phase1 -Sharing.tar.gz。1、Excel中的表型信息:1-男,2-女;-9999或空白表示缺少数据。

2404_88295660的博客 1542

MFC C++ 中开发 PLC 通信客户端

OPC UA | 工业标准化通信 | `open62541`(开源OPC UA库)或 `UA-.NET` 封装 | 跨平台、安全可靠,支持复杂数据类型 |- 连接句柄(`modbus_t*`/`UA_Client*`/`TS7Client*`)不可跨线程共享,需为每个线程创建独立连接。Modbus 是工业常用协议,分 **RTU**(串口)和 **TCP**(以太网)两种模式,以下以 Modbus TCP 为例。s=Tag`),复杂类型需解析扩展对象。

寒寒豆豆软件公司的博客 139

大数据领域数据采集的人工智能辅助技术

在大数据时代,“数据是石油”的隐喻早已深入人心,但采集高质量数据的过程却像“在大海里捞针”——数据源分散如繁星、格式杂乱如碎片、质量参差不齐如泥沙,传统采集方法(写死的脚本、固定的ETL流程)早已力不从心。这篇文章将带你走进AI辅助数据采集的世界:我们会用“智能捕鱼船”的比喻拆解AI如何解决传统采集的痛点,深入解析“数据源自动发现”“自适应数据解析”“实时质量管控”等核心技术的原理,用代码示例还原AI采集的真实流程,最后探讨多模态采集、联邦学习等未来趋势。

小程序开发 2357

车载以太网:PHY(物理层)介绍

本文以TJA1101芯片为实例介绍了车载以太网PHY(物理层)组成。

kevin1499的博客 6231

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

上一篇: 5、气动人工肌肉测试台设计与车架纵梁断裂分析
下一篇: 24、ASCII、Unicode、UTF - 8 与汇编代码优化解析
python9snake
博客等级 码龄1天 0粉丝 1724原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值