###### tags: `東京威力 TEL` {%hackmd DzaUykeiRfWOMkjFL_QkCQ %} # V. STM ## <font color = "orange">01. STM x ROS : *main.c* - *mainpp.cpp* - *mainpp.h*</font> ### <font color = "pink">1-1. 前言 </font> 這裡寫的是 [III. 3-3. STM端設定](/s/S13-14zRc?type=view#3-2-STM-端設定) 的編譯過程。 我們在 `main.c` 中 *include* `mainpp.h`,其中 `mainpp.h` 只宣告函式和宣告 *extern* 變數, `mainpp.cpp` 則是實作 `mainpp.h`。 按下 `F11` 組建後依時間線發生的事情如下。 :::success 過程:source file -> 預處理 -> 編譯 -> 目的檔 -> linker -> 執行檔案 -> 執行。 組建 (build) 包含預處理、編譯及 link。 ::: ### <font color = "pink">1-2. 前處理 - main.c</font> 副檔名 `.c` 或 `.cpp` 會直接決定要用 C 或 C++ 編譯器/前處理器。 首先是 `main.c`: ```cpp=17 ... /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "mainpp.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ ... ``` 前處理器看到 `#include "mainpp.h"` 後,會去讀 `mainpp.h`,然後把處理後的文字複製到 `main.c` 。 ```cpp= /* * mainpp.h * * Created on: 2018/01/17 * Author: yoneken */ #ifndef MAINPP_H_ // 1-1. 沒 define 過 MAINPP_H_ #define MAINPP_H_ // 1-2. define MAINPP_H_ #ifdef __cplusplus // 2-1. C 前處理器並沒有 define 過 __cplusplus extern "C" { #endif // 2-2. 直接跳到這行 void setup(void); // 3. 宣告函式(還沒實作定義) void loop(void); extern int count; // 4. 宣告 extern 變數 #ifdef __cplusplus // 5. 和 2-1.一樣 } #endif #endif /* MAINPP_H_ */ // 1-3. 結束 ``` 幾點補充說明: * 1-1 ~ 1-3 做的事情叫做 [Include/Header guard](https://blog.techbridge.cc/2017/09/30/cpp-header-guard/),避免有函式或變數 redefine。 * `__cplusplus` 是 C++ 前處理器的保留字,所以 C++ 處理器有 define 過而 C 沒有。 前處理器做完邏輯判斷後,會刪除判斷本身的句子,變成這樣: ```cpp= void setup(void); void loop(void); extern int count; ``` 然後編譯成目的檔 `.o`(Object file)。 ### <font color = "pink">1-3. 前處理 - mainpp.cpp</font> ```cpp= #include "mainpp.h" // C++ 前處理器讀到這行,會先去看那個標頭檔案。 ... void setup(void) { nh.initNode(); nh.subscribe(sub); } void loop(void) { nh.spinOnce(); } ``` 於是讀到 `mainpp.h`: ```cpp= /* * mainpp.h * * Created on: 2018/01/17 * Author: yoneken */ #ifndef MAINPP_H_ // 1-1. 還沒 define 過! #define MAINPP_H_ // 1-2. define MAINPP_H_ #ifdef __cplusplus // 2-1. 有 define 過了 extern "C" // 2-2. 用 C 前處理器處理括號裡的內容 { #endif void setup(void); // 3. (用 C 前處理器)宣告函式和變數 void loop(void); extern int count; #ifdef __cplusplus // 4-1. 和 2-1. 一樣,已 define 過。 } // 4-2. extern "C" 的右大括號 #endif #endif /* MAINPP_H_ */ // 1-3. 結束 ``` 幾個重點: * 每個 source file(`.c` / `.cpp` )是獨立的,有沒有 define 過 header guard 與其他檔案無關。 * `extern "C"` 是關鍵字,表示接下來會使用用 C 前處理器/編譯器。 * 因此 `setup()` 和 `loop()` 的前處理和編譯都是 C,這樣才能讓 main.c 去使用。 最後也是編譯成一個 `.o` 檔案。 ### <font color = "pink">1-4. Link & Run </font> link 就是把所有的目的檔都連結在一起,這時候所有的函式都應該有明確定義,接著產生執行檔。 ### <font color = "pink">1-5. 整合注意事項 </font> 這邊整理學長在 ROS 和 STM 溝通上的一些整合建議。 <font color = "yellow">1. 頻率</font>:ROS to STM = <font color = "red">30~50 hz</font>;STM to ROS = <font color = "red">50~100 hz</font>。 <font color = "yellow">2. Node數量 </font>:STM 上最好只有一個 NodeHandler,避免節點間傳送訊息打架。 <font color = "yellow">3. 變數共用</font>:假設 STM 要從 ROS 得到速度 vel,然後丟給馬達。比較好的方式是像下面這樣: ```cpp= /*** 在 main.c 中 ***/ double vel; /*** 在 mainpp.h 中 ***/ extern double vel; /*** 在 mainpp.cpp 中 ***/ vel = msg.data; // 直接使用 vel ``` 也就是 STM 聽到 ROS 給新速度時,會丟到 `vel`,然後另邊控馬達的程式就會順應改變。 <font color = "yellow">4. 派間通訊</font>:兩個派可以直接連乙太網路通訊。