KG_KAIROS/MCU (Arduino & STM32)

🤖 MCU 기반 로봇팔 제어 시스템 만들기 🚀

projectlim 2024. 10. 25. 17:07
728x90
반응형
SMALL

이번 포스트에서는 MCU (Microcontroller Unit) 를 사용하여 로봇팔을 제어하는 방법을 소개합니다.

STM32PWM(Pulse Width Modulation) 방식으로 서보 모터를 제어하는 코드를 작성해보겠습니다.

이 시스템은 서보 모터의 각도를 제어하여 로봇팔을 움직이게 됩니다.


🔧 필요한 부품 및 구성

부품 용도

STM32 MCU 마이크로컨트롤러, 제어 시스템 구현
서보 모터 로봇팔의 움직임을 담당
타이머 (TIM4) PWM 신호를 생성하는 타이머
GPIO 핀 서보 모터와 연결된 핀

🧑‍💻 코드 설명

  1. 타이머 초기화
    TIM4를 사용하여 PWM 신호를 생성합니다. 서보 모터를 제어하기 위해 1ms ~ 2ms 사이의 펄스를 보내야 하므로, 타이머의 주기를 적절히 설정합니다.
  2. PWM 제어
    각 서보 모터의 각도를 제어하는 방법은 PWM 신호의 듀티 사이클을 변경하는 것입니다. 예를 들어, 1ms는 0도, 1.5ms는 90도, 2ms는 180도를 의미합니다.

📋 코드 흐름

  • PWM 신호 시작
    타이머를 초기화하고, PWM을 채널별로 시작합니다. 이후, 각 서보 모터에 대해 다른 각도를 설정하여 움직이게 합니다.
  • 서보 모터 각도 설정
    __HAL_TIM_SET_COMPARE() 함수를 이용해 각 서보 모터의 각도를 제어합니다. 예를 들어, 1ms(0도), 1.5ms(90도), 2ms(180도)의 신호를 차례대로 보내어 서보 모터가 해당 각도로 회전하도록 합니다.

🔄 서보 모터 각도 변화

  • 채널 1
    1ms → 0도, 1.5ms → 90도, 2ms → 180도
  • 채널 2
    1ms → 0도, 1.5ms → 90도, 2ms → 180도
  • 채널 3
    1ms → 0도, 1.5ms → 90도, 2ms → 180도
  • 채널 4
    1ms → 0도, 1.5ms → 90도, 2ms → 180도

⚙️ 핵심 코드 (C 언어)

// 서보 모터 각도 설정 (예: 0도)
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 100); // 1ms
HAL_Delay(1000);

// 서보 모터 각도 설정 (예: 90도)
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 150); // 1.5ms
HAL_Delay(1000);

// 서보 모터 각도 설정 (예: 180도)
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 200); // 2ms
HAL_Delay(1000);

이 코드를 통해 각 채널의 서보 모터를 제어하며, HAL_TIM_PWM_Start()로 PWM을 시작하고, __HAL_TIM_SET_COMPARE()로 각도를 변경합니다.


🔍 결과

실행 후, 각 서보 모터가 0도, 90도, 180도 순으로 회전하며, 로봇팔의 동작을 확인할 수 있습니다. 이를 통해 로봇팔의 기본적인 제어가 가능해집니다.


 

SYS=> SERIAL

 

RCC HSE CRYTI 머시기

⚙️ 전체 코드 (C 언어)

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim4;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

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

/* USER CODE END PFP */

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

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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_TIM4_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
  /* USER CODE END 2 */

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


    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  // 서보 모터 각도 설정 (예: 0도)
          //채널1
  	  	  __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 100); // 1ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 90도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 150); // 1.5ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 180도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 200); // 2ms
          HAL_Delay(1000);
          //채널2
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 100); // 1ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 90도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 150); // 1.5ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 180도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 200); // 2ms
          HAL_Delay(1000);
  	  	  //채널3
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 100); // 1ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 90도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 150); // 1.5ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 180도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 200); // 2ms
          HAL_Delay(1000);
          //채널4
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 100); // 1ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 90도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 150); // 1.5ms
          HAL_Delay(1000);
          // 서보 모터 각도 설정 (예: 180도)
          __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 200); // 2ms
          HAL_Delay(1000);
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief TIM4 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 84; //타이머 클락 1MHz 0=>84로 바꿈
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 2000; //65535였는데 2000으로 변경
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 100; //초기값 100으로 설정 원래 0 되어있음
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */
  HAL_TIM_MspPostInit(&htim4);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

 

🎉 마무리

이제 STM32를 이용하여 로봇팔을 제어할 수 있는 기본적인 시스템을 구축했습니다!

서보 모터를 활용해 다양한 각도로 로봇팔을 움직일 수 있으며, 이를 통해 로봇팔 제어의 기초를 다질 수 있습니다.

💡 추가 팁

  • PWM 주기와 듀티 사이클을 조절하여 서보 모터의 제어 범위를 확장할 수 있습니다.
  • 타이머를 적절히 조정하면 더 복잡한 동작도 구현할 수 있습니다.

로봇팔 프로젝트를 확장하여 더 많은 모터나 센서를 추가하는 것도 가능합니다. 이를 통해 더욱 정교한 동작을 구현할 수 있겠죠!


이 포스트가 도움이 되셨다면 👍 좋아요!
질문이나 피드백이 있으시면 댓글로 남겨주세요! 😊

728x90
반응형
LIST