一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

挑戰(zhàn)用一百個(gè)字節(jié)寫一個(gè)閃爍燈程序

麥克泰技術(shù) ? 來源:麥克泰技術(shù) ? 作者:麥克泰技術(shù) ? 2023-05-16 15:59 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

# 作者:Roff Segger,麥克泰技術(shù)測試、翻譯和編寫

我們使用SEGGER公司的Embedded Studio開發(fā)環(huán)境進(jìn)行測試:在一個(gè)Cortex-M微控制器上,看看需要使用多少Flash存儲器才能夠完成一個(gè)LED燈的閃爍?

目標(biāo):

· 使用少于100個(gè)字節(jié)的程序完成一個(gè)閃爍應(yīng)用

· 使用人眼容易看到的切換頻率(即1-5Hz范圍)

· 主程序用C/C++語言編寫

· 使用方便得到的硬件

· 不使用或禁用工具鏈的運(yùn)行時(shí)系統(tǒng)初始化

本文將大致介紹我們要使用的每一個(gè)字節(jié)和每一條指令。這是一個(gè)了解系統(tǒng)啟動(dòng)時(shí)到底發(fā)生了什么,即在到達(dá)main()函數(shù)之前“底層”發(fā)生什么的好途徑。

簡而言之:使用Embedded Studio開發(fā)環(huán)境可以在使用不到100字節(jié)的程序內(nèi)完成這個(gè)工作。

01硬件

我們使用的硬件是一塊STM32跟蹤參考板。它非常簡單,只有一個(gè)STM32F407微控制器、3個(gè)LED、一個(gè)調(diào)試/跟蹤接口和一個(gè)USB供電端口。

4f44b80a-f0c9-11ed-90ce-dac502259ad0.jpg

每個(gè)J-Trace仿真器交付中包含該開發(fā)板,然而,在這里,我僅僅使用常規(guī)的J-Link功能下載和調(diào)試程序。用戶也可以選擇任何帶LED的硬件測試。

02生成項(xiàng)目

非常簡單,打開Embedded Studio開發(fā)環(huán)境,從菜單中選擇File -> New Project,選擇第一個(gè)選項(xiàng),創(chuàng)建可執(zhí)行文件。

4f6b8fb6-f0c9-11ed-90ce-dac502259ad0.png

根據(jù)提示,選擇使用默認(rèn)值,單擊next幾次后,我最終得到了一個(gè)小項(xiàng)目,如下面的Project Explorer窗口中所示。

4f9a400e-f0c9-11ed-90ce-dac502259ad0.png

選擇Build->Build Mini或按F7構(gòu)建我們的程序。

Debug -> Go或F5啟動(dòng)調(diào)試器。

我們現(xiàn)在沒有連接硬件,所以Embedded Studio要求我們使用內(nèi)置模擬器。

4fb7ddb2-f0c9-11ed-90ce-dac502259ad0.png

點(diǎn)擊Yes或點(diǎn)擊Enter啟動(dòng)模擬器。

調(diào)試器停在main()函數(shù)處,這是一個(gè)標(biāo)準(zhǔn)的 “Hello world”應(yīng)用程序。

4fd6ff44-f0c9-11ed-90ce-dac502259ad0.png

現(xiàn)在,為了實(shí)現(xiàn)最小的應(yīng)用程序,我們將其簡化為一個(gè)基本上是空的循環(huán)。

int main(void) {
 int i; 
 
 do {
  i++;
 } while (1);
5000f498-f0c9-11ed-90ce-dac502259ad0.png

結(jié)果只占用了158字節(jié)的Flash。這已經(jīng)非常不錯(cuò)了,但是在添加實(shí)際LED閃爍功能之前,我需要了解內(nèi)存的占用,以及如何使我的程序最小化。

為了做到這一點(diǎn),我可以查看Memory Usage Window、鏈接器映射文件、生成的ELF文件,或者簡單地查看Project Explorer。

從Project Explorer窗口可以知道,這個(gè)可執(zhí)行文件由3個(gè)源程序文件構(gòu)成,以及它們使用了多少Code + RO空間。請注意,這些是編譯器生成對像的數(shù)值。對于最終的可執(zhí)行文件,鏈接器可以消除未使用的功能,或者在必要時(shí)添加一些結(jié)合層代碼(從Flash跳到RAM或從Thumb指令跳到ARM指令)和填充(如:保證4字節(jié)對齊)。

另一個(gè)使用Flash存儲器的地方,可能是從庫中鏈接進(jìn)來的代碼,例如:C運(yùn)行時(shí)庫。然而,我們的小項(xiàng)目并沒有使用庫函數(shù),因此我們不必考慮庫代碼的空間占用。

502279e2-f0c9-11ed-90ce-dac502259ad0.png

而且,Project Explorer展示了每個(gè)源文件的內(nèi)存使用情況(2、128和24字節(jié))和項(xiàng)目可執(zhí)行文件總的內(nèi)存使用情況:158字節(jié)。這和我們在Output窗口中看到的數(shù)值相同。

03理解項(xiàng)目結(jié)構(gòu)

這三個(gè)文件的用途?我們的應(yīng)用程序只是一個(gè)簡單的main()函數(shù)。為什么我還需要另外兩個(gè)文件呢?

main.c – 應(yīng)用程序。

C ortex_M_Startup.s – CPU相關(guān)代碼,包含中斷向量表。

SEGGER_THUMB_Startup.s – 應(yīng)用編程人員不需要修改的代碼。

讓我們更詳細(xì)地了解它們,以揭開大家都想知道的謎團(tuán):啟動(dòng)代碼是如何工作的?

有了這些知識,讓我們看看如何縮小我們的應(yīng)用程序。

04main.c

main.c包含我們的應(yīng)用,一個(gè)最簡單的main()函數(shù)。

我們的編譯器足夠智能。它可以看出這個(gè)程序什么都不做,并將其優(yōu)化為只使用一條指令或兩個(gè)字節(jié)代碼的空循環(huán)。

我怎么知道的?我們可以看看main.o,這是編譯器產(chǎn)生的輸出。在Project Explorer中,右鍵單擊main.c->Show Disassembly,或者展開它,雙擊Output files中的main.o。它揭示了主程序只有一個(gè)分支。

5045cfc8-f0c9-11ed-90ce-dac502259ad0.png

這是我們的主應(yīng)用程序。我們已經(jīng)沒有辦法再簡化它了。

05Cortex_M_Startup.s

Cortex_M_Startup.s包含了應(yīng)用程序在Cortex-M硬件上執(zhí)行所需要的CPU相關(guān)代碼。它包含中斷向量表和上電或復(fù)位時(shí)執(zhí)行的函數(shù):Reset_Handler。

此文件使用了大部分Flash空間。讓我們仔細(xì)看看它產(chǎn)生了什么。

Cortex_M_Startup.o顯示其包含中斷向量表 .vectors段及默認(rèn)的異常處理程序?qū)崿F(xiàn)。

section .vectors
<_vectors>
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
00000000 .word 0x00000000
 
section .init.NMI_Handler

E7FE b 0x00000000
 
section .init.MemManage_Handler

E7FE b 0x00000000
 
section .init.BusFault_Handler

E7FE b 0x00000000
 
section .init.UsageFault_Handler

E7FE b 0x00000000
 
section .init.SVC_Handler

E7FE b 0x00000000
 
section .init.DebugMon_Handler

E7FE b 0x00000000
 
section .init.PendSV_Handler

E7FE b 0x00000000
 
section .init.SysTick_Handler

E7FE b 0x00000000
 
section .init.Reset_Handler

F7FFFFFE bl 0x00000000
F7FFBFFE b.w 0x00000004
 
section .init.HardFault_Handler

4908 ldr r1,
680A ldr r2, [r1]
2A00 cmp r2, #0

D4FE bmi 0x00000006
F01E0F04 tst lr, #4
BF0C ite eq
F3EF8008 mrseq r0, msp
F3EF8009 mrsne r0, psp
F0424200 orr r2, r2, #0x80000000
600A str r2, [r1]
6981 ldr r1, [r0, #24]
3102 adds r1, #2
6181 str r1, [r0, #24]
4770 bx lr
E000ED2C .word 0xE000ED2C

這就是罪魁禍?zhǔn)住?/p>

ARM內(nèi)核定義了向量表中的前16個(gè)表項(xiàng),然后是設(shè)備外部中斷的表項(xiàng)。該文件提供了一個(gè)有16個(gè)表項(xiàng)(或64個(gè)字節(jié))的向量表。這些條目僅用于該表。

在應(yīng)用程序中,我們沒有處理任何故障或中斷,實(shí)際上我們只需要Reset_Handler,這是復(fù)位立即執(zhí)行的代碼。我們還需要向量表中的第一個(gè)表項(xiàng),它在復(fù)位時(shí)完成堆棧指針(SP)的初始化。

因此,我們刪除所有不必要的表項(xiàng),將此表刪減為兩個(gè)表項(xiàng),同時(shí)將消除默認(rèn)的異常處理程序。

我們重新生成應(yīng)用程序。不錯(cuò)!現(xiàn)在應(yīng)用減少為42個(gè)字節(jié)。

5061ec3a-f0c9-11ed-90ce-dac502259ad0.png5083fc58-f0c9-11ed-90ce-dac502259ad0.png

讓我們看看輸出的elf文件的內(nèi)容。

從0x0000 0000開始的8個(gè)字節(jié):向量表,包含初始化SP和指向Reset_Handler的指針。

從0x0000 001E 開始的8個(gè)字節(jié): Reset_Handler,兩條4字節(jié)指令。鏈接器插入的一條nop指令,代替SystemInit的調(diào)用(在應(yīng)用程序中不存在),以及一個(gè)跳轉(zhuǎn)到_start的指令。

從0x0000 0008開始的20字節(jié):SEGGER_THUMB_Startup.s的通用運(yùn)行時(shí)初始化,它執(zhí)行鏈接器生成的對來自SEGGER_init_table的初始化函數(shù)的調(diào)用,然后,調(diào)用main,如果main返回,則停在exit循環(huán)中。

從0x0000 0028開始4字節(jié):鏈接器生成了SEGGER_init_table,

其中包含需要在main之前調(diào)用的初始化函數(shù)。它可能包含段初始化(復(fù)制初始化的數(shù)據(jù))、段填充(用于0初始化的靜態(tài)變量或堆棧的預(yù)填充)、堆初始化或全局C++對象的構(gòu)造函數(shù)調(diào)用。這些都沒有在我們的應(yīng)用程序中使用。

最后一條(唯一)指令是跳到運(yùn)行時(shí)初始化的末尾,調(diào)用main函數(shù)。

加上從0x00000026開始的為對齊SEGGER_int_table的 2個(gè)字節(jié)的填充,總共是42個(gè)字節(jié)。

50a46984-f0c9-11ed-90ce-dac502259ad0.png50c193f6-f0c9-11ed-90ce-dac502259ad0.png

因?yàn)閼?yīng)用中沒有使用SystemInit功能,所以我們可以刪除bl SystemInit語句,并用nop取代,以節(jié)省4個(gè)字節(jié),并減少到38+2=40個(gè)字節(jié)。

我們的應(yīng)用程序已經(jīng)是盡可能小了。下面我們開始添加閃爍代碼!

06添加閃爍代碼

我們編寫了一些用于初始化和控制參考板上LED的代碼和一個(gè)簡單的延遲函數(shù)。

有了這些代碼,我們就可以創(chuàng)建帶有閃爍功能的主應(yīng)用程序了,如下所示:

/****************************************
*
* main()
*
* Function description
* Application entry point.
*/
int main(void) {
  _InitLED();
  for (;;) {
   _SetLED();
  _Delay(NUM_DELAY_LOOPS);
  _ClrLED();
  _Delay(NUM_DELAY_LOOPS);
  }
}

完整的源代碼工程可以訪問(可點(diǎn)擊“閱讀原文”):https://blog.segger.com/wp-content/uploads/2020/08/Blinky_Mini.zip

讓我們重新構(gòu)建并檢查輸出。

成功了!應(yīng)用程序的大小只有96個(gè)字節(jié)(需要使用release模式構(gòu)建,使用debug模式代碼體積會(huì)比較大)。

它真的可以運(yùn)行嗎?讓我們試一試。我們將電路板連接到J-Link,并將J-Link連接到我們的計(jì)算機(jī)。按F5鍵運(yùn)行它。就像這個(gè)項(xiàng)目開始時(shí)一樣,調(diào)試會(huì)話開始并運(yùn)行到main函數(shù),只是這次是在實(shí)際硬件而非模擬器上。當(dāng)我們再次點(diǎn)擊F5繼續(xù)執(zhí)行時(shí),我們可以看到開發(fā)板上的LED0在閃爍。

07小結(jié)

C語言寫的閃爍程序確實(shí)可以放在不到100字節(jié)的程序(或者更準(zhǔn)確地說是只讀)存儲器中。

啟動(dòng)代碼不需要那么復(fù)雜。它只是完成了硬件的初始化(SystemInit的用途)和運(yùn)行時(shí)系統(tǒng)的初始化。

運(yùn)行時(shí)系統(tǒng)初始化由Embedded Studio和SEGGER鏈接器負(fù)責(zé)。它確保只包含必要的代碼,以使生成的可執(zhí)行文件盡可能小。

SEGGER鏈接器還能夠包括特定的初始化,例如:在需要的時(shí)候,完成堆的初始化和調(diào)用構(gòu)造函數(shù)。這些功能是由鏈接器中的腳本控制。

initialize by symbol __SEGGER_init_heap { block heap }; // Init the heap if there is one
initialize by symbol __SEGGER_init_ctors { block ctors }; // Call constructors for globalobjects which need

SEGGER鏈接器生成的啟動(dòng)代碼非常小,并且易于理解。聯(lián)合高效的SEGGER編譯器與模塊化的運(yùn)行庫和主機(jī)端輸出printf()函數(shù),我們就可以傲視群雄了。

看看電腦上簡單的“Hello World”程序的大小,也許我們還應(yīng)該提供一個(gè)可以在電腦上生成相同小程序的SEGGER Studio。

你程序還能更精簡嗎?用你的工具鏈試試,挑戰(zhàn)用100字節(jié)寫一個(gè)閃爍程序!我相信,在同樣的硬件上,這將是很難被擊敗的。

08這個(gè)項(xiàng)目的代碼還能更緊湊嗎?

令人驚訝的答案是:是的。

首先:一些微控制器具有切換寄存器,這允許將循環(huán)切割為_ToggleLED() / Delay()。

還有,初始化內(nèi)容需要的代碼量各不相同,在其他硬件上可能會(huì)更小。

但是即使在相同的硬件上,我們也可以進(jìn)一步減小程序大小。

我們可以將_start放入向量表中,這樣程序就可以在通用啟動(dòng)代碼中開始執(zhí)行,從而節(jié)省了4字節(jié)的跳轉(zhuǎn)空間。

我們可以刪除exit() 和2字節(jié)的分支,因?yàn)槲覀冎續(xù)ain()程序中永遠(yuǎn)不會(huì)返回。

因?yàn)槲抑幌胍坏?00個(gè)字節(jié)的程序,所以,讓我們到此為止吧。

祝大家編碼快樂!

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 微控制器
    +關(guān)注

    關(guān)注

    48

    文章

    7943

    瀏覽量

    154622
  • led燈
    +關(guān)注

    關(guān)注

    22

    文章

    1596

    瀏覽量

    109689
  • 存儲器
    +關(guān)注

    關(guān)注

    38

    文章

    7647

    瀏覽量

    167161
  • usb
    usb
    +關(guān)注

    關(guān)注

    60

    文章

    8173

    瀏覽量

    272439
  • STM32
    +關(guān)注

    關(guān)注

    2293

    文章

    11031

    瀏覽量

    364298

原文標(biāo)題:挑戰(zhàn)用一百個(gè)字節(jié)寫一個(gè)閃爍燈程序!

文章出處:【微信號:麥克泰技術(shù),微信公眾號:麥克泰技術(shù)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    proteus仿真MCS-51的一百個(gè)例子(2)

    proteus仿真MCS-51的一百個(gè)例子(2)
    發(fā)表于 07-28 10:19 ?35次下載
    proteus仿真MCS-51的<b class='flag-5'>一百個(gè)</b>例子(2)

    proteus仿真MCS-51的一百個(gè)例子(3)

    proteus仿真MCS-51的一百個(gè)例子(3)
    發(fā)表于 07-28 10:22 ?64次下載
    proteus仿真MCS-51的<b class='flag-5'>一百個(gè)</b>例子(3)

    proteus仿真MCS-51的一百個(gè)例子

    proteus仿真MCS-51的一百個(gè)例子
    發(fā)表于 07-28 10:27 ?191次下載
    proteus仿真MCS-51的<b class='flag-5'>一百個(gè)</b>例子

    示波器基礎(chǔ)知識一百個(gè)問答

    示波器基礎(chǔ)知識一百個(gè)問答:1. 對個(gè)已設(shè)計(jì)完成的產(chǎn)品,如何用示波器經(jīng)行檢測分析其可靠性?答:示波器早已成為檢測電子線路最有效的工具之,通過觀察線路關(guān)鍵節(jié)點(diǎn)的
    發(fā)表于 08-21 22:23 ?0次下載

    一個(gè)字節(jié)到24c02中(源程序)

    一個(gè)字節(jié)到24c02中(源程序) 24c02是個(gè)非揮發(fā)eeprom存儲器器件,采用的IIC總線技術(shù)。24c02在許多試驗(yàn)中都有出現(xiàn)。
    發(fā)表于 08-11 19:17 ?3008次閱讀

    UART 發(fā)送數(shù)據(jù)丟失最后一個(gè)字節(jié)

    STM32 UART 發(fā)送數(shù)據(jù)丟失最后一個(gè)字節(jié)
    發(fā)表于 12-04 15:10 ?0次下載

    不同頻率閃爍1個(gè)LED

    6-不同頻率閃爍1個(gè)LED---51單片機(jī)源代碼 keil直接打開
    發(fā)表于 06-15 18:17 ?2次下載

    1602與51一個(gè)字母_源代碼

    1602與51一個(gè)字母_源代碼,感興趣的可以看看。
    發(fā)表于 07-19 16:55 ?2次下載

    示波器基礎(chǔ)知識一百個(gè)問答

    電路教程相關(guān)知識的資料,關(guān)于示波器基礎(chǔ)知識一百個(gè)問答
    發(fā)表于 10-10 14:34 ?0次下載

    Proteus的51單片機(jī)仿真和程序一百個(gè)實(shí)例說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是Proteus的51單片機(jī)仿真和程序一百個(gè)實(shí)例說明。
    發(fā)表于 01-07 08:00 ?91次下載
    Proteus的51單片機(jī)仿真和<b class='flag-5'>程序</b>的<b class='flag-5'>一百個(gè)</b>實(shí)例說明

    使用51單片機(jī)進(jìn)行EEPROM AT24c02存儲讀取一個(gè)字節(jié)程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用51單片機(jī)進(jìn)行EEPROM AT24c02存儲讀取一個(gè)字節(jié)程序資料免費(fèi)下載。
    發(fā)表于 06-10 17:48 ?9次下載
    使用51單片機(jī)進(jìn)行EEPROM AT24c02存儲讀取<b class='flag-5'>一個(gè)字節(jié)</b>的<b class='flag-5'>程序</b>免費(fèi)下載

    淺談STM32串口通信()基本介紹和一個(gè)字節(jié)傳輸?shù)膶?shí)現(xiàn)

    文章目錄0 傳輸引腳1 傳輸一個(gè)字節(jié)1.1 發(fā)送一個(gè)字節(jié)1.2 接收一個(gè)字節(jié)2 代碼2.1 配置2.2 發(fā)送一個(gè)字節(jié)2.3 接收一個(gè)字節(jié)0
    發(fā)表于 12-24 18:51 ?14次下載
    淺談STM32串口通信(<b class='flag-5'>一</b>)基本介紹和<b class='flag-5'>一個(gè)字節(jié)</b>傳輸?shù)膶?shí)現(xiàn)

    編寫個(gè)閃爍LED代碼

    電子發(fā)燒友網(wǎng)站提供《編寫個(gè)閃爍LED代碼.zip》資料免費(fèi)下載
    發(fā)表于 10-24 10:55 ?2次下載
    編寫<b class='flag-5'>一</b><b class='flag-5'>個(gè)</b><b class='flag-5'>閃爍</b>LED<b class='flag-5'>燈</b>代碼

    個(gè)exe程序

    圖形界面可以嘗試下 tkinter ,可以寫出來個(gè)圖形程序,以下 Python3 作為示
    的頭像 發(fā)表于 03-03 15:00 ?1329次閱讀
    <b class='flag-5'>寫</b><b class='flag-5'>一</b><b class='flag-5'>個(gè)</b>exe<b class='flag-5'>程序</b>

    UART發(fā)送數(shù)據(jù)丟失最后一個(gè)字節(jié)

    電子發(fā)燒友網(wǎng)站提供《UART發(fā)送數(shù)據(jù)丟失最后一個(gè)字節(jié).pdf》資料免費(fèi)下載
    發(fā)表于 08-01 17:57 ?1次下載
    UART發(fā)送數(shù)據(jù)丟失最后<b class='flag-5'>一個(gè)字節(jié)</b>