멋쟁이v의 개발일지

STM32F303 - GPIO, TIMER, 인터럽트 제어 본문

0년차/임베디드

STM32F303 - GPIO, TIMER, 인터럽트 제어

멋쟁이v 2023. 9. 24. 21:07
728x90
320x100
목차
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 클럭을 카운팅한다.

clock
    - HSE(High Speed External)
    - HSI(High Speed Internal)
RTC
    - LSE(Low Speed External)
    - LSI(Low Speed Internal)

종류
    - 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, TIM7
Timer 설정 화면


그리고 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 함수를 호출한다.

버튼은 눌렀을 때마다 LED가 토글되는 모습을 볼 수 있다.






일단 여기까지 정리하고 다음엔 UART 통신에 대해 정리를 해보겠다요.

728x90
320x100
Comments