Skip to content

MCU trong hệ thống Linux

Trang này trình bày cách tích hợp MCU vào một hệ thống chạy Linux, ở góc độ thiết kế board và phần mềm hệ thống. Trang lần lượt so sánh hai mô hình tích hợp, trình bày mô hình MCU nằm ngoài SoC, mô hình lõi MCU nằm trong SoC, và cuối cùng là cách cập nhật firmware MCU từ phía Linux. Cách viết driver cho các framework được nhắc tới thuộc tab Linux Kernel.

Hai mô hình tích hợp

MCU có thể là một chip riêng trên board, nối với SoC qua bus ngoại vi, hoặc là một lõi Cortex-M nằm cùng die với các lõi Cortex-A chạy Linux. Hai mô hình khác nhau ở kênh giao tiếp, cách nạp firmware và mức độ độc lập của MCU.

Tiêu chí MCU ngoài SoC Lõi MCU trong SoC
Kênh giao tiếp UART, I2C, SPI, USB Bộ nhớ chia sẻ và mailbox phần cứng
Nạp firmware Firmware nằm trong flash của MCU Linux nạp firmware từ rootfs qua remoteproc, hoặc bootloader nạp trước khi vào Linux
Nguồn và clock Độc lập với SoC, có thể chạy khi SoC tắt Phụ thuộc nguồn và clock của SoC, tùy thiết kế từng chip
Giao thức Nhóm phát triển tự định nghĩa RPMsg là chuẩn chung giữa Linux và firmware
Chia ngoại vi Mỗi chip có ngoại vi riêng Ngoại vi trên SoC phải được chia cho một trong hai bên
Ví dụ MCU quản lý nguồn, radio co-processor, ChromeOS Embedded Controller Cortex-M4 trong i.MX 8M Mini, Cortex-M7 trong i.MX 8M Plus, Cortex-M4F trong AM62x, Cortex-M4 trong STM32MP15

Mô hình MCU ngoài phù hợp khi MCU phải chạy trước SoC hoặc tiếp tục chạy khi SoC tắt, ví dụ để điều khiển nguồn. Mô hình lõi trong SoC phù hợp khi cần xử lý thời gian thực gắn chặt với dữ liệu của Linux mà không muốn thêm một chip và một ảnh firmware riêng trên board.

MCU ngoài SoC

Kênh vật lý

Ngoài bus dữ liệu, thiết kế board thường nối thêm một số đường GPIO giữa SoC và MCU để Linux điều khiển được MCU khi firmware bị treo hoặc cần nạp lại.

Kênh Ưu điểm Hạn chế
UART Đơn giản, dùng được cho cả debug và nạp firmware qua bootloader trong ROM Không có địa chỉ, mỗi MCU một cổng, cần tự đóng khung (framing) dữ liệu
I2C Hai dây, nhiều thiết bị trên một bus, hợp với mô hình đọc ghi thanh ghi Tốc độ thấp, MCU phải đáp ứng kịp vai trò slave
SPI Tốc độ cao, phù hợp truyền khối dữ liệu lớn Nhiều dây hơn, master quyết định nhịp nên MCU cần một đường ngắt để báo có dữ liệu
USB Tốc độ cao, Linux có sẵn driver cho các lớp chuẩn (CDC ACM, HID) MCU phải có USB, firmware phức tạp hơn

Các đường GPIO thường dùng:

  • Reset của MCU: Linux khởi động lại MCU khi mất phản hồi.
  • Chân chọn chế độ boot: ví dụ BOOT0 trên STM32, dùng để đưa MCU vào bootloader trong ROM khi cần nạp firmware.
  • Đường ngắt từ MCU về SoC: MCU báo có sự kiện hoặc dữ liệu mới, tránh để Linux phải hỏi vòng (polling).
  • Đường đánh thức: MCU đánh thức SoC từ chế độ ngủ.

Giao thức giữa SoC và MCU

Có hai kiểu giao thức thường gặp. Kiểu thứ nhất là bản đồ thanh ghi (register map): MCU đóng vai một thiết bị I2C hoặc SPI có các thanh ghi ở địa chỉ cố định, Linux đọc và ghi từng thanh ghi. Kiểu này đơn giản, giống cách Linux làm việc với các chip ngoại vi khác. Kiểu thứ hai là thông điệp (message): mỗi bên gửi gói tin gồm header, mã lệnh, độ dài, dữ liệu và mã kiểm tra lỗi (CRC), thường dùng trên UART hoặc SPI khi cần trao đổi dữ liệu có cấu trúc.

Dù chọn kiểu nào, giao thức cần có các thành phần sau để vận hành lâu dài:

  • Lệnh đọc phiên bản firmware và phiên bản giao thức, để Linux phát hiện firmware MCU không khớp với phần mềm phía Linux.
  • Mã kiểm tra lỗi trên kênh không có sẵn cơ chế này (UART, SPI).
  • Cơ chế timeout và gửi lại ở phía Linux.
  • Lệnh đưa MCU vào chế độ cập nhật firmware.

ChromeOS Embedded Controller (EC) là một ví dụ mã nguồn mở hoàn chỉnh của mô hình này: EC là một MCU giao tiếp với SoC qua I2C, SPI hoặc LPC bằng giao thức có phiên bản, và kernel Linux có sẵn họ driver cros_ec cho nó.

Phía Linux: user space hay driver

Phần mềm phía Linux có thể giao tiếp với MCU trực tiếp từ user space hoặc qua một driver trong kernel.

Cách làm Giao diện Phù hợp khi
User space /dev/ttyS* hoặc /dev/ttyACM* cho UART và USB, /dev/i2c-* (i2c-dev), /dev/spidev* (spidev) Một ứng dụng duy nhất dùng MCU, cần làm nhanh, giao thức thay đổi thường xuyên
Kernel driver MCU xuất hiện dưới dạng thiết bị chuẩn: GPIO, hwmon, input, watchdog, regulator Nhiều thành phần cùng dùng chức năng của MCU, hoặc cần tích hợp với framework sẵn có của kernel

Khi một MCU cung cấp nhiều chức năng khác nhau (GPIO mở rộng, cảm biến nhiệt, watchdog), kernel thường dùng một driver MFD (multi-function device) để chia MCU thành nhiều thiết bị con, mỗi thiết bị con dùng driver của framework tương ứng. Nội dung này thuộc tab Linux Kernel.

Lõi MCU trong SoC

Thành phần phần cứng

Một SoC lai cung cấp cho lõi MCU các thành phần sau. Tên gọi khác nhau theo hãng, nhưng vai trò tương tự:

  • Bộ nhớ riêng của lõi MCU: TCM hoặc SRAM nội, nơi chạy firmware.
  • Vùng DDR dành riêng: dùng làm bộ nhớ chia sẻ cho bộ đệm thông điệp, được khai báo trong mục reserved-memory của Device Tree để Linux không cấp phát vùng này cho mục đích khác.
  • Mailbox phần cứng: khối tạo ngắt giữa hai bên để báo có dữ liệu mới, ví dụ Messaging Unit (MU) trên i.MX và IPCC trên STM32MP15.
  • Khối phân quyền ngoại vi: gán mỗi ngoại vi cho một bên, ví dụ Resource Domain Controller (RDC) trên i.MX 8M và ETZPC trên STM32MP15.

Ngoại vi do lõi MCU sử dụng phải được tắt (status = "disabled") trong Device Tree của Linux. Nếu cả hai bên cùng điều khiển một ngoại vi hoặc cùng tắt một clock, lỗi xuất hiện không theo quy luật và khó tìm nguyên nhân.

remoteproc: quản lý vòng đời lõi MCU

remoteproc là framework của kernel để Linux nạp firmware, khởi động và dừng một bộ xử lý khác trong hệ thống. Firmware là một file ELF đặt trong /lib/firmware. File này chứa một section gọi là resource table, mô tả các tài nguyên firmware cần Linux chuẩn bị, như vùng nhớ, kênh virtio cho RPMsg và bộ đệm trace.

Từ user space, mỗi lõi được điều khiển qua sysfs:

# Chọn file firmware trong /lib/firmware
echo app_m4.elf > /sys/class/remoteproc/remoteproc0/firmware
# Khởi động và dừng lõi MCU
echo start > /sys/class/remoteproc/remoteproc0/state
echo stop > /sys/class/remoteproc/remoteproc0/state
# Xem trạng thái hiện tại
cat /sys/class/remoteproc/remoteproc0/state

Lõi MCU cũng có thể được khởi động trước Linux, ví dụ bằng lệnh bootaux của U-Boot trên i.MX, để chạy các tác vụ cần có ngay sau khi bật nguồn. Khi đó remoteproc gắn vào (attach) lõi đang chạy thay vì nạp lại firmware, nếu driver của SoC hỗ trợ chế độ này.

RPMsg: trao đổi thông điệp

RPMsg (Remote Processor Messaging) là giao thức thông điệp chạy trên virtio, dùng bộ nhớ chia sẻ và mailbox ở trên. Mỗi bên tạo các endpoint có địa chỉ, và thông điệp được gửi giữa các endpoint. Firmware phía MCU dùng thư viện OpenAMP hoặc RPMsg-Lite (của NXP) để cài đặt phía còn lại của giao thức.

Phía Linux có ba cách dùng RPMsg:

  • rpmsg_char: tạo các file /dev/rpmsg* để ứng dụng user space đọc và ghi thông điệp.
  • rpmsg_tty: biểu diễn một kênh RPMsg dưới dạng cổng serial ảo.
  • Driver RPMsg riêng: driver trong kernel đăng ký với tên kênh do firmware công bố, phù hợp khi cần đưa dữ liệu của MCU vào một framework của kernel.

Todo

Bổ sung ví dụ đầy đủ trên một board cụ thể (i.MX 8M Mini hoặc STM32MP15): Device Tree, firmware mẫu từ SDK của hãng và lệnh kiểm tra sau khi chạy. Cần thử trên phần cứng trước khi đưa vào trang.

Cập nhật firmware MCU từ Linux

Firmware MCU là một phần của sản phẩm và cần được cập nhật cùng với phần mềm Linux. Cách làm phụ thuộc mô hình tích hợp.

Với lõi MCU trong SoC, firmware là một file trong rootfs. File này được cập nhật cùng rootfs qua cơ chế OTA Update, nên phiên bản firmware luôn khớp với phiên bản phần mềm phía Linux, và cơ chế A/B của rootfs cũng áp dụng cho firmware.

Với MCU ngoài, firmware nằm trong flash của MCU nên Linux phải chủ động nạp lại. Các cách thường gặp:

Cách Mô tả Công cụ phía Linux
Bootloader trong ROM Linux dùng GPIO đưa MCU vào bootloader có sẵn trong ROM (qua chân boot mode và reset), sau đó nạp firmware qua UART, I2C hoặc USB stm32flash cho STM32, esptool cho ESP32
Bootloader riêng MCU chạy một bootloader như MCUboot, nhận ảnh firmware mới qua giao thức SMP, kiểm tra chữ ký và có khả năng quay lại bản cũ mcumgr hoặc thư viện SMP tương đương
Lệnh trong giao thức ứng dụng Firmware ứng dụng nhận ảnh mới qua giao thức riêng, ghi vào vùng flash thứ hai rồi khởi động lại Phần mềm do nhóm phát triển tự viết

Một cách tích hợp thường dùng là đặt ảnh firmware MCU trong rootfs và cho một service kiểm tra khi Linux khởi động: nếu phiên bản firmware đọc được từ MCU khác với phiên bản trong rootfs thì nạp lại. Cách này giữ được tính nhất quán như trường hợp lõi trong SoC, nhưng cần xử lý trường hợp mất nguồn giữa chừng, nên bootloader trong ROM hoặc bootloader có cơ chế quay lại bản cũ được ưu tiên hơn cách tự viết.

Liên quan

Tài liệu tham khảo