-
라즈베리파이 → 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 */
}
※ 참고자료
반응형'Autonomous Lawn Mower > Raspberry pi & STM32' 카테고리의 다른 글
IMU sensor (MPU-6500) python code with thonny (0) 2025.02.15 DMA를 이용한 2ch ADC (0) 2025.02.08 Systick callback function (0) 2025.02.01 라즈베리파이 RealVNC로 직접 연결 (0) 2025.01.11 라즈베리파이 연결방법 (0) 2024.12.24