ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 라즈베리파이 → STM32 uart를 통한 모터 제어
    Autonomous Lawn Mower/Raspberry pi & STM32 2025. 1. 5. 04:36
    반응형

    통신 개념

     

    라즈베리파이가 상위 제어기로 주변 환경을 인식하고 STM32를 통해서 모터 제어를 수행하게 된다. 상호간 통신은 UART를 이용한다. UART 통신 구현을 위해서 UART communication protocol을 아래와 같이 정의 하였다. Communication protocol은 데이터를 주고 받는데 있어서의 규칙 또는 약속이다. 통신 보안을 위한 여러가지 방법이 있지만, Hash 같은 기능을 구현하려면 좀 더 고가의 마이컴이 필요하고, UART 방식은 간단하고 저렴해서 개인 프로젝트에서 구현하기에 용이하다.

     

    아래표는 라즈베리파이와 STM32간 데이터를 주고 받는데 있어서의 규칙 또는 약속인 통신 프로토콜이다.

    UART communication protocol

     

    Command table은 아래와 같다.

      Command Length Data Direction
    Traction + Mower 0x11 3 Left, Right, Mower Forward
    0x12 3 Left, Right, Mower Backward
    Traction 0x21 2 Left, Right Forward
    0x22 2 Left, Right Backward
    Stop 0x23 2 stop Stop

     

    파이썬 송신 코드 

    import serial

    # Constants
    STX = 0xFF
    ETX = 0xFE

    def calculate_checksum(cmd, length, data):
        """
        Calculate the checksum using XOR.
        """
        checksum = cmd ^ length
        for byte in data:
            checksum ^= byte
        return checksum

    def send_uart_command(serial_port, cmd, data):
        """
        Send a UART command with the specified data structure.
        
        :param serial_port: Opened serial port object
        :param cmd: Command byte
        :param data: List of data bytes
        """
        length = len(data)
        checksum = calculate_checksum(cmd, length, data)
        packet = [STX, cmd, length] + data + [checksum, ETX]
        
        # Convert packet to bytes and send
        serial_port.write(bytearray(packet))
        print(f"Sent packet: {[hex(byte) for byte in packet]}")

    # Functions for individual commands
    def send_traction_command(serial_port, cmd, data):
        """
        Send a traction command with variable-length data.
        
        :param serial_port: Serial port object
        :param cmd: Command for traction operation (0x11, 0x12, 0x13)
        :param data: List of data bytes
        """
        send_uart_command(serial_port, cmd, data)


    # Example usage
    if __name__ == "__main__":
        # configure UART settings
        port_name = '/dev/serial0' # use '/dev/ttyS0' or '/dev/serial0'
        baud_rate = 115200
        
        try:
            # Open serial connection
            with serial.Serial(port_name, baud_rate, timeout=1) as ser:
                print("Serial port opened successfully.")
                
                # Send individual commands
                send_traction_command(ser, 0x11, [30, 30, 30])          # Traction forward with data value 50
        
        except serial.SerialException as e:
            print(f"Error: {e}")

     

     

    STM32 코드는 아래와 같다. 

    /* USER CODE BEGIN 0 */

    #define STX 0xFF

    #define ETX 0xFE

    #define MAX_PACKET_SIZE 256 // Maximum buffer size

     

     

    uint8_t cmd = 0;

    uint8_t len = 0;

    uint8_t data[MAX_PACKET_SIZE];

    uint8_t packet_buffer[MAX_PACKET_SIZE];

    uint8_t rx_buffer[1];

    uint8_t receiving_packet = 0;

    uint8_t packet_index = 0;

     

    uint8_t uart2_duty = 25; // traction and mower motor speed

     

     

    // uint8_t key_value, n=0;

     

     

    void process_packet(uint8_t *packet, uint8_t length);

    uint8_t calculate_checksum(uint8_t cmd, uint8_t len, uint8_t *data);

     

    uint8_t calculate_checksum(uint8_t cmd, uint8_t len, uint8_t *data) {

    uint8_t checksum = cmd ^ len;

    for (uint8_t i = 0; i < len; i++) {

    checksum ^= data[i];

    }

    return checksum;

    }

     

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {

     

    if (huart->Instance == USART1) { // Check if UART1 triggered the callback

    uint8_t received_byte = rx_buffer[0];

     

    if (!receiving_packet) {

    if (received_byte == STX) {

    // Start of a new packet

    receiving_packet = 1;

    packet_index = 0;

    packet_buffer[packet_index++] = received_byte;

    }

    } else {

    packet_buffer[packet_index++] = received_byte;

     

    // Check for the end of the packet

    if (received_byte == ETX && packet_index > 5) {

    // Extract global variables

    cmd = packet_buffer[1];

    len = packet_buffer[2];

    memcpy(data, &packet_buffer[3], len);

     

    uint8_t received_checksum = packet_buffer[3 + len];

    uint8_t calculated_checksum = calculate_checksum(cmd, len, data);

     

    // Print the packet_buffet if it receives a valid packet

    if (received_checksum == calculated_checksum) {

    process_packet(packet_buffer, packet_index);

    }

    // Reset for the next packet

    receiving_packet = 0;

    packet_index = 0;

    }

     

    // Prevent buffer overflow

    if (packet_index >= MAX_PACKET_SIZE) {

    receiving_packet = 0;

    packet_index = 0;

    }

    }

     

    // Restart UART1 reception

    HAL_UART_Receive_IT(&huart1, rx_buffer, 1);

    }

    }

     

    // Print the packet data to UART2 for printing

    void process_packet(uint8_t *packet, uint8_t length) {

     

    UART_Transmit("Packet Received: ");

     

    for (uint8_t i = 0; i < length; i++) {

    char buffer[10];

    sprintf(buffer, "0x%02X ", packet[i]);

    UART_Transmit(buffer);

    }

    UART_Transmit("\r\n");

    }

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

    MX_TIM1_Init();

    MX_USART1_UART_Init();

    /* USER CODE BEGIN 2 */

    HAL_UART_Receive_IT(&huart1, rx_buffer, 1);

     

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

    // HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1); // turn on complementary channel

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);

    // HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2); // turn on complementary channe2

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);

    // HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3); // turn on complementary channe3

    /* USER CODE END 2 */

     

    /* Infinite loop */

    /* USER CODE BEGIN WHILE */

     

    motor_control_init();

     

    while (1)

    {

    uint8_t direction = 1; // forward = 1, reverse = 0

     

    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

    HAL_Delay(1000);

     

    motor_control_process(cmd, len, data);

     

    // Uart input

    #if 0

    // Usart input for motor speed

    UART_Transmit("Enter a number between 0 and 100:\r\n");

     

    int number = UART_ReceiveNumber();

     

    if (number != -1) {

    char message[50];

    snprintf(message, sizeof(message), "You entered: %d\r\n", number);

    UART_Transmit(message);

    UART_Transmit("Enter another number between 0 and 100:\r\n");

    }

    uart2_duty = number;

    #endif

    /* USER CODE END WHILE */

     

    /* USER CODE BEGIN 3 */

    }

    /* USER CODE END 3 */

    }

    while (1)

    {

    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

    HAL_Delay(1000);

     

    // process_packet(packet_buffer, packet_index);

     

    /* USER CODE END WHILE */

     

    /* USER CODE BEGIN 3 */

    }

    /* USER CODE END 3 */

    }

     

     

     

     

    ※ 참고자료

    https://www.youtube.com/watch?v=0LhKIVuH6nY

    반응형
Designed by Tistory.