[TOC]
<font color="red">請先照著步驟設定CubeMX檔,下面會接著對照生成檔案與CubeMX的設定分別出現於何處以及看看HAL庫</font>
# LED閃爍實例
## 功能描述
- 本次以主迴圈方式實現LED的閃爍控制
## 設定CubeMX
- 新建專案時點選**要**initialize
- 在圖形化接腳設定中設定板上的PD12~PD15為輸出,在板上已連接至LED。
- 可以善用右下角的腳位搜尋

- PD12~15的輸出設定皆相同

- 綠色代表有做設定,這邊把沒有要用的I2C1和SPI1先disable

- 時鐘樹的配置先不用動,依照原本預設的即可
- 
## 程式添加
- 找到main函式,在main中找到while(1)迴圈,這是我們的主迴圈,要在這添加程式
- 在while內的user begin之間添加如下程式
``` c++=
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1){
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_SET);
HAL_Delay(1000);
}
/* USER CODE END 3 */
```
## 燒錄
- 使用Keil燒錄程式,將會看到LD3的LED閃爍
# 程式碼架構
## CubeMX生成程式
- 除了while之外,其餘的程式碼,包含所有main.c檔案以外的.c檔都是CubeMX生成的
- 直接看到main函式中,在infinite loop之前,呼叫了一些帶有 INIT 尾綴的函式,這些都是初始設定的部分,如果你使用的編輯器有跳轉功能,你可以到那個函式去看看
- HAL_INIT啟用了中斷群組、系統時鐘和底層硬體
- SystemClock_Config對應了你在CubeMX時鐘樹的設定
- MX_GPIO_Init對應了你在CubeMX做的GPIO設定
- 在MX_GPIO_Init中找到有關LED腳位的設定如下
``` c++=
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, LD4_Pin|LD3_Pin|LD5_Pin|LD6_Pin
|Audio_RST_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PDPin PDPin PDPin PDPin
PDPin */
GPIO_InitStruct.Pin = LD4_Pin|LD3_Pin|LD5_Pin|LD6_Pin
|Audio_RST_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
```
- 可以看到這些設定都是在CubeMX中做的設定
- 其中User Label的部分是讓使用者可以自定義腳位名稱,其實可以在main.h看到以下define,這些腳位(pin)原本在HAL庫的名稱是GPIO_PIN_XX 對應 埠(port,端口) GPIOX
``` c++=
#define LD4_Pin GPIO_PIN_12
#define LD4_GPIO_Port GPIOD
#define LD3_Pin GPIO_PIN_13
#define LD3_GPIO_Port GPIOD
#define LD5_Pin GPIO_PIN_14
#define LD5_GPIO_Port GPIOD
#define LD6_Pin GPIO_PIN_15
#define LD6_GPIO_Port GPIOD
```
- 這邊帶著大家看軟體設定如何對應到程式,如果你的程式是需要在主迴圈中不斷更改設定的,可以參考設定的程式部分如何更動
## STM 的 GPIO設定
- 可參照STM32 HAL庫開發時戰指南 第7章
- GPIO output設定描述
- GPIO output level : 初始化後默認的輸出狀態
- GPIO mode : 推挽或開漏,可見[這篇](https://www.youtube.com/watch?v=dgIyNq3TB_M)
- GPIO Pull-up/Pull-down :
輸出其實沒必要設上下拉電阻,但還是有一種可能是你在主程式中常做GPIO模式的切換,見[討論](https://community.st.com/t5/stm32-mcus-products/output-push-pull-with-pull-up-or-pull-down-capability/td-p/575348)
- Max. output speed :
[GPIO 速度區別](https://blog.csdn.net/tianshi_1988/article/details/53317429)
- User Label : 自定義名稱
- GPIO input 描述設定
- input的話只有上下拉電阻與User label需要自訂
- 上或下拉電阻用途可見[微處理器基礎](https://hackmd.io/6Rj1vNC5S2ya2y2MNScFbg)的GPIO部分
## STM的GPIO操作
- 首先從本例點亮LED的函式HAL_GPIO_WritePin看起,我們到他所在的.h或.c檔中看看
- 在stm32f4xx_hal_gpio.c可以看到這個函式的程式,其餘跟GPIO相關的函式其實也放在這個檔案,在檔案最上面也有GPIO Peripheral features、How to use this driver 的描述,可以大略知道有哪些函式可以用
- 接著看到函式定義,以HAL_GPIO_WritePin來說,它的使用與定義如下
``` c++=
// 庫定義
// @stm32f4xx_hal_gpio.c
/**
* @brief Sets or clears the selected data port bit.
*
* @note This function uses GPIOx_BSRR register to allow atomic read/modify
* accesses. In this way, there is no risk of an IRQ occurring between
* the read and the modify access.
*
* @param GPIOx where x can be (A..K) to select the GPIO peripheral for STM32F429X device or
* x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices.
* @param GPIO_Pin specifies the port bit to be written.
* This parameter can be one of GPIO_PIN_x where x can be (0..15).
* @param PinState specifies the value to be written to the selected bit.
* This parameter can be one of the GPIO_PinState enum values:
* @arg GPIO_PIN_RESET: to clear the port pin
* @arg GPIO_PIN_SET: to set the port pin
* @retval None
*/
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if(PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;
}
}
```
``` C++=
// 函式使用範例
HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_RESET);
```
- 針對HAL庫GPIO所有結構體的命名與內部結構可以看看HAL庫實戰開發內容
- 可以看到函數註解有告訴我們以下事情
- GPIOx表示不同的端口
- GPIO_Pin表示要被更動的端口的某個bit,也就是腳位
- PinState有兩種,GPIO_PIN_RESET表示clear,也就是使腳位為0(低電位),GPIO_PIN_SET相反,使該腳位輸出為1(高電位)
- 在stm32f4xx_hal_gpio.c中常用的GPIO操作函式 :
- **HAL_GPIO_ReadPin** : 腳位做為輸入時,讀取腳位
- **HAL_GPIO_WritePin** : 腳位做為輸出時,設定腳位為高或低電平輸出
- **HAL_GPIO_TogglePin** : 腳位做為輸出時,翻轉腳位輸出(反向運算)
## STM 的Delay
- 延遲使用HAL_Delay做延遲,參數單位為毫秒
# 總結
- HAL庫針對STM各種微處理器功能有一大堆函式可以使用,除了上網查,問GPT外,還是希望各位試著培養閱讀與查找源碼的能力,當上網查不到或是GPT也解決不了時還是只能靠自己!
- 作為微處理器開發用,HAL庫已經非常方便了,幾乎已經不再需要針對各個暫存器的直接操作,但還是需要知道實際操作都是對暫存器做處理的