如果供应商为我自己的项目提供了一个起点,那就太好了。工作'blinky'始终是一个伟大的首发。方便总是有代价,而且“blinky”就是夸大“切换GPIO引脚”的代码大小。对于具有少量RAM和FLASH的设备,这可能会引起关注:如果'blinky'占用那么多,我的应用程序是否适合该设备?不要担心:可以轻松地修剪掉(或任何其他项目)。

恩智浦LPC845-BRK主板上的Binky

我在这里使用一个'blinky'项目作为一个例子:修剪技巧也适用于任何其他类型的项目。

在本教程中,我在BRK(突破)板上使用NXPLPC845:

恩智浦LPC845-BRK板

1Blinky示例

我所使用的是基于Eclipse的NXP MCUXpresso IDE:

选择SDK板

我使用供应商默认设置创建了'blinky'项目:

Blinky项目

一个'blinky'应该闪烁一个LED,对任何项目来说都是一个好的开手机。构建相当小的项目,代码大小如下:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

10536 B

64 KB

16.08%

SRAM:

2424 B

16 KB

14.79%

text

data

bss

dec

hex

filename

10532

4

2420

12956

329c

lpc845breakout_led_blinky.axf

该信息也在控制台中显示,分为文本,数据和bss:

10K的'blinky'看起来有点夸张。但是我们现在将在接下来的步骤中修改它。

2、大小信息

有关大小信息的含义,请阅读“ text,data和bss:Code and Data Size Explained ”。查看我的设备上使用空间的正常方法是检查链接器映射文件(* .map):

链接器映射文件

但是这个map文件很难阅读,而且对于专家来说更是如此:它列出了具有地址和大小的部分:

链接器映射文件内容

使用MCUXpresso IDE V11,有一个很好的“图像信息”视图,它基本上是一个更好的ma'p文件信息查看器:

图像信息查看

我可以过滤和排序数据,这让我知道代码和数据使用了多少空间:

图像信息存储器内容

当然,它需要一些关于应用程序应该做什么的知识。我总是浏览视图中的项目列表,看看是否有任何我不希望的东西:也许应用程序正在使用可以删除的东西。

3、源代码

对于一个简单的眨眼,这是相当小的。首先要检查程序正在做什么。main.c有这个:

/* * Copyright 2017 NXP* All rights reserved.** SPDX-License-Identifier: BSD-3-Clause*/#include "board.h"
#include "fsl_gpio.h"#include "pin_mux.h"
/******************************************************************************** Definitions******************************************************************************/
#define BOARD_LED_PORT 1U#define BOARD_LED_PIN 2U
/******************************************************************************** Prototypes******************************************************************************/
/******************************************************************************** Variables******************************************************************************/
volatile uint32_t g_systickCounter;
/******************************************************************************** Code******************************************************************************/void SysTick_Handler(void)
{if (g_systickCounter != 0U){g_systickCounter--;}
}void SysTick_DelayTicks(uint32_t n)
{g_systickCounter = n;while (g_systickCounter != 0U){}
}/*! * @brief Main function */
int main(void)
{/* Define the init structure for the output LED pin*/gpio_pin_config_t led_config = {kGPIO_DigitalOutput,0,};/* Board pin init */BOARD_InitPins();BOARD_InitBootClocks();BOARD_InitDebugConsole();/* Init output LED GPIO. */GPIO_PortInit(GPIO, BOARD_LED_PORT);GPIO_PinInit(GPIO, BOARD_LED_PORT, BOARD_LED_PIN, &led_config);/* Set systick reload value to generate 1ms interrupt */if (SysTick_Config(SystemCoreClock / 1000U)){while (1){}}while (1){/* Delay 1000 ms */SysTick_DelayTicks(1000U);GPIO_PortToggle(GPIO, BOARD_LED_PORT, 1u << BOARD_LED_PIN);}
}

基本上,代码正在初始化引脚,时钟,设置SysTick定时器,然后在循环中执行'blinky',使用Systick计数器延迟闪烁周期。

4、调试控制台

但我可以看到它初始化一个调试控制台(以及它的UART硬件):

BOARD_InitDebugConsole();

去掉这些,我们就可以得到:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

5616 B

64 KB

8.57%

SRAM:

2400 B

16 KB

14.65%

在许多情况下,演示应用程序会设置一些通信通道,但之后就不会使用它们。链接器可以很好地删除未使用的对象(函数/变量),但前提是它们没有被引用。

5、半主控和printf()

接下来要看的是是否存在任何半主机或printf()。该项目正在使用'Redlib',这是一个优化的库,与'标准'newlib或较小标准的newlib-nano相比:

Redlib

尽管如此,该库可能会增加代码大小,因为它使用半主机(通过调试器发送消息)。查看Memory视图,我可以直接或间接地看到所需的所有这些标准I / O函数:

stdio功能

拥有该功能的所有钩子只有在使用它时才有意义,并且“blinky”不会使用它。因此,摆脱半主机和所有未使用的标准I / O意味着使用'none'变体:

没有标准I / O的库

这让我们了解到这一点:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

3372 B

64 KB

5.15%

SRAM:

2208 B

16 KB

13.48%

或者使用较小的变体或实现。有关此问题的更多背景信息,请参阅本文末尾的链接。

6DEBUGNDEBUG

接下来要检查编译器是否定义了列出的DEBUG。事实上,情况就是这样:

DEBUG定义

使用该定义集,SDK和示例驱动程序中有许多额外的代码,它们使用'assert()'宏检查好的值:

SDK代码中断言的用法

在这里,图像信息视图再次有用:它向我展示了使用assert()的所有地方:

断言用法

实际上,在代码中使用断言来尽早捕获编程错误是一种很好的做法。但是所有的assert()代码确实加起来了。要关闭额外的代码(和安全带!),我将宏更改为NDEBUG:

NDEBUG

这让我们了解到一点:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

3144 B

64 KB

4.80%

SRAM:

2208 B

16 KB

13.48%

7、中断和向量

图像信息视图再次是一个很好的起点。我正在检查使用过的中断。Blinky正在使用预期的SysTick中断。但是仍然使用UART中断?

使用中断

大多数中断都实现为“weak”:实现为默认/空,可以被应用程序覆盖。但UART没有意义,因为”blinky”没有使用任何UART通信?

事实证明,NXP SDK默认启用了UART事务API:

UART Transactional API设置

事务API允许在通信组块/事务中发送/接收UART数据。但我们不需要在我们的眨眼中,所以让我们把它关掉:

关闭UART TransactionalAPI

这样一来,内存情况为:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

2964 B

64 KB

4.52%

SRAM:

2184 B

16 KB

13.33%

但我认为CMSIS(设置中断优先级,通用时钟设置)非常有用,所以我不在这里触摸它。应用程序中最大的功能是SysTick代码用来将定时器的优先级设置为最低优先级,以节省另外220个字节:

CMSIS作为最大的单一功能代码大小贡献者

8、优化

到目前为止,我已经删除了不需要的或未使用的功能。接下来我可以打开编译器优化。默认情况下,项目设置为-O0:

编译器优化

-O0表示无优化:代码直观且易于调试。

-O1主要优化函数进入/退出代码,并且能够在不影响调试的情况下减少代码大小。在这个例子中,它将代码大小减少了一半!

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1540 B

64 KB

2.35%

SRAM:

2184 B

16 KB

13.33%

-O2优化更多并尽可能地将事物保存在寄存器中。因为应用程序中的功能相当小,所以改进并不大:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1516 B

64 KB

2.31%

SRAM:

2184 B

16 KB

13.33%

-O3通过额外的内联优化最佳。-O3的目标是速度,所以难怪代码大小再次增加:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1792 B

64 KB

2.73%

SRAM:

2184 B

16 KB

13.33%

代码大小优化的最佳选择是-Os(针对大小进行优化):

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1456 B

64 KB

2.22%

SRAM:

2184 B

16 KB

13.33%

现在看起来很合理!当然现在有一些方法可以为“裸露的裸眼”切断更多,但是现有的一切(启动代码,时钟和GPIO初始化)对于真正的应用程序是有意义的,所以我现在停在这里。

9RAM:堆和堆栈

看起来不正确的是SRAM的使用。'heap'使用了一大块:

堆内存使用情况

该堆用于动态内存分配(malloc())。嵌入式编程的一般规则是避免它。但它默认在这里。它可以在链接器设置中关闭:演示使用1K用于堆和堆栈。由于我没有使用malloc(),我可以将堆大小设置为0x0。对于真正依赖于应用程序的保留堆栈。在ARM Cortex上,MSP用于启动/主控和中断(参见“ ARMCortex-M中断和FreeRTOS ”)。0x100(256字节)应该足够我的眨眼。

堆和堆栈大小

这让我了解到一点:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1456 B

64 KB

2.22%

SRAM:

392 B

16 KB

2.39%

如果它是关于进一步减小堆栈大小,我可以查看调用图信息,它给出了有关使用多少堆栈空间的信息:

堆栈大小的图形显示

有一些项目的大小信息未知(标有“?”)因为它们在库中。验证实际堆栈使用情况的方法是编写模式(例如0xffff'ffff),然后运行应用程序一段时间:

使用的堆栈

这表明实际使用了72个字节。有一点余地,在这种情况下将堆栈大小设置为128字节看起来是合理的。这给出了:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1456 B

64 KB

2.22%

SRAM:

264 B

16 KB

1.61%

堆栈溢出可能是嵌入式应用程序中最常见的问题。如果可以的话,可以为堆栈提供尽可能多的RAM。如果缩小尺寸,请确保进行了足够的分析以证明堆叠尺寸合理。

10MTB

剩下的一件事就是使用RAM空间:MTB缓冲区。微跟踪缓冲区用于跟踪,这非常有用(请参阅“ 使用MTB跟踪调试ARM Cortex-M0 +硬故障 ”)。可以使用宏禁用缓冲区:

mtb.c

__MTB_DISABLE

这让我对此:

Memory region

Used Size

Region Size

%age Used

PROGRAM_FLASH:

1456 B

64 KB

2.22%

SRAM:

136 B

16 KB

0.83%

我想在这里我们可以很开心

11、摘要

供应商的例子很棒:它们给了我一个很好的起点。它们没有经过优化,这是故意的。但它们可能带有我不需要的功能和功能。了解使用切断功能或调整设置来优化应用程序的不同方法对于优化RAM和FLASH使用非常有用。在本教程中,我展示了如何将'blinky'降低到大约1KB闪存和大约136字节的SRAM。当然这一切都取决于功能和用法,但我认为现在为我的应用程序添加额外的功能是一个非常合理的状态。

我希望这些提示可能对您的项目有用。

12、链接

  • 文本,数据和bss:代码和数据大小说明
  • 拆箱恩智浦LPC845-BRK板
  • 教程:使用恩智浦LPC845-BRK主板闪烁
  • 使用恩智浦Kinetis SDK V2.0进行半主机(再次!)
  • 为什么我不喜欢printf()
  • XFormat,轻量级printf()和sprintf()替代品
  • 优化Kinetis gcc启动
  • 新的恩智浦MCUXpresso Eclipse IDE v11.0

声明: 此篇由 Erich Styger的《Tutorial: How to Optimize Code and RAM Size》翻译。原文地址为:https://mcuoneclipse.com/2019/08/17/tutorial-how-to-optimize-code-and-ram-size/。权属归原作者所有。

欢迎关注:

如何优化代码和RAM大小相关推荐

  1. 小猫爪:嵌入式小知识07-MCUXpresso GCC ld链接文件解析-链接代码至RAM

    小猫爪:嵌入式小知识07-MCUXpresso GCC ld链接文件解析-链接代码至RAM 1 前言 2 基本语法 3 实例解析 4 链接代码至RAM 4.1 链接自定义section至RAM 4.2 ...

  2. 四种高性能数据类型,Python collections助你优化代码、简洁任务

    在这篇文章中,机器学习工程师 George Seif 介绍了 Python collections 模块最受欢迎的四种数据类型以及它们各自的使用方法.这些数据类型可以对代码进行优化,进而实现更简洁的任 ...

  3. android Lint优化代码

    今天,简单的讲讲  android Lint优化代码. 作为移动应用开发者,我们总希望发布的apk文件越小越好,不希望资源文件没有用到的图片资源也被打包进apk,不希望应用中使用了高于minSdk ...

  4. 如何通过map文件优化代码

      在平时写代码的时候,特别是嵌入式相关的代码时,能想到的优化方法一般就是通过设置编译器的优化等级.或者是在定义变量的时候考虑变量的使用范围,然后根据数据范围选择比较适合的数据类型.但是这种优化方式操 ...

  5. 浅谈嵌入式MCU软件开发之S32K1xx系列MCU启动过程及重映射代码到RAM中运行方法详解

    内容提要 注:本文摘自NXP工程师胡恩伟的微信公众号"汽车电子expert成长之路",大家感兴趣可以关注一下. 引言 1. S32K1xx系列MCU启动过程详解(startup_S ...

  6. 比xgboost强大的LightGBM:调参指南(带贝叶斯优化代码)

    向AI转型的程序员都关注了这个号??? 大数据挖掘DT数据分析  公众号: datadw xgboost的出现,让数据民工们告别了传统的机器学习算法们:RF.GBM.SVM.LASSO........ ...

  7. 单片机Flash大小和RAM大小空间理解与分析

    我们以上图中的 STM32为例来进行分析和理解 1,空间大小问题 Flash大小 32kBytes 意思是 321024 = 32768 字节(Bytes) RAM大小 8kBytes 意思是 810 ...

  8. 小猫爪:嵌入式小知识06-KEIL scf分散加载文件解析-链接代码至RAM

    小猫爪:嵌入式小知识06-KEIL scf分散加载文件解析-链接代码至RAM 1 前言 2 执行域和加载域 2 相关语法解析 3 实例解说 4 链接代码至RAM运行 4.1 链接单个section至R ...

  9. 为duilib的MenuDemo增加消息响应,优化代码和显示效果

    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/38253297 第一部分 我在前一段时间研究了怎么制作duilib的菜单, ...

最新文章

  1. 前端学习(2553):内容概述
  2. 关于HTTPS认证,这里解决你所有疑惑
  3. android studio第三方调试,Android Studio直接运行调试签名包
  4. 【easy!】LeetCode 14. Longest Common Prefix
  5. 数字金字塔MySQL存储过程_千金良方——MySQL性能优化金字塔法则
  6. 简历制作器App隐私政策
  7. POJO类中属性必须使用包装数据类型
  8. 高级计算机图形学建模技术与方法
  9. 邮件安全风险评估方案
  10. 高工指数首发,德赛西威/哈曼/比亚迪「领衔」智能车机TOP10
  11. Mysql索引灵魂拷问
  12. uniapp获取手机号流程
  13. 联盟CPS聚合联盟聚推客推广项目赚钱吗?揭秘有人月入10万+
  14. mysql根据字长查询_MYSQL常用查命令
  15. 键盘驱动系列---JIURL键盘驱动 3
  16. Photon Pun
  17. 如何解决Tomcat下中文乱码问题?
  18. 用故事讲清楚统计学的Confidence Interval(置信区间)and Hypothesis Test
  19. 【实战】Windows 10 CodeSoft 6 条形码标签打印开发实战 【产品标签设计印刷】【Codesoft】
  20. 键盘事件和keycode对照表

热门文章

  1. 百年诺奖的那些争议与放弃
  2. adb push ,adb pull和adb install的区别
  3. endl与'\n'的区别
  4. linux的基础知识——signal信号捕捉,信号集操作函数
  5. 牛客16464 神奇的幻方
  6. HashMap源码分析(搞懂HashMap看这个就够了)
  7. Leetcode--870. 优势洗牌
  8. java调用一个方法后怎么继续执行不等待该方法的返回_Java面试题大全2020版(二)...
  9. 计算机综合应用实验,计算机综合应用实验二WORD应用.doc
  10. java c 基本类型_java 基本数据类型