---
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

## 中斷
- 啟用/關閉中斷
```
中斷寄存器是可編程的,用於控制中斷請求(異常編號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 的地方為所設的值

另外, 如果去查看 map 檔的話也能看到 .ARM.__AT 的字樣

- ``` __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)