Nuvoton Cortex-M0 韌體開發實作:EBI 並列匯流排、RGB565 圖像顯示、精準記憶體規劃、Keil 工具鏈 + J-Link 下載。
/* 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 ... */ };
/* 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 }
0x7BEF0x7BEFU#787878| Register | Offset | Reset | Key Fields |
|---|---|---|---|
| EBI_CTL | 0x00 | 0x0000_0000 | ENABLE[0]:EBI 致能;DATA_WIDTH[17:16]:匯流排寬度(00=8b, 01=16b) |
| EBI_TCTL0 | 0x04 | 0x03FF_FFFF | tAS[27:24]:address setup;tAH[19:16]:address hold;W_DLY[11:8]:WR 脈寬(× HCLK) |
| SYS_GPA_MFP | SYS+0x30 | 0x0000_0000 | PA[15:0] multifunction:設為 EBI bus,覆蓋 GPIO 預設值 |
| SYS_GPB_MFP | SYS+0x34 | 0x0000_0000 | PB[10:8] → EBI_ALE / CS0 / WR;PB[7] → EBI_RD |
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 的理論起點。
/* 啟用 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 */ }
CPU 迴圈搬運 44,000 個像素(200×220)需要 ~88,000 個 load/store cycle,佔滿 2 ms。改用 PDMA burst-transfer 讓 CPU 在搬圖期間執行其他任務(ADC 採樣、UART 輸出),有效實現「軟性多工」。
/* 設定 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 可繼續跑其他邏輯 */
/* 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);
/* 發 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); }
NANO130 上電後執行 ROM Bootloader 自我測試,跳入使用者 Reset Handler,依序初始化時脈樹(PLL → HCLK 42 MHz)、周邊(EBI / SPI / ADC / BPWM / PDMA)、TFT 驅動 IC 初始化序列,最後進入主迴圈。