1、背景
Baseflight是一款開(kāi)源的飛行控制軟件,最初是MultiWii項(xiàng)目的32位分支,旨在為多旋翼、固定翼和直升機(jī)等飛行器提供高效的飛行控制解決方案。Baseflight的發(fā)展歷程可以追溯到MultiWii項(xiàng)目,它在性能和功能上進(jìn)行了顯著的優(yōu)化和擴(kuò)展,特別是在移植到更強(qiáng)大的32位處理器(如STM32)后,解決了早期8位單片機(jī)的性能瓶頸問(wèn)題。
1.開(kāi)源與社區(qū)支持
Baseflight是一個(gè)完全開(kāi)源的項(xiàng)目,代碼托管在GitHub上(https://github.com/multiwii/baseflight,下載其實(shí)幾百k,純c代碼的空間都不大),獲得了全球眾多開(kāi)發(fā)者和愛(ài)好者的貢獻(xiàn)和支持。
2.兼容多種硬件平臺(tái):
Baseflight支持多種流行的飛行控制器硬件,例如NAZE32、SKYLINE32 Mini等,這些硬件具有高性價(jià)比和良好的性能表現(xiàn)(http://www.yinyanmodel.com/ch/ProductView.asp?ID=393)。
3.豐富的配置選項(xiàng):
通過(guò)Baseflight Configurator圖形用戶界面,用戶可以方便地調(diào)整飛控參數(shù),如PID值、傳感器校準(zhǔn)等,以滿足不同的飛行需求。
所謂調(diào)整飛控參數(shù),就是調(diào)整源碼中類(lèi)似這種的變量:
float magneticDeclination = 0.0f; ? ? ? // calculated at startup from config
4.命令行CLI(command line)接口:
除了圖形界面外,Baseflight還提供了命令行接口(CLI),允許用戶直接通過(guò)串口進(jìn)行高級(jí)設(shè)置和調(diào)試操作。
5.家族譜系:
Baseflight是Cleanflight和Betaflight等后續(xù)項(xiàng)目的前身。Cleanflight在其基礎(chǔ)上增加了更多特性,而B(niǎo)etaflight則進(jìn)一步提升了性能,成為目前FPV競(jìng)速無(wú)人機(jī)中最常用的固件之一https://oscarliang.com/baseflight-cleanflight-comparison/。
2、代碼結(jié)構(gòu)概述
三層架構(gòu):驅(qū)動(dòng)-計(jì)算-工具
BaseFlight代碼通常采用分層架構(gòu)。最上層是與硬件交互的驅(qū)動(dòng)層,負(fù)責(zé)采集傳感器數(shù)據(jù)(如加速度計(jì)、陀螺儀、磁力計(jì)等)并將控制信號(hào)輸出到電機(jī)驅(qū)動(dòng)模塊。中間層是核心控制算法層,包含姿態(tài)估計(jì)、導(dǎo)航和控制算法等。最下層是一些基礎(chǔ)的工具函數(shù)庫(kù)。
圖1 筆者用source insight閱讀baseflight全代碼
主要文件的統(tǒng)計(jì)和解釋
整個(gè)項(xiàng)目不過(guò)1.6w行,78個(gè)文件。最大的文件是cli.c超過(guò)一千行(包含大量的交互處理,所以代碼多一點(diǎn)),其余文件均小于1k行,符合良好的c語(yǔ)言代碼設(shè)計(jì)規(guī)范。數(shù)量最多的是drv_開(kāi)頭的驅(qū)動(dòng)層程序。
其次是各種計(jì)算和控制文件,比如fw_nav,是前向?qū)Ш降囊馑?,也就是控制無(wú)人機(jī)往前飛。比如mixer是混合器,混合器是混合操控命令環(huán)境感知等等渠道的信息,形成最終的每個(gè)旋翼的電動(dòng)機(jī)的控制命令。
最后是各種輔助文件,包含各種輔助函數(shù),比如utils.c.h,比如printf.c.h。
還有一個(gè)不屬于上面三類(lèi)的重要文件makefile,是把baseflight編譯成固件.bin的核心文件,一般不要去改。
圖2 baseflight的簡(jiǎn)明項(xiàng)目報(bào)告
文件名 | 類(lèi)型 | 大小 | 行數(shù) | 解釋 |
board.h | src | 9197 | - | 包含與硬件板相關(guān)的定義和配置信息的頭文件 |
buzzer.c | src | 6377 | 113 | 用于控制蜂鳴器的源代碼文件 |
buzzer.h | src | 1756 | - | 包含與控制蜂鳴器相關(guān)的定義和函數(shù)聲明的頭文件 |
cli.c | src | 48960 | 917 | 用于處理命令行界面的源代碼文件 |
cli.h | src | 158 | - | 包含與命令行界面相關(guān)的定義和函數(shù)聲明的頭文件 |
config.c | src | 12733 | 316 | 用于處理配置信息的源代碼文件 |
drv_adc.c | src | 3462 | 63 | 用于控制ADC(模數(shù)轉(zhuǎn)換器)的源代碼文件 |
drv_adc.h | src | 570 | - | 包含與控制ADC相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_adxl345.c | src | 3486 | 60 | 用于控制ADXL345加速度計(jì)的源代碼文件 |
drv_adxl345.h | src | 189 | - | 包含與控制ADXL345加速度計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_ak8975.c | src | 1546 | 30 | 用于控制AK8975磁力計(jì)的源代碼文件 |
drv_ak8975.h | src | 122 | - | 包含與控制AK8975磁力計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_bma280.c | src | 1332 | 27 | 用于控制BMA280加速度計(jì)的源代碼文件 |
drv_bma280.h | src | 51 | - | 包含與控制BMA280加速度計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_bmp085.c | src | 9395 | 149 | 用于控制BMP085氣壓計(jì)的源代碼文件 |
drv_bmp085.h | src | 50 | - | 包含與控制BMP085氣壓計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_bmp280.c | src | 7015 | 76 | 用于控制BMP280氣壓計(jì)的源代碼文件 |
drv_bmp280.h | src | 48 | - | 包含與控制BMP280氣壓計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_gpio.c | src | 2900 | 62 | 用于控制GPIO(通用輸入輸出)的源代碼文件 |
drv_gpio.h | src | 1179 | - | 包含與控制GPIO相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_hcsr04.c | src | 4049 | 83 | 用于控制HCSR04超聲波傳感器的源代碼文件 |
drv_hcsr04.h | src | 185 | - | 包含與控制HCSR04超聲波傳感器相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_hmc5883l.c | src | 8102 | 93 | 用于控制HMC5883L磁力計(jì)的源代碼文件 |
drv_hmc5883l.h | src | 133 | - | 包含與控制HMC5883L磁力計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_i2c.c | src | 16997 | 289 | 用于控制I2C總線的源代碼文件 |
drv_i2c.h | src | 546 | - | 包含與控制I2C總線相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_i2c_soft.c | src | 4692 | 168 | 用于軟件模擬I2C總線的源代碼文件 |
drv_l3g4200d.c | src | 2709 | 48 | 用于控制L3G4200D陀螺儀的源代碼文件 |
drv_l3g4200d.h | src | 68 | - | 包含與控制L3G4200D陀螺儀相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_ledring.c | src | 1214 | 40 | 用于控制LED環(huán)的源代碼文件 |
drv_ledring.h | src | 95 | - | 包含與控制LED環(huán)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_mma845x.c | src | 4010 | 41 | 用于控制MMA845X加速度計(jì)的源代碼文件 |
drv_mma845x.h | src | 52 | - | 包含與控制MMA845X加速度計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_mpu.c | src | 18305 | 353 | 用于控制MPU6050/6500/9250 ?IMU(慣性測(cè)量單元)的源代碼文件 |
drv_mpu.h | src | 605 | - | 包含與控制MPU6050/6500/9250 ?IMU相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_ms5611.c | src | 5821 | 117 | 用于控制MS5611氣壓計(jì)的源代碼文件 |
drv_ms5611.h | src | 50 | - | 包含與控制MS5611氣壓計(jì)相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_pwm.c | src | 14536 | 237 | 用于控制PWM(脈沖寬度調(diào)制)輸出的源代碼文件 |
drv_pwm.h | src | 2090 | - | 包含與控制PWM輸出相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_serial.c | src | 1019 | 27 | 用于控制串口通信的源代碼文件 |
drv_serial.h | src | 1644 | - | 包含與控制串口通信相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_softserial.c | src | 14155 | 297 | 用于軟件模擬串口通信的源代碼文件 |
drv_softserial.h | src | 1619 | - | 包含與軟件模擬串口通信相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_spi.c | src | 3091 | 93 | 用于控制SPI(串行外設(shè)接口)的源代碼文件 |
drv_spi.h | src | 260 | - | 包含與控制SPI相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_system.c | src | 5970 | 79 | 用于系統(tǒng)級(jí)別控制的源代碼文件 |
drv_system.h | src | 564 | - | 包含與系統(tǒng)級(jí)別控制相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_timer.c | src | 8105 | 104 | 用于控制定時(shí)器的源代碼文件 |
drv_timer.h | src | 856 | - | 包含與控制定時(shí)器相關(guān)的定義和函數(shù)聲明的頭文件 |
drv_uart.c | src | 14017 | 329 | 用于控制UART(通用異步收發(fā)器)通信的源代碼文件 |
drv_uart.h | src | 1231 | - | 包含與控制UART通信相關(guān)的定義和函數(shù)聲明的頭文件 |
flash.bat | support | 681 | - | 用于執(zhí)行閃存操作的批處理文件 |
fw_nav.c | src | 9207 | 176 | 包含導(dǎo)航相關(guān)功能的源代碼文件 |
gps.c | src | 57742 | 812 | 包含GPS相關(guān)功能的源代碼文件 |
ibus.c | src | 2178 | 49 | 用于處理i-Bus(Flysky接收機(jī)的信號(hào)協(xié)議)的源代碼文件 |
imu.c | src | 15173 | 294 | 包含IMU(慣性測(cè)量單元)相關(guān)功能的源代碼文件 |
JLinkSettings.ini | - | 573 | - | J-Link調(diào)試器的配置文件 |
main.c | src | 9069 | 202 | 程序的入口點(diǎn),包含主要的執(zhí)行邏輯 |
Makefile | - | 6751 | - | 用于管理項(xiàng)目編譯的Makefile文件 |
mixer.c | src | 24140 | 342 | 用于控制飛行器的混合器的源代碼文件 |
mw.c | src | 43199 | 832 | 主要的飛行控制代碼的源代碼文件 |
mw.h | src | 26858 | - | 包含與飛行控制相關(guān)的定義和函數(shù)聲明的頭文件 |
printf.c | src | 6704 | 167 | 實(shí)現(xiàn)了格式化輸出功能的源代碼文件 |
printf.h | src | 4923 | - | 包含與格式化輸出相關(guān)的定義和函數(shù)聲明的頭文件 |
rxmsp.c | src | 533 | 18 | 用于處理R-XSR接收機(jī)的源代碼文件 |
sbus.c | src | 3398 | 58 | 用于處理S.Bus(Futaba接收機(jī)的信號(hào)協(xié)議)的源代碼文件 |
sensors.c | src | 17656 | 406 | 用于處理各種傳感器的源代碼文件 |
serial.c | src | 37714 | 703 | 用于控制串口通信的源代碼文件 |
spektrum.c | src | 5512 | 116 | 用于處理Spektrum接收機(jī)的源代碼文件 |
sumd.c | src | 1950 | 47 | 用于處理SUMD(Graupner接收機(jī)的信號(hào)協(xié)議)的源代碼文件 |
telemetry_common.c | src | 3237 | 90 | 包含通用遙測(cè)功能的源代碼文件 |
telemetry_common.h | src | 288 | - | 包含與通用遙測(cè)功能相關(guān)的定義和函數(shù)聲明的頭文件 |
telemetry_frsky.c | src | 7648 | 172 | 用于處理FrSky遙測(cè)的源代碼文件 |
telemetry_frsky.h | src | 332 | - | 包含與FrSky遙測(cè)相關(guān)的定義和函數(shù)聲明的頭文件 |
telemetry_hott.c | src | 9205 | 126 | 用于處理HoTT遙測(cè)的源代碼文件 |
telemetry_hott.h | src | 8791 | - | 包含與HoTT遙測(cè)相關(guān)的定義和函數(shù)聲明的頭文件 |
utils.c | src | 3944 | 114 | 包含一些常用功能的實(shí)用函數(shù)的源代碼文件 |
utils.h | src | 211 | - | 包含與實(shí)用函數(shù)相關(guān)的定義和函數(shù)聲明的頭文件 |
表1 全文件列表
里面的main.c,會(huì)把主循環(huán)拉起來(lái)。就像任何持續(xù)執(zhí)行的嵌入式系統(tǒng),都是靠一個(gè)無(wú)限循環(huán)來(lái)保證器件的無(wú)限執(zhí)行的。
?// loopy
?while?(1) {?//這里,main啟動(dòng)無(wú)限循環(huán)
? ??loop();?
#ifdef?SOFTSERIAL_LOOPBACK
? ??if?(loopbackPort1) {
? ? ? ??while?(serialTotalBytesWaiting(loopbackPort1)) {
? ? ? ? ? ??uint8_t?b =?serialRead(loopbackPort1);
? ? ? ? ? ??serialWrite(loopbackPort1, b);
? ? ? ? ? ??//serialWrite(core.mainport, 0x01);
? ? ? ? ? ??//serialWrite(core.mainport, b);
? ? ? ? };
? ? }
? ??if?(loopbackPort2) {
? ? ? ??while?(serialTotalBytesWaiting(loopbackPort2)) {
? ? ? ? ? ??serialRead(loopbackPort2);
? ? ? ? };
? ? }
#endif
}
區(qū)區(qū)幾行代碼,其內(nèi)容卻很豐富:
1. 無(wú)限循環(huán) (while (1))
while (1) 創(chuàng)建了一個(gè)無(wú)限循環(huán),使得代碼不斷重復(fù)執(zhí)行。后面會(huì)說(shuō)如何終止無(wú)限循環(huán)。
2. 調(diào)用 loop() 函數(shù)
這個(gè)loop() 就是飛行的主要函數(shù),不在main。c里面,而在mw.c里面。mw的意思就是main work。之所以分開(kāi)無(wú)非是軟件工程良好代碼規(guī)范的需要:不要讓單個(gè)文件太過(guò)龐大。
3. 條件編譯 (#ifdef SOFTSERIAL_LOOPBACK)
a. #ifdef SOFTSERIAL_LOOPBACK 是一個(gè)預(yù)處理器指令,表示只有在定義了 SOFTSERIAL_LOOPBACK 宏的情況下,才會(huì)編譯下面的代碼塊。
4. 處理 loopbackPort1
a. 如果 loopbackPort1 存在(即不為 NULL 或假值),則進(jìn)入一個(gè)內(nèi)部循環(huán)。
b. while (serialTotalBytesWaiting(loopbackPort1)):此循環(huán)將持續(xù)運(yùn)行,直到 loopbackPort1 上沒(méi)有任何數(shù)據(jù)等待讀取為止。
c. uint8_t b = serialRead(loopbackPort1);:從 loopbackPort1 讀取一個(gè)字節(jié)的數(shù)據(jù)并存儲(chǔ)在變量 b 中。
d. serialWrite(loopbackPort1, b);:將剛剛讀取到的數(shù)據(jù)再寫(xiě)回到 loopbackPort1,這實(shí)際上是在模擬一個(gè)回環(huán)(loopback)操作。
5. 處理 loopbackPort2
a. 如果 loopbackPort2 存在,則進(jìn)入另一個(gè)內(nèi)部循環(huán)。
b. while (serialTotalBytesWaiting(loopbackPort2)):此循環(huán)將持續(xù)運(yùn)行,直到 loopbackPort2 上沒(méi)有任何數(shù)據(jù)等待讀取為止。
c. serialRead(loopbackPort2);:從 loopbackPort2 讀取一個(gè)字節(jié)的數(shù)據(jù),但沒(méi)有將其存儲(chǔ)或重新發(fā)送出去,因此這似乎只是簡(jiǎn)單地丟棄數(shù)據(jù)。
其中值得注意的幾個(gè)點(diǎn):
● 死循環(huán):由于 while (1) 是一個(gè)死循環(huán),除非通過(guò)外部中斷或其他方式終止程序,否則這段代碼會(huì)一直運(yùn)行下去。
● 資源消耗:在沒(méi)有數(shù)據(jù)可讀的情況下,serialTotalBytesWaiting 函數(shù)會(huì)持續(xù)返回 false,導(dǎo)致循環(huán)快速結(jié)束。然而,如果端口上有大量數(shù)據(jù)等待讀取,這可能會(huì)導(dǎo)致 CPU 資源的高占用率。
● 調(diào)試和測(cè)試:#ifdef SOFTSERIAL_LOOPBACK 指令使得這部分代碼可以根據(jù)需要啟用或禁用。這對(duì)于調(diào)試和測(cè)試不同的功能場(chǎng)景非常有用。
3、如何安裝?
假設(shè)某一位讀者花了很大力氣根據(jù)自己的設(shè)備改寫(xiě)了baseflight的程序,而且這位讀者可能就是某個(gè)無(wú)人機(jī)廠商的創(chuàng)始人或者總師,需要把自己的產(chǎn)品(這些改進(jìn)就是你的產(chǎn)品)變現(xiàn)才能盈利,那么我們?cè)趺窗研薷暮蟮某绦颉白⑷搿憋w控里面。
Baseflight 主要運(yùn)行在基于 STM32 系列微控制器 (MCU) 的硬件平臺(tái)上。STM32 是一種廣泛應(yīng)用于無(wú)人機(jī)飛行控制器的 32 位 ARM Cortex-M 處理器,具有高性能和豐富的外設(shè)接口。具體來(lái)說(shuō),Baseflight 支持多種常見(jiàn)的飛控板,例如 Naze32、F3、CC3D 等 。
步驟 | 描述 | 操作 |
1 | 準(zhǔn)備工具和環(huán)境 | 下載并安裝 Betaflight Configurator 或 Baseflight Configurator。 |
確保電腦上已安裝相應(yīng)的驅(qū)動(dòng)程序。 | ||
2 | 連接飛控板 | 使用 ?USB 線將飛控板連接到電腦,確保連接穩(wěn)固,飛控板上的電源指示燈亮起。 |
3 | 進(jìn)入引導(dǎo)模式 | 某些飛控板需要按下并按住“Boot”按鈕,然后插入 USB 線。 |
具體操作請(qǐng)參考飛控板說(shuō)明書(shū)。 | ||
4 | 刷寫(xiě)固件 | 1.?打開(kāi) Betaflight Configurator 或 Baseflight ?Configurator。 |
2.?連接成功后,軟件會(huì)自動(dòng)檢測(cè)到飛控板。 | ||
3.?選擇最新的 ?Baseflight 固件文件(通常為 .bin 文件格式)。 | ||
4.?點(diǎn)擊“Flash ?Firmware”按鈕開(kāi)始刷寫(xiě)過(guò)程。 | ||
5.?刷寫(xiě)完成后,確保所有步驟都正確無(wú)誤,并等待提示確認(rèn)固件已成功安裝。 | ||
5 | 配置和校準(zhǔn) | 通過(guò)配置工具對(duì)飛控板進(jìn)行必要的設(shè)置和校準(zhǔn)。 |
包括陀螺儀、加速度計(jì)、磁羅盤(pán)等傳感器校準(zhǔn)。 | ||
PID 參數(shù)調(diào)整。 |
表2 安裝 Baseflight 固件到硬件中
那么固件哪來(lái)的?baseflight都是源文件啊。
步驟 | 描述 | 操作 |
1 | 獲取源代碼,加入你自己的修改 | 從 Baseflight 的官方 GitHub 倉(cāng)庫(kù)獲取最新版本的源代碼。 |
確保使用的是最新的穩(wěn)定版本或開(kāi)發(fā)分支。 | ||
2 | 配置硬件目標(biāo) | 根據(jù)使用的具體硬件平臺(tái)(例如 STM32F103C8),配置 Makefile 或其他構(gòu)建工具來(lái)指定目標(biāo)硬件。 |
確保生成的固件與硬件兼容。 | ||
3 | 編譯固件 | 使用 Makefile 或類(lèi)似的構(gòu)建工具來(lái)編譯源代碼。 |
通過(guò)命令行運(yùn)行 make 命令啟動(dòng)編譯過(guò)程。 | ||
Makefile 會(huì)調(diào)用編譯器、鏈接器等工具,生成 .bin ?文件。 | ||
4 | 生成二進(jìn)制文件 | 編譯完成后,生成的固件將以二進(jìn)制文件形式保存,通常是 ?.bin 文件格式。這個(gè)文件可以直接燒錄到 MCU 的閃存中運(yùn)行。 |
5 | 驗(yàn)證固件(避免把硬件燒成磚) | 在燒錄之前,建議對(duì)生成的 .bin 文件進(jìn)行驗(yàn)證。 |
可以通過(guò)檢查文件大小、校驗(yàn)和等方式進(jìn)行初步驗(yàn)證。 | ||
一些開(kāi)發(fā)者還會(huì)在固件中嵌入版本信息,以便在燒錄前進(jìn)行進(jìn)一步驗(yàn)證。 | ||
6 | 燒錄固件 | 使用適當(dāng)?shù)墓ぞ撸ㄈ?ST-Link、JTAG 等)將生成的 .bin 文件燒錄到飛控板的閃存中。 |
確保在燒錄時(shí)選擇正確的啟動(dòng)模式(例如,STM32F103C8 ?板有引導(dǎo)模式開(kāi)關(guān)),以便正確加載新固件。 |
表3 baseflight固件生成
特別注意,除非你改變了源工程的文件結(jié)構(gòu),否則不要改原先的makefile文件。