ubuntu 上位机 编写C++ 与stm32 进行串口通信,读写数据, 读取的数据存储在一个vector中
时间: 2025-06-23 10:16:26 浏览: 20
<think>好的,用户想要在Ubuntu上用C++编写上位机程序,与STM32进行串口通信,并且把读取的数据存储到vector中。首先,我需要确认用户的需求细节。他们可能是在做一个嵌入式项目,比如数据采集或控制,STM32作为下位机负责传感器数据的上传,而上位机需要接收这些数据并进行处理或存储。
用户可能已经具备一定的C++基础,但可能对Linux下的串口编程不太熟悉。需要提供一步步的指导,包括串口配置、读写操作以及数据存储。同时,用户提到使用vector,说明他们希望动态管理接收到的数据,可能需要实时处理或后续分析。
接下来,我需要考虑Ubuntu下的串口通信库。常见的有使用Linux系统调用(如termios)或者第三方库如Boost.Asio。对于新手来说,系统调用可能更直接,不需要额外安装库,但代码可能稍复杂。Boost.Asio虽然功能强大,但需要安装Boost库,可能会增加复杂度。根据用户的情况,可能更适合使用termios,因为依赖较少,更适合嵌入式开发环境。
然后,需要详细说明如何配置串口参数:波特率、数据位、停止位、校验位等,这些需要与STM32端的设置一致。例如,常用的设置是115200波特率,8数据位,1停止位,无校验。用户可能需要根据实际情况调整这些参数。
读写部分,需要分同步和异步。对于简单的应用,同步读写可能足够,但如果有大量数据或需要实时处理,可能需要异步读写。不过,用户的需求是存储到vector中,可能同步读取更简单。需要注意缓冲区管理,避免数据丢失或溢出。
数据存储方面,使用vector来动态添加接收到的数据,需要注意线程安全。如果在主循环中读取并存储,可能不需要多线程。但如果使用异步读取,可能需要锁机制来保护vector的访问。
可能的错误点包括串口权限问题。在Linux下,普通用户可能需要加入dialout组才能访问串口设备,或者使用sudo。需要提醒用户检查/dev/ttyUSB0或ttyACM0的权限,避免运行时出现权限拒绝的错误。
另外,STM32端的代码也需要正确配置USART,确保数据格式和发送频率与上位机匹配。可能需要建议用户先测试STM32的串口发送功能,例如用串口调试工具确认数据正确传输,再编写上位机程序。
代码结构方面,应该提供一个简单的例子,包括打开串口、配置参数、读取数据并存入vector的步骤。可能需要循环读取,每次读取一定字节,解析后存储。例如,如果STM32发送的是以换行符结尾的字符串,可以按行读取;如果发送的是二进制数据,可能需要定义帧结构,处理粘包问题。
还需要考虑错误处理,比如串口打开失败、读取错误等情况,确保程序健壮性。此外,数据解析部分,用户可能需要将接收的字节流转换为实际的数据类型,比如整数、浮点数等,这取决于STM32发送的数据格式。
最后,测试和调试是关键。建议用户逐步测试每个部分,先确保能打开串口,然后测试读取数据是否正确,再验证vector存储是否正常。可能使用printf或日志输出vector的内容,或者写入文件进行验证。
总结下来,用户的真实需求是实现Ubuntu与STM32的稳定串口通信,并可靠地存储数据。可能需要详细的代码示例、配置步骤和调试建议,帮助他们一步步实现目标,避免常见的陷阱如权限问题、配置错误和数据解析错误。</think>以下是基于**Ubuntu上位机**通过**C++**与**STM32**实现串口通信的完整指南,包含代码示例和关键步骤解析:
---
### 一、开发环境准备
#### 1. 硬件连接
- STM32开发板通过**USB转TTL模块**连接Ubuntu主机(设备路径通常为`/dev/ttyUSB0`或`ttyACM0`)
- 确保双方**波特率一致**(如115200)
#### 2. 系统权限配置
```bash
sudo usermod -aG dialout $USER # 将当前用户加入串口组
sudo reboot # 重启生效
```
#### 3. 安装必要工具
```bash
sudo apt install g++ libserial-dev # 安装C++编译器及串口库(或使用系统自带termios)
```
---
### 二、C++串口通信核心代码
#### 1. 使用`termios`系统级API(无需额外库)
```cpp
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <vector>
class SerialPort {
private:
int fd;
std::vector<uint8_t> rx_buffer; // 存储接收数据的vector
public:
SerialPort(const char* port, int baudrate = B115200) {
fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) throw std::runtime_error("无法打开串口");
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, baudrate);
cfsetospeed(&options, baudrate);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; // 无校验
options.c_cflag &= ~CSTOPB; // 1位停止位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8数据位
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式
tcsetattr(fd, TCSANOW, &options);
}
// 读取数据到vector
void readData() {
uint8_t buffer[256];
int n = read(fd, buffer, sizeof(buffer));
if (n > 0) {
rx_buffer.insert(rx_buffer.end(), buffer, buffer + n);
}
}
// 发送数据
void sendData(const std::vector<uint8_t>& data) {
write(fd, data.data(), data.size());
}
~SerialPort() {
close(fd);
}
};
```
#### 2. 使用`libserial`库(简化开发)
```cpp
#include <libserial/SerialPort.h>
#include <vector>
LibSerial::SerialPort sp;
std::vector<uint8_t> rx_data;
sp.Open("/dev/ttyUSB0");
sp.SetBaudRate(LibSerial::BaudRate::BAUD_115200);
sp.SetCharacterSize(LibSerial::CharacterSize::CHAR_SIZE_8);
sp.SetFlowControl(LibSerial::FlowControl::FLOW_CONTROL_NONE);
// 读取数据到vector
LibSerial::DataBuffer buffer = sp.Read(256);
rx_data.insert(rx_data.end(), buffer.begin(), buffer.end());
```
---
### 三、STM32端关键配置(基于HAL库)
```c
// CubeMX配置:启用USART1,波特率115200,8N1
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
// 接收数据到缓冲区
HAL_UART_Receive_IT(&huart1, &rx_byte, 1); // 持续接收
}
}
// 发送数据示例
uint8_t tx_data[] = {0xAA, 0x55, 0x01};
HAL_UART_Transmit(&huart1, tx_data, sizeof(tx_data), 100);
```
---
### 四、数据帧协议设计建议
| 字段 | 长度(字节) | 说明 |
|------------|------------|--------------------|
| 帧头 | 2 | 固定0xAA55 |
| 数据长度 | 1 | 后续数据长度 |
| 数据载荷 | N | 实际传输数据 |
| 校验和 | 1 | 所有字节累加和取低8位 |
**解析示例**:
```cpp
void parseData(std::vector<uint8_t>& buffer) {
while (buffer.size() >= 4) { // 至少包含帧头+长度+校验
if (buffer[0] == 0xAA && buffer[1] == 0x55) {
uint8_t length = buffer[2];
if (buffer.size() >= 3 + length + 1) {
// 计算校验和
uint8_t checksum = std::accumulate(buffer.begin()+2,
buffer.begin()+3+length, 0);
if (checksum == buffer[3+length]) {
// 提取有效数据
std::vector<uint8_t> payload(
buffer.begin()+3,
buffer.begin()+3+length
);
processPayload(payload); // 用户自定义处理函数
// 删除已处理数据
buffer.erase(buffer.begin(),
buffer.begin()+4+length);
}
}
} else {
buffer.erase(buffer.begin()); // 丢弃无效数据
}
}
}
```
---
### 五、完整工作流程
1. **初始化连接**
```cpp
try {
SerialPort sp("/dev/ttyUSB0");
while (true) {
sp.readData();
parseData(sp.rx_buffer); // 实时解析
// 发送示例
std
阅读全文