[TOC] <font color="red">請先照著步驟設定CubeMX檔,下面會接著對照生成檔案與CubeMX的設定分別出現於何處以及看看HAL庫</font> # LED閃爍實例 ## 功能描述 - 本次以主迴圈方式實現LED的閃爍控制 ## 設定CubeMX - 新建專案時點選**要**initialize - 在圖形化接腳設定中設定板上的PD12~PD15為輸出,在板上已連接至LED。 - 可以善用右下角的腳位搜尋 ![image](https://hackmd.io/_uploads/rkUAPDX2A.png) - PD12~15的輸出設定皆相同 ![image](https://hackmd.io/_uploads/Hku0uDQnR.png) - 綠色代表有做設定,這邊把沒有要用的I2C1和SPI1先disable ![image](https://hackmd.io/_uploads/BkT-FR7nA.png)![image](https://hackmd.io/_uploads/B1G_g1NnA.png) - 時鐘樹的配置先不用動,依照原本預設的即可 - ![image](https://hackmd.io/_uploads/Hyp3YA72A.png) ## 程式添加 - 找到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庫已經非常方便了,幾乎已經不再需要針對各個暫存器的直接操作,但還是需要知道實際操作都是對暫存器做處理的