目的
本文會試用 GPIO output / input / interrupt
GPIO 架構
Output 介紹
在 ioc 那邊選個 pin,選 GPIO_Output
在左邊欄位 System Core 選擇 GPIO
有五個欄位可以設定
-
GPIO output level
-
GPIO mode
- push pull 和 open drain
- 位於架構圖下方那部分,push pull 可以用 PMOS 和 NMOS 來得到高低電位,open drain 會 disable PMOS,讓你可以在外面自己接上拉電阻
-
GPIO Pull-up/Pull-down
-
Maximum output speed
-
User Label
用完記得 ctrl+s 讓他 generate code
1
2
3
4
5
6
7
8
9
|
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = RED_LED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(RED_LED_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET);
|
1
2
3
4
|
HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET); // 低電位
HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_SET); // 高電位
HAL_Delay(1000); //等一秒
HAL_GPIO_TogglePin(RED_LED_GPIO_Port, RED_LED_Pin)
|
根據架構圖左側,你可以透過修改 BSRR 來修改 ODR,達到修改輸出的效果,請見 Reference Manuals,實際上 HAL_GPIO_WritePin
也是這樣實現的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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 = (uint32_t)GPIO_Pin;
}
else
{
GPIOx->BRR = (uint32_t)GPIO_Pin;
}
}
|
看架構圖上方,用 Schmitt trigger 取得高低電位資料,他有 upper threshold 和 lower threshold,而不是用 single threshold
1
2
3
4
5
|
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = GREEN_LED_INPUT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GREEN_LED_INPUT_GPIO_Port, &GPIO_InitStruct);
|
1
|
uint8_t green_led_input = HAL_GPIO_ReadPin(GREEN_LED_INPUT_GPIO_Port, GREEN_LED_INPUT_Pin);
|
Interrupt
ioc 選個 pin,設定 GPIO_EXTI,這邊我選 B1(PC13),也就是開發版上的藍色按鈕
可以選 GPIO mode,這邊選 Falling Edge Trigger,值得一提的是他的設計是上拉電阻,所以這樣不是放開後觸發,是按下後觸發。
ioc 的 System Core 的 NVIC 還要把 EXTI line[15:10] interrupts 給 enabled,然後 Code generation 打開 Generate IRQ handler,還有 Call HAL handler。
在 stm32l4xx_it.c
裡,
現在會有
1
2
3
4
5
6
7
8
9
10
|
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(B1_Pin);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
|
這兩行各別是因為我們剛剛開的功能生的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
|
__weak 代表有同名 function 的話,就會採用沒 __weak prefix 的
所以我們可以在 gpio.c 放下面的程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <stdbool.h>
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == B1_Pin){
static bool prev_val = false;
if(prev_val == false){
HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_SET);
prev_val = true;
}
else{
HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET);
prev_val = false;
}
}
}
|
實驗
設定兩個輸入,一個輸出,一個 interrupt
-
當按下按鈕時,切換紅色 LED 的亮滅,並且讓板子上的綠色 LED 輸出和紅色 LED 相反的結果
-
B1(PC13、藍色按鈕) 按下去的時候,會發出 interrupt,並讓 RED_LED(PC10) 輸出和上次相反的電位,讓麵包版上的紅色 LED 亮滅,正極那邊接一條杜邦線給 GREEN_LED_INPUT (PC12),並且 LD2(PA5、板子上的綠色 LED) 會輸出和紅色 LED 相反的結果。
程式碼