본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 38일차 - ARM & RTOS 활용(ADC, Interrupt, Timer)

by Graffitio 2023. 8. 18.
[Harman 세미콘 아카데미] 38일차 - ARM & RTOS 활용(ADC, Interrupt, Timer)
728x90
반응형
[ADC 활용]

 

ADC Parameter Setting

 

하나의 채널 사용 시에는 별도의 세팅이 필요없지만,

여러 개의 채널을 사용할 때는 Parameter setting에 신경을 좀 써줘야 한다.

 


 

Clock Configuration

 

Clock Configuration

<Clock 계산법>

   Clock Prescaler 84MHz ÷ 4분주 = 21MHz

   Resolution 15 cycle 

   = 약 0.8ms

 

 


 

[Interrupt]

 

ADC Interrupt

 

순차적으로 잘 작동되고 있는 Flow 중에 어떠한 돌발 상황이 발생하여 다른 작업이 실행될 때,

여기서 돌발 상황이 바로 Interrupt

Interrupt가 발생하면, 이전까지의 작업은 저장되고 Holding(일시정지)되며

ISR을 수행한 뒤 다시 원래 작업으로 복귀한다.

 - ISR(Interrupt Service Routine, ≒ Callback)

 

함수 호출과 유사하지만,

ISR은 이전 작업들은 싹 무시하고 독립적인 작업을 수행한다는 점에서 차이가 있다.

 

Interrupt는 우선순위를 가지며, 이 순위에 따라 처리된다.

최우선 순위 Interrupt : NMI(Non Maskable Interrupt)

대부분의 NMI는 devide by 0(0으로 나눈 경우)에 의해 발생하며, 대부분 하드웨어 설정에 따른다.

 

 

위와 같이 ADC 마다 하나의 Interrupt가 제공되고 Conversion이 수행되었을 때, Interrupt가 발생된다.

 

Interrupt 발생 시마다 Joystick 위치가 출력되게끔 만들어보자.

(ATmega할 때, ISR쓰는 방식이랑 비슷함)

Sampling이 끝날 때마다 Interrupt 발생

메인 함수에서는 while loop만 무한으로 돌아감

 

<주의사항>

각각의 채널을 개별적으로 처리해줘야 된다. 

따라서 Callback 함수를 만들어 Case별로 구분해주어야 한다.

 

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int __io_putchar(int ch) // 1 byte output function to CONSOLE(monitor)
{
//	char a = ch;
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

int nCount;
int xVal, yVal, zVal; // 전역변수이므로 초기화 필요없음.

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) // Callback function
{
	switch(nCount)
	{
		case 0 :
			xVal = HAL_ADC_GetValue(&hadc1)*90/4095;
			nCount++;
			break;
		case 1 :
			yVal = HAL_ADC_GetValue(&hadc1)*90/4095;
			nCount++;
			break;
		case 2 :
			zVal = !(HAL_GPIO_ReadPin(Z_Value_GPIO_Port, Z_Value_Pin));
			nCount++;
			break;
		case 3 :
			printf("Joystick(Interrupt) X : %d, Y = %d, Z = %d \r\n", xVal, yVal, zVal);
			nCount = 0;
	}
	HAL_ADC_Start_IT(&hadc1); // Restart ADC conversion
}
/* USER CODE END 0 */

 

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_USART2_UART_Init();
  MX_ADC1_Init();

  HAL_ADC_Start_IT(&hadc1); // ISR 시작하게 해주는 함수
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  HAL_Delay(300);
  }
  /* USER CODE END 3 */
}

 

  HAL_ADC_Start_IT(&hadc);

    : interrupt service roiutine을 재시작하라는 의미

     안 써주면, 인터럽트 한 번 실행 후 종료되어버린다.

 

ISR은 따로 딜레이를 지정해줄 수 없기 때문에 엄청난 속도로 출력된다.

 


 

Timer

 

 

NMI 수준은 아니지만, 아주 중요한 인터럽트 중 하나이다.

 

1. Timer mode 

Timer mode

     가장 유념해야될 항목

     Clock Source(어디에서 clock을 인가받을 것인가 설정하는 기능)

 

2. Timer Configuration

 

    <주기>

    Trigger, Tick이라 불리우는 것들을 어떤 간격으로 할 것인지 설정해줘야 한다.

    분주기를 활용하여 설정 가능하다.

    Visual studio에 비해 좀 복잡하게 되어 있다.

 

    우리가 사용할 Timer(나는 TIM3 사용)가 PCLK1, 2 중 어느 것을 쓰는 지는 

    RM(Reference Manual)을 보면 알 수 있다.

 

    <계산법>

      Clock 주파수,  Prescaler,  Period

      이 세가지를 알아야 정확한 clock 발생 주기를 계산하여 설정할 수 있다.

      ex)

       Clock 주파수 : 84MHz

       Prescaler : clock 주파수와 맞춰서 설정해주면 편하다.

                         84,000,000Hz인데, 제약조건(16bit = 2^16 = 65,536)이 있다.

                         84,000에서 끊으면 편한데, 제약조건때문에 8400 에서 끊어야 된다.

                         또한 devide by 0 를 방지하기 위해 8400-1

                         ∴ Prescaler = 8399

       Period : 8400에서 끊었으므로, 주기는 기본적으로 10,000이 되어야 한다.

                    하지만 얘도 devide by 0를 방지하기 위해 10,000-1

                    ∴ Period = 9999

       이렇게 만들어진 clock 발생 주기

       84,000,000 ÷ 8399×9999 ≒ 1,000ms(1sec)

       참고 사항)

       웬만하면 Prescaler는 건드리지 않는 것이 좋다.

       Clodk 발생 주기를 변경하고 싶다면, Period를 조절해서 변경할 것.

 

얘도 마찬가지로 Interrupt Enable
Clock Configuration에서 84MHz인 것을 확인할 수 있다.
1초의 주기를 가지는 Timer setting
main.c 에서도 셋팅이 가능하다.

하지만, 소스파일에서 수정하면 ioc 에서는 적용이 되지 않는다.

그 상태로 code generation하면, ioc의 내용이 소스파일에 override된다.

따라서 한 곳에서만 작업하는 일관성을 가지다.

 

타이머 인스턴스가 TIM3인지 확인하는 과정

 

3. Timer Interrupt를 활용하여 Joystick_Test 함수 출력

 

 

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM3_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int __io_putchar(int ch) // 1 byte output function to CONSOLE(monitor)
{
//	char a = ch;
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

void Joystick_Test()
{
	int xVal = 0;
	int yVal = 0;
	int zVal = 0;

	HAL_ADC_Start(&hadc1); // ADC 변환 시작
    
    // 첫 번쨰 ADC 변환 완료를 대기
	HAL_ADC_PollForConversion(&hadc1, 100);
	xVal = HAL_ADC_GetValue(&hadc1)*90/4095; // xVal Read(Read Rank1)
    
    // 두 번째 ADC 변환 완료를 대기
	HAL_ADC_PollForConversion(&hadc1, 100);
	yVal = HAL_ADC_GetValue(&hadc1)*90/4095; // yVal Read(Read Rank2)
    
	HAL_ADC_Stop(&hadc1); // ADC 변환 중지
    
	zVal = !(HAL_GPIO_ReadPin(Z_Value_GPIO_Port, Z_Value_Pin));
	printf("X : %d, Y = %d, Z = %d \r\n", xVal, yVal, zVal);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM3) // 타이머 인스턴스가 TIM3인지 확인
		Joystick_Test();
}
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_USART2_UART_Init();
  MX_TIM3_Init();
  MX_ADC1_Init();

  HAL_TIM_Base_Start_IT(&htim3);
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

1초에 1번씩 Interrupt가 실행되는 것을 볼 수 있다.

 

Timer & LED

 

1. Timer Interrupt 발생 시, LED가 Toggle되게끔 코딩

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

int __io_putchar(int ch) // 1 byte output function to CONSOLE(monitor)
{
//	char a = ch;
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
		HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
/* USER CODE END 0 */

위 코드는 1sec로 설정한 독립적인 TIMER에 대한 Interrupt가 아닌,

어떤 타이머가 동작하더라도 작동되는 ISR이다.

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

int __io_putchar(int ch) // 1 byte output function to CONSOLE(monitor)
{
//	char a = ch;
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM3) // 타이머 인스턴스가 TIM3인지 체크
		HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
/* USER CODE END 0 */

위와 같이 코딩해주면, TIM3에 대해서만 동작한다.

 

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_USART2_UART_Init();
  MX_TIM3_Init();

  HAL_TIM_Base_Start_IT(&htim3);
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 


 

728x90
반응형