로봇/STM32

STM32 자동차 제어하기(2) - LED / BUZZER 제어하기

with-RL 2023. 3. 19. 21:27

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

아래 과정은 이전 과정을 완료 후 진행하시길 추천드립니다.

1. 통신규격 정의하기

  • 우선 STM32 보드를 제어하기 위한 프로토콜을 아래와 같이 정의하였습니다.
    START FLAG: 명령의 시작을 의미하는 flag (0xFF 고정 값)
    LENGTH: START FLAG와 LENGTH를 제외한 나머지 데이터의 길이
    SEQUENE NO: 1 ~ 0xFE 사이의 명령 일련번호, 0xFF에 도달하면 다시 1부터 시작
    COMMAND: 수행할 명령어 (bsp_uart.h 파일에 정의)
    RESULT: 명령어 수행 결과 (0: REQUEST, 1: RESULT OK, 기타 오류는 필요에 따라 정의)
    DATA: 명령어 수행에 필요한 데이터 (명령어에 따라서 길이가 입력)
    CHECK SUM: 명령어 데이터의 유효성 검증을 위한 값

  • 우선 아래 코드와 같이 bsp_uart.h 에 COMMAND 및 RESULT를 정의합니다.
/* BSP UART command */
#define CMD_LED		0x01
#define CMD_BUZZER	0x02

/* BSP UART error */
#define REQUEST		0x00
#define RES_OK		0x01

  • python의 Car_driver.py에도 아래와 같이 COMMAND 및 RESULT를 정의합니다.
# BSP UART command
CMD_LED = 0x01
CMD_BUZZER = 0x02
CMD_MOTOR = 0x03

# BSP UART error
REQUEST = 0x00
RES_OK = 0x01

  • 확장보드의 LED 제어를 위해서는 아래와 같이 STM32 보드에 보내면 됩니다.
    COMMAND: 0x01
      RESULT: 0x00
      DATA: [state (off: 0x00, on: 0x01) ]

  • 확장보드의 BUZZER를 제어를 위해서는 아래와 같이 STM32 보드에 보내면 됩니다.
     COMMAND: 0x02
      RESULT: 0x00
      DATA: [state (off: 0x00, on: 0x01) ]

2. STM32 - 메시지 수신 및 제어 기능 구현 (LED, BUZZER)

  • 아래 그림과 같이 PC13핀을 GPIO_Output으로 설정합니다.

  • 다음은 아래 그림과 같이 PC5핀을 GPIO_Output으로 설정합니다.

  • 다음은 'System Core' >> 'GPIO' >> 'PC13'의 'User Label'을 'LED2'로 지정합니다.

  • 다음은 'System Core' >> 'GPIO' >> 'PC5'의 'User Label'을 'BUZZER'로 지정합니다.

  • 툴바의 'Device Configuration Tool Code Generation' 버튼을 눌러서 코드를 생성합니다.

  • 아래와 같이 'bsp_uart.h' 파일을 수정합니다.
#ifndef BSP_UART_H_
#define BSP_UART_H_

/* BSP UART command */
#define CMD_LED		0x01
#define CMD_BUZZER	0x02

/* BSP UART error */
#define REQUEST		0x00
#define RES_OK		0x01

/* BSP UART function */
void USART2_Init(void);

#endif /* BSP_UART_H_ */
  • 다음은 아래와 같이 'bsp_uart.c' 파일을 수정합니다.
#include <string.h>
#include "main.h"
#include "bsp_uart.h"


extern UART_HandleTypeDef huart2;
uint8_t rx_flag;
uint8_t rx_data[256];
uint8_t rx_csum;
uint8_t tx_data[256];
uint8_t tx_csum;


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

int Check_RxCheckSum() {
	int len = rx_data[1] + 2;
	int sum = 0;
	for(int i = 0; i < len; i++) {
		sum += rx_data[i];
	}
	sum &= 0xFF;
	return rx_csum == sum;
}

void Make_TxCheckSum() {
	int len = tx_data[1] + 2;
	int sum = 0;
	for(int i = 0; i < len; i++) {
		sum += tx_data[i];
	}
	tx_csum = sum & 0xFF;
}

void Send_Data() {
	Make_TxCheckSum();
	HAL_UART_Transmit(&huart2, tx_data, tx_data[1] + 2, 10);
	HAL_UART_Transmit(&huart2, &tx_csum, 1, 10);
}

void Set_Led() {
	if (rx_data[3] == CMD_LED) {
		HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, rx_data[5]);

		// response
		memset(tx_data, 0x00, sizeof(tx_data));
		memcpy(tx_data, rx_data, rx_data[1] + 2);
		tx_data[4] = RES_OK; // result
		Send_Data(); // send result
	}
}

void Set_Buzzer() {
	if (rx_data[3] == CMD_BUZZER) {
		HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, rx_data[5]);

		// response
		memset(tx_data, 0x00, sizeof(tx_data));
		memcpy(tx_data, rx_data, rx_data[1] + 2);
		tx_data[4] = RES_OK; // result
		Send_Data(); // send result
	}
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART2) {
		if (rx_flag == 0xFF) {
			memset(rx_data, 0x00, sizeof(rx_data));
			rx_data[0] = rx_flag;
			HAL_StatusTypeDef uartState = HAL_UART_Receive(&huart2, &rx_data[1], 1, 10); // recv len
			if (uartState == HAL_OK) {
				uartState = HAL_UART_Receive(&huart2, &rx_data[2], rx_data[1], 10); // recv data
			}
			if (uartState == HAL_OK) {
				uartState = HAL_UART_Receive(&huart2, &rx_csum, 1, 10); // recv crc
			}

			if (uartState == HAL_OK && Check_RxCheckSum() > 0) {
				switch (rx_data[3]) {
					case CMD_LED:
						Set_Led();
						break;
					case CMD_BUZZER:
						Set_Buzzer();
						break;
				}
			}
		}
		HAL_UART_Receive_IT(&huart2, &rx_flag, 1);
	}
}
  • 다음은 아래 그림과 같이 툴바의 'Build Debug' 버튼을 눌러 컴파일을 진행합니다.

  • 다음은 아래 그림과 같이 툴바의 'Debug' 버튼을 눌러서 프로그램을 보드에 다운로드합니다.

3. Python - STM32 보드 제어기능 구현 (LED, BUZZER)

  • 아래와 같이 보드를 제어하기 위한 'Car_driver.py' 코드를 작성합니다.
import itertools
import threading

import serial

# BSP UART command
CMD_LED = 0x01
CMD_BUZZER = 0x02

# BSP UART error
REQUEST = 0x00
RES_OK = 0x01


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())[0]
            print(data)

    def set_led(self, state):
        self.__send_data(CMD_LED, [1 if state else 0])

    def set_buzzer(self, state):
        self.__send_data(CMD_BUZZER, [1 if state else 0])

    def __send_data(self, command, data):
        seq = next(self.seq) % 0xFE + 1
        cmd = [0xFF, 0, seq, command, REQUEST]
        cmd.extend(data)
        cmd[1] = len(cmd) - 2
        checksum = sum(cmd) & 0xFF
        cmd.append(checksum)
        print(cmd)
        self.ser.write(cmd)

  • 다음은 보드를 제어하기 위한 'Car_driver_test.ipynb' 파일의 내용을 아래와 같이 바꿉니다.
from Car_driver import CarDriver

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

driver.set_led(True)

driver.set_led(False)

driver.set_buzzer(True)

driver.set_buzzer(False)

  • 명령이 실행됨에 따라 LED 또는 Buzzer가 켜지거나 꺼지는 것을 확인할 수 있습니다.