Firmware Engineer · MCU Development

從 Datasheet 到
實機驅動的完整流程

Nuvoton Cortex-M0 韌體開發實作:EBI 並列匯流排、RGB565 圖像顯示、精準記憶體規劃、Keil 工具鏈 + J-Link 下載。

Cortex-M0 Nano130KE3BN Keil µVision 5 J-Link SWD EBI / SPI / I2C / UART RGB565 128 KB Flash 16 KB SRAM
NANO130 KE3BN Cortex-M0 · 42 MHz 128K FLASH · 16K SRAM EBI[0:15] SPI0 MOSI I2C SDA UART TX TFT RS GPIO LED PWM ADC
01

核心技術規格

bill of materials / datasheet
🧠
MCU Core
Cortex-M0
42 MHz · Thumb-2 · NVIC
💾
Flash / SRAM
128 KB / 16 KB
ISP · ICP · Data Flash
Supply Voltage
1.8 V – 3.6 V
-40°C to 85°C
🔌
Display Bus
EBI Parallel
memory-mapped I/O · 16/8-bit
🎨
Pixel Format
RGB565
16-bit · 2 B/px
📐
Max Resolution
200 × 220
~86 KB image payload
🛠
Toolchain
Keil + J-Link
ARMCC · ELF · SWD flash
📦
Package
LQFP 48/64/128
選 LQFP64 for EBI + LCD
02

超低功耗模式

ultra-low power · wearable target

// NANO130 Power Profile — 設計目標:穿戴式 / 可攜式醫療量測

Normal Mode
200 μA/MHz
Idle Mode
75 μA/MHz
Power-down + RTC
2.5 μA
Power-down + RAM
1 μA
Wake-up latency
< 7 μs
03

周邊矩陣

peripheral overview · datasheet §4
📡
ADC
12-ch · 12-bit
2 MSPS · 內建溫度感測器 ±1°C
🔁
PWM
4 × 16-bit
8 輸出 或 4 組互補對 · 死區控制
🖥
LCD Controller
4×40 / 6×38
COM/SEG 段碼顯示器驅動
🔗
USB
USB 2.0 FS
Full-Speed Device · 12 Mbps
🔄
SPI
× 3 ports
最高 32 MHz · Master/Slave
🔀
I²C / UART
2 / 5 ports
I²C 1 MHz · UART 1 Mbps × 2
🕐
Oscillators
4–24 MHz XTAL
12 MHz IRC (±1%) · 10 kHz IRC
🔒
Programming
ISP · ICP
Data Flash · Bootloader 支援
02

記憶體預算計算機

flash budget · interactive
Code / Data
Image
Total
剩餘 Flash
04

Flash-resident 影像 + EBI blit

img2.c · main.c
img2.c — 200×220 RGB565 · Flash-resident
/* Generated from PNG via Rinky-Dink RGB565 converter
   const 讓編譯器鎖在 Flash,零 SRAM 執行時分配 */
const unsigned short img2[44000] = {
    0x0000, 0x0000, 0x1082, 0x2965, 0x41A7, 0x62AB,
    0x832F, 0xA4B3, 0xC636, 0xE7BA, 0xFFFF, 0xFFDE,
    /* ... 44 K entries, each 16-bit RGB565 pixel ... */
};
main.c — EBI blit loop
/* memory-mapped I/O: 寫到 EBI address = 送一個像素到 TFT */
void tft_blit(const uint16_t *buf, uint32_t n) {
    for (uint32_t i = 0; i < n; i++) {
        EBI_WRITE_DATA(buf[i]);    // triggers CS/WR strobe
    }
}

int main(void) {
    SYS_Init();         // clock tree + multifunction pins
    EBI_Open(0, 16);   // 16-bit bus, bank 0
    TFT_Init();         // controller reset + init sequence
    tft_blit(img2, 44000);
    while (1) { __WFI(); } // sleep, wake on interrupt
}
05

PNG → MCU 流程

5-step toolchain
影像輸入
PNG / JPG < 300 KB,建議 200 × 220
RGB565 轉換
Rinky-Dink converter 輸出 C header
加入專案
替換 img2.c,以 const 鎖進 Flash
EBI 時序對齊
設 tAS / tAH 匹配 TFT setup/hold
J-Link 下載
SWD flash + 邏輯分析儀驗證
06

技術難點 & 解決

problem → mitigation
✕ Flash 空間爆表
一張 200×220 RGB565 約 86 KB,加上 code 只剩 12 KB 餘裕。
✓ 外掛 SPI Flash
影像搬到外部 W25Q 系列,MCU 以 SPI DMA 逐行讀取。
✕ EBI 時序不對
TFT 亂碼或部分列遺失,通常是 setup/hold 沒對齊。
✓ 邏輯分析儀校正
量 CS/WR/RD 波形反推 tAS/tAH 調到穩定。
✕ Keil 32 KB 上限
免費版無法編譯大型 image。
✓ PSN 註冊碼
申請免費教育版完整授權解除限制。
07

RGB565 互動計算機

即時轉換 · 位元視覺化
HEX 16-bit0x7BEF
C literal0x7BEFU
RGB888#787878
R[4:0]G[5:0]B[4:0]
08

EBI 時序波形

WRITE / READ cycle · tAS / tAH
// EBI bus cycle @ 42 MHz HCLK (23.8 ns/cycle)
CS_n WR_n RS D[15:0] DATA VALID (output) tAS tAH W_DLY
tAS2 × HCLK = 47.6 ns
W_DLY6 × HCLK = 142.8 ns
tAH2 × HCLK = 47.6 ns
cycle~10 × HCLK = 238 ns
RegisterOffsetResetKey Fields
EBI_CTL0x000x0000_0000ENABLE[0]:EBI 致能;DATA_WIDTH[17:16]:匯流排寬度(00=8b, 01=16b)
EBI_TCTL00x040x03FF_FFFFtAS[27:24]:address setup;tAH[19:16]:address hold;W_DLY[11:8]:WR 脈寬(× HCLK)
SYS_GPA_MFPSYS+0x300x0000_0000PA[15:0] multifunction:設為 EBI bus,覆蓋 GPIO 預設值
SYS_GPB_MFPSYS+0x340x0000_0000PB[10:8] → EBI_ALE / CS0 / WR;PB[7] → EBI_RD
09

理論基礎 — MIT 6.004 計算機結構

Computation Structures · Cortex-M0 原理對應

為什麼看 6.004?

MIT 6.004《Computation Structures》從數位邏輯、有限狀態機、到 Beta RISC 處理器的完整設計, 正是理解 Cortex-M0 如何執行 Thumb-2 指令的理論骨架—— Fetch → Decode → Execute 三級管線、暫存器檔案、ALU 設計、記憶體映射 I/O(MMIO) 在 Nano130 的 EBI 實作中都有直接對應。 第 13 講深入有限狀態機(FSM), 是理解韌體 State Machine Pattern 的理論起點。

數位邏輯 → GPIO
組合邏輯閘映射到 Nano130 的 PA/PB multifunction 腳位設定
FSM → 韌體狀態機
6.004 FSM 模型直接對應 display init state machine(RESET → CMD → DATA → IDLE)
Beta ISA → Thumb-2
Beta 32-bit RISC 與 Cortex-M0 Thumb-2 16/32-bit 混合 ISA 的結構對比
MMIO → EBI Write
6.004 的 memory-mapped I/O 概念直接解釋 EBI_WRITE_DATA() 為何等同「送像素」
中斷 → NVIC
Cortex-M0 NVIC 優先權仲裁對應 6.004 的中斷向量與 supervisor call
10

完整韌體教學播放清單

YouTube · Nano MCU series
11

技術文章

blog
12

跨域延伸

DSP × biomed

從 Bit 到分子:一條完整的工程線

以韌體底層為基礎,整合生醫研究專業:MCU 上實作 ECG / PPG 訊號 FIR / IIR 濾波、心率擷取演算法,結合站內已展示的 蛋白質 AINGS基因 AI 平台,形成「硬體 ↔ 韌體 ↔ 演算法 ↔ 系統」同一條技術線。

13

NVIC 向量中斷控制器

Nested Vectored Interrupt Controller · Cortex-M0
中斷通道
32 個外部 IRQ
+ 16 個系統例外(HardFault / SysTick …)
🎯
優先級位元
2-bit(4 級)
M0 最小化實作 · 低延遲搶佔
🕐
進入延遲
12 cycles
自動 push 8 個暫存器 → 無需 prologue
🔒
Tail-chaining
6 cycles
連續 ISR 切換省去 push/pop overhead
irq_setup.c — EBI / GPIO 中斷配置
/* 啟用 EINT0(EBI 資料就緒信號)與 UART0 接收中斷 */
NVIC_SetPriority(EINT0_IRQn, 0);   /* 最高優先 — 顯示撕裂修復 */
NVIC_SetPriority(UART0_IRQn, 1);
NVIC_EnableIRQ(EINT0_IRQn);
NVIC_EnableIRQ(UART0_IRQn);

void EINT0_IRQHandler(void) {
    GPIO_CLR_INT_FLAG(PB, BIT14);     /* 清 pending bit */
    tft_vsync_cb();                   /* 觸發下一幀 blit */
}
14

PDMA 直接記憶體傳輸

Peripheral DMA · zero-CPU pixel blit

為什麼需要 PDMA?

CPU 迴圈搬運 44,000 個像素(200×220)需要 ~88,000 個 load/store cycle,佔滿 2 ms。改用 PDMA burst-transfer 讓 CPU 在搬圖期間執行其他任務(ADC 採樣、UART 輸出),有效實現「軟性多工」。

pdma_blit.c — Flash → EBI 零 CPU 傳輸
/* 設定 PDMA Ch0:Flash 圖陣 → EBI data 暫存器 */
PDMA_Open(1 << 0);
PDMA_SetTransferCnt(PDMA, 0, PDMA_WIDTH_16, 44000);
PDMA_SetTransferAddr(PDMA, 0,
    (uint32_t)img2,          PDMA_SAR_INC,  /* src: Flash array */
    EBI_BANK0_BASE_ADDR,    PDMA_DAR_FIX); /* dst: TFT EBI 位址(固定) */
PDMA_SetBurstType(PDMA, 0, PDMA_REQ_BURST, PDMA_BURST_128);
PDMA_EnableInt(PDMA, 0, PDMA_INT_TRANS_DONE);
NVIC_EnableIRQ(PDMA_IRQn);
PDMA_Trigger(PDMA, 0);  /* 啟動 — CPU 可繼續跑其他邏輯 */
✕ CPU 迴圈搬運
~88,000 cycle · CPU 100% 忙碌 · UART / ADC 延遲 2 ms 以上
✓ PDMA Burst-128
~690 cycle 觸發後 CPU 釋放 · 傳完觸發 IRQ 通知主迴圈
15

Timer / BPWM 多頻道配置

backlight · heartbeat LED · scan clock
🕐
Timer 0 / 1
系統節拍 1 ms
SysTick + 應用層軟體 timer 佇列
🔄
Timer 2
ADC 觸發 250 Hz
每 4 ms 啟動一次 ECG/PPG 採樣
💡
BPWM0 Ch0
LCD 背光 500 Hz
Duty 0–100% 調光 · 低頻閃爍避免感測雜訊
❤️
BPWM0 Ch1
心跳 LED 1 Hz
互補輸出 · dead-band 200 ns 防橋臂直通
pwm_init.c — 背光 + ADC 觸發配置
/* BPWM0 Ch0: LCD 背光 500 Hz,初始 duty 80% */
BPWM_ConfigOutputChannel(BPWM0, 0,
    500,   /* freq Hz  */
    80);   /* duty %   */
BPWM_EnableOutput(BPWM0, BPWM_CH_0_MASK);
BPWM_Start(BPWM0, BPWM_CH_0_MASK);

/* Timer2: 每 4 ms 觸發 ADC(250 SPS for ECG) */
TIMER_Open(TIMER2, TIMER_PERIODIC_MODE, 250);
TIMER_SetTriggerSource(TIMER2, TIMER_TRGSRC_TIMEOUT_EVENT);
TIMER_SetTriggerTarget(TIMER2, TIMER_TRG_TO_ADC);
TIMER_Start(TIMER2);
16

SPI Flash 外部影像儲存

W25Q64 · 64 Mbit · XIP-ready
✕ 影像鎖在 MCU Flash
128 KB 只能放 1 張 200×220(86 KB),程式碼加 heap 幾乎爆表,無法做多圖輪播。
✓ 外掛 W25Q64 SPI Flash
64 Mbit = 8 MB,可存 ~93 張全彩圖,透過 SPI0 以 32 MHz DMA 讀取,每幀 <3 ms。
✕ SPI 讀取阻塞 CPU
同步輪詢 64 KB Sector 讀取需 ~5 ms 忙等待,顯示幀率下降。
✓ SPI + PDMA double-buffer
前景顯示 buf[0] 時,PDMA 搬 buf[1];切換時 swap pointer,CPU 始終零等待。
spi_flash.c — W25Q64 讀取啟動
/* 發 Read Data (0x03) 指令,從 sector 0 讀 86 KB */
void spiflash_read(uint32_t addr, uint8_t *dst, uint32_t len) {
    SPI_SET_SS_LOW(SPI0);
    SPI_WriteData(SPI0, 0x03);           /* Read cmd */
    SPI_WriteData(SPI0, (addr >> 16) & 0xFF);
    SPI_WriteData(SPI0, (addr >>  8) & 0xFF);
    SPI_WriteData(SPI0,  addr        & 0xFF);
    PDMA_SetTransferAddr(PDMA, 1,
        SPI0_BASE + 0x20, PDMA_SAR_FIX,  /* SPI RX FIFO */
        (uint32_t)dst,      PDMA_DAR_INC);
    PDMA_SetTransferCnt(PDMA, 1, PDMA_WIDTH_8, len);
    PDMA_Trigger(PDMA, 1);
}
17

完整系統主迴圈架構

boot → init → main loop · full picture

從上電到畫面:完整啟動流程

NANO130 上電後執行 ROM Bootloader 自我測試,跳入使用者 Reset Handler,依序初始化時脈樹(PLL → HCLK 42 MHz)、周邊(EBI / SPI / ADC / BPWM / PDMA)、TFT 驅動 IC 初始化序列,最後進入主迴圈。

Power-on Reset + ROM Bootloader
自我測試 Flash CRC、讀取 Config0 Boot 設定(ISP / ICP / User Flash),決定跳轉目標。
CLK_SetCoreClock(42000000)
外部 12 MHz XTAL → PLL × 3.5 → 42 MHz HCLK。PCLK = HCLK / 2 = 21 MHz 供 ADC / SPI。
周邊初始化
EBI 時序(tAS=2, tAH=1, tACC=6)→ SPI0 32 MHz → BPWM0 500 Hz → Timer2 250 Hz ADC trigger → PDMA Ch0/1 → NVIC 優先級。
TFT ILI9225 驅動初始化
透過 EBI 發送 ~20 條暫存器設定指令(電源、Gamma、掃描方向、色彩模式 RGB565),等待 120 ms Vcom 穩定。
主迴圈 — while(1)
PDMA 搬圖(非阻塞)→ ADC 結果處理(ECG/PPG)→ UART 輸出數據 → 低功耗 Sleep 等 SysTick 喚醒(1 ms),迴圈週期 < 4 ms。