목차 1. LED Blink 2. 타이머 인터럽트로 LED Blink 3. 외부 모듈 인터럽트(EXTI)
STM32f3-discovery 보드로 LED 제어를 해보겠다요.
일단 새 프로젝트를 열고 Board Selector 메뉴에서 검색 후 프로젝트를 연다.
프로젝트 생성
프로젝트를 생성하고 나면 자동으로 핀이 맵핑 되어있는걸 볼 수 있다. 핀 설정 화면
여기서 LED는 밑에 8개가 있다. LED 핀
GPIO 설정 쪽에서 확인 할 수도 있다. GPIO Configuration 화면
이 상태에서 저장(Ctrl + s)를 하면 설정되어 있는대로 코드를 생성해준다.
코드가 생성 되었다면 main.c 에 있는 코드에서 GPIO.Init 함수 부분에 HAL_GPIO_WritePin(GPIO 포트, 핀이름, 상태) 함수가 정의 되어있다. main.c 의 GPIO 초기 설정 함수
WritePin 함수를 사용해도 되지만 우리는 Toggle함수를 사용해서 LED를 Blink 해볼거다. main 함수 부분의 While 문 안에 해당 코드를 작성한다. 프로젝트 빌드 후 코드를 실행하면 1초마다 8개의 LED가 깜빡이는걸 볼 수 있다.
/* 코드생략 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(GPIOE, LD3_Pin);
HAL_GPIO_TogglePin(GPIOE, LD4_Pin);
HAL_GPIO_TogglePin(GPIOE, LD5_Pin);
HAL_GPIO_TogglePin(GPIOE, LD6_Pin);
HAL_GPIO_TogglePin(GPIOE, LD7_Pin);
HAL_GPIO_TogglePin(GPIOE, LD8_Pin);
HAL_GPIO_TogglePin(GPIOE, LD9_Pin);
HAL_GPIO_TogglePin(GPIOE, LD10_Pin);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* 코드생략 */
이제 타이머 인터럽트를 사용해서 LED를 Blink 해보겠다요.
일단 타이머에 대한 개념부터 알아 보자요.
타이머(Timer)란? - 주기적으로 시간을 얻을 때 사용하는 디지털 카운터 회로 - 보통 2가지 형태의 타이머를 가지고 있다. * 범용 타이머 : 아주 빠른 클럭(수 MHz ~ 수 백 MHz)을 카운팅을 할 수 있다. * RTC(Real Time Clock) : 32.768Hz 클럭을 카운팅한다.
종류 - SysTick : 항상 동작하는 시스템 타이머, HAL_Delay() 함수 등에 사용 - WatchDog : CPU의 오동작을 탐지하여 문제가 발생하면 재부팅 시켜주는 타이머 - Basic : 입출력 기능은 없고 시간기반 타이머 용도 - General purpose : 범용 타이머, 출력 비교, 원펄스, 입력캡쳐 등으로 사용 - Advanced control : 범용 타이머보다 많은 기능을 가짐. 주로 모터 제어와 디지털 파워변환 용도로 사용
Clock 용어 정리 - HCLK : Core Clock으로 실제 소스 코드를 동작시키는 Clock입니다. - SYSCLK : System Clock으로 Power On Reset 직후에는 무조건 내부 Clock으로 먼저 동작합니다. - HSE (High Speed External) : 외부 고속 Clock으로 STM32 외부에 Crystal/Ceramic resonator가 필요합니다. Duty가 50% 이하인 외부 구형파, 삼각파 신호로도 사용이 가능합니다. - HSI (High Speed Internal) : 내부 고속 Clock으로 STM32에 내장되어 있는 RC 발진 회로에 의해서 동작되는 Clock입니다. 자체 Calibration 기능이 있지만, RC 발진회로의 특성 문제로 온도 상승에 따른 오차가 발생합니다. - LSE (Low Speed External) : 외부 저속 Clock으로 32.768kHz의 Crystal / Ceramic resonator를 사용합니다. 용도는 저전력 구현 및 정확한 시간(RTC)을 맞추기 위해서입니다. - LSI (Low Speed Internal) : 내부 저속 Clock으로 Independent Watchdog과 AWU(Auto Wakeup)기능 및 RTC Clock에 사용됩니다. (정확성 확보가 어려움) - CSS(Clock Scurity System) : HSE clock에 문제가 발생할 경우, NMI interrupt 발생 및 Clock source를 HSI Clock으로 변경해주는 기능입니다.
이게 다 무슨 말인지는 Clock 설정을 하다보면 다 알게 된다.
핀 설정을 다시 가서 Timer를 활성화 시켜준다. Basic을 사용하기 때문에 TIM6를 Activated 해준다. (data sheet에 TIM 참고)
Advanced control - TIM1, TIM8 General purpose - TIM2, TIM3, TIM4, TIM5, TIM9~14 Basic - TIM6, TIM7Timer 설정 화면
그리고 Parameter Settings 부분에 Counter Settings를 해줄거다.
용어정리 - Prescaler: 타이머로 들어오는 APB1, APB2 클럭을 나누기 위한 Prescaler 값 - CounterMode: 카운터가 증가, 감소, 증가 후 감소 (CENTERALIGNED) 모드 등을 설정하는 값 - Period: 타이머의 주기를 결정하는 최대 값, 0x0 으로 설정 시 타이머 구동 중지 - ClockDivision: 내부 타이머 클럭 주기와 ETRx, TIx 핀에서 사용되는 샘플링 클럭 사이의 Division 비율 - RepetitionCounter: Update Register 가 Set 되기 전 몇번의 Overflow/Underflow 이벤트가 있어야 하는지 설정, 즉 특정 회수를 반복해야 IRQ 가 발생하도록 설정 가능
시간 설정 공식 - UpdateEvent = Timer clock / (Prescaler+1)(Period+1)
Timer clock 설정
시간 설정 -> 48M / (23999 x 1999) = 1초
시간 설정하는 계산식으로 setting을 해준다. 현재는 1초로 설정이 되어있는 상태이다. Timer 설정
그리고 NVIC Settings에서 인터럽트를 Enable 시켜준다.
NVIC 설정
인터럽트(interrupt) - 마이크로프로세서(CPU)가 프로그램을 실행하고있을 때, 입출력하드웨어 등의 장치에 예외상황이 발생하여 처리가 필요할 경우에 마이크로프로세서에게 알려 처리할 수 있도록 하는 것을 말한다.
NVIC(Nested Vectored Interrupt Controller) - Cortex-M MCU 내부에서 인터럽트를 받아들이고 처리하기 위해 사용되는 인터럽트 컨트롤러를 말한다.
지금 상태에서 저장을 하게 되면 TIM6가 생긴 코드가 생성된다. Timer 초기화 함수
그 다음 main문 안에 타이머 인터럽트 함수를 호출하면 타이머 IRQHandler 함수가 실행이 되면서 HAL_TIM_PeriodElapsedCallback 함수를 호출하게 된다.
그래서 다른 파일에 선언만 되어있는 HAL_TIM_PeriodElapsedCallback 함수를 main.c 파일에 구현한다. (LED 토글을 함수 안으로 이동)
/* 코드생략 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* 코드생략 */
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance==TIM6)
{
HAL_GPIO_TogglePin(GPIOE, LD3_Pin);
HAL_GPIO_TogglePin(GPIOE, LD4_Pin);
HAL_GPIO_TogglePin(GPIOE, LD5_Pin);
HAL_GPIO_TogglePin(GPIOE, LD6_Pin);
HAL_GPIO_TogglePin(GPIOE, LD7_Pin);
HAL_GPIO_TogglePin(GPIOE, LD8_Pin);
HAL_GPIO_TogglePin(GPIOE, LD9_Pin);
HAL_GPIO_TogglePin(GPIOE, LD10_Pin);
}
}
/* USER CODE END 4 */
/* 코드생략 */
코드를 실행하게 되면 기존에 While문 안에서 반복하는 방식과 똑같이 LED가 작동한다.
이번에는 MCU 보드에 있는 USER 버튼을 사용해서 LED를 제어 해보겠다요.
먼저 외부 인터럽트에 대한 개념은 이렇다.
EXTI(External Interrupt-외부 인터럽트) - MCU 외부에 있는 IC나 모듈로부터 인터럽트를 받아들이고 처리하기 위해 사용되는 인터럽트 컨트롤러를 말한다.
핀 Configuration에서 GPIO 세팅을 보면 이렇게 핀이 설정 되어있는걸 볼 수 있다. GPIO 설정
설정은 이미 되어있으니 ioc 에선 건들게 없다.
버튼pin이 B1의 이름으로 설정되어 있는걸 확인했으니 다시 main.c 파일로 돌아와서 해당 코드를 작성한다.
/* 코드생략 */
/* USER CODE BEGIN MX_GPIO_Init_2 */
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
/* USER CODE END MX_GPIO_Init_2 */
/* 코드생략 */
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
if(GPIO_Pin == B1_Pin){
HAL_GPIO_TogglePin(GPIOE, LD3_Pin);
HAL_GPIO_TogglePin(GPIOE, LD4_Pin);
HAL_GPIO_TogglePin(GPIOE, LD5_Pin);
HAL_GPIO_TogglePin(GPIOE, LD6_Pin);
HAL_GPIO_TogglePin(GPIOE, LD7_Pin);
HAL_GPIO_TogglePin(GPIOE, LD8_Pin);
HAL_GPIO_TogglePin(GPIOE, LD9_Pin);
HAL_GPIO_TogglePin(GPIOE, LD10_Pin);
}
}
/* USER CODE END 4 */
/* stm32f3xx_it.c <- 파일로 이동 */
/* USER CODE BEGIN 1 */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(B1_Pin);
}
/* USER CODE END 1 */
인터럽트가 발생하면 HAL_GPIO_EXTI_IRQHandler가 호출된다. 그리고 해당 핀의 상태를 Clear 해주고 Callback 함수를 호출한다.