// FIRMWARE SOURCE BASE · STM32L151CBT6 · CORTEX-M3

AK Base Kit

Nền firmware bare-metal cho STM32L151, dựng trên kernel AK Active Object: các task giao tiếp qua message, chạy hết việc mới nhường (run-to-completion), không cần RTOS. Có sẵn bootloader, cập nhật firmware không cần ST-Link, và build bằng một lệnh. Kit trên trang này đang chạy thật: đó là chính mã C của firmware, biên dịch sang WebAssembly.

ak-mcu-base v1.3.11 base cũ v1.3.0 PlatformIO · CMake OTA RS485 / UART nanoMODBUS (MIT)

Bấm B1 B2 B3 (hoặc phím 1 2 3) để tự điều khiển · mở bản đầy đủ có shell →

01Tổng quan

Repo gồm hai phần: ak-mcu-base (bản viết lại, tách khỏi chip, có unit test; dự án mới bắt đầu từ đây) và base cũ trong legacy/ (dành cho dự án đang chạy trên nó). Cả hai cùng dựa trên kernel AK.

KERNEL

Không cần RTOS

Task giao tiếp bằng message, chạy hết việc mới nhường. Tốn ít RAM, không có race giữa các task.

BOOT

Bootloader có sẵn

Kiểm ảnh trước khi chạy, cài bản mới từ flash ngoài, tự cứu khi app hỏng.

OTA

Nạp không cần ST-Link

Qua Modbus RS485 (v1.3.0) hoặc UART console (ak-mcu-base), có tool PC đi kèm.

BUILD

Một lệnh build

Bật/tắt module bằng cờ biên dịch, release tự gắn version, vượt dung lượng flash là báo lỗi ngay.

128K
Flash trong · RAM 32K · 32 MHz
116K
Dành cho app (staging ở flash SPI ngoài)
≈ 78 s
OTA ảnh 58 KB qua RS485 @ 9600 baud (đo trên board)
6.504
Điểm cắt điện đã test ở ak-mcu-base (giả lập)

02Kernel AK hoạt động thế nào

Mỗi task là một active object: có hàng đợi message riêng, chỉ xử lý một message mỗi lần. Ngắt (ISR) không làm việc nặng, chỉ post message rồi thoát. Scheduler luôn chọn task có ưu tiên cao nhất đang có message.

ISR / task

Có sự kiện (ngắt UART, hết giờ timer, task khác gửi) → task_post_*()

Message pool

Lấy message từ pool tĩnh (pure / common / dynamic), không malloc trong ISR

Hàng đợi ưu tiên

8 mức ưu tiên, mỗi task một hàng đợi FIFO

Run-to-completion

Gọi handler của task, chạy xong mới lấy message tiếp; giải phóng message

Viết một task

/* task_list.h — thứ tự enum phải khớp bảng */
enum {
    TASK_TIMER_TICK_ID,
    TASK_SYSTEM_ID,
    TASK_SENSOR_ID,      /* task mới */
    AK_TASK_EOT_ID,
};

/* task_list.c */
task_t app_task_table[] = {
  {TASK_TIMER_TICK_ID, TASK_PRI_LEVEL_7, task_timer_tick},
  {TASK_SYSTEM_ID,     TASK_PRI_LEVEL_2, task_system},
  {TASK_SENSOR_ID,     TASK_PRI_LEVEL_3, task_sensor},
  {AK_TASK_EOT_ID,     TASK_PRI_LEVEL_0, (pf_task)0},
};
/* task_sensor.c */
void task_sensor(ak_msg_t* msg) {
    switch (msg->sig) {
    case SENSOR_SIG_INIT:
        /* đọc cảm biến mỗi 500 ms */
        timer_set(TASK_SENSOR_ID, SENSOR_SIG_READ,
                  500, TIMER_PERIODIC);
        break;

    case SENSOR_SIG_READ: {
        uint8_t v = sensor_read();
        task_post_common_msg(TASK_SYSTEM_ID,
                  SYSTEM_SIG_DATA, &v, 1);
    }   break;
    }
}

Kernel tra task theo chỉ số task_table[id], nên dòng thứ n phải mang id = n. Lệch thứ tự thì FATAL("TK", 0x08) ngay lúc khởi động, thay vì để message đi nhầm task.

03Hai nhánh trong repo

Làm sản phẩm ngay thì dùng base v1.3.0. ak-mcu-base là hướng đi tiếp theo: cùng ý tưởng nhưng tách khỏi chip để port sang chip khác, và có test chạy trên máy tính.

Base cũ v1.3.0 — legacy/ak-mcu-base — gốc repo
Trạng tháiỔn định, đã kiểm trên boardv1.1.0, đã chạy trên board; còn thiếu driver và Modbus
ChipSTM32L151CBT6Đa nền tảng qua lớp HAL · port STM32L151 + host (PC)
BuildPlatformIOCMake (đã kiểm) · PlatformIO (mới kiểm cú pháp)
Cập nhật firmwareModbus RS485 (env app_mbslave), UART bootloaderGiao thức fw_proto trên UART console, nhận qua bootloader hoặc app
Chỗ chứa ảnh mớiFlash SPI ngoàiFlash SPI ngoài (mặc định) hoặc flash trong
An toàn mất điệnCắt ngang khi nhận: app cũ vẫn chạy (đã thử trên board)Cắt ở bất kỳ bước ghi flash nào cũng tự cài tiếp (test giả lập)
Thư viện kèmnanoMODBUS, ArduinoJson, QRCode, driver OLED/nút/buzzerTối giản: kernel, log, CRC, OTA
TestTest host cho Modbus slaveUnit test kernel + bootloader, test OTA đầu-cuối trên trình giả lập

04Base ổn định v1.3.0

Dựng lại từ ak-base-kit-stm32l151 (bản gốc build bằng Makefile) theo mô hình PlatformIO đã chạy ổn định trong sản phẩm. Bare-metal, SPL + CMSIS + startup + linker script nằm sẵn trong repo.

Bản đồ flash

Flash trong STM32L151 · 128K0x08000000 → 0x08020000
Bootloader (6,8K đang dùng) BSF: dữ liệu chung boot ↔ app (4K) Application Firmware mới chờ cài: flash SPI ngoài

Các lớp phần mềm

APP TASKStask_system · task_fw · task_shell · task_life · task_if · task_uart_if · task_dbg · task_display
KERNEL / LIBSAK kernel: scheduler · message · timer · fsm/tsmnanoMODBUS · net/link UART · ArduinoJson · QR
DRIVERSbutton · buzzer · eeprom · flash · led · OLEDxprintf · cmd_line · fifo / ring_buffer · view
PLATFORMstartup/vector · io_cfg/sys_cfg · Arduino core · SPL StdPeriph + CMSIS · ak.ld
HARDWARESTM32L151CBT6 (Cortex-M3 · 32 MHz · 128K/16K) + flash SPI ngoài

Build và nạp

pio run -e app                 # firmware ứng dụng (Modbus master, mặc định)
pio run -e app_mbslave         # biến thể Modbus SLAVE (OTA qua RS485)
pio run -e boot                # bootloader
pio run -e boot -t upload      # 1. nạp boot (board trắng phải nạp cả hai)
pio run -e app  -t upload      # 2. nạp app bằng ST-Link
pio device monitor             # console UART1 115200

Cập nhật firmware qua RS485

GIAO DIỆN · WINDOWS

EPCB Modbus Flash (exe)

Không cần cài Python. Chọn cổng COM, baud 9600, slave 1 → Kiểm tra board → chọn file .bin của app_mbslave → Nạp. Xong hiện PASS/FAIL và tự đọc lại phiên bản.
legacy/tools/EPCB_Modbus_Flash_v1.0.0.exe

DÒNG LỆNH

Python

Kiểm ảnh trước khi gửi, tự xác nhận phiên bản sau khi board khởi động lại.

python -m epcb_applib.ota COMx \
  legacy/release/app_mbslave/ak_base_kit_app_mbslave_v1.3.0.bin \
  --slave 1 --baud 9600
Chỉ gửi ảnh build từ app_mbslave. Ảnh của env app vẫn qua được bước kiểm nhưng không có task Modbus slave: nạp xong board mất đường OTA, phải quay lại dùng ST-Link.

Dùng cho dự án mới

  1. Clone theo tag v1.3.0, xoá .git, git init, ghi "Khởi tạo từ ak-base-kit-pio v1.3.0" vào README.
  2. Đổi định danh trong platformio.ini: APP_TITLE, APP_VERSION, build_dir, prefix tên file release.
  3. Chọn module bằng cờ: TASK_MBMASTER_EN / TASK_MBSLAVE_EN / SERIAL2_EN (USART2 chỉ một chủ), OLED, link UART…
  4. Sửa phần cứng theo schematic mới: io_cfg.h/.c, sys_cfg.c.
  5. Build thử cả hai env: 0 lỗi, 0 cảnh báo; nạp boot + app, console lên log, LED nháy.
  6. Viết chức năng theo mô hình AK: thêm task ID + dòng trong bảng task đúng vị trí, tạo app/task_xxx.cpp.
  7. Release: tăng APP_VERSION, build, lấy file trong release/.

05ak-mcu-base v1.3.11

Kernel AK, bootloader và OTA viết một lần, chạy trên mọi chip có port. Code phía trên lớp HAL không include header của chip. Port host chạy unit test và giả lập cả bootloader lẫn app ngay trên máy tính, không cần board.

APP / BOOTapp/: task_system · task_console (shell) · task_fw (OTA)boot/: boot_main · boot_core (quyết định + cài ảnh)
KERNEL / SERVICESkernel/: task · message · timer · fsm · tsmservices/fw/: fw_image · fw_update · fw_proto · boot_ctrl
HALhal.h · hal_flash.h (theo phân vùng) · ak_port.h — giao diện duy nhất chạm tới phần cứng
PORTport/stm32l151/: SPL, startup, linker, driver W25Qxxport/host/: flash giả lập, giả lập cắt điện, ak_sim

Bản đồ flash (mặc định, staging ở flash SPI ngoài)

Flash trong STM32L151 · 128K0x08000000 → 0x08020000
Flash SPI ngoài W25Qxx (SPI1, CS PB14)sector 4K
Bootloader (9,6K đang dùng) App (đang chạy) Ảnh OTA chờ cài, ở 0x80000 như base cũ

Board không gắn chip SPI: build make stm32-internal → APP 58K + STAGING 58K, cả hai trong flash trong.

Bootloader quyết định thế nào

Tình huốngHành động
App gửi lệnh LOADERỞ lại bootloader, chờ ảnh qua UART
App gửi lệnh UPDATE, STAGING hợp lệ, chưa quá 3 lần thửCài: chép STAGING → APP
APP hợp lệChạy app (không đọc STAGING, boot nhanh)
APP không có header (nạp .elf debug) và không có lần cài dởChạy kèm cảnh báo
APP hỏng, STAGING còn ảnhCứu: cài lại từ STAGING
Còn lạiBootloader chờ nạp
MẤT ĐIỆN

Header ghi sau cùng

Trang header của APP bị xóa đầu tiên và chép cuối cùng. Mất điện ở bất kỳ bước nào thì APP luôn "không hợp lệ", lần boot sau tự chép lại từ STAGING.

AN TOÀN

Kiểm đủ trước khi chạy

CRC32 của header và ảnh, tên board (chống nạp nhầm board), địa chỉ nạp, bảng vector. App không ghi được vào chính nó hay vào bootloader.

TỐC ĐỘ

Ghi half-page

Khối 128 B được ghi trong một lần thay vì 32 lần, bằng hàm chạy từ RAM. Header nằm sẵn trong .elf, nạp bằng SWD vẫn boot được.

Chạy thử ngay trên máy tính

cd ak-mcu-base
make test       # unit test + test OTA đầu-cuối trên trình giả lập
make sim        # chạy bootloader + app giả lập: gõ 'help' trong shell
make stm32      # build/stm32l151/{boot.bin, app.img, *.elf}

# nạp firmware qua UART console (board thật hoặc trình giả lập)
python3 tools/ak_fw.py --port /dev/ttyUSB0 flash build/stm32l151/app.img
python3 tools/ak_fw.py --sim build/host/ak_sim info

Đã kiểm gì

Hạng mụcKết quả
Unit test kernel (16 ca), ASan + UBSanpass
Unit test bootloader/OTA, 2 kiểu staging, 6.504 điểm cắt điệnpass
OTA đầu-cuối trên trình giả lập bằng chính tool ak_fw.py (13 bước)pass
Build STM32 cả hai chế độ, -Wall -Wextra -Werrorpass
Chạy trên board thật (UART, flash, SPI NOR, watchdog, nhảy app, OTA)đã chạy — v1.1.0, 06/10/2026

README đầy đủ của ak-mcu-base → · Hướng dẫn port sang chip mới →

06Lịch sử phiên bản

Chi tiết từng bản ở CHANGELOG.md.

  • ak-mcu-base10/2026

    Base đa nền tảng: kernel AK tách khỏi chip, bootloader + OTA an toàn khi mất điện, ghi half-page, staging trên flash SPI ngoài, port host + trình giả lập.

  • v1.3.025/09/2026

    OTA qua Modbus RS485 dùng được thật trên env app_mbslave; đã kiểm trên board (≈ 78 s/lượt @ 9600, cắt ngang vẫn chạy app cũ).

  • v1.2.025/09/2026

    Modbus chuyển sang nanoMODBUS (MIT), thêm chế độ slave, bỏ thư viện thương mại, thêm test host.

  • v1.1.224/09/2026

    Sửa cờ biên dịch không tới file nguồn, version lấy từ APP_VERSION, kiểm thứ tự bảng task, giới hạn flash theo env.

  • v1.1.0 – v1.1.123/09/2026

    Bootloader 0.0.2/0.0.3: tự kiểm bảng vector, tự vá BSF; sửa 11 lỗi (UART, RS485, ADC, GPIO, cờ build).

  • v1.0.016/08/2026

    Bản đầu tiên đánh tag: port AK Embedded Base Kit sang PlatformIO, chống ghi đè bootloader khi nạp.