資料介紹
描述
介紹
本文中的“app”是指預編譯的二進制文件,無需使用 Arduino IDE,即可直接在 Arduino 板卡上運行。
并且因為它是一個文件,“應用程序”可以通過 SD 卡、以太網、WiFi 或任何合適的方法分發。
標題圖顯示了執行 Arduino 應用程序RTT-QRCode的 MKR ZERO 板。
你有興趣嗎?
(本文基于Arduino RT-Thread庫v0.6.0。)
動態模塊
在 RT-Thread 架構中,“app”被稱為動態模塊,構建為動態共享庫,擴展名為“ .mo
”或“ .so
”。(什么是 RT-Thread?=> Arduino 上的多任務處理)
RT-Thread 提供 API 來訪問動態模塊。更有趣的是,MSH(一個微型外殼)能夠.mo
直接執行“”文件(詳細信息在以下部分中)。
RT-Thread 的原始動態鏈接器似乎不適用于 ARM Cortex-M。所以我修改了 Arduino RT-Thread 庫的代碼。
多發性硬化癥
Module SHell (MSH) 是默認啟用的一項新功能(從 v0.5.1 開始),它構建在 FinSH 之上。(什么是 FinSH?=> Arduino 上的多任務處理)
由于 Arduino 應用程序是由 MSH 執行的,讓我們簡單介紹一下。
相比 FinSH,MSH 更符合 Unix shell 的使用習慣:
- 在 FinSH 中發出命令
led(0, 1)
copy("datalog.txt", "copy.txt")
- 在 MSH 中發出命令
led 0 1
cp datalog.txt copy.txt
但是,MSH 不支持像 FinSH 提供的那樣的 shell 變量。
另一個限制是用戶定義的 MSH 命令的原型是固定的:
int my_msh_cmd(int argc, char **argv)
MSH 執行用戶命令時,參數argc
為參數個數加一,參數列表argv
為參數列表(firstentryiscommandname)。您可能已經猜到了,所有參數只能是char
數組類型。
以下是 MSH 命令格式的“led”示例。
int led(int argc, char **argv) {
// argc - the number of arguments
// argv[0] - command name, e.g. "led"
// argv[n] - nth argument in the type of char array
rt_uint32_t id;
rt_uint32_t state;
if (argc != 3) {
rt_kprintf("Usage: led \n");
return 1;
}
rt_kprintf("led%s=%s\n", argv[1], argv[2]);
// convert arguments to their specific types
sscanf(argv[1], "%u", &id);
sscanf(argv[2], "%u", &state);
if (id != 0) {
rt_kprintf("Error: Invalid led ID\n");
return 1;
}
if (state) {
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
return 0;
}
制作 Arduino 應用程序
首先,CONFIG_USING_MODULE
在“rtconfig.h”中啟用,因為默認情況下它是禁用的。
構建可執行文件
讓我們在 Arduino IDE 中打開“HelloMo”示例,然后按“驗證”。(該示例也可以在下面的“代碼”部分中找到。)代碼現在被構建到一個包含草圖和庫的單個可執行文件中。我們可以使用 GCC 工具readelf
(Arduino IDE 提供)來驗證。
{path_to_gcc_tools}\arm-none-eabi-readelf -h {path_to_output}\HelloMo.ino.elf
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0xf7fd
Start of program headers: 52 (bytes into file)
Start of section headers: 798052 (bytes into file)
Flags: 0x5000002, has entry point, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 18
Section header string table index: 15
如果您不確定 GCC 工具和編譯輸出的位置,請在 File-> Preferences 中啟用以下選項。
再次單擊“驗證”,您將在輸出窗口中觀察到信息:
...
Compiling sketch...
"C:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\arm-none-eabi-gcc\\4.8.3-2014q1/bin/arm-none-eabi-gcc" -mcpu=cortex-m0plus -mthumb -c -g -Os -Wall -Wextra -std=gnu11 -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -MMD -DF_CPU=48000000L -DARDUINO=10809 -DARDUINO_SAMD_MKRZERO -DARDUINO_ARCH_SAMD -DUSE_ARDUINO_MKR_PIN_LAYOUT -D__SAMD21G18A__ -DUSB_VID=0x2341 -DUSB_PID=0x804f -DUSBCON "-DUSB_MANUFACTURER="Arduino LLC"" "-DUSB_PRODUCT="Arduino MKRZero"" "-IC:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\CMSIS\\4.5.0/CMSIS/Include/" "-IC:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\CMSIS-Atmel\\1.1.0/CMSIS/Device/ATMEL/" "-IC:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\samd\\1.6.21\\cores\\arduino" "-IC:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\samd\\1.6.21\\variants\\mkrzero" "-IC:\\Users\\onelife\\Documents\\Arduino\\libraries\\RT-Thread\\src" "-IC:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\samd\\1.6.21\\libraries\\SPI" "C:\\Users\\onelife\\AppData\\Local\\Temp\\arduino_build_508434\\sketch\\hello_mo.c" -o "C:\\Users\\onelife\\AppData\\Local\\Temp\\arduino_build_508434\\sketch\\hello_mo.c.o"
...
就我而言,GCC 工具位于“ C:\\Users\\onelife\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\arm-none-eabi-gcc\\4.8.3-2014q1/bin/
”,編譯輸出位于“ C:\\Users\\onelife\\AppData\\Local\\Temp\\arduino_build_508434\\
”。
構建應用程序(動態共享庫)
但是,我們要構建的目標“應用程序”是一種共享庫。它必須與位置無關,因此可以加載到任何 RAM 地址中。為了讓它更小(因為我們的 RAM 大小是有限的),最終的二進制文件將不包含其他庫的任何功能。(所有外部功能都應由固件端提供。)
壞消息是 Arduino IDE 不提供這些選項。好消息是 Arduino IDE 確實提供了我們需要的所有工具。我們開始做吧。
第一步是編譯。
我們必須將選項“ -mlong-calls -fPIC
”添加到原始編譯命令中(在輸出窗口中查找“正在編譯草圖...”)。
{path_to_gcc_tools}\arm-none-eabi-gcc -mlong-calls -fPIC ... {path_to_output}\sketch\hello_mo.c -o {path_to_output}\sketch\hello_mo.c.o
{path_to_gcc_tools}\arm-none-eabi-gcc -mlong-calls -fPIC ... {path_to_output}\sketch\load_mo.c -o {path_to_output}\sketch\load_mo.c.o
第二步是鏈接。
在這一步中,我們選擇將目標文件構建為“ app ”(帶有入口點的“.mo”文件)或將其構建為庫(不帶入口點的“.so”文件)。在以下示例中,我們將“ load_mo.c.o
”構建為“app”,并將“ hello_mo.c.o
”構建為庫。
我們通過以下方式修改鏈接命令(尋找“將所有內容鏈接在一起......”)
-
只保留目標目標文件,例如“
load_mo.c.o
”,并刪除其他 -
刪除選項“
-Wl,--unresolved-symbols=report-all
” -
刪除選項“
-L{path_to_output}
” -
刪除選項“
-T.../flash_with_bootloader.ld
” -
刪除選項“
-Wl,--start-group ... -Wl,--end-group
” -
添加選項“
-shared -fPIC -nostdlib -Wl,-marmelf -Wl,-z,max-page-size=0x4
” -
添加入口點選項(例如“
-Wl,-eload_hello
”或“-Wl,-e0
”表示無)
{path_to_gcc_tools}\arm-none-eabi-g++ -shared -fPIC -nostdlib -Wl,-e0 -Wl,-marmelf -Wl,-z,max-page-size=0x4 ... -o {path_to_output}\hello_mo.elf {path_to_output}\hello_mo.c.o
{path_to_gcc_tools}\arm-none-eabi-g++ -shared -fPIC -nostdlib -Wl,-eload_hello -Wl,-marmelf -Wl,-z,max-page-size=0x4 ... -o {path_to_output}\load_mo.elf {path_to_output}\load_mo.c.o
第三步是分條。
為了進一步減小文件大小,我們必須去掉 ELF 文件中不必要的部分。
{path_to_gcc_tools}\arm-none-eabi-strip -R .hash -R .comment -R .ARM.attributes {path_to_output}\hello_mo.elf -o {path_to_output}\hello.so
{path_to_gcc_tools}\arm-none-eabi-strip -R .hash -R .comment -R .ARM.attributes {path_to_output}\load_mo.elf -o {path_to_output}\load.mo
第四步是檢查大小(可選)。
{path_to_gcc_tools}\arm-none-eabi-size {path_to_output}\hello.so
{path_to_gcc_tools}\arm-none-eabi-size {path_to_output}\load.mo
恭喜!您剛剛構建了一個 Arduino 應用程序。讓我們檢查一下輸出。
{path_to_gcc_tools}\arm-none-eabi-readelf -h {path_to_output}\hello.so
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 52 (bytes into file)
Start of section headers: 896 (bytes into file)
Flags: 0x5000000, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 8
{path_to_gcc_tools}\arm-none-eabi-readelf -h {path_to_output}\load.mo
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x285
Start of program headers: 52 (bytes into file)
Start of section headers: 1060 (bytes into file)
Flags: 0x5000002, has entry point, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 8
它表明“ .so
”和“ .mo
”文件都是“ DYN
”(動態)類型。不同之處在于“ .so
”文件沒有入口點,而“ .mo
”文件有。
我們還沒有完成。
最后一步是公開應用程序所需的功能。
在文件“ ”中,如果啟用mo_sym.h
,所有內核 API 都已經公開。CONFIG_USING_MODULE
如有必要,您可以添加自己的。
發出 MSH 命令“ lsym
”將列出所有暴露的符號:
運行 Arduino 應用程序
hello.so
讓我們將“ ”和“ ”復制load.mo
到具有以下文件結構的SD卡。
SD_ROOT/
├── lib/
│ └── hello.so
└── mo/
└── load.mo
規則是,如果我們將相對路徑傳遞給dlopen()
或 MSH,它將分別在和中查找“ .so
”和“ ” 。.mo
/lib/
/mo/
現在我們將卡插入 Arduino 板,在本例中為 MRKZERO ,上傳“HelloMo”草圖(草圖什么都不做),然后發出命令“ load
”。
為了顯示有關應用程序執行過程的更多詳細信息,我們可以在“ dlmodule.c
”中啟用調試消息:
#define LOG_LVL LOG_LVL_DBG
結果揭示了以下過程:
-
MSH 線程 ("tshell") 將 "
load.mo
" 加載到 RAM 并創建一個新線程 ("load") 以執行入口點函數 "load_hello()
" -
“
load_hello()
”然后加載“hello.so
”,調用它的“module_init()
”函數,調用它的“say_hello()
”函數(不是入口點) -
在 "
say_hello()
" 返回后, "load_hello()
" 關閉 "hello.so
" (調用它的 "module_cleanup()
" 函數然后銷毀它的 RAM 副本) -
“加載”線程標記以銷毀“”的RAM副本,
load.mo
然后退出 -
" " 的 RAM 副本
load.mo
最終被空閑線程 ("tidle0") 銷毀
" module_init()
" 和 " module_cleanup()
" 是特殊函數。如果定義,前者.mo
在將應用程序加載到 RAM 后由 MSH 線程(在“”文件的情況下)調用,后者.mo
在銷毀 RAM 副本之前由空閑線程(在“”文件的情況下)調用。
讓我們將“hello_mo.c”重建為一個應用程序(入口點是“ say_hello()
”,例如-Wl,-esay_hello
)并執行。
結果清楚地表明“ module_init()
”被MSH線程(“tshell”)module_cleanup()
調用,“ ”被空閑線程(“tidle0”)調用。順便說一句,傳遞給這兩個函數的參數是指向模塊描述符的指針。
優點缺點
優點
(我們感興趣的是)Arduino 應用程序可以構建一次并在許多板上運行。根據 Wiki ,“可用于 Cortex-M0 / Cortex-M0+ / Cortex-M1 的二進制指令無需修改即可在 Cortex-M3 / Cortex-M4 / Cortex-M7 上執行。可用于 Cortex-M3 的二進制指令無需修改即可執行在 Cortex-M4 / Cortex-M7 / Cortex-M33 / Cortex-M35P 上進行修改。”
因此,為 MKR Zero 板(SAMD 架構)構建的應用程序應該在 Arduino Due(SAM 架構)上運行而不會出現問題。
此功能可以實現遠程添加或更新功能而無需重新啟動(與 OTA 固件更新相比)等。
缺點
與 MSH 命令相比,Arduino 應用程序需要更多 RAM。另一個主要缺點是在固件方面,應用程序所需的所有外部功能都必須在那里待機(盡管固件可能不會使用它們)并暴露(在“ mo_sym.h
”中)。
動態模塊狀態
Arduino RT-Thread 庫 v0.6.0 中的功能仍處于 beta 階段。
在原始代碼(RT-Thread 項目)中,除了DYN
ELF 文件的類型,動態鏈接器還支持REL
類型。但是,經過一些測試,我發現至少對于 ARM Cortex-M 架構,只有“ .o
”(對象)文件類型為REL
. 所以目前REL
Arduino RT-Thread 庫不支持 ELF 文件類型。
此外,僅測試了兩種重新分配類型:
-
R_ARM_JUMP_SLOT
-
R_ARM_RELATIVE
我需要一些代碼來測試其他類型。因此,如果您遇到其他類型的錯誤,請幫助提出問題。
最后,并非所有“libgcc”函數都默認公開。例如,開關助手功能沒有公開。您可以將它們添加到“ mo_sym.h
”或在您的應用程序中將“ switch...case...
”替換為“ ” 。if...else...
RTT-QRCode 應用程序
有一個更復雜的示例RTT-QRCode ,可以構建為 MSH 命令或 Arduino 應用程序。請查看代碼并玩得開心!
下一步
- Arduino上的多任務處理
- 帶有 RT-Thread 的更好的 SD 庫
- RT-Thread Primer(即將推出)
- 帶有RT-Thread的更好的SD庫
- RT-Thread文檔_workqueue
- RT-Thread文檔_ringbuffer
- RT-Thread文檔_completion
- RT-Thread文檔_RT-Thread SMP 介紹與移植
- RT-Thread文檔_內核基礎
- RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南
- RT-Thread文檔_RT-Thread 簡介
- RT-Thread Smart 上手指南
- 【RT-Thread開源作品秀】基于RT-Thread的星務平臺研究
- RT-Thread AI kit開源:輕松實現一鍵部署AI模型至 RT-Thread
- 嵌入式RT-Thread應用與開發 71次下載
- 嵌入式實時操作系統RT-Thread的特點與體系結構及移植方法詳細說明 27次下載
- RT-Thread用戶手冊 0次下載
- RT-Thread編程指南 0次下載
- 基于 RT-Thread專業版的EtherCAT主站方案 429次閱讀
- RT-Thread qemu mps2-an385 bsp移植制作 :系統運行篇 675次閱讀
- i.MX RT1170:VGLite移植RT-Thread Nano過程講解(下) 738次閱讀
- i.MX RT1170:VGLite移植RT-Thread Nano過程講解(上) 2094次閱讀
- rt-studio潘多拉開發板最新rt-thread不能運行解決辦法 1064次閱讀
- RT-Thread自動初始化機制 2362次閱讀
- RT-Thread 4.1.0的CMake構建教程 3129次閱讀
- 如何使用xmake工具來編譯rt-thread工程 2090次閱讀
- RT-Thread v4.1.0中FAL介紹 3523次閱讀
- 如何創建RT-Thread Nano工程 3572次閱讀
- 如何在rt-smart簡化應用程序開發 1832次閱讀
- 簡要分析Thread的通用GPIO設備驅動 1413次閱讀
- RT-Thread NetUtils的使用方法 7848次閱讀
- 如何創建標準的RT-Thread項目工程?詳細過程分析概述 9102次閱讀
- RT-Thread軟件包定義和使用 1w次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數據手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數字電路基礎pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多