# STM32 HAL Driver Note
這裡會存放 STM32 HAL Driver 的相關程式筆記。
# 目錄
[TOC]
# Intro
- HAL Library(Hardware Abstraction Layer,硬體抽象層函式庫)。
- 抽離硬體暫存器的概念,可以用於易讀的程式碼方便開發應用,也可以方便的移植程式碼到其他平台。

# GPIO
## Enum
- GPIO_PinState:腳位狀態
```c
typedef enum
{
GPIO_PIN_RESET = 0,
GPIO_PIN_SET
} GPIO_PinState;
```
## Output
```c
// 設置該腳位為高電位
HAL_GPIO_WritePin(PORT, PIN, GPIO_PIN_SET);
// 設置該腳位為低電位
HAL_GPIO_WritePin(PORT, PIN, GPIO_PIN_RESET);
// 反向該腳位的電位 (低 => 高, 高 => 低)
HAL_GPIO_TogglePin(PORT, PIN);
```
## Input
```c
// 讀取該腳位電位狀態後反回GPIO_PinState
HAL_GPIO_ReadPin(PORT, PIN);
```
## Interrupt
1. 不建議在中斷向量呼叫任何阻斷系統/GPIO操作的函式,如`HAL_Delay()`。
- 中斷內的程式需要快速完成,不要影響到主程式的運作。
- 可以更改變數值,在主迴圈中實現剩下的邏輯。
2. 若遇到多個向量中斷得同時進行,需要設定優先順序 (Preemption Priority)。
- 數字越高,中斷優先權越高。
### Steps
1. 於STM32CubeMX中,調整按鈕的腳位為EXIT_XX_XX (外部中斷)。

2. 到`System Core` -> `GPIO`,設定該腳位的中斷觸發方法。
1. 上升沿觸發 (Rising Edge)。
2. 下降沿觸發 (Falling Edge)。
3. 上升與下降沿同時觸發 (Rising / Falling Edge)。

3. 若未在電路中實作上拉/下拉電路,需配置GPIO的Pull Up/Down。
- 有的話則選`No Pull Up / Pull Down`。

4. 到`System Core` -> `NVIC`,設定該腳位的中斷向量函式。

5. 重新生成程式。
## Code
- 主程式宣告:main.h
```c
/* USER CODE BEGIN EC */
extern GPIO_PinState _btn_status;
/* USER CODE END EC */
```
- 主程式:main.c
```c
/* USER CODE BEGIN PV */
GPIO_PinState _btn_status = GPIO_PIN_RESET;
/* USER CODE END PV */
// 以下略...
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
// 當按鈕按下,更換Delay為500ms,再次按下則更換為2000ms。
if (_btn_status == GPIO_PIN_SET)
HAL_Delay(500);
else
HAL_Delay(2000);
}
/* USER CODE END 3 */
```
- 中斷:stm32xxx_it.c
```c
/**
* @brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
// reverse flag
_btn_status = (_btn_status == GPIO_PIN_SET) ? GPIO_PIN_RESET : GPIO_PIN_SET;
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
```
# UART
## Steps
1. `Connectivity -> USARTx / UARTx`
- `Mode`:Asynchoronous (非同步/異步)
- 由各自系統的時鐘計算頻率
- `BaudRate`:傳輸速率,單位為 Bit/s
- `Word Length`:字元長度,預設都是8位元
- `Parity`:校驗碼,預設為None
- `Stop Bits`:停止位元,預設為1

2. `Configuration -> NVIC Settings`
- 可以根據需求開啟/關閉NVIC中斷
- 也可以到`System Core -> NVIC`設定啟用與優先級

## Code
### Init
```c
/* Initialize all configured peripherals */
MX_USARTx_UART_Init();
```
### Write - No Interrupt
- 傳輸訊息的Buffer資料型別必須為`uint8_t`陣列。
- 長度的資料型別為`uint16_t`整數。
```c
HAL_UART_Transmit(&huart1, buffer, 4);
```
### Write - With Interrupt
- HAL_UART_TxCpltCallback為UART TX的中斷回調函式。
```c
HAL_UART_Transmit_IT(&huart1, buffer, 4); // Send Measure Message To PC
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
// Do Smth...
}
```
# Timer
- 分成5個Timer (F1/F3/F4/F7)
- **Basic Timer** (基本定時器)
- 只有上數模式 (Up)
- 可以自動重載
- **Low Power Timer** (低功耗定時器)
- **General-Purpose Timers** (通用定時器)
- 上數模式、下數模式、上/下數自動重置模式
- 四個獨立通道
1. 輸入捕獲
2. 輸出比較
3. PWM波型產生
4. 一次性波形輸出
- **Advanced-Control Timers** (進階控制定時器)
- 部分同通用定時器
- **High-Resolution Timers** (高解析度定時器)
- 217ps高解析度
## 觸發時間計算
$$
T_{out}=\frac{PSC * ARR}{Fclk}
$$
- Tout:觸發時間,單位為秒(s)。
- PSC:預分頻係數,最高為65535。
- ARR:重載計數器數值,最高為65535。
- Fclk:系統時鐘頻率,根據OSC而定,單位為Hz。
## Basic Timer
- 系統頻率為32MHz,以每秒5Hz的頻率 (200ms),進入中斷控制LED亮滅
- $0.2s * 32MHz = 6400000$
- $6400000 / 1000 = 6400 (Preload)$
### Steps
1. 開啟TIM6/TIM7的定時器並設定如下:

- PSC設定1000
- Counter Perlod(Preload)設定6400
- 開啟auto-reload preload
2. 到NVIC Settings,啟用NVIC TIM6全域中斷

## Code
### Init
```c
/* Initialize all configured peripherals */
MX_TIM6_Init();
```
- 開啟中斷
- 這步沒做,中斷就不會開啟
```c
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim); // Start Timer Interrupt
/* USER CODE END 2 */
```
- TIM中斷函數
- HAL_TIM_PeriodElapsedCallback為 Timer 在 Up 模式下,計數器到達Period數字時的中斷回調函式。
- `htim->Instance`:發生中斷的Timer Instance,可以用來判斷是哪個Timer產生了中斷。
```c
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// Check Callback Is Generated By Timer X
if (htim->Instance == TIMX)
{
// Toggle LED
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
}
/* USER CODE END 4 */
```
# ADC
> TODO: 整理得好看一點
## Steps



## Code
### Init
```c
/* Initialize all configured peripherals */
MX_ADC1_Init();
```
### Polling
```c
HAL_ADC_Start(&hadc1); // Open ADC Conversion
HAL_ADC_PollForConversion(&hadc1, 1); // Wait Conversion Complete or Timeout
adc_val = HAL_ADC_GetValue(&hadc1); // Get ADC Value
```
### Interrupt
```c
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
/*
* Check Callback Is Generated By Specific ADC Unit
*/
if (hadc->Instance == ADC1)
{
adc_val = HAL_ADC_GetValue(hadc); // Get ADC Value
adc_volt = ((3.3 / 4096) * adc_val) * 1000; // Calculate Convert Volt (Format: mV, FK407MK2 VDDA is 3.3V)
HAL_ADC_Stop_IT(hadc); // Stop ADC Interrupt Convert
}
}
```
# CAN
- 主要應用在車內訊息與IoT物聯網之間的傳輸。
- 有高可靠性與不錯的抗干擾、糾錯能力。

## CAN 2.0 vs FDCAN
| 項目 | CAN2.0 | FDCAN |
| ------------ | ----------------- | -------------------------------- |
| 最大資料長度 | 8 Bytes | 64 Bytes |
| 傳輸速率 | 固定速率 ≤ 1 Mb/s | 仲裁 ≤ 1 Mb/s,資料階段可高達 8× |
| CRC | 15-bit | 17/21-bit (以Payload 長度而定) |
| Control Byte | RTR/IDE | RTR/IDE/EDL/BRS/ESI |
| 過濾機制 | 傳統標準器/mask | 用RAM分割,可靈活配置 |
| 硬體特性 | 簡易 | Message RAM、FIFO、延遲補償、CCU |
| 相容性 | 不支援 | 向下相容 CAN 2.0 |
* EDL:區分是否為 FD 幀
* BRS:控制資料階段速率
* ESI:顯示錯誤狀態(error-active 或 passive)
### Packet Structure
- CAN 2.0
.jpg)
- FDCAN
.jpg)
### Filter Config
- CAN 2.0 (HAL)
```c
CAN_FilterTypeDef canFilterConfig;
canFilterConfig.FilterActivation = CAN_FILTER_ENABLE;
canFilterConfig.FilterBank = 0; // which filter bank to use from the assigned ones
canFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
canFilterConfig.FilterIdHigh = 0x0;
canFilterConfig.FilterIdLow = 0x0;
canFilterConfig.FilterMaskIdHigh = 0x0;
canFilterConfig.FilterMaskIdLow = 0x0;
canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
canFilterConfig.SlaveStartFilterBank = 0; // how many filters to assign to the CAN1 (master can)
if (HAL_CAN_ConfigFilter(&HCAN, &canFilterConfig) != HAL_OK)
{
Error_Handler();
}
```
- FDCAN (HAL)
- 要設定HAL_FDCAN_ConfigGlobalFilter (FDCAN全域Filter),否則FDCAN無法正常運作。
- **hfdcan**:要設定的CAN Handle
- **StdIdFilter**:標準 ID 如何處理其他非 filter 範圍的 frame
- **ExtIdFilter**:延伸 ID 的處理
- **StdRmtFilter**:標準 Remote frame 處理
- **ExtRmtFilter**: 延伸 Remote frame 處理
```c
FDCAN_FilterTypeDef canFilterConfig;
canFilterConfig.idType = FDCAN_STANDARD_ID;
canFilterConfig.filterIndex = 0;
canFilterConfig.filterType = FDCAN_FILTER_MASK;
canFilterConfig.filterConfig = FDCAN_FILTER_TO_RXFIFO0;
canFilterConfig.filterID1 = 0x00000000; // Filter ID 1
canFilterConfig.filterID2 = 0x00000000; // Filter ID 2
canFilterConfig.rxBufferIndex = 0; // Use RX FIFO 0
HAL_FDCAN_ConfigFilter(&hfdcan1, &canFilterConfig);
/**
* Standard Filter Configuration, Need To Add This Line, Otherwise, FDCAN will not work properly
* FDCAN_ACCEPT_IN_RX_FIFO0: Accepts messages in RX FIFO 0 (Std And Ext Messages)
* FDCAN_FILTER_REMOTE: Accepts remote frames (Std And Ext Messages)
*/
HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE);
```
# Misc
- HAL_Delay
- 採用阻斷式 (讓卡住CPU一段時間),單位為ms。
```c
HAL_Delay(1000); // 延遲1000毫秒 = 1秒
```
- NOP
- 什麼事都不做,方便在Debug模式時設定中斷點。
```c
__NOP();
```