如何优化代码和RAM大小
如果供應(yīng)商為我自己的項目提供了一個起點,那就太好了。工作'blinky'始終是一個偉大的首發(fā)。方便總是有代價,而且“blinky”就是夸大“切換GPIO引腳”的代碼大小。對于具有少量RAM和FLASH的設(shè)備,這可能會引起關(guān)注:如果'blinky'占用那么多,我的應(yīng)用程序是否適合該設(shè)備?不要擔(dān)心:可以輕松地修剪掉(或任何其他項目)。
恩智浦LPC845-BRK主板上的Binky
我在這里使用一個'blinky'項目作為一個例子:修剪技巧也適用于任何其他類型的項目。
在本教程中,我在BRK(突破)板上使用NXPLPC845:
恩智浦LPC845-BRK板
1、Blinky示例
我所使用的是基于Eclipse的NXP MCUXpresso IDE:
選擇SDK板
我使用供應(yīng)商默認(rèn)設(shè)置創(chuàng)建了'blinky'項目:
Blinky項目
一個'blinky'應(yīng)該閃爍一個LED,對任何項目來說都是一個好的開手機(jī)。構(gòu)建相當(dāng)小的項目,代碼大小如下:
| 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 |
該信息也在控制臺中顯示,分為文本,數(shù)據(jù)和bss:
10K的'blinky'看起來有點夸張。但是我們現(xiàn)在將在接下來的步驟中修改它。
2、大小信息
有關(guān)大小信息的含義,請閱讀“?text,data和bss:Code and Data Size Explained?”。查看我的設(shè)備上使用空間的正常方法是檢查鏈接器映射文件(* .map):
鏈接器映射文件
但是這個map文件很難閱讀,而且對于專家來說更是如此:它列出了具有地址和大小的部分:
鏈接器映射文件內(nèi)容
使用MCUXpresso IDE V11,有一個很好的“圖像信息”視圖,它基本上是一個更好的ma'p文件信息查看器:
圖像信息查看
我可以過濾和排序數(shù)據(jù),這讓我知道代碼和數(shù)據(jù)使用了多少空間:
圖像信息存儲器內(nèi)容
當(dāng)然,它需要一些關(guān)于應(yīng)用程序應(yīng)該做什么的知識。我總是瀏覽視圖中的項目列表,看看是否有任何我不希望的東西:也許應(yīng)用程序正在使用可以刪除的東西。
3、源代碼
對于一個簡單的眨眼,這是相當(dāng)小的。首先要檢查程序正在做什么。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);} }基本上,代碼正在初始化引腳,時鐘,設(shè)置SysTick定時器,然后在循環(huán)中執(zhí)行'blinky',使用Systick計數(shù)器延遲閃爍周期。
4、調(diào)試控制臺
但我可以看到它初始化一個調(diào)試控制臺(以及它的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% |
在許多情況下,演示應(yīng)用程序會設(shè)置一些通信通道,但之后就不會使用它們。鏈接器可以很好地刪除未使用的對象(函數(shù)/變量),但前提是它們沒有被引用。
5、半主控和printf()
接下來要看的是是否存在任何半主機(jī)或printf()。該項目正在使用'Redlib',這是一個優(yōu)化的庫,與'標(biāo)準(zhǔn)'newlib或較小標(biāo)準(zhǔn)的newlib-nano相比:
Redlib
盡管如此,該庫可能會增加代碼大小,因為它使用半主機(jī)(通過調(diào)試器發(fā)送消息)。查看Memory視圖,我可以直接或間接地看到所需的所有這些標(biāo)準(zhǔn)I / O函數(shù):
stdio功能
擁有該功能的所有鉤子只有在使用它時才有意義,并且“blinky”不會使用它。因此,擺脫半主機(jī)和所有未使用的標(biāo)準(zhǔn)I / O意味著使用'none'變體:
沒有標(biāo)準(zhǔn)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% |
?
或者使用較小的變體或?qū)崿F(xiàn)。有關(guān)此問題的更多背景信息,請參閱本文末尾的鏈接。
6、DEBUG和NDEBUG
接下來要檢查編譯器是否定義了列出的DEBUG。事實上,情況就是這樣:
DEBUG定義
使用該定義集,SDK和示例驅(qū)動程序中有許多額外的代碼,它們使用'assert()'宏檢查好的值:
SDK代碼中斷言的用法
在這里,圖像信息視圖再次有用:它向我展示了使用assert()的所有地方:
斷言用法
實際上,在代碼中使用斷言來盡早捕獲編程錯誤是一種很好的做法。但是所有的assert()代碼確實加起來了。要關(guān)閉額外的代碼(和安全帶!),我將宏更改為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正在使用預(yù)期的SysTick中斷。但是仍然使用UART中斷?
使用中斷
大多數(shù)中斷都實現(xiàn)為“weak”:實現(xiàn)為默認(rèn)/空,可以被應(yīng)用程序覆蓋。但UART沒有意義,因為”blinky”沒有使用任何UART通信?
事實證明,NXP SDK默認(rèn)啟用了UART事務(wù)API:
UART Transactional API設(shè)置
事務(wù)API允許在通信組塊/事務(wù)中發(fā)送/接收UART數(shù)據(jù)。但我們不需要在我們的眨眼中,所以讓我們把它關(guān)掉:
關(guān)閉UART TransactionalAPI
這樣一來,內(nèi)存情況為:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 2964 B | 64 KB | 4.52% |
| SRAM: | 2184 B | 16 KB | 13.33% |
但我認(rèn)為CMSIS(設(shè)置中斷優(yōu)先級,通用時鐘設(shè)置)非常有用,所以我不在這里觸摸它。應(yīng)用程序中最大的功能是SysTick代碼用來將定時器的優(yōu)先級設(shè)置為最低優(yōu)先級,以節(jié)省另外220個字節(jié):
CMSIS作為最大的單一功能代碼大小貢獻(xiàn)者
8、優(yōu)化
到目前為止,我已經(jīng)刪除了不需要的或未使用的功能。接下來我可以打開編譯器優(yōu)化。默認(rèn)情況下,項目設(shè)置為-O0:
編譯器優(yōu)化
-O0表示無優(yōu)化:代碼直觀且易于調(diào)試。
-O1主要優(yōu)化函數(shù)進(jìn)入/退出代碼,并且能夠在不影響調(diào)試的情況下減少代碼大小。在這個例子中,它將代碼大小減少了一半!
?
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1540 B | 64 KB | 2.35% |
| SRAM: | 2184 B | 16 KB | 13.33% |
-O2優(yōu)化更多并盡可能地將事物保存在寄存器中。因為應(yīng)用程序中的功能相當(dāng)小,所以改進(jìn)并不大:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1516 B | 64 KB | 2.31% |
| SRAM: | 2184 B | 16 KB | 13.33% |
-O3通過額外的內(nèi)聯(lián)優(yōu)化最佳。-O3的目標(biāo)是速度,所以難怪代碼大小再次增加:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1792 B | 64 KB | 2.73% |
| SRAM: | 2184 B | 16 KB | 13.33% |
代碼大小優(yōu)化的最佳選擇是-Os(針對大小進(jìn)行優(yōu)化):
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 2184 B | 16 KB | 13.33% |
現(xiàn)在看起來很合理!當(dāng)然現(xiàn)在有一些方法可以為“裸露的裸眼”切斷更多,但是現(xiàn)有的一切(啟動代碼,時鐘和GPIO初始化)對于真正的應(yīng)用程序是有意義的,所以我現(xiàn)在停在這里。
9、RAM:堆和堆棧
看起來不正確的是SRAM的使用。'heap'使用了一大塊:
堆內(nèi)存使用情況
該堆用于動態(tài)內(nèi)存分配(malloc())。嵌入式編程的一般規(guī)則是避免它。但它默認(rèn)在這里。它可以在鏈接器設(shè)置中關(guān)閉:演示使用1K用于堆和堆棧。由于我沒有使用malloc(),我可以將堆大小設(shè)置為0x0。對于真正依賴于應(yīng)用程序的保留堆棧。在ARM Cortex上,MSP用于啟動/主控和中斷(參見“?ARMCortex-M中斷和FreeRTOS?”)。0x100(256字節(jié))應(yīng)該足夠我的眨眼。
堆和堆棧大小
這讓我了解到一點:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 392 B | 16 KB | 2.39% |
如果它是關(guān)于進(jìn)一步減小堆棧大小,我可以查看調(diào)用圖信息,它給出了有關(guān)使用多少堆棧空間的信息:
堆棧大小的圖形顯示
有一些項目的大小信息未知(標(biāo)有“?”)因為它們在庫中。驗證實際堆棧使用情況的方法是編寫模式(例如0xffff'ffff),然后運(yùn)行應(yīng)用程序一段時間:
使用的堆棧
這表明實際使用了72個字節(jié)。有一點余地,在這種情況下將堆棧大小設(shè)置為128字節(jié)看起來是合理的。這給出了:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 264 B | 16 KB | 1.61% |
堆棧溢出可能是嵌入式應(yīng)用程序中最常見的問題。如果可以的話,可以為堆棧提供盡可能多的RAM。如果縮小尺寸,請確保進(jìn)行了足夠的分析以證明堆疊尺寸合理。
10、MTB
剩下的一件事就是使用RAM空間:MTB緩沖區(qū)。微跟蹤緩沖區(qū)用于跟蹤,這非常有用(請參閱“?使用MTB跟蹤調(diào)試ARM Cortex-M0 +硬故障?”)。可以使用宏禁用緩沖區(qū):
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、摘要
供應(yīng)商的例子很棒:它們給了我一個很好的起點。它們沒有經(jīng)過優(yōu)化,這是故意的。但它們可能帶有我不需要的功能和功能。了解使用切斷功能或調(diào)整設(shè)置來優(yōu)化應(yīng)用程序的不同方法對于優(yōu)化RAM和FLASH使用非常有用。在本教程中,我展示了如何將'blinky'降低到大約1KB閃存和大約136字節(jié)的SRAM。當(dāng)然這一切都取決于功能和用法,但我認(rèn)為現(xiàn)在為我的應(yīng)用程序添加額外的功能是一個非常合理的狀態(tài)。
我希望這些提示可能對您的項目有用。
12、鏈接
- 文本,數(shù)據(jù)和bss:代碼和數(shù)據(jù)大小說明
- 拆箱恩智浦LPC845-BRK板
- 教程:使用恩智浦LPC845-BRK主板閃爍
- 使用恩智浦Kinetis SDK V2.0進(jìn)行半主機(jī)(再次!)
- 為什么我不喜歡printf()
- XFormat,輕量級printf()和sprintf()替代品
- 優(yōu)化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/。權(quán)屬歸原作者所有。
歡迎關(guān)注:
總結(jié)
以上是生活随笔為你收集整理的如何优化代码和RAM大小的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言around用法,KET基础语法:
- 下一篇: 滤波器开发之一:基于算数平均的平滑滤波器