閱讀文章...
“ RISC-V BL602上的Zig:快速窺視Apache nuttx rtos”
“使用Zig和Lorawan建立IoT應用程序”
要在bl602上構建nuttx的hello Zig應用程序...
# # Enable Zig App in NuttX menuconfig
make menuconfig
# # TODO: Select "Application Configuration > Examples > Hello Zig Example"
# # Save the configuration and exit menuconfig.
# # Build Nuttx
make
# # NuttX Build fails with Undefined Reference to `hello_zig_main`
# # That's OK, here's the fix...
# # Download our modified Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
-target riscv32-freestanding-none
-mcpu sifive_e76
hello_zig_main.zig
# # Dump the ABI for the compiled app
riscv64-unknown-elf-readelf -h -A hello_zig_main.o
# # Shows "Flags: 0x1, RVC, soft-float ABI"
# # Which is Software Floating-Point.
# # This won't link with NuttX because NuttX is compiled with Hardware Floating-Point
# # We change Software Floating-Point to Hardware Floating-Point...
# # Edit hello_zig_main.o in a Hex Editor, change byte 0x24 from 0x01 to 0x03
# # (See https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header)
# # Dump the ABI for the compiled app
riscv64-unknown-elf-readelf -h -A hello_zig_main.o
# # Shows "Flags: 0x3, RVC, single-float ABI"
# # Which is Hardware Floating-Point and will link with NuttX
# # Copy the compiled app to NuttX and overwrite `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp hello_zig_main.o $HOME /nuttx/apps/examples/hello/ * hello.o
# # Build NuttX to link the Zig Object from `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make
# # NuttX build should now succeed啟動nuttx並在nuttx shell ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
對於Lorawan Zig應用程序,請參閱此...
要使用Zig編譯器在C中編譯C中的Lora SX1262庫,請參閱此...
這是我們使Zig和Lorawan在BL602 Nuttx上運行的方式...
apache nuttx rtos與一個簡單的Zig應用程序捆綁在一起...讓我們在bl602上運行此操作:hello_zig_main.zig
// Import the Zig Standard Library
const std = @import ( "std" );
// Import printf() from C
pub extern fn printf (
_format : [ * : 0 ] const u8
) c_int ;
// Main Function
pub export fn hello_zig_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
_ = printf ( "Hello, Zig! n " );
return 0 ;
}我們修復了最後兩行,以使曲折編譯器快樂...
// Previously: printf("Hello, Zig!n");
// Zig needs us to use the returned value from printf()...
_ = printf ( "Hello, Zig! n " );
// Previously this was missing.
// Zig needs us to return a value...
return 0 ;原始版本在這裡:hello_zig_main.zig
啟用nuttx中的Zig應用...
make menuconfig選擇“應用程序配置>示例> Hello Zig示例”
保存配置並退出menuconfig。
當我們構建nuttx ...
make我們看到這個錯誤...
LD: nuttx
riscv64-unknown-elf-ld: nuttx/staging/libapps.a(builtin_list.c.home.user.nuttx.apps.builtin.o):(.rodata.g_builtins+0xbc):
undefined reference to `hello_zig_main'
(來源)
看起來與這個問題相似...
apache/nuttx#6219
這似乎是由NutTX構建引起的,並不稱為曲折編譯器。
但是不用擔心!讓我們自己編譯Zig應用程序,然後鏈接到NutTx。
這是我們為RISC-V BL602編譯我們的ZIG應用程序的方式,並將其與Nuttx鏈接...
# # Download our modified Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
-target riscv32-freestanding-none
-mcpu sifive_e76
hello_zig_main.zig
# # Copy the compiled app to NuttX and overwrite `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp hello_zig_main.o $HOME /nuttx/apps/examples/hello/ * hello.o
# # Build NuttX to link the Zig Object from `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make為什麼目標是riscv32-freestanding-none ?
Zig目標的形式<arch><sub>-<os>-<abi> ...
riscv32 :因為BL602是32位RISC-V處理器
freestanding :因為嵌入式目標不需要操作系統
none :因為嵌入式目標未指定ABI
為什麼目標cpu sifive_e76 ?
BL602被指定為RV32IMACF ...
| 指定 | 意義 |
|---|---|
RV32I | 帶基本整數說明的32位RISC-V |
M | 整數乘法 +除法 |
A | 原子說明 |
C | 壓縮說明 |
F | 單精度浮點 |
(來源)
在所有ZIG目標中,只有sifive_e76具有相同的名稱...
$ zig targets
...
" sifive_e76 " : [ " a " , " c " , " f " , " m " ],(來源)
因此,我們將sifive_e76用作我們的CPU目標。
另外,我們可以將baseline_rv32-d用作我們的CPU目標...
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
hello_zig_main.zig因為...
baseline_rv32表示RV32IMACFD
(d用於雙精度浮點)
-d表示卸下雙精度浮點(d)
(但是保持單精度浮點)
(有關RISC-V功能標誌的更多信息。感謝Matheus!)
將編譯的Zig應用程序與NutTx鏈接時,我們會看到此錯誤...
$ make
...
riscv64-unknown-elf-ld: nuttx/staging/libapps.a(hello_main.c.home.user.nuttx.apps.examples.hello.o):
can ' t link soft-float modules with single-float modules這是因為NutTX是為(單精量)硬件浮點ABI(應用程序二進制界面)編譯的...
# # Do this BEFORE overwriting hello.o by hello_zig_main.o.
# # "*hello.o" expands to something like "hello_main.c.home.user.nuttx.apps.examples.hello.o"
$ riscv64-unknown-elf-readelf -h -A $HOME /nuttx/apps/examples/hello/ * hello.o
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: REL (Relocatable file)
Machine: RISC-V
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 4528 (bytes into file)
Flags: 0x3, RVC, single-float ABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 26
Section header string table index: 25
Attribute Section: riscv
File Attributes
Tag_RISCV_stack_align: 16-bytes
Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_c2p0"(來源)
(NutTX用GCC標誌-march=rv32imafc -mabi=ilp32f )
而Zig編譯器則使用軟件浮點ABI生成對象文件...
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
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: REL (Relocatable file)
Machine: RISC-V
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 11968 (bytes into file)
Flags: 0x1, RVC, soft-float ABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 24
Section header string table index: 22
Attribute Section: riscv
File Attributes
Tag_RISCV_stack_align: 16-bytes
Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_c2p0"(來源)
海灣合作委員會將不允許我們將對象文件與軟件浮點數和硬件浮點abis聯繫起來!
(當sifive_e76支持硬件浮點點時
Zig編譯器使用軟件浮點ABI(應用程序二進制接口)生成一個對象文件...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x1, RVC, soft-float ABI這不會與nuttx鏈接,因為NutTX與硬件浮點ABI編輯。
我們通過修改精靈標頭來解決此問題...
編輯hello_zig_main.o在十六進制編輯器中
(例如VSCODE HEX編輯器)
更改字節0x24 (標誌)從0x01 (軟float)更改為0x03 (硬float)
(請參閱此)
我們驗證對象文件已更改為硬件浮點ABI ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x3, RVC, single-float ABI現在,這是硬件浮點ABI,並將與Nuttx鏈接。
現在,我們將修改的對象文件與Nuttx鏈接...
# # Copy the compiled app to NuttX and overwrite `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp hello_zig_main.o $HOME /nuttx/apps/examples/hello/ * hello.o
# # Build NuttX to link the Zig Object from `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeNuttx構建現在應該成功。
像這樣更改ABI真的可以嗎?
從技術上講, ABI是由Zig編譯器正確生成的...
# # Dump the ABI for the compiled Zig app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x1, RVC, soft-float ABI
Tag_RISCV_arch: " rv32i2p0_m2p0_a2p0_f2p0_c2p0 "最後一行轉換為RV32IMACF ,這意味著RISC-V指令集確實針對硬件浮點。
我們只是編輯精靈標頭,因為它似乎沒有反映對象文件的正確ABI。
有適當的修復嗎?
將來,Zig編譯器可能會允許我們將浮點ABI指定為目標...
# # Compile the Zig App for BL602
# # ("ilp32f" means Hardware Floating-Point ABI)
zig build-obj
-target riscv32-freestanding-ilp32f
...(請參閱此)
敬請關注!
我們可以通過命令行修補小精靈標頭嗎?
是的,我們可以通過命令行修補精靈標頭...
xxd -c 1 hello_zig_main.o
| sed ' s/00000024: 01/00000024: 03/ '
| xxd -r -c 1 - hello_zig_main2.oNuttx構建成功。 Zig在Nuttx BL602上運行正常!
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
請記住,我們用ZIG編譯的對象文件覆蓋hello.o 。
除非我們提供hello_main函數,否則NutTX構建將失敗。
riscv64-unknown-elf-ld: nuttx/staging/libapps.a(builtin_list.c.home.user.nuttx.apps.builtin.o):(.rodata.g_builtins+0xcc):
undefined reference to `hello_main'
這就是為什麼我們在Zig應用中定義hello_main ...
pub export fn hello_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
_ = printf ( "Hello, Zig! n " );
return 0 ;
}(來源)
這意味著hello應用程序也會調用我們的Zig代碼...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello
Hello, Zig!
Pine64 Pinecone BL602板(右)連接到SPI的SEMTECH SX1262 LORA收發器(左)
Zig編譯器會用作替換GCC來編譯NutTX庫的倒入嗎?
讓我們在apache nuttx rtos的Lora SX1262庫上進行測試!
這是Nuttx用GCC編譯Lora SX1262庫的方式...
# # LoRa SX1262 Source Directory
cd $HOME /nuttx/nuttx/libs/libsx1262
# # Compile radio.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/radio.c
-o src/radio.o
# # Compile sx126x.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x.c
-o src/sx126x.o
# # Compile sx126x-nuttx.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x-nuttx.c
-o src/sx126x-nuttx.o我們做出這些更改...
將riscv64-unknown-elf-gcc更改為zig cc
添加目標-target riscv32-freestanding-none -mcpu=baseline_rv32-d
刪除-march=rv32imafc
我們運行這個...
# # LoRa SX1262 Source Directory
cd $HOME /nuttx/nuttx/libs/libsx1262
# # Compile radio.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/radio.c
-o src/radio.o
# # Compile sx126x.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x.c
-o src/sx126x.o
# # Compile sx126x-nuttx.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x-nuttx.c
-o src/sx126x-nuttx.o
# # Link Zig Object Files with NuttX after compiling with `zig cc`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeZig編譯器顯示這些錯誤...
In file included from src/sx126x-nuttx.c:3:
In file included from nuttx/include/debug.h:39:
In file included from nuttx/include/sys/uio.h:45:
nuttx/include/sys/types.h:119:9: error: unknown type name '_size_t'
typedef _size_t size_t;
^
nuttx/include/sys/types.h:120:9: error: unknown type name '_ssize_t'
typedef _ssize_t ssize_t;
^
nuttx/include/sys/types.h:121:9: error: unknown type name '_size_t'
typedef _size_t rsize_t;
^
nuttx/include/sys/types.h:174:9: error: unknown type name '_wchar_t'
typedef _wchar_t wchar_t;
^
In file included from src/sx126x-nuttx.c:4:
In file included from nuttx/include/stdio.h:34:
nuttx/include/nuttx/fs/fs.h:238:20: error: use of undeclared identifier 'NAME_MAX'
char parent[NAME_MAX + 1];
^
我們通過包括正確的標頭文件來解決此問題...
#if defined( __NuttX__ ) && defined( __clang__ ) // Workaround for NuttX with zig cc
#include <arch/types.h>
#include "../../nuttx/include/limits.h"
#endif // defined(__NuttX__) && defined(__clang__)進入這些源文件...
(請參閱更改)
我們插入此代碼以告訴我們(在運行時)是否與Zig編譯器或GCC進行了編譯...
void SX126xIoInit ( void ) {
#ifdef __clang__
#warning Compiled with zig cc
puts ( "SX126xIoInit: Compiled with zig cc" );
#else
#warning Compiled with gcc
puts ( "SX126xIoInit: Compiled with gcc" );
#endif // __clang__(來源)
用zig cc編譯,Lora SX1262庫在Nuttx上運行正常!
nsh> lorawan_test
SX126xIoInit: Compiled with zig cc
...
###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 000E268C
DATA RATE : DR_2
...
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923400000
TX POWER : 0
CHANNEL MASK: 0003
(請參閱完整日誌)
讓我們用Zig編譯器編譯巨大的Lorawan庫。
nuttx像這樣編譯了洛萬庫...
# # LoRaWAN Source Directory
cd $HOME /nuttx/nuttx/libs/liblorawan
# # Compile mac/LoRaMac.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/mac/LoRaMac.c
-o src/mac/LoRaMac.o我們切換到Zig編譯器...
# # LoRaWAN Source Directory
cd $HOME /nuttx/nuttx/libs/liblorawan
# # Compile mac/LoRaMac.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/mac/LoRaMac.c
-o src/mac/LoRaMac.o
# # Link Zig Object Files with NuttX after compiling with `zig cc`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make我們將正確的標頭文件包括到loramac.c ...
#if defined( __NuttX__ ) && defined( __clang__ ) // Workaround for NuttX with zig cc
#include <arch/types.h>
#include "../../nuttx/include/limits.h"
#endif // defined(__NuttX__) && defined(__clang__)(請參閱更改)
loramac.c用鋸齒形編譯器編譯確定。
todo:用build.zig在洛萬庫中編譯其他文件
https://ziglang.org/documentation/master/#zig-build-system
托多:測試洛萬圖書館
現在,我們使用Zig編譯器編譯Lorawan應用程序Lorawan_test_main.c。
nuttx編譯lorawan應用程序lorawan_test_main.c這樣...
# # App Source Directory
cd $HOME /nuttx/apps/examples/lorawan_test/lorawan_test_main.c
# # Compile lorawan_test_main.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe
-I " $HOME /nuttx/apps/graphics/lvgl "
-I " $HOME /nuttx/apps/graphics/lvgl/lvgl "
-I " $HOME /nuttx/apps/include "
-Dmain=lorawan_test_main lorawan_test_main.c
-o lorawan_test_main.c.home.user.nuttx.apps.examples.lorawan_test.o我們切換到Zig編譯器...
# # App Source Directory
cd $HOME /nuttx/apps/examples/lorawan_test
# # Compile lorawan_test_main.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe
-I " $HOME /nuttx/apps/graphics/lvgl "
-I " $HOME /nuttx/apps/graphics/lvgl/lvgl "
-I " $HOME /nuttx/apps/include "
-Dmain=lorawan_test_main lorawan_test_main.c
-o * lorawan_test.o
# # Link Zig Object Files with NuttX after compiling with `zig cc`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make我們將正確的標頭文件包括到lorawan_test_main.c ...
#if defined( __NuttX__ ) && defined( __clang__ ) // Workaround for NuttX with zig cc
#include <arch/types.h>
#include "../../nuttx/include/limits.h"
#endif // defined(__NuttX__) && defined(__clang__)(請參閱更改)
Lorawan應用程序與zig cc編譯後,在Nuttx上運行正常!
nsh> lorawan_test
lorawan_test_main: Compiled with zig cc
...
###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 00DC5ED5
DATA RATE : DR_2
...
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923400000
TX POWER : 0
CHANNEL MASK: 0003
(請參閱完整日誌)
ZIG編譯器可以將C代碼自動變壓到ZIG。 (請參閱此)
這是我們自動翻譯我們的lorawan應用程序lorawan_test_main.c從C到Zig ...
將zig cc更改為zig translate-c
通過-cflags包圍C標誌--
像這樣...
# # App Source Directory
cd $HOME /nuttx/apps/examples/lorawan_test
# # Auto-translate lorawan_test_main.c from C to Zig
zig translate-c
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-cflags
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
--
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-I " $HOME /nuttx/apps/graphics/lvgl "
-I " $HOME /nuttx/apps/graphics/lvgl/lvgl "
-I " $HOME /nuttx/apps/include "
-Dmain=lorawan_test_main
lorawan_test_main.c
> lorawan_test_main.zig這是原始C代碼:lorawan_test_main.c
以及從C到Zig的自動翻譯:翻譯/lorawan_test_main.zig
這是原始C代碼的片段...
int main ( int argc , FAR char * argv []) {
#ifdef __clang__
puts ( "lorawan_test_main: Compiled with zig cc" );
#else
puts ( "lorawan_test_main: Compiled with gcc" );
#endif // __clang__
// If we are using Entropy Pool and the BL602 ADC is available,
// add the Internal Temperature Sensor data to the Entropy Pool
init_entropy_pool ();
// Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = APP_TX_DUTYCYCLE + randr ( - APP_TX_DUTYCYCLE_RND , APP_TX_DUTYCYCLE_RND );
const Version_t appVersion = { . Value = FIRMWARE_VERSION };
const Version_t gitHubVersion = { . Value = GITHUB_VERSION };
DisplayAppInfo ( "lorawan_test" ,
& appVersion ,
& gitHubVersion );
// Init LoRaWAN
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
{
printf ( "LoRaMac wasn't properly initializedn" );
// Fatal error, endless loop.
while ( 1 ) {}
}
// Set system maximum tolerated rx error in milliseconds
LmHandlerSetSystemMaxRxError ( 20 );
// The LoRa-Alliance Compliance protocol package should always be initialized and activated.
LmHandlerPackageRegister ( PACKAGE_ID_COMPLIANCE , & LmhpComplianceParams );
LmHandlerPackageRegister ( PACKAGE_ID_CLOCK_SYNC , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_REMOTE_MCAST_SETUP , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_FRAGMENTATION , & FragmentationParams );
IsClockSynched = false;
IsFileTransferDone = false;
// Join the LoRaWAN Network
LmHandlerJoin ( );
// Set the Transmit Timer
StartTxProcess ( LORAMAC_HANDLER_TX_ON_TIMER );
// Handle LoRaWAN Events
handle_event_queue ( NULL ); // Never returns
return 0 ;
}(來源)
和自動翻譯的曲折代碼...
pub export fn lorawan_test_main ( arg_argc : c_int , arg_argv : [ * c ][ * c ] u8 ) c_int {
var argc = arg_argc ;
_ = argc ;
var argv = arg_argv ;
_ = argv ;
_ = puts ( "lorawan_test_main: Compiled with zig cc" );
init_entropy_pool ();
TxPeriodicity = @bitCast ( u32 , @as ( c_int , 40000 ) + randr ( - @as ( c_int , 5000 ), @as ( c_int , 5000 )));
const appVersion : Version_t = Version_t {
. Value = @bitCast ( u32 , @as ( c_int , 16908288 )),
};
const gitHubVersion : Version_t = Version_t {
. Value = @bitCast ( u32 , @as ( c_int , 83886080 )),
};
DisplayAppInfo ( "lorawan_test" , & appVersion , & gitHubVersion );
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) {
_ = printf ( "LoRaMac wasn't properly initialized n " );
while ( true ) {}
}
_ = LmHandlerSetSystemMaxRxError ( @bitCast ( u32 , @as ( c_int , 20 )));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 0 ))), @ptrCast ( ? * anyopaque , & LmhpComplianceParams ));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 1 ))), @intToPtr ( ? * anyopaque , @as ( c_int , 0 )));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 2 ))), @intToPtr ( ? * anyopaque , @as ( c_int , 0 )));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 3 ))), @ptrCast ( ? * anyopaque , & FragmentationParams ));
IsClockSynched = @as ( c_int , 0 ) != 0 ;
IsFileTransferDone = @as ( c_int , 0 ) != 0 ;
LmHandlerJoin ();
StartTxProcess ( @bitCast ( c_uint , LORAMAC_HANDLER_TX_ON_TIMER ));
handle_event_queue ( @intToPtr ( ? * anyopaque , @as ( c_int , 0 )));
return 0 ;
}(來源)
當我們手動將Lorawan應用程序lorawan_test_main.c從C到Zig中的lorawan應用程序在下一部分...
pine64 pinedio stack bl604(左)說話的洛拉萬與rakwireless wisgate(右)
最後,我們將lorawan應用程序lorawan_test_main.c從C轉換為ZIG,以表明我們可以在Zig中構建複雜的物聯網應用程序。
Lorawan應用程序在Pinedio Stack BL604(RISC-V)上運行。該應用程序連接到Lorawan網關(例如Chirpstack或Things Network),並定期發送數據包。
這是原始C代碼:lorawan_test_main.c
(700行C代碼)
和我們轉換後的Lorawan Zig應用程序:Lorawan_test.zig
(673條曲折代碼)
/// Import the LoRaWAN Library from C
const c = @cImport ({
// NuttX Defines
@cDefine ( "__NuttX__" , "" );
@cDefine ( "NDEBUG" , "" );
@cDefine ( "ARCH_RISCV" , "" );
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" );
@cDefine ( "__int_c_join(a, b)" , "a" ); // Bypass zig/lib/include/stdint.h
// NuttX Header Files
@cInclude ( "arch/types.h" );
@cInclude ( "../../nuttx/include/limits.h" );
@cInclude ( "stdio.h" );
// LoRaWAN Header Files
@cInclude ( "firmwareVersion.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/githubVersion.h" );
@cInclude ( "../libs/liblorawan/src/boards/utilities.h" );
@cInclude ( "../libs/liblorawan/src/mac/region/RegionCommon.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/Commissioning.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/LmHandler.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpCompliance.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpClockSync.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpFragmentation.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandlerMsgDisplay.h" );
});
// Main Function that will be called by NuttX
pub export fn lorawan_test_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
// Call the LoRaWAN Library to set system maximum tolerated rx error in milliseconds
_ = c . LmHandlerSetSystemMaxRxError ( 20 );
// TODO: Call the LoRaWAN Library to Join LoRaWAN Network
// and send a Data Packet編譯Lorawan Zig應用程序Lorawan_test.zig ...
# # Download our LoRaWAN Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
--verbose-cimport
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-isystem " $HOME /nuttx/nuttx/include "
-I " $HOME /nuttx/apps/examples/lorawan_test "
lorawan_test.zig
# # Patch the ELF Header of `lorawan_test.o` from Soft-Float ABI to Hard-Float ABI
xxd -c 1 lorawan_test.o
| sed ' s/00000024: 01/00000024: 03/ '
| xxd -r -c 1 - lorawan_test2.o
cp lorawan_test2.o lorawan_test.o
# # Copy the compiled app to NuttX and overwrite `lorawan_test.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp lorawan_test.o $HOME /nuttx/apps/examples/lorawan_test/ * lorawan_test.o
# # Build NuttX to link the Zig Object from `lorawan_test.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make我們的lorawan Zig應用Lorawan_test.zig在進行以下修復後與Zig編譯器一起編譯了確定...
Lorawan Zig App的某些部分Lorawan_test.zig可能會從C轉換為Zig,例如此C代碼...
// Original C code...
#define APP_TX_DUTYCYCLE 40000
#define APP_TX_DUTYCYCLE_RND 5000
uint32_t TxPeriodicity =
APP_TX_DUTYCYCLE +
randr (
- APP_TX_DUTYCYCLE_RND ,
APP_TX_DUTYCYCLE_RND
);(來源)
具有衝突的簽名( randr )和未簽名( APP_TX_DUTYCYCLE )類型。
我們通過參考自動翻譯曲折代碼獲得幫助:translated/lorawan_test_main.zig
// Converted from C to Zig...
const APP_TX_DUTYCYCLE : c_int = 40000 ;
const APP_TX_DUTYCYCLE_RND : c_int = 5000 ;
// Cast to u32 because randr() can be negative
var TxPeriodicity : u32 = @bitCast ( u32 ,
APP_TX_DUTYCYCLE +
c . randr (
- APP_TX_DUTYCYCLE_RND ,
APP_TX_DUTYCYCLE_RND
)
);通過將簽名結果施放到未簽名中來解決衝突類型。
當我們在Lorawan Zig App lorawan_test.zig中引用LmHandlerCallbacks時...
_ = & LmHandlerCallbacks ;Zig編譯器將顯示此不透明的類型錯誤...
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1353:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions
Fields: struct_sInfoFields,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1563:5: note: while checking this field
PingSlot: PingSlotInfo_t,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1579:5: note: while checking this field
PingSlotInfo: MlmeReqPingSlotInfo_t,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1585:5: note: while checking this field
Req: union_uMlmeParam,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:2277:5: note: while checking this field
OnMacMlmeRequest: ?fn (LoRaMacStatus_t, [*c]MlmeReq_t, TimerTime_t) callconv(.C) void,
^
不透明類型錯誤在這裡說明...
“用Zig擴展C/C ++項目”
“翻譯失敗”
讓我們通過不透明的類型錯誤跟踪...
export fn OnMacMlmeRequest (
status : c.LoRaMacStatus_t ,
mlmeReq : [ * c ] c.MlmeReq_t ,
nextTxIn : c . TimerTime_t
) void {
c . DisplayMacMlmeRequestUpdate ( status , mlmeReq , nextTxIn );
}我們的函數OnMacMlmeRequest具有MlmeReq_t類型的參數,由Zig Compiler自動輸入為...
pub const MlmeReq_t = struct_sMlmeReq ;
pub const struct_sMlmeReq = extern struct {
Type : Mlme_t ,
Req : union_uMlmeParam ,
ReqReturn : RequestReturnParam_t ,
};其中包含另一個自動象徵類型的union_uMlmeParam ...
pub const union_uMlmeParam = extern union {
Join : MlmeReqJoin_t ,
TxCw : MlmeReqTxCw_t ,
PingSlotInfo : MlmeReqPingSlotInfo_t ,
DeriveMcKEKey : MlmeReqDeriveMcKEKey_t ,
DeriveMcSessionKeyPair : MlmeReqDeriveMcSessionKeyPair_t ,
};其中包含MlmeReqPingSlotInfo_t ...
pub const MlmeReqPingSlotInfo_t = struct_sMlmeReqPingSlotInfo ;
pub const struct_sMlmeReqPingSlotInfo = extern struct {
PingSlot : PingSlotInfo_t ,
};其中包含一個PingSlotInfo_t ...
pub const PingSlotInfo_t = union_uPingSlotInfo ;
pub const union_uPingSlotInfo = extern union {
Value : u8 ,
Fields : struct_sInfoFields ,
};其中包含一個struct_sInfoFields ...
pub const struct_sInfoFields = opaque {};但是ZIG編譯器不知道struct_sInfoFields的字段!
如果我們參考原始C代碼...
typedef union uPingSlotInfo
{
/*!
* Parameter for byte access
*/
uint8_t Value ;
/*!
* Structure containing the parameters for the PingSlotInfoReq
*/
struct sInfoFields
{
/*!
* Periodicity = 0: ping slot every second
* Periodicity = 7: ping slot every 128 seconds
*/
uint8_t Periodicity : 3 ;
/*!
* RFU
*/
uint8_t RFU : 5 ;
} Fields ;
} PingSlotInfo_t ;(來源)
我們看到sInfoFields包含位字段,即曲折編譯器無法翻譯。
早些時候,我們發現這無法在我們的lorawan Zig應用程序lorawan_test.zig中編譯。
_ = & LmHandlerCallbacks ;這是因為LmHandlerCallbacks引用了自動成像類型MlmeReq_t ,其中包含位字段,無法由Zig編譯器翻譯。
讓我們將MlmeReq_t轉換為不透明的類型,因為無論如何我們都不會訪問字段...
/// We use an Opaque Type to represent MLME Request, because it contains Bit Fields that can't be converted by Zig
const MlmeReq_t = opaque {};(來源)
我們將LmHandlerCallbacks轉換為使用我們的不透明類型MlmeReq_t ...
/// Handler Callbacks. Adapted from
/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2818-L2833
pub const LmHandlerCallbacks_t = extern struct {
GetBatteryLevel : ? fn () callconv ( .C ) u8 ,
GetTemperature : ? fn () callconv ( .C ) f32 ,
GetRandomSeed : ? fn () callconv ( .C ) u32 ,
OnMacProcess : ? fn () callconv ( .C ) void ,
OnNvmDataChange : ? fn ( c.LmHandlerNvmContextStates_t , u16 ) callconv ( .C ) void ,
OnNetworkParametersChange : ? fn ([ * c ] c.CommissioningParams_t ) callconv ( .C ) void ,
OnMacMcpsRequest : ? fn ( c.LoRaMacStatus_t , [ * c ] c.McpsReq_t , c.TimerTime_t ) callconv ( .C ) void ,
/// Changed `[*c]c.MlmeReq_t` to `*MlmeReq_t`
OnMacMlmeRequest : ? fn ( c.LoRaMacStatus_t , * MlmeReq_t , c.TimerTime_t ) callconv ( .C ) void ,
OnJoinRequest : ? fn ([ * c ] c.LmHandlerJoinParams_t ) callconv ( .C ) void ,
OnTxData : ? fn ([ * c ] c.LmHandlerTxParams_t ) callconv ( .C ) void ,
OnRxData : ? fn ([ * c ] c.LmHandlerAppData_t , [ * c ] c.LmHandlerRxParams_t ) callconv ( .C ) void ,
OnClassChange : ? fn ( c.DeviceClass_t ) callconv ( .C ) void ,
OnBeaconStatusChange : ? fn ([ * c ] c.LoRaMacHandlerBeaconParams_t ) callconv ( .C ) void ,
OnSysTimeUpdate : ? fn ( bool , i32 ) callconv ( .C ) void ,
};(來源)
MlmeReq_t從...
[ * c ] c . MlmeReq_t對於我們不透明的類型...
* MlmeReq_t我們還更改LmHandlerCallbacks_t ...
[ * c ] c . LmHandlerCallbacks_t轉換為轉換的LmHandlerCallbacks_t ...
* LmHandlerCallbacks_t這意味著我們需要自己進口受影響的洛拉萬的功能...
/// Changed `[*c]c.MlmeReq_t` to `*MlmeReq_t`. Adapted from
/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2905
extern fn DisplayMacMlmeRequestUpdate (
status : c.LoRaMacStatus_t ,
mlmeReq : * MlmeReq_t ,
nextTxIn : c . TimerTime_t
) void ;
/// Changed `[*c]c.LmHandlerCallbacks_t` to `*LmHandlerCallbacks_t`. Adapted from
/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2835
extern fn LmHandlerInit (
callbacks : * LmHandlerCallbacks_t ,
handlerParams : [ * c ] c . LmHandlerParams_t
) c.LmHandlerErrorStatus_t ;(來源)
修復了不透明類型後,Zig編譯器成功地編譯了我們的Lorawan Test應用程序Lorawan_test.zig yay!
在編譯Lorawan測試應用Lorawan_test.zig時,我們看到了此宏錯誤...
zig-cache/o/23409ceec9a6e6769c416fde1695882f/cimport.zig:2904:32:
error: unable to translate macro: undefined identifier `LL`
pub const __INT64_C_SUFFIX__ = @compileError("unable to translate macro: undefined identifier `LL`");
// (no file):178:9
根據Zig Docs的說法,這意味著Zig編譯器未能翻譯一個宏...
所以我們自己定義LL ...
/// Import the LoRaWAN Library from C
const c = @cImport ({
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" ); ( LL是C常量的“長”後綴,當我們將C類型和功能導入Zig時,可能不需要)
然後Zig編譯器發出此錯誤...
zig-cache/o/83fc6cf7a78f5781f258f156f891554b/cimport.zig:2940:26:
error: unable to translate C expr: unexpected token '##'
pub const __int_c_join = @compileError("unable to translate C expr: unexpected token '##'");
// /home/user/zig-linux-x86_64-0.10.0-dev.2351+b64a1d5ab/lib/include/stdint.h:282:9
它指的是stdint.h ...
#define __int_c_join ( a , b ) a ## b __int_c_join宏失敗,因為LL後綴現在為空白,並且## Concatenation Operator失敗。
我們重新定義了__int_c_join宏,而無需##串聯操作員...
/// Import the LoRaWAN Library from C
const c = @cImport ({
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" );
@cDefine ( "__int_c_join(a, b)" , "a" ); // Bypass zig/lib/include/stdint.h現在,Zig編譯器成功編譯了我們的Lorawan測試應用Lorawan_test.zig
Zig編譯器試圖在啟動時初始化計時器結構時崩潰...
/// Timer to handle the application data transmission duty cycle
var TxTimer : c.TimerEvent_t =
std . mem . zeroes ( c . TimerEvent_t );
// Zig Compiler crashes with...
// TODO buf_write_value_bytes maybe typethread 11512 panic:
// Unable to dump stack trace: debug info stripped(來源)
因此,我們在主函數中初始化計時器結構...
/// Timer to handle the application data transmission duty cycle.
/// Init the timer in Main Function.
var TxTimer : c.TimerEvent_t = undefined ;
/// Main Function
pub export fn lorawan_test_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
// Init the Timer Struct at startup
TxTimer = std . mem . zeroes ( c . TimerEvent_t );(來源)
解決上述問題後,我們在Nuttx上測試Lorawan Zig應用程序:lorawan_test.zig
nsh> lorawan_test
Application name : Zig LoRaWAN Test
...
###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 00D803AB
DATA RATE : DR_2
...
PrepareTxFrame: Transmit to LoRaWAN (9 bytes): Hi NuttX
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923200000
TX POWER : 0
CHANNEL MASK: 0003
(請參閱完整日誌)
Lorawan Zig App Lorawan_test.zig成功加入了Lorawan Network(Rakwireless Wisgate上的Chirpstack),並將數據包發送到Lorawan Gateway yay!
自動翻譯我們的C代碼到ZIG時,ZIG編譯器會揭示有趣的見解。
該C代碼複製一個數組,字節由字節...
static int8_t FragDecoderWrite ( uint32_t addr , uint8_t * data , uint32_t size ) {
...
for ( uint32_t i = 0 ; i < size ; i ++ ) {
UnfragmentedData [ addr + i ] = data [ i ];
}
return 0 ; // Success
}(來源)
這是自動翻譯的曲折代碼...
pub fn FragDecoderWrite ( arg_addr : u32 , arg_data : [ * c ] u8 , arg_size : u32 ) callconv ( .C ) i8 {
...
var size = arg_size ;
var i : u32 = 0 ;
while ( i < size ) : ( i +%= 1 ) {
UnfragmentedData [ addr +% i ] = data [ i ];
}
return 0 ;
}(來源)
請注意,陣列索引在C ...
// Array Indexing in C...
UnfragmentedData [ addr + i ]在Zig中翻譯成這個...
// Array Indexing in Zig...
UnfragmentedData [ addr +% i ] +在c中變為+% !
曲折的+%是什麼?
那是環繞式添加的曲折操作員。
這意味著如果添加溢出整數,則結果可以回到0(及以後)。
但這不是我們想要的,因為我們不希望增加溢出。這就是為什麼在我們的最終轉換曲折代碼中,我們將+%恢復為+ ...
export fn FragDecoderWrite ( addr : u32 , data : [ * c ] u8 , size : u32 ) i8 {
...
var i : u32 = 0 ;
while ( i < size ) : ( i += 1 ) {
UnfragmentedData [ addr + i ] = data [ i ];
}
return 0 ; // Success
}(來源)
如果增加溢出會怎樣?
我們將看到一個運行時錯誤...
panic: integer overflow
(來源)
這可能是一件好事,以確保我們的價值觀明智。
如果我們的數組索引超出了界限怎麼辦?
我們將獲得此運行時錯誤...
panic: index out of bounds
(來源)
這是Zig在運行時完成的安全檢查列表...
如果我們寧願魯ck生活,這就是我們禁用安全檢查的方式...
一些調試功能似乎無法正常工作?像unreachable std.debug.assert和std.debug.panic一樣?
這是因為對於嵌入式平台,我們需要實施自己的恐慌處理程序...
“使用ZIG在裸骨操作系統上為內核恐慌提供堆棧痕跡”
默認恐慌處理程序: std.debug.default_panic
有了我們自己的恐慌處理程序,這種斷言失敗...
// Create a short alias named `assert`
const assert = std . debug . assert ;
// Assertion Failure
assert ( TxPeriodicity != 0 );將顯示此堆棧跟踪...
!ZIG PANIC!
reached unreachable code
Stack Trace:
0x23016394
0x23016ce0
根據我們的RISC-V拆卸,第一個地址23016394看起來並不有趣,因為它在assert功能內...
/home/user/zig-linux-x86_64-0.10.0-dev.2351+b64a1d5ab/lib/std/debug.zig:259
pub fn assert(ok: bool) void {
2301637c: 00b51c63 bne a0,a1,23016394 <std.debug.assert+0x2c>
23016380: a009 j 23016382 <std.debug.assert+0x1a>
23016382: 2307e537 lui a0,0x2307e
23016386: f9850513 addi a0,a0,-104 # 2307df98 <__unnamed_4>
2301638a: 4581 li a1,0
2301638c: 00000097 auipc ra,0x0
23016390: f3c080e7 jalr -196(ra) # 230162c8 <panic>
if (!ok) unreachable; // assertion failure
23016394: a009 j 23016396 <std.debug.assert+0x2e>
但是第二個地址23016ce0揭示了失敗的斷言...
/home/user/nuttx/zig-bl602-nuttx/lorawan_test.zig:95
assert(TxPeriodicity != 0);
23016ccc: 42013537 lui a0,0x42013
23016cd0: fbc52503 lw a0,-68(a0) # 42012fbc <TxPeriodicity>
23016cd4: 00a03533 snez a0,a0
23016cd8: fffff097 auipc ra,0xfffff
23016cdc: 690080e7 jalr 1680(ra) # 23016368 <std.debug.assert>
/home/user/nuttx/zig-bl602-nuttx/lorawan_test.zig:100
TxTimer = std.mem.zeroes(c.TimerEvent_t);
23016ce0: 42016537 lui a0,0x42016
這是我們的Zig恐慌處理程序的實施...
/// Called by Zig when it hits a Panic. We print the Panic Message, Stack Trace and halt. See
/// https://andrewkelley.me/post/zig-stack-traces-kernel-panic-bare-bones-os.html
/// https://github.com/ziglang/zig/blob/master/lib/std/builtin.zig#L763-L847
pub fn panic (
message : [] const u8 ,
_stack_trace : ? * std . builtin . StackTrace
) noreturn {
// Print the Panic Message
_ = _stack_trace ;
_ = puts ( " n !ZIG PANIC!" );
_ = puts ( @ptrCast ([ * c ] const u8 , message ));
// Print the Stack Trace
_ = puts ( "Stack Trace:" );
var it = std . debug . StackIterator . init ( @returnAddress (), null );
while ( it . next ()) | return_address | {
_ = printf ( "%p n " , return_address );
}
// Halt
while ( true ) {}
}(來源)
我們已經實施了在此處描述的debug記錄std.log.debug ...
這是我們調用std.log.debug打印日誌消息的方式...
// Create a short alias named `debug`
const debug = std . log . debug ;
// Message with 8 bytes
const msg : [] const u8 = "Hi NuttX" ;
// Print the message
debug ( "Transmit to LoRaWAN ({} bytes): {s}" , .{
msg . len , msg
});
// Prints: Transmit to LoRaWAN (8 bytes): Hi NuttX .{ ... }創建一個匿名結構,其中具有可變數量的參數,該參數將傳遞給std.log.debug進行打印。
以下是我們實施std.log.debug ...
/// Called by Zig for `std.log.debug`, `std.log.info`, `std.log.err`, ...
/// https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d
pub fn log (
comptime _message_level : std.log.Level ,
comptime _scope : @Type ( .EnumLiteral ),
comptime format : [] const u8 ,
args : anytype ,
) void {
_ = _message_level ;
_ = _scope ;
// Format the message
var buf : [ 100 ] u8 = undefined ; // Limit to 100 chars
var slice = std . fmt . bufPrint ( & buf , format , args )
catch { _ = puts ( "*** log error: buf too small" ); return ; };
// Terminate the formatted message with a null
var buf2 : [ buf . len + 1 : 0 ] u8 = undefined ;
std . mem . copy (
u8 ,
buf2 [0 .. slice . len ],
slice [0 .. slice . len ]
);
buf2 [ slice . len ] = 0 ;
// Print the formatted message
_ = puts ( & buf2 );
}(來源)
此實現調用puts() ,它由apache nuttx rtos支持,因為它是符合posix的。
原始的C代碼和我們Lorawan應用程序的轉換後的曲折代碼看起來非常相似。
這是我們原始C代碼的主要功能...
/// Main Function that will be called by NuttX. We call the LoRaWAN Library
/// to Join a LoRaWAN Network and send a Data Packet.
int main ( int argc , FAR char * argv []) {
// If we are using Entropy Pool and the BL602 ADC is available,
// add the Internal Temperature Sensor data to the Entropy Pool
init_entropy_pool ();
// Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = APP_TX_DUTYCYCLE + randr ( - APP_TX_DUTYCYCLE_RND , APP_TX_DUTYCYCLE_RND );
const Version_t appVersion = { . Value = FIRMWARE_VERSION };
const Version_t gitHubVersion = { . Value = GITHUB_VERSION };
DisplayAppInfo ( "lorawan_test" ,
& appVersion ,
& gitHubVersion );
// Init LoRaWAN
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
{
printf ( "LoRaMac wasn't properly initializedn" );
// Fatal error, endless loop.
while ( 1 ) {}
}
// Set system maximum tolerated rx error in milliseconds
LmHandlerSetSystemMaxRxError ( 20 );
// The LoRa-Alliance Compliance protocol package should always be initialized and activated.
LmHandlerPackageRegister ( PACKAGE_ID_COMPLIANCE , & LmhpComplianceParams );
LmHandlerPackageRegister ( PACKAGE_ID_CLOCK_SYNC , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_REMOTE_MCAST_SETUP , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_FRAGMENTATION , & FragmentationParams );
IsClockSynched = false;
IsFileTransferDone = false;
// Join the LoRaWAN Network
LmHandlerJoin ( );
// Set the Transmit Timer
StartTxProcess ( LORAMAC_HANDLER_TX_ON_TIMER );
// Handle LoRaWAN Events
handle_event_queue ( NULL ); // Never returns
return 0 ;
}(來源)
以及我們轉換後的曲折代碼的主要功能(在擦洗後)...
/// Main Function that will be called by NuttX. We call the LoRaWAN Library
/// to Join a LoRaWAN Network and send a Data Packet.
pub export fn lorawan_test_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
// Init the Timer Struct at startup
TxTimer = std . mem . zeroes ( c . TimerEvent_t );
// If we are using Entropy Pool and the BL602 ADC is available,
// add the Internal Temperature Sensor data to the Entropy Pool
// TODO: init_entropy_pool();
// Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = @bitCast ( u32 , // Cast to u32 because randr() can be negative
APP_TX_DUTYCYCLE +
c . randr (
- APP_TX_DUTYCYCLE_RND ,
APP_TX_DUTYCYCLE_RND
)
);
// Show the Firmware and GitHub Versions
const appVersion = c.Version_t {
. Value = c . FIRMWARE_VERSION ,
};
const gitHubVersion = c.Version_t {
. Value = c . GITHUB_VERSION ,
};
c . DisplayAppInfo ( "Zig LoRaWAN Test" , & appVersion , & gitHubVersion );
// Init LoRaWAN
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams )
!= c . LORAMAC_HANDLER_SUCCESS ) {
std . log . err ( "LoRaMac wasn't properly initialized" , .{});
// Fatal error, endless loop.
while ( true ) {}
}
// Set system maximum tolerated rx error in milliseconds
_ = c . LmHandlerSetSystemMaxRxError ( 20 );
// The LoRa-Alliance Compliance protocol package should always be initialized and activated.
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_COMPLIANCE , & LmhpComplianceParams );
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_CLOCK_SYNC , null );
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_REMOTE_MCAST_SETUP , null );
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_FRAGMENTATION , & FragmentationParams );
// Init the Clock Sync and File Transfer status
IsClockSynched = false ;
IsFileTransferDone = false ;
// Join the LoRaWAN Network
c . LmHandlerJoin ();
// Set the Transmit Timer
StartTxProcess ( LmHandlerTxEvents_t . LORAMAC_HANDLER_TX_ON_TIMER );
// Handle LoRaWAN Events
handle_event_queue (); // Never returns
return 0 ;
}(來源)
TODO:清理類型,功能和變量的名稱
TODO:閱讀內部溫度傳感器
TODO:用Tinycbor編碼溫度傳感器數據並傳輸到事物網絡
https://lupyuen.github.io/articles/cbor2
TODO:使用Prometheus和Grafana監視傳感器數據
https://lupyuen.github.io/articles/prometheus
todo: @import()添加新代碼
https://zig.news/mattnite/import-and-packages-23mb
TODO:導出到C時,我們是否需要將緩衝區與32位保持一致?
/// User application data
/// (Aligned to 32-bit because it's exported to C)
var AppDataBuffer : [ LORAWAN_APP_DATA_BUFFER_MAX_SIZE ] u8 align ( 4 ) =
std . mem . zeroes ([ LORAWAN_APP_DATA_BUFFER_MAX_SIZE ] u8 );Zig類型反射...我們可以使用它來為C庫生成結構化的調用圖...就像洛拉萬庫一樣嗎? ?
該Zig程序從C導入Lorawan庫,並拋棄Lorawan類型和功能...
// Do Type Reflection on the imported C functions
fn reflect () void {
// We run this at Compile-Time (instead of Runtime)...
comptime {
// Allow Zig Compiler to loop up to 100,000 times (Default is 1,000)
@setEvalBranchQuota ( 100_000 );
// Get the Type Info of the C Namespace
const T = @typeInfo ( c );
// Show the Type Info of the C Namespace (Struct)
@compileLog ( "@typeInfo(c): " , T );
// Shows | *"@typeInfo(c): ", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}
// Show the number of Fields in the C Namespace (0)
@compileLog ( "T.Struct.fields.len: " , T . Struct . fields . len );
// Shows | *"T.Struct.fields.len: ", 0
// Show the number of Declarations in the C Namespace (4743)
@compileLog ( "T.Struct.decls.len: " , T . Struct . decls . len );
// Shows | *"T.Struct.decls.len: ", 4743
// Show the first Declaration in the C Namespace (__builtin_bswap16)
@compileLog ( "T.Struct.decls[0].name: " , T . Struct . decls [ 0 ]. name );
// Shows | *"T.Struct.decls[0].name: ", "__builtin_bswap16"
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) {
// Dump the C Declaration
var T2 = @typeInfo ( c );
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );
// Strangely we can't do this...
// @compileLog("decl.name: ", decl.name);
// Because it shows...
// *"decl.name: ", []const u8{76,109,110,83,116,97,116,117,115,95,116}
}
}
} // End of Compile-Time Code
}(來源)
( @typeInfo在這裡解釋)
當Zig編譯器編譯上面的代碼時,我們會在編譯時看到這一點...
$ zig build-obj --verbose-cimport -target riscv32-freestanding-none -mcpu=baseline_rv32-d -isystem /Users/Luppy/pinecone/nuttx/nuttx/include -I /Users/Luppy/pinecone/nuttx/apps/examples/lorawan_test reflect.zig
info(compilation): C import output: zig-cache/o/e979b806463a36dcecc2ef773bd2d2ad/cimport.zig
| *"@typeInfo(c): ", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}
| *"T.Struct.fields.len: ", 0
| *"T.Struct.decls.len: ", 4744
| *"T.Struct.decls[0].name: ", "__builtin_bswap16"
| *"decl.name: ", "LmnStatus_t"
| *"decl.name: ", "LmHandlerAdrStates_t"
| *"decl.name: ", "LmHandlerFlagStatus_t"
...
| *"decl.name: ", "LmHandlerInit"
| *"decl.name: ", "LmHandlerIsBusy"
| *"decl.name: ", "LmHandlerProcess"
| *"decl.name: ", "LmHandlerGetDutyCycleWaitTime"
| *"decl.name: ", "LmHandlerSend"
| *"decl.name: ", "LmHandlerJoin"
...
./reflect.zig:836:9: error: found compile log statement
@compileLog("@typeInfo(c): ", T);
^
./reflect.zig:840:9: error: found compile log statement
@compileLog("T.Struct.fields.len: ", T.Struct.fields.len);
^
./reflect.zig:844:9: error: found compile log statement
@compileLog("T.Struct.decls.len: ", T.Struct.decls.len);
^
./reflect.zig:848:9: error: found compile log statement
@compileLog("T.Struct.decls[0].name: ", T.Struct.decls[0].name);
^
./reflect.zig:857:17: error: found compile log statement
@compileLog("decl.name: ", T2.Struct.decls[i].name);
^
(來源)
這是Lorawan庫中C類型和功能的列表。
讓我們使用它來可視化lorawan庫的呼叫圖(由模塊)。
我們將使用mermaid.js渲染呼叫圖...
https://mermaid-js.github.io/mermaid/#/./flowchart?id=flowcharts
我們將通過Lorawan模塊(子圖)將Lorawan函數分組在呼叫圖中,因此我們可以在Lorawan模塊上看到呼叫。
在上面的代碼中,為什麼我們使用T2.Struct.decls[i].name而不是decl.name ?
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) {
// Dump the C Declaration
var T2 = @typeInfo ( c );
// Can't use decl.name here...
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );
}
}我們希望此代碼打印聲明的名稱...
@compileLog ( "decl.name: " , decl . name );喜歡...
"decl.name: ", "LmnStatus_t"
但奇怪的是,它打印了字節...
"decl.name: ", []const u8{76,109,110,83,116,97,116,117,115,95,116}
Zig編譯器在我們之前提到名稱後似乎以不同的方式解釋名稱...
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) { ...因此,我們改用此解決方法...
// Get a fresh reference to the Type Info
var T2 = @typeInfo ( c );
// This works OK
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );這產生了我們需要的結果...
"decl.name: ", "LmnStatus_t"
我們可以通過分析HEADER_NAME_H__ c頭宏來自動識別每個Lorawan功能的Lorawan模塊嗎?
讓我們嘗試一下以拋棄從C ...進口的所有C聲明和宏
// Get the Type Info of the C Namespace
const T = @typeInfo ( c );
// Remember the C Header
var header : [] const u8 = "" ;
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
var T2 = @typeInfo ( c );
// If the C Declaration ends with "_H"...
if (
std . mem . endsWith ( u8 , decl . name , "_H" ) or
std . mem . endsWith ( u8 , decl . name , "_H_" ) or
std . mem . endsWith ( u8 , decl . name , "_H__" )
) {
// Dump the C Header and remember it
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( "-----" , name );
header = name ;
} else {
// Dump the C Declaration
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( "decl.name:" , name );
}
} // End of C Declaration(來源)
我們得到了洛萬功能和洛萬宏的列表...
| *"decl.name:", "LmHandlerInit"
| *"decl.name:", "LmHandlerIsBusy"
| *"decl.name:", "LmHandlerProcess"
| *"decl.name:", "LmHandlerGetDutyCycleWaitTime"
| *"decl.name:", "LmHandlerSend"
| *"decl.name:", "LmHandlerJoin"
...
| *"-----", "__LORAMAC_HANDLER_H__"
| *"-----", "__LORAMAC_HANDLER_TYPES_H__"
| *"decl.name:", "LMH_SYS_TIME_UPDATE_NEW_API"
| *"decl.name:", "__LMHP_COMPLIANCE__"
(來源)
這沒用。 LmHandlerInit is actually declared inside the C Header File for __LORAMAC_HANDLER_H__ __LORAMAC_HANDLER_H__ LmHandlerInit之前。
因此,似乎我們需要手動將Lorawan功能分組為Lorawan模塊。
Lorawan函數似乎按照C標頭文件進行了測序,因此我們只需要手動標記每個Lorawan模塊的第一個Lorawan函數即可。像這樣...
LmHandlerInit → __LORAMAC_HANDLER_H__
LoRaMacInitialization → __LORAMAC_H__
RegionCommonValueInRange → __REGIONCOMMON_H__
SX126xInit → __SX126x_H__
SX126xIoInit → __SX126x_BOARD_H__
我們的Zig應用程序可以對自己鍵入反射,以發現其類型,常數,變量和函數...
// Show the Type Info for our Zig Namespace
const ThisType = @typeInfo ( @This ());
@compileLog ( "ThisType: " , ThisType );
@compileLog ( "ThisType.Struct.decls.len: " , ThisType . Struct . decls . len );
@compileLog ( "ThisType.Struct.decls[0].name: " , ThisType . Struct . decls [ 0 ]. name );
@compileLog ( "ThisType.Struct.decls[1].name: " , ThisType . Struct . decls [ 1 ]. name );
@compileLog ( "ThisType.Struct.decls[2].name: " , ThisType . Struct . decls [ 2 ]. name );
// Shows...
// | *"ThisType: ", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}
// | *"ThisType.Struct.decls.len: ", 66
// | *"ThisType.Struct.decls[0].name: ", "std"
// | *"ThisType.Struct.decls[1].name: ", "c"
// | *"ThisType.Struct.decls[2].name: ", "ACTIVE_REGION" (來源)
我們將使用它來繪製從Lorawan庫中C函數的曲線函數的函數調用。
之前,我們捕獲了此呼叫日誌:lorawan庫中C函數的呼叫日誌...
init_event_queue
TimerInit: 0x4201c76c
SX126xIoInit: Compiled with gcc
init_gpio
...
RadioSetChannel: freq=923200000
RadioSetTxConfig: modem=1, power=13, fdev=0, bandwidth=0, datarate=10, coderate=1, preambleLen=8, fixLen=0, crcOn=1, freqHopOn=0, hopPeriod=0, iqInverted=0, timeout=4000
RadioSetTxConfig: SpreadingFactor=10, Bandwidth=4, CodingRate=1, LowDatarateOptimize=0, PreambleLength=8, HeaderType=0, PayloadLength=255, CrcMode=1, InvertIQ=0
RadioStandby
RadioSetModem
SX126xSetTxParams: power=13, rampTime=7
SX126xSetPaConfig: paDutyCycle=4, hpMax=7, deviceSel=0, paLut=1
(來源)
呼叫日誌的每一行都包含Lorawan函數名稱。我們將與Zig類型反射的信息匹配,以繪製呼叫圖。
我們將呼叫登錄導入我們的Zig應用程序中...
/// Call Log captured for this app. From
/// https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b
const call_log =
\init_event_queue
\TimerInit: 0x4201c76c
\SX126xIoInit: Compiled with gcc
\init_gpio
...
\RadioSetChannel: freq=923200000
\RadioSetTxConfig: modem=1, power=13, fdev=0, bandwidth=0, datarate=10, coderate=1, preambleLen=8, fixLen=0, crcOn=1, freqHopOn=0, hopPeriod=0, iqInverted=0, timeout=4000
\RadioSetTxConfig: SpreadingFactor=10, Bandwidth=4, CodingRate=1, LowDatarateOptimize=0, PreambleLength=8, HeaderType=0, PayloadLength=255, CrcMode=1, InvertIQ=0
\RadioStandby
\RadioSetModem
\SX126xSetTxParams: power=13, rampTime=7
\SX126xSetPaConfig: paDutyCycle=4, hpMax=7, deviceSel=0, paLut=1
\SecureElementRandomNumber: 0xbc9f21c2
\RadioSend: size=23
\00 00 00 00 00 00 00 00 00 5b b1 7b 37 e7 5e c1 4b c2 21 9c 04 48 1b
\RadioSend: PreambleLength=8, HeaderType=0, PayloadLength=23, CrcMode=1, InvertIQ=0
\TimerStop: 0x4201b86c
\TimerStart2: 0x4201b86c, 4000 ms
\callout_reset: evq=0x420131a8, ev=0x4201b86c
\
\###### =========== MLME-Request ============ ######
\###### MLME_JOIN ######
\###### ===================================== ######
\STATUS : OK
\StartTxProcess
\handle_event_queue
\DIO1 add event
\handle_event_queue: ev=0x4201b894
\RadioOnDioIrq
\RadioIrqProcess
\IRQ_TX_DONE
\TimerStop: 0x4201b86c
\TODO: RtcGetCalendarTime
\TODO: RtcBkupRead
\RadioOnDioIrq
\RadioIrqProcess
\ProcessRadioTxDone: RxWindow1Delay=4988
\RadioSleep
...
;(來源)
Zig編譯器可以按線路按線處理呼叫日誌。
// Show the first line of the Call Log
var call_log_split = std . mem . split ( u8 , call_log , " n " );
const line = call_log_split . next ();
@compileLog ( "line:" , line );
// Shows | *"line:", []const u8{103,112,108,104,95,101,110,97,98,108,101,58,32,87,65,82,78,73,78,71,58,32,112,105,110,57,58,32,65,108,114,101,97,100,121,32,100,101,116,97,99,104,101,100}(來源)
要遍歷呼叫日誌的所有行,我們做到了...
// For every line in the Call Log...
var call_log_split = std . mem . split ( u8 , call_log , " n " );
while ( call_log_split . next ()) | line | {
// TODO: Process the line
@compileLog ( "line:" , line );
...
} // End of Call Log(來源)
讓我們將呼叫日誌與我們Lorawan庫中的函數名稱匹配...
// For every line in the Call Log...
var call_log_split = std . mem . split ( u8 , call_log , " n " );
while ( call_log_split . next ()) | line | {
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
var T2 = @typeInfo ( c );
// If the C Declaration matches the Call Log...
if ( std . mem . startsWith ( u8 , line , decl . name )) {
// Dump the C Declaration
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( "Found call log" , name );
break ;
}
} // End of C Declaration
} // End of Call Log(來源)
上面的代碼產生了此結果...
| *"Found call log", "LoRaMacInitialization"
| *"Found call log", "TimerInit"
| *"Found call log", "SX126xIoInit"
| *"Found call log", "SX126xSetTx"
| *"Found call log", "SX126xSetPaConfig"
| *"Found call log", "TimerInit"
| *"Found call log", "TimerInit"
| *"Found call log", "RadioSetModem"
| *"Found call log", "RadioSetModem"
| *"Found call log", "RadioSetPublicNetwork"
| *"Found call log", "RadioSleep"
| *"Found call log", "RadioSetModem"
| *"Found call log", "RadioSetPublicNetwork"
...
(來源)
這是Lorawan函數及其被稱為序列的列表。
距離渲染我們的結構化呼叫圖更近一步!
讓我們擴展上面的代碼並渲染幼稚的呼叫圖...
對於我們在呼叫日誌中識別的每個Lorawan功能...
我們繪製lorawan函數與呼叫日誌中的下一個洛萬函數之間的界線
因此,我們只是繪製函數調用的連接順序
(是的,超級天真!)
我們這樣做...
// Draw the graph for all functions in the Call Log
var call_log_split = std . mem . split ( u8 , call_log , " n " );
var prev_name : [] const u8 = "Start" ;
@compileLog ( "flowchart TD;" ); // Top-Down Flowchart
// For every line in the Call Log...
while ( call_log_split . next ()) | line | {
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
var T2 = @typeInfo ( c );
// If the C Declaration matches the Call Log...
if ( std . mem . startsWith ( u8 , line , decl . name )) {
// Draw the graph: [previous function]-->[current function]
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( " " , prev_name , "-->" , name , ";" );
prev_name = name ;
break ;
}
} // End of C Declaration
} // End of Call Log
@compileLog ( " " , prev_name , "-->" , "End" , ";" );(來源)
Zig編譯器在編譯時產生此結果...
| *"flowchart TD;"
| *" ", "Start", *"-->", "LoRaMacInitialization", *";"
| *" ", "LoRaMacInitialization", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "SX126xIoInit", *";"
| *" ", "SX126xIoInit", *"-->", "SX126xSetTx", *";"
| *" ", "SX126xSetTx", *"-->", "SX126xSetPaConfig", *";"
| *" ", "SX126xSetPaConfig", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "RadioSetModem", *";"
...
(來源)
我們會從上面的曲折編譯器日誌中手動刪除定界符...
flowchart TD;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->RadioSetModem;
...
要獲得此Mermaid.js流程圖...
流程圖TD;
啟動 - >勞拉米斯式化;
Loramacinitialization-> timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時 - > sx126xioInit;
SX126XIOINIT-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> timerinit;
定時 - > timerinit;
定時儀 - > radiosetmodem;
RadioSetModem-> RadioSetModem;
RadioSetModem-> RadioSetPublicNetwork;
RadioSetPublicNetwork-> RadioSleep;
輻射 - > radiosetmodem;
RadioSetModem-> RadioSetPublicNetwork;
RadioSetPublicNetwork-> timerinit;
定時 - > timerinit;
定時 - > timerinit;
定時儀 - > radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettXConfig-> RadioSettXConfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機 - >輻射式;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> timerinit;
timerinit-> timersetValue;
計時值 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_TX_DONE;
irq_tx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> irq_header_valid;
IRQ_HEADER_VALID-> RADITONDIOIRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_RX_DONE;
irq_rx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettXConfig-> RadioSettXConfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機 - >輻射式;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_TX_DONE;
irq_tx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_RX_TX_TIMEOUT;
IRQ_RX_TX_TIMEOUT-> TIMERSTOP;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettXConfig-> RadioSettXConfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機 - >輻射式;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_TX_DONE;
irq_tx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadiodoiRQ-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_RX_TX_TIMEOUT;
IRQ_RX_TX_TIMEOUT-> TIMERSTOP;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettXConfig-> RadioSettXConfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機 - >輻射式;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_TX_DONE;
irq_tx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_RX_TX_TIMEOUT;
IRQ_RX_TX_TIMEOUT-> TIMERSTOP;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettXConfig-> RadioSettXConfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機 - >輻射式;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_TX_DONE;
irq_tx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> RadioDioiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_RX_TX_TIMEOUT;
IRQ_RX_TX_TIMEOUT-> TIMERSTOP;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettXConfig-> RadioSettXConfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機 - >輻射式;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioIRQProcess-> IRQ_TX_DONE;
irq_tx_done-> timerstop;
Timerstop-> RadioDoiiRQ;
RadioDioIrq-> RadioiRQProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
我們生產的超級呼叫圖有什麼問題?
flowchart TD;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->RadioSetModem;
...
(來源)
我們的呼叫圖缺乏結構。請記住,我們只是繪製函數調用的順序...
TimerInit → TimerInit沒有意義(因為我們沒有遞歸功能)
TimerInit → RadioSetModem是完全錯誤的,因為TimerInit是一個低級功能(敏捷的多線程庫),而RadioSetModem是一個高級函數(SX1262庫)
我們可以添加一些結構來改進呼叫圖嗎?
要產生一個結構化的調用圖,我們將將功能分組為模塊,從高級到低級...
Zig應用程序(最高級別)
洛萬圖書館
SX1262庫
敏捷的多線程庫(最低級別)
並且我們將確保我們永遠不會從低級功能到高級功能繪製一條線。
這是我們想要實現的片段...
流程圖TD;
勞拉萬子;
開始;
Loramacinialization;
結尾;
SX1262子圖;
SX126XIOINIT;
sx126xsettx;
SX126XSETPACONFIG;
結尾;
子圖名;
計時;
計時;
結尾;
啟動 - >勞拉米斯式化;
Loramacinitialization-> timerinit;
SX126XIOINIT-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> timerinit;
讓我們通過將C函數分組到模塊中,向我們的呼叫圖添加一些結構。
這就是我們定義模塊的方式,從高水平到低水平...
// Define the Modules and the First / Last Functions in each Module.
// We order the Modules from High-Level to Low-Level.
var all_modules = [ _ ] Module {
// LoRaMAC Handler is the Top Level Module that drives the LoRaWAN Stack
Module {
. name = "LMHandler" ,
. first_function = "LmHandlerInit" ,
. last_function = "DisplayAppInfo" ,
. first_index = undefined ,
. last_index = undefined ,
},
// LoRaMAC Module is the implementation of the LoRaWAN Driver
Module {
. name = "LoRaMAC" ,
. first_function = "LoRaMacInitialization" ,
. last_function = "LoRaMacDeInitialization" ,
. first_index = undefined ,
. last_index = undefined ,
},
// Radio Module is the abstract interface for LoRa Radio Transceivers
Module {
. name = "Radio" ,
. first_function = "RadioInit" ,
. last_function = "RadioAddRegisterToRetentionList" ,
. first_index = undefined ,
. last_index = undefined ,
},
// SX1262 Module is the LoRa Driver for Semtech SX1262 Radio Transceiver
Module {
. name = "SX1262" ,
. first_function = "SX126xInit" ,
. last_function = "SX126xSetOperatingMode" ,
. first_index = undefined ,
. last_index = undefined ,
},
// NimBLE is the Bottom Level Module that contains Multithreading Functions like Timers and Event Queues
Module {
. name = "NimBLE" ,
. first_function = "TimerInit" ,
. last_function = "TimerGetElapsedTime" ,
. first_index = undefined ,
. last_index = undefined ,
},
};(來源)
請注意,我們僅指定每個模塊的第一個也是最後一個功能。
這是因為來自同一模塊的函數被聚集在曲折類型反射中。
然後,我們將每個模塊渲染為Mermaid.js子圖...
/// Render all Modules and their Functions as Subgraphs
fn render_modules ( all_modules : [] Module ) void {
comptime {
// Render every Module
for ( all_modules ) | module , m | {
@compileLog ( " subgraph " , module . name , ";" );
// For every line in the Call Log...
var call_log_split = std . mem . split ( u8 , call_log , " n " );
while ( call_log_split . next ()) | line | {
var T = @typeInfo ( c );
// If the Call Log matches a C Declaration...
if ( get_decl_by_name_filtered ( all_modules , line )) | decl_index | {
// Get the Module Index for the C Declaration
if ( get_module_by_decl ( all_modules , decl_index )) | m2 | {
// If the C Declaration matches our Module Index...
if ( m == m2 ) {
// Print the Function Name
var name = T . Struct . decls [ decl_index ]. name ;
@compileLog ( " " , name , ";" );
}
} else {
// Missing Declaration
var name = T . Struct . decls [ decl_index ]. name ;
@compileLog ( "Missing Decl:" , name );
}
}
} // End of Call Log
@compileLog ( " end;" );
} // End of Module
}
}(來源)
每個子圖包含屬於模塊的功能列表...
flowchart TD;
subgraph LMHandler;
LmHandlerSend;
end;
subgraph LoRaMAC;
LoRaMacInitialization;
end;
subgraph Radio;
RadioSetModem;
RadioSetPublicNetwork;
RadioSleep;
RadioSetChannel;
RadioSetTxConfig;
RadioStandby;
RadioSend;
RadioIrqProcess;
...
end;
subgraph SX1262;
SX126xIoInit;
SX126xSetTx;
SX126xSetPaConfig;
end;
subgraph NimBLE;
TimerInit;
TimerStop;
TimerStart;
TimerSetValue;
end;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->SX126xIoInit;
...
(來源)
當我們使用Mermaid.js渲染輸出時,我們會得到一個結構化的調用圖,看起來更有意義...
流程圖TD;
子圖LMHANDLER;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
lmhandlersend;
結尾;
勞拉馬克子圖;
Loramacinialization;
結尾;
子圖廣播;
輻射儀;
輻射儀;
RadioSetPublicNetwork;
輻射;
輻射儀;
RadioSetPublicNetwork;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
RadioirqProcess;
輻射納爾;
RadioSettxConfig;
RadioSettxConfig;
radiostandby;
輻射儀;
收音機端;
收音機端;
RadioirqProcess;
RadioirqProcess;
輻射;
radiostandby;
輻射納爾;
radiosetxconfig;
radiostandby;
輻射儀;
radiosetxconfig;
radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
輻射;
結尾;
SX1262子圖;
SX126XIOINIT;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
sx126xsettx;
SX126XSETPACONFIG;
結尾;
子圖名;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
計時;
蒂姆斯特普;
Timerstart;
計時;
計時值;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
計時值;
Timerstart;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
Timerstart;
蒂姆斯特普;
蒂姆斯特普;
蒂姆斯特普;
結尾;
啟動 - >勞拉米斯式化;
Loramacinitialization-> timerinit;
定時 - > sx126xioInit;
SX126XIOINIT-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> timerinit;
定時儀 - > radiosetmodem;
RadioSetModem-> RadioSetPublicNetwork;
RadioSetPublicNetwork-> RadioSleep;
輻射 - > radiosetmodem;
RadioSetModem-> RadioSetPublicNetwork;
RadioSetPublicNetwork-> timerinit;
定時儀 - > radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> timerinit;
timerinit-> timersetValue;
計時值 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> lmhandlersend;
lmhandlersend-> radiosthannel;
radiosterchannel-> radiosettxconfig;
RadioSettxConfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> SX126XSETTX;
SX126XSETTX-> SX126XSETPACONFIG;
SX126XSETPACONFIG-> RADIOSEND;
收音機端 - >蒂姆斯托普;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > timersetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> TimerSetValue;
計時值 - > TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
radiostandby-> radiosthannel;
radiosthannel-> radiosetrxConfig;
radiosetxconfig-> radiostandby;
radiostandby-> radiosetmodem;
RadioSetModem-> radiosetrxConfig;
radiosetxconfig-> radiorx;
radiorx-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> RadioirqProcess;
RadioIrqProcess-> Timerstop;
Timerstop-> RadioirqProcess;
RadioirqProcess-> RadioSleep;
輻射 - > Timerstop;
Timerstop-> end;
托多
當我們運行此代碼以通過模塊為C函數時,Zig編譯器會崩潰...
Zig-Bl602-NUTTX/Reffle.Zig
F5A5C82中的第825至1086行
這是輸出日誌...
https://gist.github.com/lupyuen/5738abefa9d4c138e9d731e22222201500f
在MacOS上,Zig編譯器會消耗超過34 GB的內存和崩潰...
(在WSL上,ZIG編譯器在4 GB的內存中消耗WSL過程)
之所以發生這種情況,是因為我們的代碼循環在處理1,500行的原始呼叫日誌時反復反复超過4,700 c聲明。
讓我們優化我們的代碼。
我們的代碼通過反复循環超過4,700 c聲明來搜索C聲明。
/// Return the C Declaration Index for the Function Name.
/// We match the C Declaration Name against the start of the Function Name.
/// This is the slower, unfiltered version that searches all C Declarations.
fn get_decl_by_name ( name : [] const u8 ) ? usize {
comptime {
const T = @typeInfo ( c );
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
// If the C Declaration starts with the Function Name...
if ( std . mem . startsWith ( u8 , name , decl . name )) {
// Return the C Declaration Index
return i ;
}
} // End of C Declaration
return null ; // Not found
}
}(來源)
這會導致Zig編譯器隨著內存而崩潰。但是我們實際上不需要循環瀏覽所有C聲明!
根據我們的模塊列表,我們僅稱為173個功能。讓我們修復上述功能,以便我們循環瀏覽173個函數...
/// Return the C Declaration Index for the Function Name.
/// We match the C Declaration Name against the start of the Function Name.
/// This is the faster, filtered version that searches C Declarations by Modules.
fn get_decl_by_name_filtered ( all_modules : [] Module , name : [] const u8 ) ? usize {
comptime {
const T = @typeInfo ( c );
// Search all Modules for the Function Name
for ( all_modules ) | module | {
// For every C Declaration in the Module...
var decl_index = module . first_index ;
while ( decl_index <= module . last_index ) {
// Get the C Declaration
var decl = T . Struct . decls [ decl_index ];
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
// If the C Declaration starts with the Function Name...
if ( std . mem . startsWith ( u8 , name , decl . name )) {
// Return the C Declaration Index
return decl_index ;
}
decl_index += 1 ;
} // End of C Declaration
} // End of Module
return null ; // Not found
}
}(來源)
我們的代碼與Zig編譯器一起運行!
(MacOS,雖然不是WSL)
用Zig編譯器在編譯時構建呼叫圖看起來很具有挑戰性。
我希望Zig編譯器的未來版本將支持以下...
C聲明的類型反射將提供源文件的完整路徑
(使每個聲明的模塊更容易)
@compileLog將發出任何類型的字符串
(以便@compileLog可以生成適當的美人魚Start-->LoRaMacInitialization;