STM32 與 linux 雙向串口通信實驗 本文記錄STM32 與 linux 雙向串口通信,包含stm32發送、Linux阻塞式接收;Linux發送,STM32阻塞式接收;本實驗的目的在於調通數據鏈路,為之後使用奠定基礎。 實驗平臺為: STM32方面用的是STM32H723ZGT6為核心的開發 ...
STM32 與 linux 雙向串口通信實驗
本文記錄STM32 與 linux 雙向串口通信,包含stm32發送、Linux阻塞式接收;Linux發送,STM32阻塞式接收;本實驗的目的在於調通數據鏈路,為之後使用奠定基礎。
實驗平臺為:
STM32方面用的是STM32H723ZGT6為核心的開發板;開發環境為 VSCode + AC5編譯器,調試器用的是STLINK-V2;
Linux方面:用的是luckfox的RV1106-G3為核心的開發板;開發環境為window環境下的Clion+交叉編譯器,linux為Ubuntu22.04虛擬機,使用ADB將編譯後的程式發送到linux開發板;
STM32 向 Linux 串口發送數據
STM32方面代碼使用CubuMx生成,串口基本參數為:
1.波特率:115200;
2.數據寬度為8bits;
3.無停止位;
4.無奇偶校驗位;
5.一個停止位;
初始化方面代碼比較簡單,故不放了,放一張CubeMx的配置截圖:
發送代碼在main的while迴圈中,代碼邏輯為:每隔500ms發送一次數據,發送10次數據後發送"exit",使Linux端退出阻塞式接收,代碼如下:
uint8_t TransTemp[] = {"receiveTemp"};
while (1)
{
/* Transmit data code*/
for(int i=0; i<10; i++)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_7);
HAL_UART_Transmit(&huart2, TransTemp, sizeof(TransTemp), 0xffff);
HAL_Delay(500);
}
HAL_UART_Transmit(&huart2, "exit", 4, 0xffff);
}
Linux方面:如果沒有開啟串口,需要在設備數中開啟串口,開啟過程可以見Luckfox的wiki:https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-UART#5修改設備樹
Linux方面串口配置和接收邏輯直接由代碼配置,代碼邏輯為:首先以打開UART3,對其進行基本配置,配置完成後進入阻塞式接收程式,串口一直接收數據並列印,若一段時間未接收到數據則列印Timeout,開始下一輪接收;直到接收到"exit"數據,退出接收,關閉串口。程式如下:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int main() {
int serial_port_num=3;
char serial_port[15];
sprintf(serial_port,"/dev/ttyS%d",serial_port_num);
int serial_fd;
serial_fd = open(serial_port, O_RDWR | O_NOCTTY);
if (serial_fd == -1) {
perror("Failed to open serial port");
return 1;
}
struct termios tty;
memset(&tty, 0, sizeof(tty));
if (tcgetattr(serial_fd, &tty) != 0) {
perror("Error from tcgetattr");
return 1;
}
cfsetospeed(&tty, B115200);
cfsetispeed(&tty, B115200);
tty.c_cflag &= ~PARENB; // NO parity bit
tty.c_cflag &= ~CSTOPB; // 1 stop bit
tty.c_cflag &= ~CSIZE; // clear transimit bit
tty.c_cflag |= CS8; // set data bits to 8 bits
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines
// Set input mode (non-canonical, no echo,...)
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO;
// Set output mode (no post-processing)
tty.c_oflag &= ~OPOST; // no post-processiing means that out row data
// Set timeout to 1 second
tty.c_cc[VTIME] = 1; // inter-character timer unused
tty.c_cc[VMIN] = 1; // wait for up to 1 second for 1 byte
if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) {
perror("Error from tcsetattr");
return 1;
}
/* Receive data in block mode! */
char rx_buffer[16] = {};
int bytes_read;
// 主迴圈
while (1) {
memset(rx_buffer, 0, sizeof(rx_buffer)); // 清空緩衝區
bytes_read = read(serial_fd, rx_buffer, sizeof(rx_buffer) - 1);
if (bytes_read > 0) {
rx_buffer[bytes_read] = '\0'; // 添加字元串結束符
printf("\rrx_buffer: %s\n", rx_buffer);
} else if (bytes_read == 0) {
printf("No data received.\n");
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("Timeout occurred.\n");
} else {
perror("Error reading from serial port");
}
}
// 檢查是否接收到 "exit" 以退出迴圈
if (strncmp(rx_buffer, "exit", 4) == 0) {
break;
}
}
close(serial_fd);
return 0;
}
實驗現象見圖,在Linux端可以一直接收到數據,直到STM32端exit數據發送。
可以看到最後一個數據為"exitreceiveTemp"這是為什麼呢?
從STM32端的代碼可以發現:
這兩個發送間距很快,因此接收端認為其是一次發送;
Linux 向 STM32 串口發送數據
發送完成後,接收就大同小異,STM32方面代碼較為簡單,邏輯為:阻塞式接收,通過判斷接收首位是否為0及字元'\0',判斷是否接收到數據,若接收到數據則printf出來,代碼如下:
uint8_t ReceiveTemp[11] = {};
while (1)
{
/* Receive data code */
memset(ReceiveTemp, 0, sizeof(ReceiveTemp));
HAL_UART_Receive(&huart2, ReceiveTemp, 11, 0xffff);
if(ReceiveTemp[0] != 0)
{
printf("Receive data:%s\n", ReceiveTemp);
}
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_7);
}
Linux方面,只需將接收部分換成發送即可,代碼邏輯為:定時發送。代碼如下:
/* Transmit data in 1s intervals */
char tx_buffer[] = {"0123456789"};
while(1)
{
ssize_t bytes_written = write(serial_fd, tx_buffer, sizeof(tx_buffer));
if(bytes_written != sizeof(tx_buffer))
{
fprintf(stderr, "Filed to send all data. Sent %zd out of %zu bytes.\n",
bytes_written, sizeof(tx_buffer));
}else{
printf("Data send successfully.Data len is:%d\n", bytes_written);
}
sleep(1);
}
實驗現象為:
Linux方面:每發送成功一次則列印一次;
STM32方面:每接收成功一次則將接收結果列印出來;
總結
本文實現了STM32 與 Linux平臺的串口雙向阻塞式通訊,根據實驗效果驗證效果可行。