notes
ToolsFibril can

fibril_can CAN ルータ (uplink 1 + downlink N)

uplink CAN 1 本と downlink CAN N 本を束ねる同梱ランタイム。bridge は単一バス前提のまま動く。std_id → busmask で分配、node → bus で単一バス転送

要点: fibril_can_runtime には uplink 1 + downlink N を束ねるルータが同梱される。FCAN_ROUTER_ENABLE でガードされて通常スレーブビルドでは .text も RAM も増えない。master からは「1 本のバス」として透過的に見え、fibril_can_bridge は単一バス前提のまま動く。

デプロイ形態 3 択#

形態FCAN_ROUTER_ENABLEfcan_router_attach_self
通常スレーブ0 (既定)ルータコード自体なし
透過 router1NULL (自分の node_id なし、中継専用)
ノード兼 router1初期化済み fcan_node_t * (自分もスレーブとして動く)

有効化#

cmake -DFCAN_ROUTER_ENABLE=ON ...

FCAN_ROUTER_MAX_BUSES (既定 8) でマスク型幅がコンパイル時決定:

MAX_BUSESmask 型最大 downlink 数
≤ 8uint8_t6
≤ 16uint16_t14
≤ 32uint32_t30

分配の中核ロジック#

分配先ビット = stdid_to_busmask[std_id] & ~source_bus

master が「downlink A の publisher (S2M)」と「downlink B の subscriber (M2S)」に同じ std_can_id を FRAME_DEFINE すると、内部 busmask は UPLINK | A | B の 3 ビット。A から来たフレームは & ~A = UPLINK | B に自動分配。bridge は単一バス前提で同じ ID を振るだけ、cross-downlink Topic 中継はルータが勝手に成立させる。

バス識別子#

bus意味
FCAN_ROUTER_BUS_UPLINK (0xFE センチネル)uplink
0 .. downlink_count-1各 downlink の生インデックス
self内部擬似バス。caller から名指ししない (uplink 経由の ext_id.node_id で自動判別)

初期化 (self ノード使う場合、3 段)#

// 1) ルータ本体
fcan_router_config_t rcfg = {
    .uplink_hal = uplink_hal,
    .downlink_hals = downlink_hals,
    .limits = {
        .downlink_count = 2,
        .max_stdids = 1024,
        .max_node_ids = 128,
        .pending_fd_slots = 4,
    },
    .allocator = my_allocator,
};
fcan_router_init(router, &rcfg);

// 2) self 用ラッパ HAL を取って fcan_config_t に詰める
fcan_config_t ncfg = make_self_config();
ncfg.hal = fcan_router_get_self_hal(router);
fcan_init(self_node, &ncfg);

// 3) self を attach
fcan_router_attach_self(router, self_node);

fcan_router_get_self_hal を init 前に呼ぶと全 NULL の HAL が返る (誤用検知)。self を後から detach するには fcan_router_attach_self(router, NULL) で透過モードに戻せる。

動作中 API#

void fcan_router_on_rx(router, bus, id, ext, data, len);   // ISR / RX スレッド
void fcan_router_poll(router);                              // メインループ
void fcan_router_get_diag(router, &diag);                   // 任意

poll は self が attach されていれば fcan_poll(self) も駆動する。

学習ルール (永続化なし)#

  • node_to_bus: downlink から届く HEARTBEAT / ANNOUNCE で学習
    • 同 node_id が異なる downlink から異なる boot_id で観測 → cross-bus 衝突として先着優先、後発は転送抑制
    • 同 boot_id の再観測 (ループバック等) は良性扱いで最初の割当保持
  • stdid_to_busmask: FRAME_DEFINE の payload を peek し、対象 slave の ACK が届いた瞬間にビットを立てる。NAK は学習しない
  • DISCOVER 受信で stdid_to_busmask は全クリア (SPEC §5.11 の slave 側挙動と同期)
  • pending FRAME_DEFINE: ACK 待ちの一時表、既定 4 スロット。溢れると diag に計上

診断カウンタ (monotonic)#

fwd_up_to_down[k]       fwd_down_to_up[k]      fwd_cross_down[k]
drop_unknown_stdid      drop_unknown_node      drop_cross_bus_conflict
drop_table_overflow     drop_send_failed
learned_nodes           learned_stdids

ルータ自身は schema を持たないので、診断を Topic で吐きたければ self ノードの通常 Topic で fcan_router_get_diag の結果を publish する。

既知の制約#

  • Service は単一バス転送のみ: downlink 間サービスは master 経由でラップ
  • rx_queue_depth 経由の ISR ファストパス未実装: 同期転送のみ
  • self の S→M Topic は uplink にしか流れない: downlink にも届けたいなら master 側 bridge の FRAME_DEFINE 構成で表現

関連#

最終更新:

On this page