[STM32] Timer

pikamon·2021년 1월 10일
1

STM32

목록 보기
12/12

본 글은 STM32F769I MCU를 기준으로 작성되었습니다.
세부적인 내용은 제품군마다 조금씩 다를 수 있습니다.


1. Timer란?

Timer란 임의의 주기를 갖는 신호를 측정하거나 생성할 때 사용되는 디지털 회로를 말한다. 특정 주기마다 이벤트를 발생시키거나, 외부에서 들어온 신호의 길이를 측정할 때 사용한다. 또 PWM과 같은 파형을 생성하여 출력으로 보낼 때도 사용된다. MCU에서 GPIO, 통신 모듈과 더불어 자주 사용되는 모듈 중 하나이다.

STM32F769I에는 타이머가 총 18개 있다고 한다. 읽어보면 여러 종류의 타이머들이 있다고 한다.

일반 타이머, PMW 파형 생성용 타이머, DAC 트리거용 타이머, 저전력 타이머 등이 있다고 한다. 용도에 맞게 적절히 선택하여 사용하면 될 것 같다.

2. 예제

Basic 타이머 두 개를 이용하여 각각 1초, 10초마다 이벤트를 발생시켜보자.

TIM6, 7을 Basic 타이머라고 한다고 한다. 주로 DAC 트리거용으로 사용되지만 일반 16비트 카운터로 동작하기도 한다고 한다.

아래 그림을 보면 TIM6, 7이 APB1 버스에 물려있는 것을 볼 수 있는데, APB1의 타이머 주파수를 조정하여 타이머 이벤트 발생 주기를 조절할 수 있다.

1. Configuration

Clock Configuration에 들어가자.

APB1의 클럭 주파수가 24MHz로 설정되어 있는데, 변경하고 싶으면 변경하자.

그리고 Pinout & Configuration에서 TIM6, 7을 활성화한다.

여기서 Counter Mode와 Prescaler, Counter Period를 설정해주어야 한다.

Counter Mode는 Up으로 설정되어 있으며, 이는 카운터 값을 0에서부터 증가시켜가며 원하는 값에 도달했는지를 비교하는 방식을 말한다.
다른 방식에는 Down과 Up/down이 있지만, 콤보박스 내려보면 Up 이외에는 선택이 안되니 그대로 두자.

Prescaler와 Counter Period를 설정해야 하는데, 아까 위에서 말한 버스 타이머 주파수와 Prescaler 값, 그리고 Counter Period 값 세 가지를 통해 타이머 이벤트 발생 주기가 결정되며, 공식은 아래와 같다.

예를 들어 Prescaler 값을 23999, Counter Period 값을 999로 설정하면 아래와 같이 1초 주기로 타이머 이벤트가 발생한다.

EventPeriod = 24,000,000 / ((24,000) * (1,000))
= 24,000,000 / 24,000,000
= 1Hz
= 1sec

마찬가지로 10초 주기로 이벤트를 발생시키려면 Counter Period 값을 9999로 설정하면 된다.

TIM6, 7를 각각 아래와 같이 설정한다.

ioc 파일을 저장하여 코드를 생성한다.

2. 코드 작성

타이머 체크를 폴링으로 하도록 하였으며, 이벤트 발생 시 UART1을 통해 로그를 출력하도록 하였다.

전체 코드는 아래와 같다.

#include "main.h"

#include <stdio.h>

TIM_HandleTypeDef htim6;
TIM_HandleTypeDef htim7;
UART_HandleTypeDef huart1;

void SystemClock_Config(void)
{
	RCC_OscInitTypeDef RCC_OscInitStruct = { 0, };
	RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0, };

	__HAL_RCC_PWR_CLK_ENABLE();
	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
	RCC_OscInitStruct.HSIState = RCC_HSI_ON;
	RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
	RCC_OscInitStruct.PLL.PLLM = 8;
	RCC_OscInitStruct.PLL.PLLN = 96;
	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
	RCC_OscInitStruct.PLL.PLLQ = 4;
	RCC_OscInitStruct.PLL.PLLR = 2;
	HAL_RCC_OscConfig(&RCC_OscInitStruct);

	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
	HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
}

static void MX_TIM6_Init(void)
{
	TIM_MasterConfigTypeDef sMasterConfig = { 0, };

	htim6.Instance = TIM6;
	htim6.Init.Prescaler = 23999;
	htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
	htim6.Init.Period = 999;
	htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
	HAL_TIM_Base_Init(&htim6);

	sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
	HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig);
}

static void MX_TIM7_Init(void)
{
	TIM_MasterConfigTypeDef sMasterConfig = { 0, };

	htim7.Instance = TIM7;
	htim7.Init.Prescaler = 23999;
	htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
	htim7.Init.Period = 9999;
	htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
	HAL_TIM_Base_Init(&htim7);

	sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
	HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig);
}

static void MX_USART1_UART_Init(void)
{
	huart1.Instance = USART1;
	huart1.Init.BaudRate = 115200;
	huart1.Init.WordLength = UART_WORDLENGTH_8B;
	huart1.Init.StopBits = UART_STOPBITS_1;
	huart1.Init.Parity = UART_PARITY_NONE;
	huart1.Init.Mode = UART_MODE_TX_RX;
	huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	HAL_UART_Init(&huart1);
}

static void MX_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = { 0, };

	__HAL_RCC_GPIOA_CLK_ENABLE();

	GPIO_InitStruct.Pin =  GPIO_PIN_10 | GPIO_PIN_9;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

int _write(int fd, char *ptr, int len)
{
	HAL_UART_Transmit(&huart1, (unsigned char*)ptr, len, HAL_MAX_DELAY);
	return len;
}

int main(void)
{
	HAL_Init();
	MX_GPIO_Init();
	MX_USART1_UART_Init();
	MX_TIM6_Init();
	MX_TIM7_Init();

	// Start Timer
	HAL_TIM_Base_Start(&htim6);
	HAL_TIM_Base_Start(&htim7);
	__HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE);
	__HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);

	unsigned int counter_1sec = 0;
	unsigned int counter_10sec = 0;


	while (1)
	{
	    if (__HAL_TIM_GET_FLAG(&htim6, TIM_FLAG_UPDATE))
	    {
			__HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE);
			printf("1sec Timer Event Occurred! (%d)\r\n", ++counter_1sec);
	    }

	    if (__HAL_TIM_GET_FLAG(&htim7, TIM_FLAG_UPDATE))
	    {
			__HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);
			printf("10sec Timer Event Occurred! (%d)\r\n", ++counter_10sec);
	    }
	}
}

void Error_Handler(void)
{

}

3. 빌드 및 실행 결과

빌드 후 실행하면 아래와 같이 1초, 10초 주기로 로그를 출력하는 것을 볼 수 있다.

profile
개발자입니당 *^^* 깃허브 https://github.com/pikamonvvs

0개의 댓글