Chương 5. Môi trường phát triển và chương trình đầu tiên
Mục tiêu
- Cài trình biên dịch C++ cho PC (host) và biết vai trò của toolchain chéo (cross) cho MCU.
- Biên dịch bằng dòng lệnh và bằng CMake.
- Kiểm tra được chuẩn C++ và trình biên dịch đang dùng.
Câu chuyện: hai máy, hai trình biên dịch
Firmware được biên dịch trên PC nhưng chạy trên MCU. Vì vậy bạn dùng hai loại toolchain:
| Loại | Ví dụ | Dùng để |
|---|---|---|
| Host (native) | g++, clang++, MSVC |
Chạy ví dụ và unit test ngay trên PC |
| Cross | arm-none-eabi-g++, avr-g++, riscv-none-elf-g++ |
Tạo file .elf cho chip |
Chiến lược xuyên suốt sách: viết logic độc lập phần cứng, chạy và test trên host; chỉ lớp mỏng chạm thanh ghi mới cần chip thật.
Kiến thức
Cài trình biên dịch host
| Hệ điều hành | Cách cài |
|---|---|
| Windows | MSYS2 (https://www.msys2.org) rồi trong shell MinGW-w64: pacman -S --needed mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-gdb |
| Ubuntu/Debian | sudo apt install build-essential cmake gdb |
| macOS | xcode-select --install hoặc brew install gcc cmake |
Kiểm tra:
g++ --version
cmake --version
Toolchain chéo cho MCU
- ARM Cortex-M: gói Arm GNU Toolchain (
arm-none-eabi-*), có cho Windows/Linux/macOS trên trang của Arm. - AVR:
avr-gcc(kèm Arduino IDE hoặcapt install gcc-avr). - RISC-V:
riscv-none-elf-gcctừ xPack hoặc nhà sản xuất chip. - Nhiều hãng cung cấp IDE đóng gói sẵn (STM32CubeIDE, ESP-IDF, PlatformIO); phần dưới của sách dùng dòng lệnh + CMake để bạn hiểu điều IDE đang làm.
Cờ biên dịch nên có
| Cờ | Tác dụng |
|---|---|
-std=c++20 |
Chọn chuẩn ngôn ngữ |
-Wall -Wextra |
Bật cảnh báo hữu ích |
-Os / -O2 |
Tối ưu kích thước / tốc độ |
-fno-exceptions -fno-rtti |
Bỏ tính năng đắt (chỉ khi cần) |
-ffunction-sections -fdata-sections + -Wl,--gc-sections |
Loại code không dùng |
Ví dụ: kiểm tra môi trường
#include <cstdio>
int main() {
std::printf("__cplusplus = %ldL\n", static_cast<long>(__cplusplus));
#if defined(__clang__)
std::printf("compiler = clang %d.%d\n", __clang_major__, __clang_minor__);
#elif defined(__GNUC__)
std::printf("compiler = gcc %d.%d\n", __GNUC__, __GNUC_MINOR__);
#elif defined(_MSC_VER)
std::printf("compiler = msvc %d\n", _MSC_VER);
#endif
return 0;
}
Cách 1: dòng lệnh.
cd code/chapter-05
g++ -std=c++20 -Wall -Wextra -o check_env main.cpp && ./check_env
Cách 2: CMake — cấu hình dùng lại được cho cả host lẫn chip.
cmake_minimum_required(VERSION 3.20)
project(chapter04 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(check_env main.cpp)
target_compile_options(check_env PRIVATE -Wall -Wextra)
cmake -S . -B build
cmake --build build
./build/check_env
Kết quả:
__cplusplus = 202002L
compiler = gcc 14.4
Nếu __cplusplus in ra 202002L là bạn đang ở C++20. Nếu ra 201703L, bạn đang ở C++17 và cần kiểm tra cờ -std=.
Góc nhúng
- Với toolchain chéo, CMake cần một toolchain file khai báo compiler, cờ CPU (
-mcpu=cortex-m4 -mthumb), linker script. Phần 6 xây một file như vậy. - Nên gắn cùng
CMAKE_CXX_STANDARDcho cả host và target để code test trên PC không khác code chạy trên chip. - Đừng chỉ tin trình biên dịch trên PC: chạy thêm một lần build bằng toolchain chéo trong CI để bắt lỗi khác biệt (kiểu
int16-bit trên AVR, ví dụ).
Lỗi thường gặp
g++: command not foundsau khi cài MSYS2: bạn chưa mở đúng shell MinGW-w64 hoặc chưa thêm thư mụcbinvàoPATH.- Lẫn
gccvàg++:gccbiên dịch file.cppnhưng không tự liên kết thư viện chuẩn C++ trong mọi trường hợp; dùngg++. - Quên
-std=c++20rồi dùngstd::span— lỗi "not a member of std". - Chạy
cmake --buildrồi sửa CMakeLists nhưng không cấu hình lại: xoá thư mụcbuild.
Bài tập
- Cài toolchain host, chạy
check_envvà ghi lại phiên bản compiler +__cplusplus. - Đổi
CMAKE_CXX_STANDARDsang 17 rồi 23. Giá trị__cplusplusthay đổi thế nào? - Cài
arm-none-eabi-g++. Chạyarm-none-eabi-g++ --versionvà so sánh vớig++. - Biên dịch
main.cppvới-Wall -Wextra -Wpedantic. Có cảnh báo mới nào không?
Tóm tắt
- Hai loại toolchain: host để chạy/test, cross để tạo firmware.
- Luôn ghi rõ
-std=và bật-Wall -Wextra. - CMake giúp dùng chung cấu hình giữa host và target.
- Xác nhận môi trường bằng
__cplusplustrước khi viết code thật.