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

恩智浦LPC845-BRK主板上的Binky

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

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

恩智浦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,
,
}; /* 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 ()
{
}
} while ()
{
/* 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 Transactional API

  这样一来,内存情况为:

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用于启动/主控和中断(参见“ ARM Cortex-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、链接

声明: 此篇由 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. 为duilib的MenuDemo增加消息响应,优化代码和显示效果

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

  2. Keil C减小代码编译量大小的方法(gai)

    keil-C减小代码编译大小的方法整理 方法一:(通过优化代码减小) 1.1少做乘除运算,使用左/右移位来实现乘除 Eg ,普通:a = 0x80*4: 优化:a = 0x80<<2: 1 ...

  3. Webpack 4教程:为什么要优化代码

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/07/30/webpack-4-course-part ...

  4. Android 性能优化:使用 Lint 优化代码、去除多余资源

    前言 在保证代码没有功能问题,完成业务开发之余,有追求的程序员还要追求代码的规范.可维护性. 今天,以“成为优秀的程序员”为目标的拭心将和大家一起精益求精,学习使用 Lint 优化我们的代码. 什么是 ...

  5. 如何优化代码中大量的if/else,switch/case?

    前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单 ...

  6. JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍

    概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...

  7. 效率包括了代码的GC 大小与内存大小,执行速度等等。其中执行速度不是关注 的重点

    效率包括了代码的GC 大小与内存大小,执行速度等等.其中执行速度不是关注的重点

  8. JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧

    概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...

  9. 通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数--菜单功能'menufile

      通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁 ...

随机推荐

  1. Kotlin函数使用综述与显式返回类型分析

    位置参数与具名参数: 继续接着上一次https://www.cnblogs.com/webor2006/p/11498842.html的方法参数学习,再定义一个函数来说明具名参数的问题: 调用一下,先 ...

  2. [转载]Fiddler界面详解

    转载地址:http://www.cnblogs.com/chengchengla1990/p/5681775.html Statistics 页签 完整页签如下图: Statistics 页签显示当前 ...

  3. C#各版本

    C#各版本 本系列文章主要整理并介绍 C# 各版本的新增功能. C# 8.0 C#8.0 于 2019年4月 随 .NET Framework 4.8 与 Visual Studio 2019 一同发 ...

  4. 【Beta】Scrum meeting3

    第三天:2019/6/26 前言: 第3次会议于6月26日在教9-501召开. 对每个人负责撰写的文档进行分配,并讨论其中模糊的问题,时长30min. 本日任务完成情况 成员 今日完成任务情况 成员贡 ...

  5. 在linux中使用Sqlplus命令登录MySQL,查看表并设置行数和宽度,使其正常显示

    在linux中使用sqlplus命令进入MySQL,设置行数和行宽   1) 查看目前的pagesize,默认是14: 1.      show pagesize;    2.      set pa ...

  6. Java线程池(ExecutorService)使用

    一.前提 /** * 线程运行demo,运行时打出线程id以及传入线程中参数 */ public class ThreadRunner implements Runnable { private fi ...

  7. [Flutter] Flexible the Widget height to available space

    Let's say you set widget height to 200, but to different screen, there might not be enough space for ...

  8. BZOJ1113 海报PLA1(单调栈入门题)

    一,自己思考下 1,先自己思考下 N个矩形,排成一排,现在希望用尽量少的海报去cover住它们. 2,不懂. 着实不懂. 3,分析下,最优性问题对吧,然后就每什么想法了.. 虽然肯定和单调栈和单调队列 ...

  9. 使用Quasar设计Material和IOS风格的响应式网站

    使用Quasar设计Material和IOS风格的响应式网站 栏目: CSS · 发布时间: 8个月前 来源: segmentfault.com   本文转载自:https://segmentfaul ...

  10. CollectionUtils.select用法

    import java.util.ArrayList;import java.util.List; import org.apache.commons.collections.CollectionUt ...