Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

📚 SerialPort 串口通信库使用手册

目录

  1. 简介
  2. 快速开始
  3. API参考
  4. 使用示例
  5. 常见问题
  6. 协议设计建议

1. 简介

SerialPort 是一个用于Jetson Nano/Linux平台的C++串口通信库,封装了Linux系统底层的POSIX串口API,提供了简洁易用的接口。特别适用于Jetson Nano与STM32、Arduino等微控制器的串口通信。

特性

  • ✅ 支持标准波特率(9600-4000000)
  • ✅ 8位数据位、无校验、1位停止位(8N1)
  • ✅ 同步读写操作
  • ✅ 超时控制
  • ✅ 字符串和二进制数据传输
  • ✅ 完善的错误处理

2. 快速开始

2.1 文件结构

your_project/
├── include/
│   └── SerialPort.h          # 头文件
├── src/
│   ├── SerialPort.cpp        # 实现文件
│   └── main.cpp              # 你的主程序
└── CMakeLists.txt            # CMake构建文件

2.2 最简单的示例

#include "SerialPort.h"
#include <iostream>

int main() {
    // 1. 创建串口对象
    SerialPort serial;

    // 2. 打开串口
    if (!serial.open("/dev/ttyTHS1", 115200)) {
        std::cerr << "打开串口失败: " << serial.getLastError() << std::endl;
        return -1;
    }

    // 3. 发送数据
    serial.writeString("Hello STM32!");

    // 4. 接收数据
    std::string reply = serial.readLine(1000);  // 等待1秒
    if (!reply.empty()) {
        std::cout << "收到: " << reply << std::endl;
    }

    // 5. 关闭串口
    serial.close();

    return 0;
}

2.3 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(SerialDemo)

set(CMAKE_CXX_STANDARD 11)

include_directories(include)

add_executable(demo
    src/main.cpp
    src/SerialPort.cpp
)

2.4 编译和运行

# 编译
mkdir build && cd build
cmake ..
make

# 设置权限并运行
sudo chmod 777 /dev/ttyTHS1
sudo ./demo

3. API参考

3.1 构造函数和析构函数

SerialPort();                    // 构造函数,初始化文件描述符为-1
~SerialPort();                   // 析构函数,自动关闭串口

3.2 打开和关闭

/**
 * 打开串口
 * @param portName 串口设备路径,如 "/dev/ttyTHS1"
 * @param baudRate 波特率,常用值:9600, 115200, 921600等
 * @return true:成功 false:失败
 */
bool open(const std::string& portName, int baudRate = 115200);

/**
 * 关闭串口
 */
void close();

/**
 * 检查串口是否已打开
 * @return true:已打开 false:未打开
 */
bool isOpen() const;

3.3 发送数据

/**
 * 发送原始字节数据
 * @param data 数据缓冲区指针
 * @param length 数据长度
 * @return 实际发送的字节数,-1表示失败
 */
int write(const uint8_t* data, int length);

/**
 * 发送字符串
 * @param str 要发送的字符串
 * @param addNewline 是否自动添加换行符(\n)
 * @return 实际发送的字节数
 */
int writeString(const std::string& str, bool addNewline = true);

// 使用示例
serial.writeString("Hello");           // 发送 "Hello"
serial.writeString("Hello", true);     // 发送 "Hello\n"
serial.writeString("Hello", false);    // 发送 "Hello" (不添加换行)

3.4 接收数据

/**
 * 读取数据到缓冲区
 * @param buffer 接收缓冲区
 * @param maxLength 最大读取长度
 * @param timeoutMs 超时时间(毫秒),-1表示无限等待
 * @return 实际读取的字节数,0表示超时,-1表示错误
 */
int read(uint8_t* buffer, int maxLength, int timeoutMs = 100);

/**
 * 读取一行数据(直到遇到换行符\n)
 * @param timeoutMs 超时时间(毫秒)
 * @return 读取到的字符串(不含换行符)
 */
std::string readLine(int timeoutMs = 100);

// 使用示例
uint8_t buf[128];
int len = serial.read(buf, sizeof(buf), 500);  // 等待最多500ms
if (len > 0) {
    // 处理接收到的数据
}

std::string line = serial.readLine(1000);      // 读取一行,等待1秒

3.5 其他功能

/**
 * 清空输入输出缓冲区
 */
void flush();

/**
 * 获取最后一次错误信息
 * @return 错误描述字符串
 */
std::string getLastError() const;

4. 使用示例

4.1 基础通信示例

#include "SerialPort.h"
#include <iostream>
#include <thread>

int main() {
    SerialPort serial;

    // 打开串口
    if (!serial.open("/dev/ttyTHS1", 115200)) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    std::cout << "串口已打开" << std::endl;

    // 发送数据
    serial.writeString("AT\r\n");  // 发送AT指令

    // 接收响应
    std::string response = serial.readLine(1000);
    std::cout << "响应: " << response << std::endl;

    // 连续通信
    for (int i = 0; i < 5; i++) {
        std::string msg = "Message " + std::to_string(i);
        serial.writeString(msg);

        std::string reply = serial.readLine(500);
        if (!reply.empty()) {
            std::cout << "收到: " << reply << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    serial.close();
    return 0;
}

4.2 二进制数据传输

#include "SerialPort.h"
#include <cstdint>

// 定义通信协议结构体
#pragma pack(1)
struct MotorCommand {
    uint8_t header;      // 帧头: 0xAA
    uint8_t cmd;         // 命令: 0x01
    int16_t left_speed;  // 左轮速度
    int16_t right_speed; // 右轮速度
    uint8_t checksum;    // 校验和
    uint8_t footer;      // 帧尾: 0x55
};

struct SensorData {
    uint8_t header;      // 帧头: 0xAA
    uint8_t cmd;         // 命令: 0x02
    uint16_t distance;   // 距离 (mm)
    uint16_t voltage;    // 电压 (mV)
    uint8_t checksum;    // 校验和
    uint8_t footer;      // 帧尾: 0x55
};
#pragma pack()

// 计算校验和
uint8_t calcChecksum(const uint8_t* data, int len) {
    uint8_t sum = 0;
    for (int i = 0; i < len; i++) sum += data[i];
    return sum;
}

int main() {
    SerialPort serial;
    serial.open("/dev/ttyTHS1", 115200);

    // 发送电机控制命令
    MotorCommand cmd;
    cmd.header = 0xAA;
    cmd.cmd = 0x01;
    cmd.left_speed = 500;
    cmd.right_speed = 300;
    cmd.footer = 0x55;
    cmd.checksum = calcChecksum((uint8_t*)&cmd,
        sizeof(cmd) - sizeof(cmd.checksum) - sizeof(cmd.footer));

    serial.write((uint8_t*)&cmd, sizeof(cmd));

    // 接收传感器数据
    uint8_t buffer[64];
    int len = serial.read(buffer, sizeof(buffer), 1000);

    if (len >= sizeof(SensorData)) {
        SensorData* sensor = (SensorData*)buffer;
        if (sensor->header == 0xAA && sensor->footer == 0x55) {
            std::cout << "距离: " << sensor->distance << " mm" << std::endl;
            std::cout << "电压: " << sensor->voltage << " mV" << std::endl;
        }
    }

    serial.close();
    return 0;
}

4.3 多线程收发示例

#include "SerialPort.h"
#include <thread>
#include <atomic>
#include <iostream>

std::atomic<bool> running(true);

// 接收线程
void receiveThread(SerialPort* serial) {
    while (running) {
        std::string line = serial->readLine(100);
        if (!line.empty()) {
            std::cout << "[接收] " << line << std::endl;
        }
    }
}

// 发送线程
void sendThread(SerialPort* serial) {
    int count = 0;
    while (running) {
        std::string msg = "Data " + std::to_string(count++);
        serial->writeString(msg);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main() {
    SerialPort serial;

    if (!serial.open("/dev/ttyTHS1", 115200)) {
        std::cerr << "打开失败" << std::endl;
        return 1;
    }

    std::cout << "串口已打开,开始通信..." << std::endl;

    // 启动收发线程
    std::thread recvThread(receiveThread, &serial);
    std::thread sendTh(sendThread, &serial);

    // 等待用户输入退出
    std::cout << "按回车键退出..." << std::endl;
    std::cin.get();

    running = false;

    sendTh.join();
    recvThread.join();

    serial.close();
    return 0;
}

5. 常见问题

5.1 权限问题

错误: 打开串口失败: Permission denied

解决:

# 临时解决
sudo chmod 777 /dev/ttyTHS1

# 永久解决(推荐)
sudo usermod -a -G dialout $USER
# 然后注销重新登录

5.2 串口不存在

错误: 打开串口失败: No such file or directory

解决:

# 检查可用的串口设备
ls -l /dev/ttyTHS*
ls -l /dev/ttyUSB*

# Jetson Nano通常使用 /dev/ttyTHS1

5.3 数据乱码

可能原因:

  • 波特率不匹配
  • 接线问题(TX-RX接反)
  • 没有共地(GND未连接)

解决:

// 确保两边的波特率一致
serial.open("/dev/ttyTHS1", 115200);  // STM32也必须是115200

5.4 接收超时

可能原因:

  • 设备未发送数据
  • 超时时间太短

解决:

// 增加超时时间
std::string data = serial.readLine(5000);  // 等待5秒

// 或者使用循环接收
while (true) {
    uint8_t buf[1];
    int n = serial.read(buf, 1, 100);
    if (n > 0) {
        // 处理数据
    }
}

5.5 数据丢失

解决: 使用带校验的协议

// 发送端添加校验和
uint8_t checksum = 0;
for (int i = 0; i < dataLen; i++) {
    checksum += data[i];
}
packet[dataLen] = checksum;  // 附加校验和

// 接收端验证
uint8_t calc = 0;
for (int i = 0; i < dataLen; i++) {
    calc += packet[i];
}
if (calc == packet[dataLen]) {
    // 数据正确
}

6. 协议设计建议

6.1 简单文本协议

适用于调试和简单控制:

命令格式: CMD:参数\r\n
示例:
  "MOTOR:100,200\r\n"  // 设置电机速度
  "SERVO:1,90\r\n"     // 设置舵机角度
  "GET:SENSOR\r\n"     // 请求传感器数据

响应格式:
  "OK:距离=150mm\r\n"
  "ERROR:无效命令\r\n"

6.2 二进制协议(推荐)

适用于可靠的数据传输:

[帧头][命令][数据长度][数据...][校验和][帧尾]

帧头: 0xAA 0x55 (2字节)
命令: 1字节 (0x01=电机, 0x02=传感器, 0x03=配置)
数据长度: 1字节 (后续数据长度)
数据: 变长 (根据命令定义)
校验和: 1字节 (从命令到数据的累加和)
帧尾: 0x5B (1字节)

6.3 示例协议实现

// 协议定义
#define FRAME_HEADER1 0xAA
#define FRAME_HEADER2 0x55
#define FRAME_FOOTER  0x5B

// 发送数据包
bool sendPacket(SerialPort& serial, uint8_t cmd,
                const uint8_t* data, uint8_t len) {
    std::vector<uint8_t> packet;
    packet.push_back(FRAME_HEADER1);
    packet.push_back(FRAME_HEADER2);
    packet.push_back(cmd);
    packet.push_back(len);

    uint8_t checksum = cmd + len;
    for (int i = 0; i < len; i++) {
        packet.push_back(data[i]);
        checksum += data[i];
    }

    packet.push_back(checksum);
    packet.push_back(FRAME_FOOTER);

    return serial.write(packet.data(), packet.size()) == packet.size();
}

// 接收数据包
bool receivePacket(SerialPort& serial, uint8_t* cmd,
                   uint8_t* data, uint8_t* len) {
    uint8_t buf[256];
    int n = serial.read(buf, sizeof(buf), 100);

    // 查找帧头
    for (int i = 0; i < n - 1; i++) {
        if (buf[i] == FRAME_HEADER1 && buf[i+1] == FRAME_HEADER2) {
            if (i + 5 >= n) return false;  // 数据不足

            *cmd = buf[i+2];
            *len = buf[i+3];

            // 验证数据完整性
            int totalLen = 5 + *len;  // 头2 + cmd1 + len1 + 数据 + 校验和1 + 尾1
            if (i + totalLen > n) return false;

            // 复制数据
            memcpy(data, &buf[i+4], *len);

            // 验证校验和
            uint8_t checksum = buf[i+4+*len];
            uint8_t calc = *cmd + *len;
            for (int j = 0; j < *len; j++) {
                calc += data[j];
            }

            // 验证帧尾
            if (checksum == calc &&
                buf[i+5+*len] == FRAME_FOOTER) {
                return true;
            }
        }
    }
    return false;
}

附录:波特率对照表

函数参数 实际波特率
9600 9600
19200 19200
38400 38400
57600 57600
115200 115200
230400 230400
460800 460800
921600 921600
1000000 1,000,000
2000000 2,000,000
3000000 3,000,000
4000000 4,000,000

版本历史

  • v1.0.0 (2024-01): 初始版本
    • 基础串口通信功能
    • 支持标准波特率
    • 同步读写操作

技术支持

如有问题,请检查:

  1. 串口设备是否存在 (ls /dev/tty*)
  2. 权限是否正确 (ls -l /dev/ttyTHS1)
  3. 接线是否正确 (TX-RX交叉连接)
  4. 波特率是否匹配
  5. GND是否连接

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages