STM32 RC카 프로젝트(2) 최종

황윤성·2024년 9월 26일

STM32

목록 보기
2/3

이번 글에서 RC카 프로젝트를 완성해 보겠습니다.

앞의 글에서는 USB를 통해 UART 통신을 테스트해보기 위해 UART2번을 사용하였지만, 이번에는 블루투스 모듈을 사용하기 위해 UART1번을 사용하도록 설정을 바꾸겠습니다.

UART1번을 추가적으로 열어둔 모습입니다.


PIN OVERVIEW를 확인해 보면 UART1이 추가적으로 열린 모습을 확인할 수 있습니다.

1. 코드 설명

#include "main.h"   // 메인 헤더 파일
#include "tim.h"    // 타이머 관련 함수 헤더
#include "usart.h"  // UART 통신 관련 함수 헤더
#include "gpio.h"   // GPIO 핀 설정 관련 헤더

헤더 선언부분 입니다.

#define MOTOR_A_IN1_PIN GPIO_PIN_0   // 모터 A의 IN1 핀 정의
#define MOTOR_A_IN2_PIN GPIO_PIN_1   // 모터 A의 IN2 핀 정의
#define MOTOR_B_IN3_PIN GPIO_PIN_2   // 모터 B의 IN3 핀 정의
#define MOTOR_B_IN4_PIN GPIO_PIN_3   // 모터 B의 IN4 핀 정의
#define MOTOR_GPIO_PORT GPIOB

포트 정의부분 입니다.
모터 제어를 위한 GPIO핀들이 설정되어있습니다.
MOTOR_A_IN1_PIN, MOTOR_A_IN2_PIN: 모터 A 제어 핀.
MOTOR_B_IN3_PIN, MOTOR_B_IN4_PIN: 모터 B 제어 핀.
이 핀들은 GPIOB 포트를 사용합니다.

uint8_t Rx_data[1];

UART 수신 데이터를 저장하는 배열 (1바이트)

void SystemClock_Config(void); 

시스템 클럭 설정 함수 선언

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)  // USART1에서 데이터가 수신된 경우
    {
        HAL_UART_Transmit_IT(&huart1, Rx_data, sizeof(Rx_data));  // 수신된 데이터를 다시 송신
        HAL_UART_Receive_IT(&huart1, Rx_data, sizeof(Rx_data));   // 다음 데이터를 받기 위해 UART 인터럽트 재설정
    }
}

UART 인터럽트 콜백 함수입니다.
HAL_UART_RxCpltCallback : UART 인터럽트가 발생할 때 호출되는 함수입니다.
USART1으로부터 데이터를 수신하면, 받은 데이터를 다시 송신하고 다음 데이터를 수신하도록 설정합니다.

다음의 내용은 두 개의 DC모터 제어를 위한 함수입니다.

void MotorA_Control(int speed, int direction) {
    TIM3->CCR1 = speed;   // 모터 A의 속도를 설정 (CCR1 값 변경)
    TIM3->CCR2 = speed;   // 모터 A의 속도를 설정 (CCR2 값도 동일하게 설정)
    
    if (direction == 1) { // 방향이 전진(1)일 때
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_A_IN1_PIN, GPIO_PIN_SET);    // IN1 핀을 HIGH
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_A_IN2_PIN, GPIO_PIN_RESET);  // IN2 핀을 LOW
    } else {              // 방향이 후진(0)일 때
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_A_IN1_PIN, GPIO_PIN_RESET);  // IN1 핀을 LOW
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_A_IN2_PIN, GPIO_PIN_SET);    // IN2 핀을 HIGH
    }
}

모터 A제어 함수(방향 및 속도)
speed : PWM신호로 제어할 속도(0~1000)
direction : 방향 제어 (1 : 전진, 0: 후진)
TIM3->CCR1, TIM3->CCR2를 이용해 PWM 신호를 제어합니다.

void MotorB_Control(int speed, int direction) {
    TIM3->CCR1 = speed;   // 모터 B의 속도를 설정 (CCR1 값 변경)
    TIM3->CCR2 = speed;   // 모터 B의 속도를 설정 (CCR2 값도 동일하게 설정)
    
    if (direction == 1) { // 방향이 전진(1)일 때
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_B_IN3_PIN, GPIO_PIN_SET);    // IN3 핀을 HIGH
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_B_IN4_PIN, GPIO_PIN_RESET);  // IN4 핀을 LOW
    } else {              // 방향이 후진(0)일 때
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_B_IN3_PIN, GPIO_PIN_RESET);  // IN3 핀을 LOW
        HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_B_IN4_PIN, GPIO_PIN_SET);    // IN4 핀을 HIGH
    }
}

모터 B제어 함수
이하 내용은 모터A와 같습니다.

int main(void)
{
	HAL_Init();  // HAL 라이브러리 초기화
    SystemClock_Config();  // 시스템 클럭 설정
    MX_GPIO_Init();        // GPIO 초기화
    MX_TIM3_Init();        // 타이머 3 초기화
    MX_USART2_UART_Init(); // USART2 UART 초기화
    MX_USART1_UART_Init(); // USART1 UART 초기화
    
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);  // TIM3 채널 1에서 PWM 시작
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);  // TIM3 채널 2에서 PWM 시작

    HAL_UART_Receive_IT(&huart1, Rx_data, sizeof(Rx_data));  //인터럽트 수신 시작
    
    uint8_t dutyValue = 0;   // UART로 수신된 값을 저장하는 변수

PWM 설정 및 초기화
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1)와 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2)로 PWM 채널을 시작합니다.

while (1)
 {
   if(Rx_data[0])  // UART 데이터가 수신된 경우
   {
       dutyValue = Rx_data[0];  // 수신된 데이터 값을 저장

       // 수신된 값에 따라 모터 동작 제어
       switch(dutyValue)
       {
           case 119:  // 'w' 키: 전진 명령
               MotorA_Control(1000, 1);  // 모터 A 전진
               MotorB_Control(1000, 1);  // 모터 B 전진
               break;
           case 115:  // 's' 키: 후진 명령
               MotorA_Control(1000, 0);  // 모터 A 후진
               MotorB_Control(1000, 0);  // 모터 B 후진
               break;
           case 97:  // 'a' 키: 좌회전 명령
               MotorA_Control(1000, 0);  // 모터 A 후진
               MotorB_Control(1000, 1);  // 모터 B 전진
               break;
           case 100:  // 'd' 키: 우회전 명령
               MotorA_Control(1000, 1);  // 모터 A 전진
               MotorB_Control(1000, 0);  // 모터 B 후진
               break;
           case 99:  // 'c' 키: 정지 명령
               MotorA_Control(0, 0);  // 모터 A 정지
               MotorB_Control(0, 0);  // 모터 B 정지
               break;
           default:
               break;
       }
   }

UART 수신 및 명령 처리
Rx_data[0]에 수신된 명령에 따라 모터의 속도와 방향을 설정합니다.
명령 값에 따라 다음과 같이 동작합니다:
'w' (119): 모터 A와 B를 전진.
's' (115): 모터 A와 B를 후진.
'a' (97): 모터 A는 후진, 모터 B는 전진 (좌회전).
'd' (100): 모터 A는 전진, 모터 B는 후진 (우회전).
'c' (99): 모터 A와 B를 정지.

2. 하드웨어 구성

작성한 코드를 기반으로 하드웨어를 구성해보겠습니다.
이전 글에서 작성한 하드웨어에서 시작해 보자면 외부의 블루투스 모듈로 부터 설치해보겠습니다.
STM32보드와 블루투스 모듈이 서로 통신할 수 있도록 RX와 TX가 서로 맞물리도록 연결을 해 줍니다. 그리고 외부 전력을 사용하기 위해 블루투스의 모듈의 VCC와 GND가 모터 드라이버로 연결해 줍니다.

사진상에서
좌측의 청록색 케이블은 블루투스 모듈의 RX -> STM32보드의 TX핀으로,
그 옆의 회색 케이블은 블루투스 모듈의 TX -> STM32보드의 RX핀으로 연결됩니다.
검정색 케이블은 그라운드인데 모터 드라이버에 외부 전원의 접지와 블루투스 모듈의 접지를 함께 연결합니다.

외부 전원을 주기 위해 18650 배터리 2개를 사용하도록 하겠습니다.

사진처럼 배터리의 +극은 모터 드라이버의 12V를 입력 곳으로, -극은 모터 드라이버의 블루투스 모듈의 접지가 연결된 곳으로 같이 연결합니다.

3. 앱 설치 및 세팅

저는 Arduino Bluetooth control 이라는 앱을 사용하였습니다.

폰에 블루투스 모듈을 연결을 하면 점멸하던 블루투스 모듈이 점등됨으로서 연결된 상태를 확인할 수 있습니다.
연결 후 앱으로 들어가 모듈을 이름을 누릅니다.
그리고 사진상의 gamepad로 들어가 줍니다.

그 뒤에 우측 상단의 톱니모양의 설정으로 들어가서 버튼마다 무슨 값을 입력할지 설정해 줍니다.

press는 버튼을 터치했을 때, release는 손을 땐 이후를 뜻합니다.
저는 제가 작성한 코드대로
w : 앞
s : 뒤
a : 좌회전
d : 우회전
c : 동작 정지
이렇게 값을 입력해두었습니다.

4. 완성 결과

작품 사진 입니다.
상단
좌측부터 STM32보드 - 모터 드라이버 - 브레드보드 - 18650배터리
측면
하단

조작 영상입니다.

profile
하드웨어 엔지니어 꿈나무

0개의 댓글