---
# System prepended metadata

title: ARM Cortex-M
tags: [MCU]

---

---
tags: MCU 
---
# ARM Cortex-M
slide: https://hackmd.io/@SquirrelPanda/r1lT9LkQ_

{%hackmd ryr9Ug6sd %}

---
> 本篇紀錄以 ARM Cortex-M0 及公司 firmware source code 用到的為主.  
> [name=Nick Xiao] [time=Feb, 2021] [color=#907bf7]




## NVIC (Nested vectored interrupt controller)


## CMSIS
![](https://i.imgur.com/yfyUoKS.png)


## 中斷
- 啟用/關閉中斷
    ```
    中斷寄存器是可編程的，用於控制中斷請求(異常編號16以上)的使能(SETENA)和禁止(CLRENA)
    ```
    ```c=
    void NVIC_EnableIRQ(IRQn_Type_IRQn); //使能中斷#IRQn;
    void NVIC_DisableIRQ(IRQn_Type_IRQn); //禁止中斷#IRQn;
    ```

- 中斷掛起和清除掛起：
    ```
    可以通過操作中斷掛起(SETPEND)和清除掛起(CLRPEND)，這兩個寄存器來訪問和修改中斷掛起狀態。
    ```

    ```c=
    void NVIC_SetPendingIRQ(IRQn_Type_IRQn) ; //設置一個中斷掛起
    void NVIC_ClearPendingIRQ(IRQn_Type_IRQn); //清除中斷掛起
    void NVIC_GetPendingIRQ(IRQn_Type_IRQn) ; //讀取中斷掛起狀態
    ```


- 中斷優先級：(0xE000E400~0xE000E41C)
	```
	1. 每個外部中斷都有一個對應的中斷有先級寄存器，每個優先級都是只有一個字節且只有最高2Bit有效
	
	2. NVIC支持字傳輸，所以每次訪問都會涉及4個中斷優先級寄存器。
	```
	
    ```c=
	void NVIC_SetPriority(IRQn_Type_IRQn, uint32_t priority) ; //設置中斷優先級
	uint32_t NVIC_GetPriority(IRQn_Type_IRQn); //讀取中斷優先級
	void NVIC_SetPriority(2, 3) ; //設置#2中斷的優先級為0xC0
	```
    
	:::info
	這裡的priority是0,1,2,3.函數內部會自動移位到對應的優先級最高2位:
	:::


- 異常屏蔽寄存器（PRIMASK）
    ```
    1.對時間敏感的應用，需要用PRIMASK來屏蔽掉除NMI和硬件錯誤異常以外的其他所有中斷和異常。

    2.PRIMASK只有1Bit有效，默認為0，為1時起屏蔽作用。
    ```
    
    ```c=
    void _enable_irq(void) ; //清除PRIMASK值
    void _disable_irq(void) ; //設置PRIMASK值
    ```

## Compiler-specific Features

官方文件[^ref1]中條列了許多編譯器支援的功能, 除了大多編譯器都支援的功能以外, 也包含ARM對C/C++的延伸功能, 下面將列出我認為重要或者在爬程式碼時看到的部分

### Stack use in C and C++
    參考章節[^ref2] 


### Variable attribute
``` __attribute__ ``` 在GNU或是ARM編譯器都有支援, 不過不同 *attribute* 的實作可能有異, 甚至有部分是 ARM 編譯器才有支援, 詳細請查看官方文件.
    
    
- ```  __attribute__((at(address))) ``` : 
    - 用於指定絕對位址, 也等同於輸出 firmware BIN檔中的位址.  
    - 實例: 
        
    	```c= 
    	typedef struct _FW_VERSION_INFO
		{
		    uint16_t minor_version;
		    uint16_t major_version;
		} FW_VERSION_INFO;
    	
    	const FW_VERSION_INFO fw_version __attribute__((at(0x1000))) = {
    	.minor_version = 0x2330, // 30 23
    	.major_version = 119, // 77 00
    	};
    	```
        設定 firmware 版號, 並指定在 0x1000 的位址.
    - 結果:
        
        可以發現輸出的 firmware BIN 檔在 0x1000 的地方為所設的值
        
        ![](https://hackmd.io/_uploads/SJ2AcUJQ_.png)

        
        另外, 如果去查看 map 檔的話也能看到 .ARM.__AT 的字樣
        ![](https://hackmd.io/_uploads/rkmkjLkXO.png)


    

- ```  __attribute__((aligned)) ``` :  
    - 用於對該變數或結構進行最小單位(byte)對齊
    - 實例:
        
        ```c=
        void testFunc() 
        {
            struct test {
        	uint8_t a __attribute__((aligned (8)));
        	uint16_t b __attribute__((aligned (8)));
        	uint32_t c __attribute__((aligned (8)));
            }; 
            
            struct test test1;
            DPRINTF("size of uint8_t  a = %d\n", sizeof(test1.a));
            DPRINTF("size of uint16_t b = %d\n", sizeof(test1.b));
            DPRINTF("size of uint32_t c = %d\n", sizeof(test1.c));
            DPRINTF("size of struct test = %d\n", sizeof(test1));
        }
        ```
        宣告一個成員記憶體大小不同的 structure, 並印出成員及 structure 本身的記憶體大小
    - 結果:
        ```
        size of uint8_t  a = 1
        size of uint16_t b = 2
        size of uint32_t c = 4
        size of struct test = 24
        ```
    :::info
    如果仔細看官方文件會發現 $__attribute__((aligned))$ 有分 type attribute 以及 variable attribute, 我的理解是, 除了針對"變數"以外對於 struct 這類"型別"的也適用. 
    :::
        
- ``` __attribute__((section("name"))) ``` :  將該函數/變數放入指定的區段 
        
    

    
## 參考資料
http://mcu.eetrend.com/content/2016/100002783.html
https://www.cnblogs.com/ollie-lin/p/10934604.html

[^ref1]: [Arm KEIL - Compiler User Guide](https://www.keil.com/support/man/docs/armcc/armcc_chr1359124965789.htm)

[^ref2]: [Arm KEIL - Stack use in C and C++](https://www.keil.com/support/man/docs/armcc/armcc_chr1359124223721.htm)