STM32单片机内存分配详细讲解

news2025/5/16 9:37:54

单片机的内存无非就两种,内部FLASH和SRAM,最多再加上一个外部的FLASH拓展。在这里我以STM32F103C8T6为例子讲解FLASH和SRAM。

STM32F103C8T6具有64KB的闪存和20KB的SRAM。

一. Flash

1.1 定义

非易失性存储器,即使在断电后,其所存储的数据也不会丢失。它可以进行多次擦除和写入操作,但擦除和写入的速度相对较慢。

对于flash而言,其内的数据只能读,不能写。

1.2 flash存储的数据

对于flash而言,其内存储的数据有以下几种:

1.用户代码

2.中断向量表

3.全局变量:

已被初始化和未被初始化的全局变量。

4.常量:

这里的常量是只可以读,不可以被修改的常量,被const修饰的变量。

1.3 STM32的flash地址

stm32f103c8t6的flash地址为:0x08000000 ~ 0x0801 FFFF

二. SRAM

2.1 定义

是一种易失性存储器,只有在通电的情况下才能保持数据,断电数据丢失。它的读写速度非常快,能够快速地响应 CPU 的访问请求。

对于SRAM而言,其内的数据可读可写。

2.2 SRAM存储的数据

1.局部变量:

2.静态变量:

被关键字static修饰的变量/常量。

在这里需要注意一点如果 static变量被初始化为常量(如 static int x = 5;),初始化值会被编译到 Flash 中,但变量本身(运行时的存储空间)仍在 SRAM 中。

运行时程序启动时,初始化值会从 Flash 复制到 SRAM 的静态数据区,之后变量的修改都发生在 SRAM 中。

3.堆Heap动态分配的内存。

注意:堆的内存分配有用户自己实现。主要通过下面几个函数进行开辟和释放

 malloc/freenew/delete 

在学习freertos的时候都应该知道freertos有5个heap管理的算法,有兴趣的可以查看freertos的源码查看一下其不同内存管理算法的区别。

4.栈Stack

存放局部变量、函数参数、返回地址等,由编译器自动管理,遵循 “后进先出” 原则。

栈的空间在调用期间自动创建和释放。

我们可以打开stm32的启动文件来查看初始系统堆和栈的大小。这个大小可以自己修改,但是一定不能超出SRAM的大小。

5.寄存器reg

2.3 STM32的SRAM地址

stm32f103c8t6的SRAM地址为:0x20000000 ~ 0x20004FFF

三.存储器映像图分析

 初始看见这个图,大家可能会很懵,但是一点点拆开来看,就很清晰了。

3.1 Flash memory

在表上可以明显看出其地址范围是0x0800 0000 ~ 0x0801 FFFF

3.2 SRAM

在图中可以看出SRAM的起始地址是0x2000 0000,但是图中并未标明结束地址,这是因为这个图是F103系列的存储器映像图,所以不同的型号其SRAM的结束地址不同

这个地址大家可以自己计算,计算公式为:起始地址 + 容量字节数 - 1

 STM32F103C8T6 的结束地址:

0x20000000+(20×1024)−1=0x20004FFF

3.3 Peripherals(外设)

在图中可以看到,外设的地址是从 0x4000 0000开始的,TIM,SPI,I2C等外设均在此地址范围内。

在此大家可能会有疑问,外设地址是不是就是寄存器地址呢?

其实不然,外设地址是寄存器的基地址寄存器的地址等于外设基地址+偏移量

打开stm32的xb.h文件,里面存放了关于每个外设的地址信息,我们可以很明显的看出,每一个外设的地址都是外设基地址+偏移量。

在查看STM32芯片手册的时候,也可以看到在配置每一个外设寄存器的时候,都会显示一个地址偏移量

 3.4 Cortex - M3 Internal Peripherals

 Cortex - M3 内核内部外设地址:0xE000 0000 ~ 0xE010 0000

包含内核调试组件、中断控制器(NVIC)等。

3.5 APB memory space(APB 总线外设内存空间)

  APB 总线上的外设,涵盖 CRC、Flash Interface、RCC、DMA 等。

3.6 特殊区域

Option Bytes:位于 0x1FFFF 800 附近,用于配置芯片的一些特殊功能,如写保护、读保护、BOR级别等。

System memory:在 0x1FFFF 000 附近,用于系统自举(Bootloader)等功能。芯片启动时可从这里加载代码执行,常用于实现 ISP功能。

四.代码段分析

 在STM32中,代码被存在flash和sram中,但其具体的存储位置还不明了。对于flash和sram,其中对不同的数据都有不同的存储位置,我上面给出了一个大概的逻辑图,接下来就对这些代码段进行具体分析。

在Cortex-M3权威指南中也有定义说明

4.1 .text段

也叫代码段,存储可执行的程序代码,包含函数体中的指令 。

比如定义的void main(void)函数以及其他我们自己定义的函数代码都存放在这里。

中断向量表一般也存放在此段,位于 Flash 起始地址附近,用于中断发生时引导 CPU 找到对应的中断处理函数 。

在STM32的启动文件中,也能看到部分代码段

 AREA定义了名为.text的区域,属性为代码段(CODE)、只读(READONLY) 。Reset_Handler是复位处理函数,先调用SystemInit进行系统初始化(如时钟配置等 ),再跳转到__main(C 库函数,最终会调用main函数 ) 。

在Cortex-M3权威指南中,也可以看到对.text段的定义和解释。

4.2 .rodata 段

只读数据段 ,存放只读的常量数据,以及一些字面量 。

在上文中,我们提到过,被const修饰的常量就是存放在此段的。

4.3 .bss 段 

未初始化数据段。用于存储未初始化的全局变量和静态变量。

比如int globalVar; (未初始化的全局变量)、static int staticVar; (未初始化的静态变量) 

在程序启动时,该段内存会被自动清零 ,这部分变量在运行时可被程序读写 。

4.2 .data段

已初始化数据段,存放已初始化的全局变量和静态变量。

例如int initializedGlobal = 5; (已初始化的全局变量)、static int initializedStatic = 3; (已初始化的静态变量)

在上面将SRAM中我们提到过,这些变量的初始值在编译时确定,程序启动时,其初始值从 Flash 复制到 SRAM 中,在程序运行过程中可被读写。

4.4 .Stack段 

栈段用于存储函数调用时的局部变量、函数参数、返回地址等

每当函数被调用,其局部变量等信息被压入栈中,函数执行结束后弹出 。

比如在一个函数中定义的int localVar = 1,该变量就存储在栈中 。

若函数调用层级过深或局部变量占用空间过大,可能导致栈溢出 。此时需要在启动文件处修改栈的大小或者修改代码来避免溢出。

4.5 .heap段

堆段用于动态内存分配的区域

当程序需要创建动态大小的数据结构(如链表节点、动态数组)或处理不确定大小的数据(如网络接收的可变长数据)时,可从堆中分配内存 。使用完后需手动释放,否则会造成内存泄漏 。

4.6 .reg

寄存器段。主要用于上电初始化配置外设模块的信息,此时需要操作寄存器配置一些参数。

五. .map文件分析(十分重要)

在编译代码结束的时候,我们都能看见这样一段

单片机是怎么知道我们的内存占用情况呢?我们应该如何分析每一个函数的内存占用空间呢?这个时候.map文件的作用就至关重要了。

5.1 什么是.map文件

.map文件是由连接器生成的列表文件,里详细的展示了每一段代码的占用情况。对分析和管理内存十分重要。

5.1.1符号与地址映射

记录程序中函数、全局变量等全局符号对应的起始地址 。通过它能知晓每个函数和变量在内存中的位置,便于调试和分析程序,比如可查找崩溃地址并定位到出错代码行 。

5.1.2内存使用情况

呈现程序各部分(如代码段、数据段等)在内存中的分布及占用空间大小 可查看 Flash 和 RAM 的占用情况 。

5.1.3段映射信息

明确程序中各个段(如.text、.data、.bss 等)实际映射的起始地址与长度 。与链接脚本(如.ld 文件 )中的段配置相关联,展示链接过程中段的具体布局 。

如果没有.map文件,点击下面的配置,再重现编译代码,就能看见了。

下面这个也建议勾选上,它可以删除冗余代码。 

5.2 Component头部信息

这个时候我们打开.map文件分析,大家可以打开自己工程的.map文件查看。这里我以我的工程为例子讲解。

我工程里的.map文件头部信息如下:

显示了编译器的版本和编译工具。

.map 文件的组成部分因编译器和开发环境不同而略有差异。

有的头文件可能会出现如硬件架构(如 Cortex-M3、ARMv7 等),工程名称、输出文件路径等基本信息。

5.3 Section Cross References交叉引用表

可以很明显的看出,交叉引用表的格式是十分统一的:

每一行的格式为:源目标文件(源段) refers to 目标目标文件(目标段) for 符号名 

通俗一点将就是在某某函数李调用了某某函数

以紫色部分为例:在main函数里调用了HAL_Init();SystemClock_Config();MX_GPIO_Init()等函数。

打开我们的工程main.c函数可以看见,确实如此。

 蓝色框里面的就是启动信息了,这个在启动文件里可以看见,就不过多赘述了。

(.o文件是链接器生成的目标文件。(i.main)代表的是main函数的基地址)

然后我们接着往下看。

5.4 Removing Unused input sections from the image

从生成的镜像文件中移除未使用的输入段。

简单来讲就是删除没用到的代码,节省空间。

来看红色框部分,移除了.rrx_text代码段的gpio.o文件,大小为6个字节。

在该段的最后会有这样一段

 这句话的意思是:从镜像文件中总共移除了 677 个未使用段,总计 66617 字节。

5.5 Image Symbol Table 镜像符号表

Local Symbols:局部符号。

Symbol Name:符号名称,这里显示的是源文件路径,对应目标文件中定义的符号来源 。

Value:符号对应的值,此处都是0x00000000 ,在不同场景下可能表示符号的地址等信息 。

Ov Type :符号的类型,这里均为Number ,表示这些符号是数值类型相关 。

Size:符号的大小,此处都是 0 。

Object(Section) :符号所在的目标文件及段,ABSOLUTE表示符号具有绝对地址 。

那么再看紫色横线的部分,意思就清晰可见了。这里就不过多赘述了。

再看下面这个图

 Global Symbols:全局符号

5.5.1 局部符号和全局符号的区别

他们的区别主要有以下几点

1.作用域
  • 局部符号作用域局限于定义它的特定模块、函数或代码块内部 。比如 C 语言中函数内部定义的局部变量、局部静态变量 ,以及函数内部声明的局部函数(如使用static修饰的内部函数 ),仅在该函数内可见和可访问 。像在一个main函数中定义的int localVar = 10; ,在main函数外部无法访问localVar 。
  • 全局符号作用域为整个程序 。在程序的任何位置,只要满足访问权限等要求,都能对其进行访问 。例如 C 语言中在所有函数外部定义的全局变量、全局函数 ,如int globalVar = 5; ,在其他源文件中通过extern声明后也可访问 。
2.可见性
  • 局部符号仅在定义它的代码区域内可见 。当程序执行流程离开该区域(如函数执行完毕返回 ),局部符号就不再可见 。比如函数中定义的临时变量,函数执行结束后其生命周期结束,无法再被访问 。
  • 全局符号在整个程序范围内可见 。只要程序在运行,全局符号始终存在且可被访问(需符合访问控制规则 ) 。
3.存储位置
  • 局部符号一般存储在栈(stack)中,像函数的局部变量,随着函数调用入栈,函数结束出栈 。局部静态变量存储在静态存储区 。
  • 全局符号全局变量通常存储在静态存储区 。未初始化的全局变量存放在.bss段,已初始化的全局变量存放在.data段 。全局函数的代码存放在代码段(.text) 。
4.生命周期
  • 局部符号自动局部变量生命周期随函数调用开始,函数返回结束 。局部静态变量在程序启动时初始化,程序结束时销毁 。
  • 全局符号全局变量和函数在程序启动时创建并初始化,程序运行期间一直存在,直到程序结束才销毁 。

那么它有什么作用呢?

5.5.2 镜像符号表的作用

5.5.2.1 调试定位

查找符号地址:在调试程序时,如果遇到程序崩溃或异常,通过错误提示中的地址信息,可在符号表中查找对应的符号名称 。比如知道一个错误地址,可在 “Symbol Name” 列找到该地址对应的源文件及符号,进而定位到具体代码位置,快速排查问题 。

函数和变量追踪:对于大型项目,函数和变量众多。符号表能帮助了解每个源文件中定义的符号情况,追踪函数调用关系和变量使用位置 。例如,想知道某个函数在哪些源文件中被引用,可借助符号表梳理 。

5.5.2.2 程序分析与优化

代码结构理解:通过查看符号表中不同源文件对应的符号,能清晰了解项目的代码结构 。比如哪些源文件属于底层驱动(如stm32f1xx_hal_xxx.c 相关 ),哪些是应用层代码(如main.c 等 ),有助于新开发者快速熟悉项目架构 。

在我们实际开发中写代码也应该分层编写,利于梳理。

未使用符号检测:可以发现一些未被使用的符号 。如果某个源文件对应的符号在整个项目中都没有被引用,可能是冗余代码,可考虑清理以优化代码体积和提高编译效率 。

5.5.2.3 链接与编译检查

符号冲突排查:在多文件编译链接过程中,可能出现符号重名冲突 。符号表能展示所有符号信息,方便开发者检查是否存在同名符号,避免因符号冲突导致的编译或运行错误 。

链接正确性验证:符号表中的信息与链接过程紧密相关 。它能反映链接器是否正确解析和处理了各个目标文件中的符号,验证链接的正确性 。如果符号表中符号信息异常,可能意味着链接过程出现问题 。

5.6 Memory Map of the image存储器映射

先看第一句:Image Entry point:表示程序镜像的入口点地址,这里是0x080000ed ,即程序开始执行时的起始地址 。

然后再往下看

5.6.1 Load Region加载区域

  • Base:加载区域的起始地址为0x08000000 ,一般对应芯片 Flash 的起始地址 。
  • Size:大小为0x0000a088 ,表示该区域占用的字节数 。
  • Max:最大允许大小为0x00010000 ,用于限定该加载区域可使用的最大空间 。
  • 属性ABSOLUTE 表示该区域地址是绝对地址 。

接着就是下面的

5.6.2 Execution Region执行区域

  • Exec base:执行基地址为0x08000000 ,即程序执行时的起始地址 。
  • Load base:加载基地址也是0x08000000 ,说明加载地址和执行地址相同 。
  • Size:大小为0x00009fa4 ,是执行区域实际占用空间 。
  • Max:最大允许大小0x00010000 。
  • 属性ABSOLUTE 。

然后就是执行区域的具体解释

  • Exec Addr:执行地址 。
  • Load Addr:加载地址 。
  • Size:对应段的大小 。
  • Type:段的类型,Data 表示数据段,Code 表示代码段 。
  • Attr:属性,RO 表示只读(Read - Only) 。
  • Idx:索引值 。
  • Section Name:段名,如RESET 是存放复位向量等的段;.emb_text 是包含部分代码的文本段 。
  • Object:该段所属的目标文件,如startup_stm32f103xb.oport.o 等 。

看蓝色箭头,RO代表执行区内容都是只读的,都存在Flash里。看起始地址0x80000000也可以看出,符合Flash的基地址。

执行区域对于分析内存占用情况是十分重要的。

我们接着往下看。

这段执行区里的内容都是可以读写的,看红色框RW即可看出。说明该段位于SRAM中。

我们从他的基地址也可以看出。从0x20000000开始,符合我们前面讲到的。

我们再看第一个 size:0x00000004,代表freertos.o占用了四个字节

然后我们再看一下我们前面提到的代码段,在.map文件里都能找到。

 这个.constdata意思是常量数据段,保存在flash里

.bss数据段,它主要用于存放未初始化的全局变量和静态变量,该段位于 SRAM 中。

 

 .text代码段,存放在Flash里

 .STACK栈段,位于SRAM里

 5.7 Image component sizes镜像组件大小

  • Code (inc. data):包含代码以及与代码相关数据的大小总和 ,这里的代码指编译后的机器指令 。
  • RO Data只读数据的大小,如程序中用const修饰的常量等 。存放在 Flash里。
  • RW Data可读写数据的大小,一般是已初始化的全局变量和静态变量 。存放在SRAM里。
  • ZI Data未初始化的全局变量和静态变量,程序启动时会被初始化为 0 。存放在SRAM里。
  • Debug:调试信息的大小,用于调试器辅助调试程序 。
  • Object Name:目标文件名称,代表这些数据所属的编译后文件 。

alert.o这一行数据64 32 0 0 28 1132 alert.o 为例:

alert.o目标文件中,包含代码及相关数据的大小为 64 字节 ;

只读数据大小是 32 字节 ;

没有可读写数据(RW Data 为 0 )

零初始化数据(ZI Data 为 0 )

调试信息大小为 28 字节 

在目标文件后面会有一个内存总计。其实就是自己写的代码的内存占用情况。

其他行数据同理,分别对应不同目标文件的各类数据大小情况 。

镜像组件大小有助于我们去了解每个目标文件对存储空间的占用情况,分析程序的内存使用布局,排查是否存在不合理的内存占用等问题 。

这个图里展示的是库文件统计。

再看最后一段

Total RO Size (Code + RO Data) 为 40868 ( 39.91kB) :是代码和只读数据大小之和,即存储在 ROM 中不会被修改的内容总大小为 40868 字节,约 39.91kB 。

Total RW Size (RW Data + ZI Data) 为 15872 ( 15.50kB) :是可读写数据(已初始化和未初始化)的总大小,这部分数据在程序运行时可能会存放在 SRAM 中,大小为 15872 字节,约 15.50kB 。

Total ROM Size (Code + RO Data + RW Data) 为 41096 ( 40.13kB) :是存储在 ROM 中的代码、只读数据和已初始化可读写数据的总大小,为 41096 字节,约 40.13kB 。

六:总结

在分析代码的内存占用情况时,查看镜像组件中用户代码的内存占用是首要步骤。用户代码涵盖了开发者编写的各类源文件经编译后生成的目标文件内容。通过关注镜像组件中如 “Code (inc. data)”“RO Data”“RW Data”“ZI Data” 等不同类别数据的大小,能清晰知晓每部分代码和数据在内存中的占用情况。

对于内存占用较大的代码,也需要慎重评估处理。

一方面,可深入分析代码逻辑,查找冗余部分进行修改精简。例如,检查是否存在重复的计算逻辑、不必要的变量定义等,通过优化算法和代码结构来降低内存开销。

另一方面,考虑将不常变动且对读取速度要求相对不高的部分存放在 Flash 中。因为 Flash 具有非易失性,可用于存储程序代码和一些只读数据。而 SRAM 作为程序运行时用于快速读写数据的区域,需确保其空间充足,以保障程序运行时变量的读写操作能高效进行,避免因 SRAM 空间不足导致程序运行出错或性能下降。

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

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

相关文章

Ubuntu 编译SRS和ZLMediaKit用于视频推拉流

SRS实现视频的rtmp webrtc推流 ZLMediaKit编译生成MediaServer实现rtsp推流 SRS指定某个固定网卡,修改程序后重新编译 打开SRS-4.0.0/trunk/src/app/srs_app_rtc_server.cpp,在 232 行后面添加: ZLMediaKit编译后文件存放在ZLMediakit/rele…

Intellij报错:the file size(3.47M) exceeds configured limit (2.56MB)

今天在部署一个教学平台的时候,当执行数据库脚本出现了以上问题。 自己把解决的方案分享给大家: 于IntelliJ IDEA或PyCharm,可以通过编辑idea.properties文件来增加文件大小限制。 打开idea.properties文件,通常位于IDE的安装目录…

Unity动画与生命周期函数

一、Animator动画组件 Animator组件是Unity中用于管理和控制动画的主要工具,它可以处理复杂的动画状态机和动画片段之间的过 1.动画状态机 Animator组件的核心是动画状态机,它由多个动画状态和状态之间的过渡组成。可以通过Unity的动画窗口来创建和编辑…

解决ubuntu20中tracker占用过多cpu,引起的风扇狂转

track是linux中的文件索引工具,ubuntu18之前是默认不安装的,所以在升级到20后会默认安装,它是和桌面程序gnome绑定的,甚至还有很多依赖项,导致无法删除,一旦删除很多依赖项都不能运行,禁用也很难…

在线文档管理系统 spring boot➕vue|源码+数据库+部署教程

📌 一、项目简介 本系统采用Spring Boot Vue ElementUI技术栈,支持管理员和员工两类角色,涵盖文档上传、分类管理、公告发布、员工资料维护、部门岗位管理等核心功能。 系统目标是打造一个简洁高效的内部文档管理平台,便于员工…

在UI 原型设计中,交互规则有哪些核心要素?

在UI 原型设计中,交互规则主要有三个核心要素,分别为重要性、原则与实践,具体表现在: 一、交互规则在 UI 原型设计中的重要性 明确交互逻辑:设计阶段制定交互规则,清晰定义界面元素操作响应。 如社交应用…

OpenResty Manager 介绍与部署(Docker部署)

概述 OpenResty-Manager 是一个基于 OpenResty 构建的开源 Web 管理平台。OpenResty 是一个高性能的 Web 平台,集成了 Nginx 和 LuaJIT,支持强大的脚本功能。OpenResty-Manager 由 Safe3 开发,提供了一个用户友好的界面,用于管理…

快速搭建一个electron-vite项目

1. 初始化项目 在命令行中运行以下命令 npm create quick-start/electronlatest也可以通过附加命令行选项直接指定项目名称和你想要使用的模版。例如,要构建一个 Electron Vue 项目,运行: # npm 7,需要添加额外的 --: npm cre…

unity terrain 在生成草,树,石头等地形障碍的时候,无法触发碰撞导致人物穿过模型

1.terrain地形的草,石头之类要选择模型预制体 2.在人物身上挂碰撞器和刚体,或者单挂一个character controller组件也行 3.在预制体上挂碰撞盒就好了,挂载meshcollider会导致碰撞无效

75.xilinx复数乘法器IP核调试

(83*j)*(57j) 935j 正确的是 1971j 分析出现的原因:(abj)* (cdj) (ac-bd)j(adbc) 其中a,b,c,d都是16bit的有符号数,乘积的结果为保证不溢出需要32bit存储,最终的复数乘法结果是两个32b…

8.ADC

目录 ADC 模拟信号和数字信号的区别和区别 信号的区别 如何采集信号 常见的接口 数字接口 模拟接口 ADC 实际应用 ADC 转换器的定义 ADC 相关的名词 ADC 采集的原理 ADC 的参考电压 相关的计算 如何实现 ADC STM32 内的 ADC 转换器讲解 STM32 的 ADC 简介 AD…

c/c++中程序内存区域的划分

c/c程序内存分配的几个区域: 1.栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放,栈内存分配运算内置于处理器的指令集中,效率很高但是分配的内存容量有…

模糊综合评价模型建立

模糊综合评价模型建立 一、整体流程 二、代码实现(含大量注释) #程序文件ex14_4.py import numpy as npa np.loadtxt(data14_4.txt) # 使用定义匿名函数的形式来定义各个评价指标的隶属函数 f1 lambda x: x/8800 f2 lambda x: 1-x/8000 f3 lambda x: (x<5.5)(8-x)/(8-…

【Linux】Linux安装mysql

该教程是使用的 CentOS 8.2 安装 mysql。 1.删除原有mysql rpm -qa|grep mariadb 如果存在在mariadb&#xff0c;卸载命令如下&#xff1a; #rpm -e --nodeps是强制卸载指令 后面是查出的依赖名称rpm -e --nodeps mariadb-libs-5.5.64-1.el7.x86_64全部卸载完输入以下指令&am…

模仿学习笔记

模仿学习总共分两类&#xff1a; 行为克隆&#xff1a;BC,Dagger逆强化学习:又分为 2.1基于最大边际逆强化学习 &#xff08;无法主要歧义问题&#xff09;&#xff1a;学徒学习 2.2 基于最大熵逆强化学习 &#xff08;主要解决歧义问题&#xff09;:GAIL 学徒学习 基于最大熵…

一文讲透 Vue3 + Three.js 材质属性之皮革篇【扫盲篇】

文章目录 前言一、Three.js材质系统基础1.1 为什么选择PBR材质&#xff1f;1.2 关键参数解析 二、不同类型皮革的材质配置2.1 牛皮材质实现2.2 羊皮材质实现2.3 仿皮材质实现 三、高级贴图技术3.1 贴图制作流程3.2 组合贴图实战 四、性能优化策略4.1 贴图压缩技术4.2 材质共享4…

MUSE Pi Pro 使用TiTanTools烧录镜像

视频讲解&#xff1a; MUSE Pi Pro 使用TiTanTools烧录镜像 下载windows下的烧录工具 https://cloud.spacemit.com/prod-api/release/download/tools?tokentitantools_for_windows_X86_X64 下载镜像文件&#xff0c;zip后缀的即可 打开软件默认界面 按住FDL键&#xff0c;同时…

安卓A15系统实现修改锁屏界面默认壁纸功能

最近遇到一个A15系统项目&#xff0c;客户要求修改锁屏界面的默认壁纸&#xff0c;客户提供了一张壁纸图片&#xff0c;但是从A15系统的源代码查看时才知道谷歌已经去掉了相关的代码&#xff0c;已经不支持了&#xff0c;A13和A14系统好像是支持的&#xff0c;A15系统的Wallpap…

IT系统的基础设施:流量治理、服务治理、资源治理,还有数据治理。

文章目录 引言I IT系统的基础设施流量治理、服务治理、资源治理,还有数据治理。开发语言的选择数据治理(监控系统):整体运维的数据其他II 基础知识的重要性第一,知道原理第二,当遇到一些比较难解的问题时,基础知识就会派上用场。例子III 大公司和小公司的权衡对比大公司…

使用 TypeScript + dhtmlx-gantt 在 Next.js 中实现

1. 安装依赖&#xff08;确保已安装&#xff09; npm install dhtmlx-gantt2. 创建 pages/gantt.tsx use clientimport { useRef, useEffect } from react import { gantt } from dhtmlx-gantt import dhtmlx-gantt/codebase/dhtmlxgantt.cssinterface Task {id: number | st…