如何优化代码和RAM大小

如果供应商为我自己的项目提供了一个起点,那就太好了。工作'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/。权属归原作者所有。

欢迎关注:

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

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

相关文章

改进初学者的PID-正反作用

最近看到了Brett Beauregard发表的有关PID的系列文章&#xff0c;感觉对于理解PID算法很有帮助&#xff0c;于是将系列文章翻译过来&#xff01;在自我提高的过程中&#xff0c;也希望对同道中人有所帮助。作者Brett Beauregard的原文网址&#xff1a;http&#xff1a;//brettb…

改进初学者的PID-测量的比例介绍

最近看到了Brett Beauregard发表的有关PID的系列文章&#xff0c;感觉对于理解PID算法很有帮助&#xff0c;于是将系列文章翻译过来&#xff01;在自我提高的过程中&#xff0c;也希望对同道中人有所帮助。作者Brett Beauregard的原文网址&#xff1a;http&#xff1a;//brettb…

改进初学者的PID-测量的比例编码

最近看到了Brett Beauregard发表的有关PID的系列文章&#xff0c;感觉对于理解PID算法很有帮助&#xff0c;于是将系列文章翻译过来&#xff01;在自我提高的过程中&#xff0c;也希望对同道中人有所帮助。作者Brett Beauregard的原文网址&#xff1a;http&#xff1a;//brettb…

PID:我应该何时计算积分项?

最近看到了Brett Beauregard发表的有关PID的系列文章&#xff0c;感觉对于理解PID算法很有帮助&#xff0c;于是将系列文章翻译过来&#xff01;在自我提高的过程中&#xff0c;也希望对同道中人有所帮助。作者Brett Beauregard的原文网址&#xff1a;http&#xff1a;//brettb…

Arduino PID自整定库

最近看到了Brett Beauregard发表的有关PID的系列文章&#xff0c;感觉对于理解PID算法很有帮助&#xff0c;于是将系列文章翻译过来&#xff01;在自我提高的过程中&#xff0c;也希望对同道中人有所帮助。作者Brett Beauregard的原文网址&#xff1a;http&#xff1a;//brettb…

LwIP应用开发笔记之二:LwIP无操作系统UDP服务器

前面我们已经完成了LwIP协议栈基于逻辑的基本移植&#xff0c;在这一节我们将以RAW API来实现UDP服务器。 1、UDP协议简述 UDP协议全称是用户数据报协议&#xff0c;在网络中它与TCP协议一样用于处理数据包&#xff0c;是一种无连接的协议。在OSI模型中&#xff0c;处于传输层…

LwIP应用开发笔记之三:LwIP无操作系统UDP客户端

前一节我们实现了基于RAW API的UDP服务器&#xff0c;在接下来&#xff0c;我们进一步利用RAW API实现UDP客户端。 1、UDP协议简述 UDP协议全称是用户数据报协议&#xff0c;在网络中它与TCP协议一样用于处理数据包&#xff0c;是一种无连接的协议。在OSI模型中&#xff0c;处…

LwIP应用开发笔记之四:LwIP无操作系统TFTP服务器

前面我们已经实现了UDP的回环客户端和回环服务器的简单应用&#xff0c;接下来我们实现一个基于UDP的简单文件传输协议TFTP。 1、TFTP协议简介 TFTP是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。端…

LwIP应用开发笔记之五:LwIP无操作系统TCP服务器

前面我们实现了UDP服务器及客户端以及基于其上的TFTP应用服务器。接下来我们将实现同样广泛应用的TCP协议各类应用。 1、TCP简述 TCP&#xff08;Transmission Control Protocol 传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;由…

LwIP应用开发笔记之六:LwIP无操作系统TCP客户端

上一篇我们基于LwIP协议栈的RAW API实现了一个TCP服务器的简单应用&#xff0c;接下来一节我们来实现一个TCP客户端的简单应用。 1、TCP简述 TCP&#xff08;Transmission Control Protocol 传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&a…

LwIP应用开发笔记之七:LwIP无操作系统HTTP服务器

前面我们实现了TCP服务器和客户端的简单应用&#xff0c;接下来我们实现一个基于TCP协议的应用协议&#xff0c;那就是HTTP超文本传输协议。 1、HTTP协议简介 超文本传输协议&#xff08;Hyper Text Transfer Protocol&#xff09;&#xff0c;简称HTTP&#xff0c;是一种基于…

LwIP应用开发笔记之八:LwIP无操作系统HTTP客户端

前面我们实现了TCP服务器和客户端的简单应用&#xff0c;接下来我们实现一个基于TCP协议的应用协议&#xff0c;那就是HTTP超文本传输协议 1、HTTP协议简介 超文本传输协议&#xff08;Hyper Text Transfer Protocol&#xff09;&#xff0c;简称HTTP&#xff0c;是一种基于T…

LwIP应用开发笔记之九:LwIP无操作系统TELNET服务器

前面我们已经实现了基于RAW API的TCP服务器和客户端&#xff0c;也在此基础上实现了HTTP应用。接下来我们实现一个基于TCP的Telnet服务器应用。 1、Telnet协议简介 Telnet协议是TCP/IP协议族中的一员&#xff0c;是Internet远程登陆服务的标准协议和主要方式。它为用户提供了…

在ARM Cortex-M上实现FreeRTOS性能计数器

说明&#xff1a;本文翻译自Erich Styger的文章《Implementing FreeRTOS Performance Counters on ARM Cortex-M》&#xff0c;文章的权属属于原作者。 当使用像FreeRTOS这样的RTOS时&#xff0c;迟早要问一个问题&#xff1a;每个任务花费多少时间&#xff1f;基于Eclipse的M…

STM32学习及开发笔记八:采用主从计时器实现精确脉冲输出

脉冲信号用于设备控制是非常常见的&#xff0c;但在一些情况下&#xff0c;我们希望精确的控制脉冲的数量以实现对运动的精确控制。实现的方式也许有多种多样&#xff0c;但使用计时器来实现此类操作是人们比较容易想到的。 1、原理概述 我们知道在STM32平台上&#xff0c;使…

外设驱动库开发笔记0:EPD总体设计

在产品开发过程中&#xff0c;不可避免需要使用很多外部的元件及传感器&#xff0c;这些元器件也许是板载的&#xff0c;也许是板外的&#xff0c;但不管怎样&#xff0c;为其开发驱动程序都是必须的。每次都需要为这些元器件编写驱动程序。但每次重复编写调试很麻烦&#xff0…

外设驱动库开发笔记1:AD56xx系列DAC驱动

DAC在我们的项目中经常使用到&#xff0c;而使用最多的就是AD56xx系列&#xff0c;包括有单通道的AD5662、双通道的AD5623和AD5663、以及四通道的AD5624和AD5664等。出于方便复用的原因&#xff0c;我们设计并实现AD56xx系列DAC的驱动。 1、功能概述 AD56xx系列DAC属于nanoDA…

外设驱动库开发笔记2:AD8400系列数字电位器驱动

一些时候我们需要在系统使用过程中改变某些电路电阻值以达到改变设定的目的&#xff0c;这时候我们就会使用电位器。在我们使用数字控制电路时多选择数字电位器。在这一篇我们就来设计AD8400系列数字电位器的驱动。 1、功能概述 AD8400/AD8402/AD8403分别是单通道/双通道/四通…

外设驱动库开发笔记3:AD527x系列数字电位器驱动

在一些时候我们需要使用精度更高的数字电位器来实现我们的应用。我们经常使用AD527x系列数字电位器来实现这类应用。在通常情况下&#xff0c;AD527x系列数字电位器完全能够满足要求。为了减少重复工作&#xff0c;在这里我们将分系并实现AD527x系列数字电位器的驱动。 1、功能…

PID控制器改进笔记之一:改进PID控制器之参数动态调整

前面我们发布了一系列PID控制器相关的文章&#xff0c;包括经典PID控制器以及参数自适应的PID控制器。这一系列PID控制器虽说实现了主要功能&#xff0c;也在实际使用中取得了良好效果&#xff0c;但还有很多的细节部分可以改进以提高性能和灵活性。所以在这篇中我们来讨论改进…