Tổng hợp các câu hỏi phỏng vấn phổ biến cho vị trí Embedded Software Engineer, từ kiến thức chuyên môn đến kỹ năng mềm. Bí quyết "chinh phục" nhà tuyển dụng.

"Giải mã" Phỏng Vấn Embedded Software Engineer: Điều gì nhà tuyển dụng tìm kiếm?

Vị trí Embedded Software Engineer không đơn thuần chỉ là “lập trình nhúng”. Đây là vai trò cầu nối giữa phần cứng và phần mềm, nơi bạn phải thiết kế, phát triển, và tối ưu hóa phần mềm chạy trên các thiết bị có tài nguyên hạn chế (như vi điều khiển, hệ thống IoT, thiết bị công nghiệp,...).

Nhà tuyển dụng thường tìm kiếm những ứng viên có:

  • Kiến thức vững chắc về hệ thống nhúng: Bao gồm hiểu biết về vi điều khiển (MCU), bộ nhớ (RAM, Flash), giao tiếp ngoại vi (SPI, I2C, UART,...), và các chuẩn truyền thông (CAN, USB, Ethernet).

  • Kỹ năng lập trình mạnh mẽ với C/C++: Đây là hai ngôn ngữ "cốt lõi" cho Embedded Systems.

  • Hiểu biết về RTOS: Khả năng quản lý task, thread, semaphore, mutex giúp xây dựng hệ thống hiệu quả và đáng tin cậy.

  • Tư duy logic và giải quyết vấn đề tốt: Bạn sẽ thường xuyên phải debug, tối ưu code, và xử lý sự cố trong môi trường hạn chế tài nguyên.

  • Khả năng làm việc nhóm: Vì dự án nhúng thường đòi hỏi phối hợp chặt chẽ với đội ngũ phần cứng, QA, và các bên liên quan.

>> Xem thêm: Embedded Software Engineer là gì? Lộ trình trở thành Embedded Software Engineer

TOP câu hỏi phỏng vấn Embedded Software Engineer

Kiến thức về hệ thống nhúng:

1. Hệ thống nhúng là gì? Kiến trúc gồm những gì?

Hệ thống nhúng (Embedded System) là hệ thống máy tính chuyên dụng được nhúng trong thiết bị để thực hiện một hoặc vài chức năng nhất định.
Kiến trúc cơ bản gồm:

  • MCU (vi điều khiển): bộ xử lý trung tâm.

  • Bộ nhớ: thường bao gồm Flash (chứa chương trình) và RAM (lưu dữ liệu tạm).

  • Thiết bị ngoại vi: như UART, SPI, I2C, GPIO để giao tiếp với cảm biến, actuator,...

2. Phân biệt Real-time và Non-real-time System:

  • Real-time: yêu cầu phản hồi trong một khoảng thời gian xác định. Ví dụ: hệ thống phanh ABS.

  • Non-real-time: không có yêu cầu chặt về thời gian phản hồi, ví dụ như hệ thống giải trí trên xe.

3. RTOS là gì? Giải thích task, thread, semaphore, mutex:

RTOS (Real-Time Operating System) là hệ điều hành thiết kế cho các hệ thống yêu cầu thời gian phản hồi chính xác.

  • Task/Thread: luồng thực thi riêng biệt.

  • Semaphore: đồng bộ hóa giữa các task hoặc bảo vệ tài nguyên.

  • Mutex: giống semaphore nhưng có tính sở hữu – chỉ task lock được mới unlock được.
    → Giúp lập trình song song an toàn và hiệu quả.

Lập trình C/C++:

4. Giải thích về con trỏ và quản lý bộ nhớ:

Con trỏ là biến lưu địa chỉ ô nhớ. Trong embedded, con trỏ cực kỳ quan trọng để truy cập thanh ghi hoặc vùng nhớ ngoại vi. Quản lý bộ nhớ thủ công giúp tránh tràn bộ nhớ hoặc leak.

5. Phân biệt volatile, static, const:

  • volatile: báo cho compiler biết giá trị có thể thay đổi ngoài ý muốn (như thanh ghi).

  • static: giữ giá trị giữa các lần gọi hàm.

  • const: không cho phép thay đổi giá trị sau khi gán.

6. Hàm đảo chuỗi không dùng thư viện:

void reverse(char *str) {
    int len = 0;
    while (str[len] != '\0') len++;
    for (int i = 0; i < len / 2; i++) {
        char tmp = str[i];
        str[i] = str[len - 1 - i];
        str[len - 1 - i] = tmp;
    }
}

7. Cách tối ưu thuật toán để tiết kiệm RAM:

  • Sử dụng biến cục bộ thay vì toàn cục.

  • Dùng cấu trúc dữ liệu tĩnh (mảng) nếu kích thước cố định.

  • Ưu tiên phép toán bit thay cho nhân/chia.

  • Tránh cấp phát động nếu không cần thiết.

Giao tiếp ngoại vi:

8. Phân biệt UART, SPI, I2C, CAN:

  • UART: truyền dữ liệu nối tiếp không đồng bộ, 2 dây (TX/RX).

  • SPI: giao tiếp đồng bộ, tốc độ cao, 4 dây.

  • I2C: 2 dây (SDA, SCL), địa chỉ hoá thiết bị.

  • CAN: mạng nhiều thiết bị, dùng trong ô tô, giao tiếp theo khung.

9. Cách khởi tạo UART trên STM32 (ví dụ HAL):

UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void) {
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  HAL_UART_Init(&huart1);
}

Giao tiếp cảm biến qua I2C:

Gửi địa chỉ thiết bị → gửi command → đọc dữ liệu trả về. Dùng HAL_I2C_Master_TransmitHAL_I2C_Master_Receive.

11. USB host vs. device là gì?

  • Host: cấp nguồn và điều khiển (PC).

  • Device: nhận lệnh từ host (chuột, USB).
    Trong embedded, đôi khi vi điều khiển có thể đóng vai OTG (On-The-Go) – vừa là host, vừa là device.

Debugging & Testing:

12. Cách sử dụng GDB/JTAG để debug:

GDB dùng để debug trên phần mềm; JTAG dùng kết hợp với debugger hardware (ST-Link, J-Link...) để đặt breakpoint, xem thanh ghi, bước qua code trực tiếp trên thiết bị thật.

13. Phân biệt unit, integration, system test:

  • Unit test: kiểm tra từng module/hàm riêng.

  • Integration test: kiểm tra sự phối hợp giữa các module.

  • System test: kiểm tra toàn bộ hệ thống vận hành.

14. Làm sao để phát hiện và xử lý memory leak:

Dùng công cụ như Valgrind (nếu môi trường hỗ trợ) hoặc viết code để track malloc/free. Cẩn thận khi dùng con trỏ và cấp phát động trong embedded.

15. Cách tối ưu hiệu suất chương trình embedded:

  • Tránh dùng hàm blocking.

  • Ưu tiên dùng interrupt.

  • Tối ưu vòng lặp, thuật toán.

  • Sử dụng profiler để xác định "nút thắt".

Kiến thức về phần cứng:

16. Vi xử lý vs. vi điều khiển là gì?

  • Vi xử lý (CPU): thường chỉ xử lý, cần chip ngoại vi hỗ trợ (RAM, ROM, I/O).

  • Vi điều khiển (MCU): tích hợp sẵn RAM, ROM, I/O → nhỏ gọn, dùng cho hệ nhúng.

17. Nguyên lý hoạt động của transistor, diode, điện trở:

  • Transistor: khuếch đại, chuyển mạch.

  • Diode: cho dòng đi một chiều.

  • Điện trở: giới hạn dòng điện.

18. Cách đọc sơ đồ mạch:

Hiểu ký hiệu linh kiện → mạch nguồn → MCU → các thiết bị ngoại vi. Theo dõi logic tín hiệu và luồng điện.

19. Bus và mapping bộ nhớ là gì?

  • Bus: đường truyền tín hiệu (data, address, control).

  • Memory mapping: ánh xạ địa chỉ vùng nhớ vào thanh ghi/tài nguyên của thiết bị (ví dụ: 0x40021000 là thanh ghi RCC trên STM32).

Bí quyết "chinh phục" nhà tuyển dụng:

Chuẩn bị kỹ lưỡng:

  • Ôn tập kiến thức chuyên môn bằng cách làm mini project hoặc giải bài tập trên nền tảng như LeetCode (cho C/C++).

  • Tìm hiểu về công ty (sản phẩm, công nghệ sử dụng, văn hóa làm việc).

  • Luyện tập trả lời phỏng vấn qua mock interview hoặc với bạn bè.

  • Chuẩn bị câu hỏi cho nhà tuyển dụng, ví dụ: “Team hiện tại sử dụng RTOS nào?”, “Các tiêu chí đánh giá hiệu quả phần mềm là gì?”

Thể hiện sự tự tin và chuyên nghiệp:

  • Đến đúng giờ, ăn mặc gọn gàng, phù hợp với văn hóa công ty.

  • Giao tiếp rõ ràng, mạch lạc, không trả lời lan man.

  • Dẫn chứng cụ thể: Ví dụ bạn từng xử lý một bug khó trong SPI communication như thế nào.

Thể hiện đam mê và tinh thần học hỏi:

  • Chia sẻ dự án cá nhân: Ví dụ tự làm thiết bị đo nhiệt độ và kết nối cloud.

  • Đóng góp open-source hoặc tham gia diễn đàn như Reddit, StackOverflow, các group Embedded trên Facebook, Zalo,...

  • Đặt câu hỏi chất lượng, thể hiện bạn nghiêm túc với ngành, ví dụ: “Công ty có lộ trình đào tạo nhân sự mới như thế nào?”, “Có được access vào thiết bị thật để test không?”

Tổng kết:

Phỏng vấn Embedded Software Engineer là một "cuộc chơi" không chỉ kiểm tra kiến thức chuyên môn mà còn đòi hỏi bạn thể hiện tư duy, kỹ năng mềm và đam mê với lĩnh vực nhúng. Với sự chuẩn bị kỹ lưỡng, bạn hoàn toàn có thể "vượt ải" phỏng vấn và ghi điểm trong mắt nhà tuyển dụng.

VietnamWorks inTECH