【嵌入式 - 关于MCU的内存分配】

一、MCU内存分配

MCU(微控制器)的内存分配是指如何在不同的内存区域(如程序存储器、数据存储器、堆栈等)中分配和管理内存。不同的MCU可能有不同的内存架构,但通常MCU的内存通常包括以下几部分:

1.1 内存分配

  1. 程序存储器(Flash/ROM): 存储程序代码和常量数据。Flash 是可擦写的,而 ROM 是只读的。
  2. 数据存储器(SRAM): 存储程序运行时的变量和数据。SRAM 是易失性的,即掉电后数据会丢失。
  3. 非易失性存储器(如EEPROM):存储需要在断电后保留的数据,容量较小,读写速度较慢。
    通常用于存储配置参数、校准数据等。
  4. 特殊功能寄存器(SFR): 用于控制和访问 MCU 的外设和功能模块。

1.2 内存区域

  1. 常量区:存储编译时确定的常量值,如字符串常量。
  2. 静态区(Static):存储全局变量和静态变量,程序运行期间始终存在。
  3. 栈区(Stack):执行函数时创建局部变量和函数参数,函数结束时释放。
  4. 堆区(Heap):动态分配的内存区域,使用malloc等函数申请内存,需手动释放。

1.3 数据段

  1. RW-data(读写数据存储区):存储已初始化的全局变量和静态变量。
  2. ZI-data(零初始化数据区):存储未初始化的全局变量和静态变量,程序开始运行时初始化为0。

1.4 内存分配示例

在典型的嵌入式系统中,内存分配通常通过链接脚本(Linker Script)进行定义。以下是一个基于 ARM Cortex-M 的 MCU 的简单链接脚本示例,用于定义内存布局:

/* Linker script for ARM Cortex-M MCU */MEMORY
{FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256KSRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}SECTIONS
{/* Program code and read-only data */.text :{KEEP(*(.isr_vector))*(.text*)*(.rodata*)*(.glue_7)*(.glue_7t)*(.eh_frame)} > FLASH/* Read-write data */.data : AT (ADDR(.text) + SIZEOF(.text)){_sdata = .;*(.data*)_edata = .;} > SRAM/* Uninitialized data (BSS) */.bss :{_sbss = .;*(.bss*)*(COMMON)_ebss = .;} > SRAM/* Stack and heap */.stack (NOLOAD) :{_estack = .;. = . + 0x1000; /* Define stack size */} > SRAM.heap (NOLOAD) :{_sheap = .;. = . + 0x8000; /* Define heap size */_eheap = .;} > SRAM
}

二、自定义内存区域

嵌入式开发中,自定义内存区域以满足特定需求非常常见。这通常涉及编译器和链接器的配置。在上述链接脚本中,可以看到定义了 FLASH 和 SRAM 的起始地址和长度,并在 SECTIONS 部分定义了各个内存区域的分配。
通过合理地使用链接脚本和内存管理函数,开发者可以有效地自定义和管理 MCU 的内存区域,以满足特定的应用需求。

2.1 编译器配置

使用编译器选项指定特定内存区域的分配,例如通过段名(Section Name)标识不同内存区域。
在代码中使用特定关键字或属性指定变量或函数的存储区域。

2.1.1 示例:自定义变量位置

假设需要将某些特定变量放置在特定的内存区域,可以使用 __attribute__ 指令。以下是一个示例,将一个数组放置在特定的内存区域中:

#include <stdint.h>#define CUSTOM_SECTION __attribute__((section(".custom_section")))CUSTOM_SECTION uint8_t custom_array[256];int main(void)
{for (int i = 0; i < 256; ++i){custom_array[i] = i;}while (1){// Main loop}
}

2.2 链接器配置

在链接器脚本中定义内存布局,指定不同内存区域的起始地址、大小和属性。
将不同代码段和数据段映射到相应的内存区域中。
例如上述代码,需要在链接脚本中定义 .custom_section

SECTIONS
{/* Other sections... */.custom_section :{*(.custom_section)} > SRAM
}

2.3 内存管理

使用自定义的内存分配器管理堆区内存,根据应用需求优化。
例如,使用内存池减少内存碎片、提高分配速度。

2.3.1 使用内存管理函数

动态内存分配可以使用标准的 C 库函数,例如 malloc 和 free。这些函数分配和释放堆内存。

#include <stdlib.h>int main(void)
{int *dynamic_array = (int *)malloc(100 * sizeof(int));if (dynamic_array != NULL){for (int i = 0; i < 100; ++i){dynamic_array[i] = i;}free(dynamic_array);}while (1){// Main loop}
}

三、开发者内存分配标准

3.1 开发者在进行内存分配时,应遵循以下标准:

安全性:

确保内存分配不会导致溢出或冲突,避免潜在的安全漏洞。为此,需要:

  • 分离代码和数据:将代码、只读数据和读写数据分开,以提高安全性和稳定性。
  • 保护关键数据:关键数据(如系统配置)应放在特定的保护区域,以避免被意外覆盖。
    效率:合理分配内存,减少碎片化,提高内存使用效率。
  • 动态内存管理:合理使用动态内存分配,避免内存泄漏。

可维护性:

代码应易于理解和维护,内存分配策略应清晰且一致。为此,需要:

  • 使用特定内存区域:有时需要将某些数据或代码放置在特定的内存区域,以满足硬件要求或优化性能。

性能:

考虑内存访问速度,优化性能,特别是在实时系统中。为此,需要:

  • 内存对齐:确保内存对齐,以提高访问效率。
  • 最小化碎片:尽量减少内存碎片,以提高内存利用率。

可扩展性:

预留足够空间以适应未来功能扩展。

符合架构:

遵循MCU内存架构,如哈佛架构或冯·诺依曼架构。

3.2 自定义内存段的原因

性能优化:

例如,将频繁访问的数据分配到高速内存区域以提高访问速度。

#define FAST_CODE_SECTION __attribute__((section(".fast_code")))FAST_CODE_SECTION void time_critical_function(void)
{// Time-critical code
}

在链接脚本中定义 .fast_code 区域:

SECTIONS
{/* Other sections... */.fast_code :{*(.fast_code)} > SRAM
}

功能隔离:

将不同功能的代码和数据分开存放,提高模块化和可维护性。

#define CODE_SECTION __attribute__((section(".my_code")))
#define DATA_SECTION __attribute__((section(".my_data")))CODE_SECTION void my_function(void)
{// Function code
}DATA_SECTION int my_data;

在链接脚本中定义 .my_code 和 .my_data 区域:

SECTIONS
{/* Other sections... */.my_code :{*(.my_code)} > FLASH.my_data :{*(.my_data)} > SRAM
}

特定硬件支持:

某些硬件外设可能需要数据存储在特定的内存区域。例如,DMA(直接内存访问)传输可能要求数据放在特定的内存位置。

#define DMA_BUFFER_SECTION __attribute__((section(".dma_buffer")))DMA_BUFFER_SECTION uint8_t dma_buffer[256];

在链接脚本中定义 .dma_buffer 区域:

SECTIONS
{/* Other sections... */.dma_buffer :{*(.dma_buffer)} > SRAM
}

内存保护:

隔离关键数据,防止非授权访问或意外修改。

#define PROTECTED_DATA_SECTION __attribute__((section(".protected_data")))PROTECTED_DATA_SECTION uint32_t system_config[10];

在链接脚本中定义 .protected_data 区域:

SECTIONS
{/* Other sections... */.protected_data :{*(.protected_data)} > SRAM
}

特殊用途:

如固件版本号、固件升级代码、异常处理代码等特殊用途的代码和数据通常需要放在特定的内存段中。这有助于提高系统的可维护性和可靠性,并确保这些关键部分不会被意外覆盖或修改。以下是一些代码示例,展示如何在特定内存段中放置这些数据和代码。

链接器脚本示例

首先,需要在链接器脚本中定义特定的内存段。例如,我们可以定义三个内存段来存储固件版本号、固件升级代码和异常处理代码:

/* 链接器脚本示例 */MEMORY
{FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 1024KRAM (rwx)   : ORIGIN = 0x20000000, LENGTH = 256K
}SECTIONS
{.text :{KEEP(*(.isr_vector)) /* 保持中断向量表 */*(.text*)            /* 程序代码 */} > FLASH.rodata : ALIGN(4){*(.rodata*)         /* 只读数据 */} > FLASH.data : ALIGN(4){PROVIDE(__data_start = .);*(.data*)           /* 初始化的全局变量 */PROVIDE(__data_end = .);} > RAM AT > FLASH.bss :{PROVIDE(__bss_start = .);*(.bss*)           /* 未初始化的全局变量 */PROVIDE(__bss_end = .);} > RAM/* 特定内存段 */.firmware_version :{*(.firmware_version*)} > FLASH.firmware_upgrade :{*(.firmware_upgrade*)} > FLASH.exception_handler :{*(.exception_handler*)} > FLASH/* 堆和栈的定义 */.heap :{. = ALIGN(4);PROVIDE(__heap_start = .);. = . + 0x8000;   /* 定义堆大小为32KB */PROVIDE(__heap_end = .);} > RAM.stack :{. = ALIGN(4);PROVIDE(__stack_start = .);. = . + 0x4000;    /* 定义栈大小为16KB */PROVIDE(__stack_end = .);} > RAM
}
C代码示例

接下来,在C代码中使用特定的段属性将数据和代码放置到这些内存段中。

/* 将固件版本号放在 .firmware_version 段中 */
__attribute__((section(".firmware_version"))) const char firmware_version[] = "1.0.0";/* 将固件升级代码放在 .firmware_upgrade 段中 */
__attribute__((section(".firmware_upgrade"))) void firmware_upgrade(void) {// 固件升级逻辑// 这段代码将被放置在 .firmware_upgrade 段中
}/* 将异常处理代码放在 .exception_handler 段中 */
__attribute__((section(".exception_handler"))) void exception_handler(void) {// 异常处理逻辑// 这段代码将被放置在 .exception_handler 段中
}
int main(void) {printf("Firmware Version: %s\n", firmware_version);// 某些条件下调用固件升级代码if (upgrade_needed) {firmware_upgrade();}// 初始化异常处理set_exception_handler(exception_handler);// 主程序逻辑while(1);return 0;
}

__attribute__((section(".firmware_version"))):将firmware_version数组放置在.firmware_version段中。这个段在链接器脚本中被定义为存储在FLASH中。
__attribute__((section(".firmware_upgrade"))):将firmware_upgrade函数放置在.firmware_upgrade段中。
__attribute__((section(".exception_handler"))):将exception_handler函数放置在.exception_handler段中。
通过这种方式,开发者可以确保特定的代码和数据被存储在预定义的内存区域中,从而满足应用的特定需求。

3.3 完整示例代码

以下是一个完整的代码示例,展示了如何根据不同的原因自定义内存段:

#include <stdint.h>#define DMA_BUFFER_SECTION __attribute__((section(".dma_buffer")))
#define FAST_CODE_SECTION __attribute__((section(".fast_code")))
#define PROTECTED_DATA_SECTION __attribute__((section(".protected_data")))DMA_BUFFER_SECTION uint8_t dma_buffer[256];
PROTECTED_DATA_SECTION uint32_t system_config[10];FAST_CODE_SECTION void time_critical_function(void)
{// Time-critical code
}int main(void)
{// Initialize system configurationfor (int i = 0; i < 10; ++i){system_config[i] = i;}// Use dma_bufferfor (int i = 0; i < 256; ++i){dma_buffer[i] = i;}// Call time-critical functiontime_critical_function();while (1){// Main loop}return 0;
}

链接脚本

MEMORY
{FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256KSRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}SECTIONS
{/* Other sections... */.dma_buffer :{*(.dma_buffer)} > SRAM.fast_code :{*(.fast_code)} > FLASH.protected_data :{*(.protected_data)} > SRAM/* Other sections... */
}

通过这种方式,开发者可以根据具体需求和硬件要求,自定义内存区域,实现更高效和可靠的内存管理。
在嵌入式开发中,自定义内存区域是一个重要的技术,可以帮助开发者优化程序性能、提高系统可靠性以及满足特定的应用需求。通过合理配置编译器、链接器和内存管理,可以将关键数据和代码放置在特定的内存段中,确保系统的稳定运行。

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

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

相关文章

高清无字幕视频素材去哪里找?这几个热门无字幕素材网站你要知道

在短视频创作过程中&#xff0c;高清无字幕的视频素材是提升视频质量的关键。无论是制作校园活动宣传片&#xff0c;还是情感励志视频&#xff0c;这些素材都能让你的视频更具吸引力。今天&#xff0c;我就来给大家推荐几个超棒的视频素材网站&#xff0c;这些网站不仅资源丰富…

Java——面向对象进阶(三)

前言&#xff1a; 抽象类&#xff0c;接口&#xff0c;内部类 文章目录 一、抽象类1.1 抽象方法1.2 抽象类1.3 抽象类的使用 二、 接口2.1 接口的定义和实现2.2 default 关键字2.3 实现接口时遇到的问题 三、内部类3.1 成员内部类3.2 静态内部类3.3 成员内部类3.4 匿名内部类&a…

sslyze一键检查服务器检查服务器的 SSL/TLS 安全性(KALI工具系列二十五)

目录 1、KALI LINUX 简介 2、sslyze工具简介 3、信息收集 3.1 目标主机IP&#xff08;服务器&#xff09; 3.2 KALI的IP 4、操作示例 4.1 扫描主机和端口 4.2 批量扫描 4.3 插件扫描 4.4 输出结果 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功能强大、多才多艺…

C++设计模式---代理模式

1、介绍 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它为其他对象提供一个代理或占位符&#xff0c;以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用&#xff0c;可以隐藏目标对象的实现细节&#xff0c;增加额外的…

[Python学习篇] Python字符串

字符串是 Python 中最常用的数据类型&#xff0c;一般使用单引号或引号来创建字符串 语法&#xff1a; 字符串变量名A 字符串变量值A 字符串变量名B "字符串变量值B" 示例&#xff1a; a Hello A print(a) b "Hello B" print(b) 字符串特征 一对引号字…

竟然与 package-lock.json 更新有关!部分用户 H5 页面白屏问题!

一.问题 1 场景 现象 接到部分用户反馈进入xxx H5 页面空白&#xff1b; 研发测日志里问题用户的线上页面URL地址可以正常访问&#xff0c;没有复现问题&#xff01;&#xff01;&#xff01; 定位问题 监控平台和客户端日志报错&#xff1a; SyntaxError: Unexpected toke…

调试了一下午,终于把tailwindcss搞进Blazor了

在Vue和Uniapp项目中使用tailwindcss后&#xff0c;实在是太香了&#xff0c;非常符合我这从XAML走过来的老程序员的手感&#xff0c;所以老想着在Blazor项目中引入。看了几个老外大佬的视频&#xff0c;调试了一下午&#xff0c;终于是捣鼓成功了。由于咱们Blazor项目不在node…

STM32硬件接口I2C应用(基于MP6050)

目录 概述 1 STM32Cube控制配置I2C 1.1 I2C参数配置 1.2 使用STM32Cube产生工程 2 HAL库函数介绍 2.1 初始化函数 2.2 写数据函数 2.3 读数据函数 3 认识MP6050 3.1 MP6050功能介绍 3.2 加速计测量寄存器 ​编辑3.3 温度计量寄存器 3.4 陀螺仪测量寄存器 4 MP60…

C++ Windows下Glog日志库安装使用教程

🙋 介绍:glog是google推出的一款轻量级c++开源日志框架。  环境配置:windows+VS2015+gflags 2.2.2+glog-0.3.5。为避免新版本(glog V0.7.1)踏坑,建议装低版本,这里我选用的是V0.3.5。 1. 下载 在gflags官方中下载gflags代码,官方地址 在Glog官方中下载,githut地址:…

简单易用的多功能图床Picsur

什么是 Picsur &#xff1f; Picsur 是一款易于使用、可自行托管的图片分享服务&#xff0c;类似于 Imgur&#xff0c;并内置转换功能。支持多种格式的图片&#xff0c;包括 QOI、JPG、PNG、WEBP&#xff08;支持动画&#xff09;、TIFF、BMP、GIF&#xff08;支持动画&#xf…

Leetcode.2601 质数减法运算

题目链接 Leetcode.2601 质数减法运算 rating : 1779 题目描述 给你一个下标从 0 0 0 开始的整数数组 n u m s nums nums &#xff0c;数组长度为 n n n 。 你可以执行无限次下述运算&#xff1a; 选择一个之前未选过的下标 i i i &#xff0c;并选择一个 严格小于 n u…

第八站:Java银——微服务架构的未来之光

在云原生时代&#xff0c;Java凭借其强大的生态系统和成熟的微服务架构支持&#xff0c;成为了构建现代分布式系统不可或缺的一部分。以下是如何将Java、Docker、Kubernetes、以及Spring Cloud结合起来&#xff0c;构建高效、可扩展的微服务应用的概述&#xff0c;结合简化的代…

初说网络攻防

网络攻防是一个复杂而深入的领域&#xff0c;涉及多方面的技术和策略。以下是关于网络攻防的深入讨论&#xff0c;分点表示和归纳如下&#xff1a; 一、网络攻防的基本概念 网络攻防是指在网络环境下&#xff0c;针对网络和系统存在的漏洞进行攻击和保护的一系列技术和行为。…

下载依赖有问题(只有自己有问题)

有缓存&#xff01; 删除node_modules 命令&#xff1a;npm run clean 前提是该项目支持这个命令&#xff1a;package.json > scripts 内有 clean 例如下面这个就没有clean&#xff0c;则直接手动删除 清除缓存 npm cache clean --force pnpm store prune删除lock文件 …

Superset 二次开发之Git篇 git cherry-pick

Cherry-Pick 命令是 Git 中的一种功能&#xff0c;用于将特定的提交&#xff08;commit&#xff09;从一个分支应用到另一个分支。它允许你选择性地应用某些提交&#xff0c;而不是合并整个分支。Cherry-Pick 非常适合在需要将特定更改移植到其他分支时使用&#xff0c;例如从开…

可视化图表:如此高颜值柱状图,其实简单配置就能实现。

这不又有某个boss给我图截图一些柱状性图表&#xff0c;说他们的前端觉得很难&#xff0c;说了一堆技术术语&#xff0c;他也不懂&#xff0c;截图我看到后&#xff0c;就给了他一个网址&#xff0c;马上就解决了。 在这里给大家摘录出几个比较有特色的柱状图出来&#xff0c;让…

PyTorch计算机视觉入门:使用自己的数据集训练神经网络

前言 计算机视觉&#xff0c;作为人工智能领域的一个重要分支&#xff0c;近年来在图像识别、物体检测、图像生成等应用上取得了显著的进步。PyTorch&#xff0c;作为一款灵活且强大的深度学习框架&#xff0c;为开发者提供了便捷的工具来构建和训练计算机视觉模型。本文将指导…

RFID技术在农产品管理中的应用

使用RFID技术对农产品生产、加工、存储和销售的全过程进行跟踪&#xff0c;追溯食品的生产和加工过程&#xff0c;能够有效加强农产品的管理&#xff0c;如图7—10所示。 将RFID技术应用于农业食品安全&#xff0c;首先是建立完整、准确的食品供应链信息记录。借助RFID 对物体…

Python 全栈系列253 再梳理flask-celery的搭建

说明 最近做了几个实验&#xff0c;将结论梳理一下&#xff0c;方便以后翻看。 1 flask-celery 主要用于数据流的同步任务&#xff0c;其执行由flask-aps发起&#xff0c;基于IO并发的方法&#xff0c;达到资源的高效利用&#xff0c;满足业务上的需求。2 目前部署环境有算网…

腾讯大模型最新路线图,主打一个“实用”

让大模型更接地气&#xff0c;腾讯交出一份阶段性答卷。 大模型狂奔一年多后&#xff0c;正迈向产业落地的深水区。如何用好这一革命性的新技术&#xff0c;已成为整个社会的一道必答题。 去年&#xff0c;国内大模型百花齐放&#xff0c;外界关注的焦点也集中在模型能力上。…