프로젝트를 하다보면 버튼 수가 모자라거나, 여러 버튼을 사용하기엔 핀이 모자랄 수 있다.
따라서 버튼 하나로 여러 기능을 구현할 수 있도록
길게 눌렀을 때, 더블 클릭했을 때, 그냥 한 번 눌렀을 때를 구분하고자 한다.
pulling 방식보단 interrupt 방식이 cpu를 덜 잡아먹으므로 나는 인터럽트 방식을 사용했다.
1. 핀, 환경 설정
버튼 핀은 사용자마다 다를 수 있다.
나는 PF7 핀에 Pull-Up 저항을 사용하였다.
버튼을 길게 눌렀다가... 떼는! 순간을 인식할 수 있어야 하기 때문에 Rising/Falling edge를 모두 읽을 수 있어야 한다!!
인터럽트를 사용할 것이므로 관련 세팅까지 꼭 해주자.
설정을 다 했으면 generate code를 하고 main.c로 넘어가자~
2. 코드 작성
printf를 사용할 예정이라 재정의를 해줬다.
/* USER CODE BEGIN PV */
int _write(int file, char* ptr, int len)
{
HAL_UART_Transmit(&huart3, (uint8_t*)ptr, len, 500);
return len;
}
/* USER CODE END PV */
HAL 드라이버의 시간 관련은 uint32_t 타입으로 되어 있다.
(HAL_GetTIck() 메서드 들어가면 확인 가능)
시간 관련 변수를 unsinged int로 정의해도 되지만, 상수 값 범위가 조금 다를 수 있다.
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ETH_Init(void);
static void MX_USART3_UART_Init(void);
static void MX_NVIC_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint32_t ctime, ltime, interval;
int level;
uint32_t double_key_cnt;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_7) {
ctime = HAL_GetTick();
interval = ctime - ltime;
ltime = ctime;
level = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
if (level == 1) {
// printf("interval = %u ", (unsigned int)interval);
if (interval < 100) {
double_key_cnt++;
}
else if (interval >= 100 && interval <= 200) {
printf("One click \r\n");
double_key_cnt = 0;
}
else if (interval >= 700) {
printf("long \r\n");
double_key_cnt =0;
}
if (double_key_cnt >= 2) {
printf("double~~~~~~~~~\r\n");
double_key_cnt = 0;
}
}
}
}
(코드를 그대로 가져오니 들여쓰기가 안 예쁘다.. 복붙해서 Ctrl + Shift + F로 정렬하는 것을 추천한다..)
버튼 인터럽트 이벤트 발생했을 때, 원하는 동작을 HAL_GPIO_EXTI_Callback 함수에 재정의하자.
다른 핀의 인터럽트에 의해서 호출될 수도 있으므로 GPIO_PIN_7이 맞는지 걸러줬다.
실행됐을 때 시간을 HAL_GetTick()으로 얻어와 ctime 변수에 넣었고, interval로 콜백 사이(0으로 유지시간, 1로 유지시간)의 간격을 나타낸다.
(중요!) Rising과 Falling을 모두 인식하게 했기 때문에, 이게 Rising상황인지 Falling 상황인지
즉, 버튼을 뗀 상황인지 누른 상황인지를 구분할 필요가 있다.
이를 위해 GPIO_Read_Pin으로 레벨 상태를 읽었고, 버튼을 뗐을 때! 이게 one인지 double인지 long인지를 구분했다.
Pull-UP 모드이므로 평소엔 1이다가 버튼을 눌렀을 때만 0이 된다.
버튼을 눌렀다가 뗐을 때 (0이었다가 1이 됐을 때)에 동작하도록 구현했다.
( Pull-down은 level == 0으로 하면 되겠죠? )
더블 클릭이 구현하기 힘들었는데,
마지막 빨간 화살표에서 더블 클릭이었구나를 인식하려면, 그 앞의 파,빨,파 화살표는 무시해야 한다.
따라서 콜백 호출마다(누르든 떼든) double_key_cnt로 값을 하나씩 증가해주고, 3이 되면 더블클릭이구나~를 알게 했다.
3. 결과 확인
버튼 클릭 상황에 따라 콘솔 창에 출력된다.
'임베디드 (Embedded) > STM32' 카테고리의 다른 글
[STM32] RTC Alarm 기능 사용하기 (0) | 2023.11.14 |
---|---|
[STM32] LCD 커서 이동 (0) | 2023.11.14 |
[STM32] STM32CubeIDE i2C-LCD 사용하기 (1) | 2023.11.08 |
[STM32] STM32CubeIDE 조이스틱 설정 ADC, DMA (0) | 2023.11.07 |
[STM32] NUCLEO-F429ZI 타이머2,3,4를 이용하여 버튼 led 토글하기 및 주파수 계산 (0) | 2023.10.26 |