---
# System prepended metadata

title: STM32 Note Week1

---

# STM32 Note Week1

Author: KT LIU
reference: Mastering_STM32-Second_Edtion

## Cortex and Cortex-M-Base Processors

### ARM Cortex
ARM Cortex 處理器分為 Cortex-A、Cortex-R、Cortex-M 三大系列，它們各自有不同的應用領域和特性。以下是簡要介紹：


#### Cortex-A 系列：
應用領域：主要用於高性能應用處理器，例如智慧型手機、平板電腦和部分嵌入式系統。
特性：支援高效能和多任務操作，通常具備強大的處理能力與先進的指令集，適合複雜的操作系統（如 Linux 或 Android）。
架構：支援 ARMv7-A 和 ARMv8-A 架構，並包含更多的記憶體管理和安全功能。

#### Cortex-R 系列：
應用領域：設計用於實時（real-time）應用，例如車輛電子控制單元（ECU）、工業控制和醫療設備。
特性：強調低延遲與高可靠性，支援硬體的錯誤檢測和容錯功能，適合時間敏感的任務。
架構：支援 ARMv7-R 和 ARMv8-R 架構，並具備高效能的內部記憶體系統，以確保穩定的實時性能。

#### Cortex-M 系列：
應用領域：廣泛應用於微控制器領域，如物聯網（IoT）、小型嵌入式設備和低功耗系統。
特性：低功耗設計，簡單的架構和指令集，適合嵌入式系統開發，易於使用。
架構：通常使用 ARMv6-M、ARMv


#### 舉例:
**Arduino**
Arduino 使用的處理器一般屬於 Cortex-M 系列，主要原因是其架構適合用於微控制器、低功耗且成本較低，非常適合 Arduino 開發板的應用範疇。

**樹莓派和 Jetson Nano**
樹莓派和 Jetson Nano 都採用了 ARM Cortex-A 系列的 CPU，特別是 Cortex-A53、Cortex-A57 和 Cortex-A72 等。



### Cortex-M 進一部探討(STM32為此架構)
#### Core Rigister(暫存器)
在嵌入式系統和微處理器的領域中，「暫存器」（register）是處理器內部的一種高速存儲單元，用於臨時存儲數據和指令。
![image](https://hackmd.io/_uploads/HykJjMmf1e.png)
在Cortex-M處理器中，常見的核心暫存器包括：

**通用暫存器：**
R0-R12：這些是通用用途的暫存器，可以用於數據操作和存儲中間結果。
Caller-saved: R0~R3, R12 可被自由使用的。
Callee_saved: 其他則需要先儲存再調用。

**特定用途暫存器：**
R13（SP，堆疊指針）：指向當前堆疊的頂部，用於函數調用和中斷處理。
R14（LR，鏈接寄存器）：存儲函數返回地址。
R15（PC，程序計數器）：存儲當前執行指令的地址。

**狀態寄存器：**
xPSR（程序狀態寄存器）：包含處理器的狀態信息，包括運行模式和條件標誌。




#### Memory Map(記憶體架構)
此為STM32的Memory Layout
![image](https://hackmd.io/_uploads/rJYXpG7GJx.png)
**Code Area (0x00000000 - 0x1FFFFFFF)：**
這是程式碼區域，包含了Flash記憶體和可能的別名區域。這些記憶體區域在系統啟動時會被用來存放程式碼和靜態資料。
0x00000000 - 0x1FFFFFFF：這些地址區域被分配給系統啟動時的映像（例如，Flash，系統記憶體或SRAM），具體內容取決於啟動配置。

**SRAM (0x20000000 - 0x3FFFFFFF)：**
這是靜態隨機存取記憶體（SRAM），用於存放在執行期間動態分配的資料，例如堆疊和堆區。
0x20000000 - 0x3FFFFFFF：這個地址範圍專門用於內部SRAM。STM32的SRAM一般分佈在這些地址範圍內。

**Peripheral (0x40000000 - 0x5FFFFFFF)：**

這是外設區域，用於存取各種外設寄存器，例如GPIO、定時器、UART等。
0x40000000 - 0x5FFFFFFF：外設記憶體映射範圍，用於各種外設的控制和狀態寄存器。

**External RAM (0x60000000 - 0x9FFFFFFF)：**
這是外部RAM的地址範圍，用於連接外部RAM設備，如SDRAM或SRAM模組。
0x60000000 - 0x9FFFFFFF：此範圍通常用於外部RAM設備，允許系統擴展其記憶體容量。


#### 進一步展開Code Area:
**System memory (0x1FFF0000 - 0x1FFFFFFF)：**
系統記憶體包含了引導加載程式和其他系統相關的功能。
0x1FFF0000 - 0x1FFFFFFF：這個範圍內的記憶體通常包含系統引導加載程式和其他內部功能。

**Option bytes (0x1FFFC000 - 0x1FFFFFFF)：**
選項字節用於配置裝置的各種選項，如讀保護、寫保護和啟動模式。
0x1FFFC000 - 0x1FFFFFFF：這些地址範圍用於存儲選項字節，影響系統的配置和保護機制。

**Flash memory (0x08000000 - 0x080FFFFF)：**
這是內部Flash記憶體的地址範圍，用於存儲程式碼和靜態資料。
0x08000000 - 0x080FFFFF：內部Flash的地址範圍，用於存放程式碼和常數數據。

**Reserved：**
保留區域是未分配或未使用的地址範圍，通常用於未來的擴展或內部用途。
0x1FFF0000 - 0x1FFF7FFF和其他特定區域：這些範圍通常保留供未來使用或特殊內部功能。



### Bit-Banding(位帶區域)
#### 位帶映射區域的定義
ARM Cortex-M3/M4 定義了兩個位帶映射區域，每個區域有 1 MB 的大小，分別對應到 SRAM 和外設記憶體：

**SRAM 位帶區域：** 地址範圍為 0x2000 0000 - 0x200F FFFF，映射到 0x2200 0000 - 0x23FF FFFF。

**外設位帶區域：** 地址範圍為 0x4000 0000 - 0x400F FFFF，映射到 0x4200 0000 - 0x43FF FFFF。
這些位帶映射區域可用於快速位元操作。

以下為位帶與映射的情況:
![image](https://hackmd.io/_uploads/SyPZmXQM1l.png)

實例：操作 GPIO 位元

使用位帶技術修改 GPIOA 的輸出資料暫存器（GPIO->ODR）中的第 5 位。
也就是 GPIOA, PIN_5
已知GPIOA的ODR在 0x4002 0014。
要修改 GPIOA 的第 5 位（PIN5），使用位帶地址計算公式：

```
alias_region_base = 0x4200 0000
region_base_offset = 0x4002 0014 - 0x4000 0000 = 0x20014
bit_band_address = 0x4200 0000 + (0x20014 * 32) + (5 * 4) = 0x4240 0294
```
**alias_region_base** 是別名區的基準地址。
**region_base_offset** 是目標地址相對於基準地址的偏移量。
**bit_number** 是想要操作的位元位置。

計算得到 0x4240 0294 即是位帶對應的位元地址，可以直接操作此位元。

以下為課本中的範例:
```
// Define base address of bit-band
#define BITBAND_SRAM_BASE 0x20000000

// Define base address of alias band
#define ALIAS_SRAM_BASE 0x22000000

// Convert SRAM address to alias region
#define BITBAND_SRAM(a,b) ((ALIAS_SRAM_BASE + ((uint32_t)&(a)-BITBAND_SRAM_BASE)*32 + (b*4)))
 
// Define base address of peripheral bit-band
#define BITBAND_PERI_BASE 0x40000000

// Define base address of peripheral alias band
#define ALIAS_PERI_BASE 0x42000000

// Convert PERI address to alias region
#define BITBAND_PERI(a,b) ((ALIAS_PERI_BASE + ((uint32_t)a-BITBAND_PERI_BASE)*32 + (b*4)))
 
#define GPIOA_PERH_ADDR 0x40020000
#define ODR_ADDR_OFF 0x14

uint32_t *GPIOA_ODR = GPIOA_PERH_ADDR + ODR_ADDR_OFF;
uint32_t *GPIOA_PIN5 = BITBAND_PERI(GPIOA_ODR, 5);
*GPIOA_PIN5 = 0x1; // Turns GPIO HIGH
```


#### 其他常見的位址(不一定如此)
在 STM32 微控制器的記憶體地圖中，每個 GPIO 埠（例如 GPIOA、GPIOB 等）都會分配到一個固定的基底地址，各暫存器基於這個基底地址進行偏移。以 STM32F4 系列為例：

GPIOA 基底地址：0x4002 0000
GPIOB 基底地址：0x4002 0400
GPIOC 基底地址：0x4002 0800

其他 GPIO 埠依此類推，每個 GPIO 埠相隔 0x400 的地址範圍。
GPIOB 中的暫存器地址舉例
GPIOB 的基底地址是 0x4002 0400，因此 GPIOB 的各個暫存器地址如下（以 32 位元偏移）：

GPIOB 模式暫存器 (MODER): 0x4002 0400
GPIOB 輸出數據暫存器 (ODR): 0x4002 0414
GPIOB 輸入數據暫存器 (IDR): 0x4002 0410
GPIOB 輸出類型暫存器 (OTYPER): 0x4002 0404
GPIOB 位設定/清除暫存器 (BSRR): 0x4002 0418



### Thumb-2 and Memory Alignment
#### ARM 處理器的 32 位元指令集
**32 位元指令集的特點：** ARM 處理器最初使用的是 32 位元的指令集，提供了豐富的指令，能夠高效執行算術和記憶體操作。在 32 位元的指令集架構下，程式運行速度較快。

**缺點：** 這種指令集在記憶體佔用上比較大，導致需要更多的快閃記憶體來儲存韌體程式碼，這樣會增加 MCU 的生產成本和功耗。


#### Thumb 16 位元指令集
**介紹 Thumb 指令集：** 為了解決 32 位元指令佔用空間大的問題，ARM 推出了 Thumb 指令集。Thumb 指令是 32 位元指令集中常用指令的子集，每條指令只有 16 位元。

**指令轉換：** Thumb 指令會自動轉換為等效的 32 位元 ARM 指令，因此在效能上與 32 位元指令無差別，但能夠降低記憶體佔用。

**優點與限制：** Thumb 指令的程式碼大小約為原本 ARM 指令集的 65%，且在 16 位元記憶體系統上運行時效能可以達到原本 ARM 程式碼的 160%。但因為縮減了指令長度，Thumb 指令的功能也有限，例如只有跳躍指令能夠有條件執行，且能操作的暫存器數量較少。


#### Thumb-2 指令集
**介紹 Thumb-2：** ARM 後來推出了 Thumb-2 指令集，這是一種將 16 位元和 32 位元指令混合在一起的指令集，提供了比 Thumb 指令集更豐富的指令。

**優點：** Thumb-2 具有可變長度指令集，使程式碼密度更高，達到類似 ARM 的豐富功能，同時保持記憶體使用效率。


#### 支援非對齊記憶體訪問的特性
**非對齊記憶體訪問的問題：** 在傳統的 ARM 架構中，記憶體存取必須是「對齊」的。32 位元的資料需存放在 32 位元對齊的地址（例如 0x2000 0000、0x2000 0004 等），若要存取非對齊的地址（例如 0x2000 0002），會引發 UsageFault 錯誤。同樣地，16 位元資料需存放在 2 字節對齊的地址。

**RAM 分散問題：** 這種限制導致 RAM 中出現空白區塊（記憶體碎片），造成浪費。例如，如果我們需要存放一個 32 位元資料在 0x2000 0007，則傳統 ARM 架構中無法做到。

**下為非對齊記憶體和對齊記憶體的示意圖:**
![image](https://hackmd.io/_uploads/BkE2NNmGkl.png)


#### Cortex-M3/4/7 支援非對齊訪問
**非對齊訪問解決方案：** Cortex-M3/4/7 核心解決了上述問題，支援非對齊記憶體存取。這樣就能更高效地排列變數，減少空白區塊。例如，在新架構中，我們可以將 32 位元變數從地址 0x2000 0007 開始存放，這樣節省了 4 個字節的 SRAM。

**限制：** 並非所有指令都支援非對齊訪問，僅限於以下幾種指令：
LDR（載入暫存器）、LDRH（載入半字）、LDRSH（載入帶符號半字）
這些指令支援非對齊訪問，其他指令仍需要對齊地址。



### Pipline

#### 流水線的基本概念
在傳統的非流水線處理器中，指令是順序執行的，即一條指令必須等前一條指令執行完畢後才能開始執行。這種方式效率較低，因為處理器中的大部分資源在等待期間都處於空閒狀態。通過引入流水線技術，指令的執行過程被分為多個階段，如下所示：

**取指（Fetch, F）：** 從內存中取出指令。
**解碼（Decode, D）：** 解碼指令，確定需要執行的操作。
**執行（Execute, E）：** 執行指令所指定的操作。


#### 流水線執行過程：
![image](https://hackmd.io/_uploads/H1xOLSQfJl.png)


在正常的流水線執行過程中，各個指令按照順序進入流水線並被執行。在圖中，綠色的 ADD 指令依次進入並通過流水線的各個階段。

**分支指令的出現：**
當流水線遇到分支指令（如圖中的 BNE，即“Branch if Not Equal”），會產生兩種可能的執行路徑。根據分支條件，程序可能會跳轉到新的指令位置或繼續執行當前路徑上的指令。

**分支影響：**
在圖中，BNE 指令之後的 MOV 和 LOAD 指令已經進入流水線。如果分支條件成立（跳轉），這些指令會被丟棄，這段被丟棄的指令稱為“分支陰影”（Branch Shadow）。

**分支跳轉：**
如果 BNE 指令決定跳轉，程序將跳轉到新的指令位置繼續執行（圖中橙色的分支箭頭所示），例如另一條 ADD 指令。這會導致已經進入流水線但未被執行的指令被丟棄，並且重新從新的指令位置開始取指和執行。


#### 分支預測
為了減少分支對流水線的影響，現代處理器會採用分支預測技術。分支預測的基本原理是猜測分支指令的執行結果，提前加載可能會執行的指令，減少等待時間和流水線清空的成本。如果預測正確，流水線效率將大大提高；如果預測錯誤，需要丟棄錯誤路徑上的指令並重新加載正確的指令，這會有一定的性能損失。



### Interrupts and Exceptions Handling

在ARM架構中，中斷（interrupts）是一種異常（exception），通常由片上外設（例如定時器）或外部輸入（例如連接到GPIO的觸摸開關）生成，有時也可以由軟件觸發。


#### 異常和中斷的標識
每個異常（以及中斷）都有一個唯一的標識號。這個標識號反映了異常處理例程在向量表中的位置，其中存儲了例程的實際地址。例如，位置15包含了SysTick中斷處理程序的內存地址，當SysTick定時器歸零時會生成中斷。


#### 優先級
除了前三個異常外，每個異常都可以分配一個優先級，這決定了在同時發生中斷時的處理順序：數字越小，優先級越高。例如，假設有兩個與外部輸入A和B相關的中斷例程。我們可以給輸入A分配一個更高的優先級（更低的數字）。如果處理器在處理來自輸入B的中斷時接收到A的中斷，則B的執行將被暫停，允許高優先級的中斷服務例程立即執行。


#### NVIC
異常和中斷由一個名為嵌套向量中斷控制器（Nested Vectored Interrupt Controller，NVIC）的專用單元處理。NVIC具有以下特點：

**1. 靈活的異常和中斷管理：** NVIC能夠處理來自外設的中斷信號/請求和來自處理器核心的異常，允許我們在軟件中啟用/禁用它們（除非是不可屏蔽中斷NMI）。

**2, 支持嵌套的異常/中斷：** NVIC允許為異常和中斷（除前三種異常類型外）分配優先級，根據用戶需求對中斷進行分類。

**3. 向量異常/中斷入口：** NVIC自動定位與異常/中斷相關的異常處理程序的位置，無需額外的代碼。

**4. 中斷屏蔽：** 開發者可以自由地暫停所有異常處理程序的執行（除NMI外），或者根據優先級暫停其中一些，這得益於一組專用寄存器。這允許在不處理異步中斷的情況下安全地執行關鍵任務。

**5. 確定性的中斷延遲：** NVIC的一個有趣特性是中斷處理的確定性延遲，對於所有Cortex-M3/4核心為12個週期，對於Cortex-M0為15個週期，對於Cortex-M0+為16個週期，無論處理器當前狀態如何，中斷延遲時間應為可預期。

**6. 異常處理程序的重定位：** 異常處理程序可以被重定位到其他的閃存位置，甚至是完全不同的外部非只讀內存。這為高級應用提供了很大的靈活性。


#### 向量表
向量表是一個數組，其中每個條目對應一個異常或中斷，存儲的是異常處理程序的地址。這使得NVIC能夠快速找到並執行相應的異常處理程序，從而減少異常處理的延遲時間。










