99Lロケットのミッション基板用ファームウェアと基板情報です。
この基板は、離床後に機体ロール角速度を取得し、動翼を制御して角速度を一定に保ち、飛行終盤でパラシュート機構を開放することを目的にしています。現状のRust実装では、ESP32-S3上で各周辺機能を BoardResources に集約し、IMU、モータ、エンコーダ、SD、CAN、I2C、5V保護railを明示的に分割して扱います。
mission_board/
├── src/
│ ├── board.rs # GPIO番号、テスト定数、board module入口
│ ├── board/
│ │ ├── resources.rs # Peripheralsを用途別resourceへ分割
│ │ ├── devices.rs # SPI/Motor/Encoder/SDなどの初期化
│ │ └── power_manager.rs # 5V railとmotor command gateの型状態管理
│ ├── motor.rs # TB67H450 PWM motor wrapper
│ ├── air_data.rs # 空気密度・対気速度の高レベル計算
│ └── bin/main.rs # 通常firmware entry point
├── tests/
│ ├── motor.rs # motor open-loop実機テスト
│ ├── motor_encoder.rs # motor + encoder実機テスト
│ ├── motor_white_noise.rs # duty比white-noise入力とencoder応答の実機テスト
│ ├── motor_sine_wave.rs # sine duty入力とencoder応答の実機テスト
│ ├── encoder_ab_phase.rs # GPIO直読みのA/B相位相確認テスト
│ ├── data/motor_white_noise.csv # motor_white_noise用duty比入力
│ ├── icm42688_bench.rs # ICM42688 SPI/FIFO/DMAベンチ
│ ├── sd_card.rs # microSD SPI read/writeテスト
│ ├── air_data.rs # SSC/LPS22DF I2C実機テスト
│ └── can_self_test.rs # TWAI self reception実機テスト
└── libs/
├── air-data/
│ └── ssc-differential-pressure/ # Honeywell SSC用async I2C driver実装
└── motor/
├── encoder/ # PCNT encoder abstraction
└── motor-driver/ # motor driver trait/library
mission_board/libs は CREATE-ROCKET/Avi_Rust_Libs submoduleです。再利用する低レベルdriverは main firmware の mission_board/src ではなく、このsubmodule内に配置します。
| 名称 | 型番 | 用途 | 仕様書url |
|---|---|---|---|
| 差圧計 | SSCDRRN005PD2A5 | 対気速度計測 | https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/2157/ssc_series_DS.pdf |
| 気圧計 | LPS22DF | 気圧計測 | https://www.st.com/resource/en/datasheet/lps22df.pdf |
| I2C/CAN物理層変換 | LT3960JMSE#PBF | ミッション基板向け通信 | https://www.analog.com/LT3960/datasheet |
SSCDRRN005PD2A5は libs/air-data/ssc-differential-pressure のasync I2C driverで読みます。LPS22DFは lps22df-rs を使用し、SSC差圧driverとは別の低レベルdriverとして扱います。空気密度や対気速度の計算は mission_board/src/air_data.rs に置き、SSC driverには入れません。
GPIO番号は mission_board/src/board.rs の gpio_num を正とします。KiCad上のラベル名と実GPIO接続が一致しない箇所があるため、特にSD周辺はラベル名ではなくこの表を基準にします。
| 機能 | GPIO | 備考 |
|---|---|---|
| ICM MISO | GPIO35 | SPI2 |
| ICM MOSI | GPIO36 | SPI2 |
| ICM SCLK | GPIO37 | SPI2 |
| ICM CS | GPIO38 | SPI2 |
| 動翼モータ IN1 | GPIO15 | TB67H450 |
| 動翼モータ IN2 | GPIO16 | TB67H450 |
| パラシュートモータ IN1 | GPIO42 | TB67H450 |
| パラシュートモータ IN2 | GPIO41 | TB67H450 |
| 動翼エンコーダ A | GPIO17 | 74LCX14反転後入力 |
| 動翼エンコーダ B | GPIO18 | 74LCX14反転後入力 |
| パラシュートエンコーダ A | GPIO7 | 74LCX14反転後入力 |
| パラシュートエンコーダ B | GPIO6 | 74LCX14反転後入力 |
| CAN RX | GPIO14 | TWAI0 |
| CAN TX | GPIO21 | TWAI0 |
| I2C SDA | GPIO47 | LT3960向け |
| I2C SCL | GPIO48 | LT3960向け |
| SD DAT1 | GPIO3 | SPI modeではpull-up保持 |
| SD DAT0 / MISO | GPIO9 | SPI3 |
| SD CLK | GPIO10 | SPI3 |
| SD CMD / MOSI | GPIO11 | SPI3 |
| SD DAT3 / CS | GPIO12 | SPI3 |
| SD DAT2 | GPIO13 | SPI modeではpull-up保持 |
| 5V EN | GPIO46 | TCKE800 EN/UVLO。起動時Low |
| External input | GPIO8 | 電源・状態入力系 |
| Motor fallback | GPIO1 | 予備/保護系 |
| LED D3 | GPIO39 | LED |
| LED D6 | GPIO40 | LED |
| LED D7 | GPIO2 | LED |
| BOOT IO0 | GPIO0 | 不用意にoutput化しない |
BoardResources::split(peripherals) でESP32-S3のperipheralとGPIOを用途別に分割します。
主なresource:
| resource | 内容 |
|---|---|
SystemResources |
TIMG0, software interrupt, CPU control |
IcmSpiResources |
SPI2, DMA_CH0, ICM用GPIO |
MotorResources |
MCPWM0, 動翼/パラシュートTB67H450入力 |
EncoderResources |
PCNT, 動翼/パラシュートA/B相 |
RemoteI2cResources |
I2C0, LT3960向けSDA/SCL |
CanResources |
TWAI0, CAN RX/TX |
SdResources |
SPI3, microSD関連GPIO |
PowerResources |
5V EN、電源状態/予備GPIO |
LedResources |
D3/D6/D7 |
DebugResources |
BOOT GPIO0 |
5V保護railは ShortProtected5vRail<RailDisabled> / ShortProtected5vRail<RailEnabled> の型状態で扱います。起動時はGPIO46をLowにして +5V_short を停止し、enable後に10 ms待ちます。通常の5V offは PowerManager と RailGated5vDevices を経由し、motor、encoder guard、remote I2C guardを同じ5V domain bundleとして扱います。
RailGated5vDevices<State, EncoderGuard, I2cGuard> は意図的にdefault guard型を持ちません。通常firmwareではencoderとremote I2Cに対して明示的なrail guardを渡す必要があり、guard指定漏れはcompile errorになります。NoRailOffGuardForTest は hardware-test feature下のmotor単体実機テスト専用で、encoder/I2Cが初期化されていない場合だけ使います。実deviceが存在する経路では使用しません。
通常停止の順序:
- encoder guardへrail off準備を通知
- remote I2C guardへrail off準備を通知
- 全motorをcoast
- encoder guardの完了待ち
- remote I2C guardの完了待ち
- 1 ms settle
- GPIO46をLow
- encoder/remote I2Cをrail off中の使用不可状態へ遷移
- motor/device bundleを
RailDisabled状態で返す
emergency offはmotor coast、encoder stop request、remote I2C stop requestを試みますが、通常停止のような完了待ちは行わず、直ちにGPIO46 Lowを試みます。rail disableが失敗した場合は、error内にまだ RailEnabled の PowerManager と RailGated5vDevices が残るため、呼び出し側は再試行またはdevice状態の確認・制御を続行できます。これは旧来のmotor-only PowerManager::disable_normal / enable_with_existing_motors からの破壊的API変更です。
remote I2Cは RemoteI2cResources のI2C0/GPIO47/GPIO48を使い、board-level初期化で400 kHzに設定します。portableなSSC sensor driver APIにはDMAを公開しません。ESP HAL内部やboard初期化側の実装詳細としてDMA等を使う場合でも、sensor driverの公開APIからは隠します。
注意点:
- 現状の基板ではpower-good/faultがMCUへ入っていないため、実際の5V成立やshort faultをsoftwareだけで完全検出することはできません。
- railを落とす通常経路では、
PowerManager::disable_normalがencoder/I2C停止要求、motor coast、完了待ち、GPIO46 Lowの順序を所有権で固定します。 - motor driveは
RailGatedMotors<RailEnabled>のときだけ許可され、5V rail off中は受理されません。 - encoder、I2Cは5V rail有効時のみ使用する前提です。5V offへ参加する実装は
RailOffGuardで停止要求と完了待ちを表現します。 - encoder実機テストは
EncoderRailOffGuardでPCNT encoderのrail状態を保持し、rail off中のencoder accessを拒否します。 - remote I2Cが5V domainに参加する場合は
RemoteI2cRailOffGuardを使います。air-data実機テストもraw I2C busではなくこのguardを共有します。
flowchart LR
ICM[ICM42688<br/>加速度・角速度取得]
AIRBOARD[差圧計基板<br/>差圧・気圧取得]
LT[LT3960<br/>I2C/CAN物理層変換]
ENC[エンコーダ A/B相]
IMU_CORR[IMU補正・推定<br/>フィルタ・角加速度推定]
AIR_CORR[空力補正・推定<br/>対気速度推定]
ENC_EST[エンコーダ計算<br/>A/B相decode・角度/角速度]
OMEGA[機体ロール角速度]
ALPHA[機体ロール角加速度]
AIRSPEED[対気速度]
FIN_ANGLE[動翼角度]
FIN_RATE[動翼角速度]
CTRL[ロール制御器<br/>目標動翼角度を計算]
FIN_CTRL[動翼角度制御器<br/>PWM duty/方向を計算]
DRIVER[TB67H450]
MOTOR[動翼モータ]
FIN[動翼]
ICM --> IMU_CORR
IMU_CORR --> OMEGA
IMU_CORR --> ALPHA
AIRBOARD --> LT --> AIR_CORR --> AIRSPEED
ENC --> ENC_EST
ENC_EST --> FIN_ANGLE
ENC_EST --> FIN_RATE
OMEGA --> CTRL
ALPHA --> CTRL
AIRSPEED --> CTRL
FIN_ANGLE --> CTRL
FIN_RATE --> CTRL
CTRL --> FIN_CTRL
FIN_CTRL --> DRIVER --> MOTOR --> FIN --> ENC
現状は、周辺機能の初期化・低レベルdriver・実機テストの整備が中心です。飛行用の閉ループ制御則、離床検知、パラシュート開放sequencingは今後の実装対象です。
Rust 2024 edition、no_std、esp-hal を使用します。
mission_board/Cargo.toml の主な設定:
rust-version = "1.88"default-run = "mission_board"- target MCU: ESP32-S3
- hardware testは
hardware-testfeatureで明示的に有効化 encodercrateはhardware test時のみ有効化
このfirmwareではPSRAMを使用しない前提です。ESP32-S3-WROOM-1 N16R8などのPSRAM搭載モジュールを使う場合でも、PSRAMを有効化しないでください。
理由:
- ICM42688やmicroSDでSPI/DMAを高頻度に使用するため、PSRAM有効化に伴う外部memory、cache、bus accessの影響を避けます。
- SPI DMAへ渡すbufferは内部SRAM上に置く必要があります。PSRAM上のbufferをDMA transferに使用しないでください。
- 飛行中の制御loopとloggingでは、外部memory accessによるlatency変動を避けます。
- PSRAMを使うcrate feature、allocator、bootloader設定を追加する場合は、ICM、SD、制御周期、loggingの実機再検証が必要です。
運用上の注意:
esp-hal、esp-alloc、bootloader、linker設定でPSRAM heapを有効化しないでください。- DMA用bufferは
esp_hal::dma_buffers!などを用いて内部SRAMに確保してください。 - 大きなlogging bufferや計測bufferを追加する場合も、PSRAMへ逃がさず、内部SRAM使用量とstack使用量を確認してください。
通常確認:
cd mission_board
cargo check --bin mission_board
cargo fmt書き込み/実行は .cargo/config.toml のrunner設定に従います。
すべての実機テストは、誤ってmotor等を動かさないよう hardware-test featureが必須です。
cd mission_board
cargo test --features hardware-test --test motor内容:
- 5V railをenable
- motor初期化後にcoast
- 通常経路で5V railをdisable
- 5V rail off中のdrive commandが
RailOffで拒否されることを確認 - 5V railを再enable
- パラシュートモータを
0からMOTOR_TEST_MAX_DUTY_PERMILLEまでramp - パラシュートモータをcoast
- 動翼モータを同様にramp
- 動翼モータをcoast
- 繰り返し
主な定数:
| 定数 | 値 |
|---|---|
MOTOR_TEST_MAX_DUTY_PERMILLE |
1000 |
MOTOR_TEST_DUTY_STEP_PERMILLE |
10 |
MOTOR_TEST_DUTY_STEP_INTERVAL_MS |
10 ms |
MOTOR_TEST_HOLD_MS |
500 ms |
MOTOR_TEST_COAST_MS |
500 ms |
cd mission_board
cargo test --features hardware-test --test motor_encoder内容:
- 5V railをenable
- PCNTでA/B相を読む
- 各duty stepで
count,delta,total_deltaをserial出力 - パラシュート、動翼の順で確認
PCNT割り当て:
| 対象 | PCNT unit | A | B |
|---|---|---|---|
| パラシュートエンコーダ | unit0 | GPIO7 | GPIO6 |
| 動翼エンコーダ | unit1 | GPIO17 | GPIO18 |
cd mission_board
cargo test --features hardware-test --test motor_white_noise目的:
tests/data/motor_white_noise.csvをinclude_str!で読み込み、起動直後に固定長配列へパースする- 10 kHz周期でduty比を変更し、その直後のencoder countを記録する
- パラシュートモータ、動翼モータの順に各10000 sampleを取得する
- 測定中はserial出力せず、測定後にCSV形式でUARTへ出力する
出力形式:
motor,index,tick_us,duty_permille,encoder_count
parachute,0,0,120,0
moving_fin,0,0,120,123主な定数:
| 定数 | 値 |
|---|---|
SAMPLE_COUNT |
10000 |
SAMPLE_INTERVAL_US |
100 us |
WHITE_NOISE_MAX_ABS_DUTY_PERMILLE |
300 |
注意: motor_white_noise.csv の値はpermille単位です。入力値が上限を超えた場合は、実機保護のためテスト側でclampします。
cd mission_board
cargo test --features hardware-test --test motor_sine_wave目的:
- sine波のduty比を1 kHz周期で与え、その直後のencoder countを記録する
- パラシュートモータ、動翼モータの順に各20000 sampleを取得する
- 測定後にraw sample CSVとsummary CSVをUARTへ出力する
- duty符号とencoder delta符号の整合、overrun countを確認する
出力形式:
kind,motor,index,tick_us,phase_millirad,duty_permille,interval_duty_permille,encoder_count,delta_count,velocity_count_per_s,delta_valid
raw,parachute,0,0,0,0,0,0,0,0,0
kind,motor,samples,overrun_count,initial_count,final_count,total_delta,positive_interval_samples,positive_delta_sum,negative_interval_samples,negative_delta_sum,neutral_interval_samples,zero_delta_samples,nonzero_delta_samples,same_sign_nonzero_samples,opposite_sign_nonzero_samples
summary,parachute,20000,0,0,123,123,9900,120,9900,-118,199,100,19900,19800,100主な定数:
| 定数 | 値 |
|---|---|
SAMPLE_COUNT |
20000 |
SAMPLE_INTERVAL_US |
1000 us |
SINE_PERIOD_SAMPLES |
2000 |
SINE_DUTY_AMPLITUDE_PERMILLE |
300 |
DIRECTION_CHECK_DUTY_THRESHOLD_PERMILLE |
50 |
cd mission_board
cargo test --features hardware-test --test encoder_ab_phase目的:
- PCNT decoderを使わず、GPIO入力としてA/B相を直接読む
- 正負duty入力時のA/B相順序、invalid transition、edge imbalanceを確認する
- パラシュートモータ、動翼モータの順に測定する
- 測定後にraw sample CSVとsummary CSVをUARTへ出力する
出力形式:
kind,motor,index,tick_us,duty_permille,a,b,state,transition_delta,software_count,invalid_transition,a_edge_count,b_edge_count,edge_imbalance,edge_imbalance_fault
raw,parachute,0,0,0,0,0,0,0,0,0,0,0,0,0
kind,motor,samples,overrun_count,initial_state,final_state,transition_samples,no_change_samples,invalid_transition_samples,forward_steps,reverse_steps,software_count,positive_duty_software_count,negative_duty_software_count,positive_duty_invalid_transitions,negative_duty_invalid_transitions,a_edge_count,b_edge_count,final_edge_imbalance,max_abs_edge_imbalance,edge_imbalance_fault_samples
summary,parachute,7500,0,0,1,1000,6499,0,600,400,200,300,-100,0,0,500,500,0,1,0主な定数:
| 定数 | 値 |
|---|---|
SAMPLE_INTERVAL_US |
1000 us |
COAST_SAMPLES |
500 |
DRIVE_SAMPLES |
3000 |
TEST_DUTY_PERMILLE |
500 |
SAMPLE_COUNT |
7500 |
cd mission_board
cargo test --features hardware-test --test icm42688_bench目的:
- ICM42688とのSPI raw transaction速度を測定
- FIFO経由でgyro/accel/tempを読める頻度を確認
- DMA利用時のFIFO read性能を比較
- 20 kHz級samplingに対して、実際にどこまで読めるかを把握
試験段階:
RawSpiRead: WHO_AM_I readを繰り返すRawSpiWrite: bank select writeを繰り返すRawSpiMixed: read/write混合FifoReadSample:icm426xxcrateのread_sample()でFIFO sample取得FifoBurstRaw: FIFOをraw burst readDmaFifoReadSample: DMA SPIでread_sample()DmaFifoBurstRaw: DMA SPIでFIFO raw burst read
主な定数:
| 定数 | 値 |
|---|---|
ICM_SPI_FREQUENCY_HZ |
1 MHz |
ICM_SPI_MAX_FREQUENCY_HZ |
24 MHz |
ICM_BENCH_TARGET_SAMPLE_HZ |
20 kHz |
ICM_BENCH_WINDOW_MS |
1000 ms |
ICM_BENCH_RAW_TRANSACTION_WINDOW_MS |
500 ms |
ICM_WHO_AM_I_EXPECTED |
0x47 |
ICM_BENCH_SPI_FREQUENCIES_HZ |
1, 4, 8, 12, 16, 20, 24 MHz |
出力はstageごとにtransaction/s、bytes/s、sample/s、error、mismatch、FIFO overflow、最後に読んだgyro/accel/temp等をserialへ出します。
通常firmwareのIMU taskでは ImuError::recovery_action() でread errorを分類します。
- SPI bus errorは一時的な通信失敗としてbackoff後にretryし、FIFO resetは行いません。
- FIFO overflowはFIFO状態の復旧が必要なため、FIFO reset後にbackoffします。
- WHO_AM_I mismatchなどdevice identity errorはfatalとしてfault backoff loopへ入ります。
Ok(None)は空pollとして扱い、error countやFIFO resetの対象にしません。
ImuStats はsample数、empty poll数、read error総数に加えて、bus/FIFO/fatal read error数、FIFO reset成功数、FIFO reset失敗数を保持します。現在の icm426xx 0.4.0 のread errorは Bus、WhoAmIMismatch、FifoOverflow の3種類だけなので、未到達の分類variantは定義していません。
cd mission_board
cargo test --features hardware-test --test sd_card目的:
- SD SPI modeでカード初期化できるか確認
- 4 KiB pattern write/read検証
- 1 MiB append stress write/read検証
- checksum、mismatch、latency、throughputをserial出力
主な定数:
| 定数 | 値 |
|---|---|
SD_SPI_INIT_FREQUENCY_HZ |
400 kHz |
SD_SPI_TEST_FREQUENCIES_HZ |
400 kHz, 1 MHz, 4 MHz, 10 MHz, 20 MHz |
SD_TEST_PATTERN_BYTES |
4096 bytes |
SD_TEST_STRESS_BYTES |
1 MiB |
SD_TEST_CHUNK_BYTES |
512 bytes |
SD_TEST_FILE_NAME |
99LTEST.BIN |
SD_TEST_STRESS_FILE_NAME |
99LSTRS.BIN |
注意:
- テストはSDカード上に
99LTEST.BINと99LSTRS.BINを作成/上書きします。 - SDはSPI modeで扱います。DAT1/DAT2はpull-up保持用に入力化します。
- 現在のテスト実装では、初期化後の主試験周波数は10 MHzです。最終目標周波数の上限は20 MHzとして検証します。
cd mission_board
cargo test --features hardware-test --test air_data目的:
- 5V railをenableしてからremote I2Cを400 kHzで初期化
- SSCDRRN005PD2A5差圧センサを1 kHzでpolling
- LPS22DF気圧計を
lps22df-rs経由で100 Hz polling - 差圧、静圧、気温から空気密度と対気速度を計算
- stale data、command mode、diagnostic fault、I2C error、out-of-rangeをserial出力で集計
注意:
- I2C sensorは自発送信しないため、ESP32-S3側がpollingします。
- SSCの
StaleDataは新鮮な測定値として扱わず、明示的にcountします。 - SSCの4 byte frameには温度出力byteがありますが、SSCDRRN005PD2A5の補償済み温度としては実機確認まで使いません。
- portableなSSC driver APIにはDMAを公開しません。
cd mission_board
cargo test --features hardware-test --test can_self_test目的:
- ESP32-S3 TWAI0をself-test/self-reception modeで初期化
- 標準ID
0x7ff、payload99Lのself-reception frameを送信 - 同じframeをRX FIFOから受信できることを確認
このtestで確認しないもの:
- MCP2562
- CANH/CANL wiring
- 終端抵抗
- 外部nodeの存在
- ACK応答
- 外部CAN busの実通信状態
- モータ系テストを実行する前に、機体・治具・ケーブルが動翼やパラシュート機構に巻き込まれない状態にしてください。
hardware-testfeature付きのtestは実機を動かします。CIや通常確認では使わないでください。- 5V railはshort protection用ですが、softwareだけで故障状態を完全判定できません。実験時は外部電源の電流制限を使用してください。
- GPIO0はboot pinです。不用意にoutputへ変更しないでください。
- SDテストはカード内容を書き換えます。飛行ログ入りカードで実行しないでください。
- 離床検知
- 飛行sequence管理
- 動翼の原点合わせとstatic保存
- ロール角速度一定化の閉ループ制御
- パラシュート開放sequence
- SDへの飛行ログ形式確定
- CAN外部bus/ACK確認test
- rail-off guardを飛行用sequence/task管理へ統合
- IMU実測ログに基づくfilter設計