로봇/STM32

STM32 자동차 제어하기(1) - 기본환경 설정

with-RL 2023. 3. 19. 19:25

이번 과정은 STM32 보드와 추가로 제작된 확장보드 및 RC카 구동체를 이용해서 자동차를 제어하기 위한 간단한 프로그램을 만드는 과정입니다.

과정이 복잡하고 길어질 수 있어서 몇 편이 될지는 알 수 없지만 시리즈로 작성할 예정입니다.

아래 과정은 아래 환경과 보드를 이용했습니다.

  • Windows 11 Home
  • STM32CubeIDE-1.11.2
  • STM32 NUCLEO-F103RB 보드
  • 자체제작 확장보드
  • 자체제작 자동체 구동체
  • Anaconda
  • Vscode

1. 구성품 소개

  • 위 그림의 오른쪽 흰색 보드는 STM32 NECLEO-F103RB 보드입니다.
  • 왼쪽의 초록색 보드는 확장보드로 IMU, 모터드라이버, 12 볼트 전원장치 등 RC카 구동을 위해 추가로 필요한 부품을 구성했습니다.

확장보드의 구성은 다음과 같습니다.

  1. LED
  2. 5 볼트
  3. 12 볼트
  4. 12 볼트에서 5 볼트 변환장치
  5. 부저
  6. IMU
  7. 모터 드라이버
  8. 모터 연결단자
  9. STM32 연결단자

2. STM32 프로젝트 생성

  • 아래 그림과 같이 STM32CubeIDE 메뉴에서 'File >> New >> STM32 Project'를 선택합니다.

  • 아래 그림과 같이 실행되는 'STM32 Project' 마법사에서 Board Selector를 선택한 후 'NUCLEO-F103RB'를 입력하고 보드를 선택한 후 'Next' 버튼을 클릭합니다.

  • 아래 그림과 같이 실행되는 'STM32 Project' 마법사에서 'Project Name'을 입력한 후 'Finish' 버튼을 눌러 프로젝트를 생성합니다.

  • 아래 그림과 같이 프로젝트가 생성됩니다.

 

3. 파이썬 프로젝트 생성

  • 아나콘다VSCode가 설치되어 있는 환경에서 파이썬 프로젝트를 생성하는 과정입니다. 아나콘다 또는 VSCode가 설치되어있지 않은 경우는 설치하시기 바랍니다.
  • 파이썬 프로젝트를 생성하는 이유는 파이썬을 이용해 STM32 보드를 제어하기 위함입니다.
  • 윈도우 실행파일에서 'Anaconda prompt (Anaconda3)'를 선택하여 실행합니다.

  •  'Anaconda prompt (Anaconda3)'에서 아래와 같은 명령을 실행하여 가상환경을 생성합니다.
conda create -n ros1 python=3.7

  •  'Anaconda prompt (Anaconda3)'에서 아래와 같은 명령을 실행하여 가상환경을 로딩합니다.
conda activate ros1

  • 'Anaconda prompt (Anaconda3)'에서 아래와 같은 명령을 실행하여 pyserial 라이브러리를 설치합니다. pyserial 라이브러리는 STM32 보드와 serial 통신을 하기 위한 라이브러리입니다.
pip install pyserial

  • 'Anaconda prompt (Anaconda3)'에서 아래와 같은 명령을 실행하여 jupyter notebook 라이브러리를 설치합니다.
pip install jupyter notebook

  • 아래 그림과 같이 윈도우 탐색기를 이용해 적당한 위치에 파이썬 프로젝트 생성을 위한 폴더를 만듭니다.

  • VSCode에서 방금 전에 생성한 폴더를 '폴더 열기'를 이용해 폴더를 엽니다.

  • 키보드에서 'Ctrl + Shift + p' 버튼을 누르면 상단에 실행되는 창에서 'python'을 입력하고 'Python: 인터프리터 선택'을 선택합니다.

  • 파이썬 인터프리터 중 'ros1'을 선택합니다.

 

4. STM32 - UART 통신하기

  • STM32에 UART 통신을 하는 기능을 설정하는 과정입니다.
  • 아래 그림과 같이 STM32CubeIDE의 'Clock Configuration' 탭을 선택한 후 'HSE'를 선택하고 '*PLLMul' 값을 'X 9'를 선택합니다.
  • 'Clock Configuration'을 설정하는 이유는 STM32에서 기본적으로 설정된 Clock이 약간 불안정하여 좀 더 안정적으로 동작하는 Clock으로 변경하기 위함입니다.

  • 아래 그림과 같이 'Pinout & Configuraiton' 탭에서 'Connectivity' >> 'USART2'를 선택하고 'Parameter Settings'를 아래와 같이 설정합니다.
    ◦ Mode: Asynchronous
    ◦ Baud Rate: 115200

  • 아래 그림과 같이 'Pinout & Configuraiton' 탭에서 'Connectivity' >> 'USART2'를 선택하고 'NVIC Settings'를 아래와 같이 설정합니다.
    ◦ USART2 global interrupt: check

  • 아래 그림과 같이 'Pinout & Configuraiton' 탭에서 'System' >> 'NVIC'를 선택하고 'USART global interrupt'를 check 합니다.

  • 아래 그림과 같이 'Device Configuration Tool Code Generation' 버튼을 눌러서 코드를 생성합니다.

  • 이후에 많은 코드를 작성해야 하므로 사용자 코드를 작성할 폴더를 만들도록 하겠습니다.
  • 아래 그림과 같이 프로젝트를 선택한 후 마우스 메뉴 버튼을 누르면 뜨는 팝업메뉴에서 'New' >> 'Source Folter'를 선택합니다.

  • 아래 그림과 같이 실행되는 'New Source Folder' 마법사에서 'Folder name'에 'BSP'를 입력하고 'Finish' 버튼을 눌러서 Source Folder를 생성합니다.

  • BSP 폴더에 *.h 파일도 함께 만들 예정이기 때문에 Include path에도 추가해야 합니다.
  • 아래 그림과 같이 프로젝트를 선택한 후 마우스 메뉴 버튼을 누르면 뜨는 팝업메뉴에서 'Properties'를 선택합니다.

  • Properties 마법사에서 'C/C++ Build' >> 'Setting'를 선택하면 변경되는 창에서 'MCU GCC Compiler' >> 'Include paths'를 선택하면 변경되는 창에서 '+' 아이콘을 선택합니다.

  • 'Add directory path' 마법사에서 'Directory'에 './BSP'를 입력 후 'Ok' 버튼을 누릅니다.

  • 아래 그림과 같이 'BSP' 폴더가 추가된 것을 확인할 수 있습니다. 'Apply', 'Apply and Close' 버튼을 눌러서 창을 종료합니다.

  • 아래 그림과 같이 'BSP' 폴더를 선택한 후 마우스 메뉴버튼을 눌러서 뜨는 팝업메뉴에서 'New' >> 'Header' 파일을 선택합니다.

  • 'New Header File' 마법사에서 'Header file' 필드에  'bsp.h'를 입력한 후 'Finish' 버튼을 눌러서 파일을 생성합니다.

  • 같은 방법으로 'bsp_uart.h' 파일을 생성합니다.

  • 아래 그림과 같이 'BSP' 폴더를 선택한 후 마우스 메뉴버튼을 눌러서 뜨는 팝업메뉴에서 'New' >> 'Source' 파일을 선택합니다.

  • 'New Source File' 마법사에서 'Source file' 필드에  'bsp.c'를 입력한 후 'Finish' 버튼을 눌러서 파일을 생성합니다.

  • 같은 방법으로 'bsp_uart.c' 파일을 생성합니다.

  • bsp.h 파일 중간 부분에 아래와 같은 코드를 입력합니다.
/* BSP include */
#include "bsp_uart.h"

/* BSP functions */
void Bsp_Init(void);
void Bsp_Loop(void);

  • bsp_uart.h 파일 중간 부분에 아래와 같은 코드를 입력합니다.
/* BSP UART function */
void USART2_Init(void);

  • bsp.c 파일아랫부분에 아래와 같은 코드를 입력합니다.
#include "bsp.h"


void Bsp_Init(void)
{
	USART2_Init();
}

void Bsp_Loop(void)
{
}

  • bsp_uart.c 파일 아랫부분에 아래와 같은 코드를 입력합니다.
#include <string.h>
#include "main.h"
#include "bsp_uart.h"


extern UART_HandleTypeDef huart2;
uint8_t rx_flag;


void USART2_Init(void)
{
	HAL_UART_Receive_IT(&huart2, &rx_flag, 1);
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART2) {
		HAL_UART_Transmit(&huart2, &rx_flag, 1, 10);

		HAL_UART_Receive_IT(&huart2, &rx_flag, 1);
	}
}

  • main.c 파일의 */ USER CODE BEGIN Includes */ 아래에 다음 코드를 입력합니다.
#include "bsp.h"

  • main.c 파일의 */ USER CODE BEGIN 2 */ 아래에 다음 코드를 입력합니다.
Bsp_Init();
  • main.c 파일의 */ USER CODE BEGIN 3 */ 아래에 다음 코드를 입력합니다.
Bsp_Loop();

  • 툴바의 'Build Debug for project ...' 버튼을 눌러서 컴파일합니다.

  • 툴바의 디버그 버튼을 누른 후 'Debug Configurations..'를 선택합니다.

  • 'Debug Configurations' 마법사에서 'STM32 C/C++ Application'을 선택한 후 'New launch configuration'을 선택합니다.

  • 아래와 같은 화면이 실행됩니다.

  • 'Debugger' 탭을 선택한 후 'ST-LINK S/N'을 체크한 후 'Scan' 버튼을 눌러서 STM32 보드를 찾고 보드를 선택 후 'Apply' 버튼을 누른 후 'Debug' 버튼을 눌러서 프로그램을 다운로드 및 실행합니다.

  • 프로그램이 실행된 후 아래 그림과 같이 'HAL_Init()' 부분에 Debug 가 멈출 때까지 기다린 후 'Resume' 버튼을 눌러 프로그램 실행을 진행합니다.

 

5. Python - UART 통신하기

  • 이번 과정을 Python을 이용해서 STM32 보드와 통신을 하는 과정입니다.
  • 아래 그림과 같이 VSCode에서 '+' 버튼을 눌러서 Python 파일을 생성합니다.

  • 입력 필드에  'Car_driver.py'라고 파일 이름을 입력합니다.

  • Car_driver.py 파일에 아래와 같은 코드를 입력합니다.
import itertools
import threading

import serial


class CarDriver:
    def __init__(self, baudrate=115200, port="COM1"):
        # unique seq counter
        self.seq = itertools.count()
        # serial communication driver
        self.ser = serial.Serial()
        self.ser.baudrate = baudrate
        self.ser.port = port
        self.ser.open()
        # serial recive thread run
        self.run_receive_thread()

    def run_receive_thread(self):
        task_receive = threading.Thread(target=self.__recv_data, name="serial_recv_task")
        task_receive.setDaemon(True)
        task_receive.start()

    def __recv_data(self):
        while True:
            data = bytearray(self.ser.read())
            print(data)

    def set_led(self, state):
        cmd = []
        cmd.append(1 if state else 0)
        self.ser.write(bytearray(cmd))

  • STM32 보드와 Serial 통신을 하기 위해서 현재 연결된 포트를 알아야 합니다.
  • 아래와 같이 윈도우 탐색기에서 '내 PC'를 선택하고 마우스 메뉴 버튼을 눌러서 뜨는 팝업창에서 '속성'을 선택합니다.

  • 아래와 같은 '설정' 마법사에서 '시스템' 탭의 '고급 시스템 설정'을 선택합니다.

  • 아래와 같은 '시스템 속성' 마법사에서 '하드웨어' 탭을 선택하고 '장치관리자' 버튼을 누릅니다.

  • '장치 관리자' 마법사에서 '포트(COM & LPT)'를 선택하면 STM32가 연결된 포트를 확인할 수 있습니다. 저는 'COM5'로 연결되어 있습니다.

  • 이제 Car_driver.py 파일을 테스트할 테스트 프로그램을 만들겠습니다.
  • 아래 그림과 같이 VSCode에서 '+' 버튼을 눌러서 Python 파일을 생성합니다.

  • 입력 필드에  'Car_driver_test.ipynb'라고 파일 이름을 입력합니다.

  • Car_driver_test.ipynb 파일에 아래와 같이 코드를 입력합니다. Jupyter Notebook 파일이므로 한 줄을 한 칸에 입력 후 실행하면 아래 그림과 같이 STM32 보드와 통신 후 결과를 화면에 출력하는 것을 확인할 수 있습니다.
from Car_driver import CarDriver

driver = CarDriver(baudrate=115200, port="COM5")

driver.set_led(True)

driver.set_led(False)

  • 다음에는 확장보드의 LED와 BUZZER를 제어하는 기능을 만들어 보겠습니다.