diff --git a/src/rk_uart_main.cpp b/src/rk_uart_main.cpp new file mode 100644 index 0000000..7ae6419 --- /dev/null +++ b/src/rk_uart_main.cpp @@ -0,0 +1,352 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// --- 协议常量定义 (需与 STM32 保持一致) --- +#define FRAME_HEAD 0xA5 +#define CMD_REPORT 0x01 +#define CMD_SET_MODE 0x02 +#define CMD_SET_ALARM 0x03 +#define CMD_ACK 0xAA + +// --- 数据结构定义 (字节对齐) --- +#pragma pack(push, 1) +struct BatteryPacket_t { + uint8_t battery_cap; // 1. 电量百分比 + uint16_t time_to_full; // 2. 预测充满时间 + uint16_t time_to_empty; // 3. 预测放电时间 + uint8_t battery_status; // 4. 电池状态 + int16_t temperature; // 5. 温度 + uint8_t system_mode; // 6. 模式 + uint8_t alarm_flag; // 7. 报警标志 +}; +#pragma pack(pop) + +// --- 简单的串口封装类 --- +class SerialPort { +public: + SerialPort(const std::string& device, int baudRate) : fd_(-1) { + openPort(device, baudRate); + } + + ~SerialPort() { + if (fd_ >= 0) + close(fd_); + } + + bool isOpen() const { + return fd_ >= 0; + } + + int writeData(const uint8_t* data, size_t len) { + if (fd_ < 0) + return -1; + return write(fd_, data, len); + } + + int readData(uint8_t* buffer, size_t maxLen) { + if (fd_ < 0) + return -1; + return read(fd_, buffer, maxLen); + } + +private: + int fd_; + + void openPort(const std::string& device, int baudRate) { + fd_ = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + if (fd_ < 0) { + perror("Failed to open serial port"); + return; + } + + // 恢复阻塞模式 (或者根据需要保持非阻塞) + fcntl(fd_, F_SETFL, 0); + + struct termios options; + tcgetattr(fd_, &options); + + // 设置波特率 + speed_t speed; + switch (baudRate) { + case 9600: + speed = B9600; + break; + case 115200: + speed = B115200; + break; + default: + speed = B115200; + break; + } + cfsetispeed(&options, speed); + cfsetospeed(&options, speed); + + // 配置为 8N1, Raw 模式 + 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_cflag &= ~CRTSCTS; // 无流控 + + // 原始输入输出模式 (禁止回显、信号处理等) + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + options.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁止软件流控 + options.c_iflag &= ~(ICRNL | INLCR); // 禁止CR/LF转换 + options.c_oflag &= ~OPOST; // 原始输出 + + tcsetattr(fd_, TCSANOW, &options); + tcflush(fd_, TCIOFLUSH); + } +}; + +// --- 通信协议处理类 --- +class CommProtocol { +public: + CommProtocol(SerialPort* serial) : serial_(serial), running_(false) {} + + ~CommProtocol() { + stop(); + } + + // 启动接收线程 + void start() { + running_ = true; + rxThread_ = std::thread(&CommProtocol::receiveLoop, this); + } + + void stop() { + running_ = false; + if (rxThread_.joinable()) + rxThread_.join(); + } + + // 发送指令:设置模式 + void sendSetMode(uint8_t mode) { + uint8_t payload = mode; + sendPacket(CMD_SET_MODE, &payload, 1); + } + + // 发送指令:设置报警 + void sendSetAlarm(uint8_t alarm) { + uint8_t payload = alarm; + sendPacket(CMD_SET_ALARM, &payload, 1); + } + +private: + SerialPort* serial_; + std::thread rxThread_; + std::atomic running_; + std::vector rxBuffer_; // 接收缓冲区 + + // CRC32 计算 (对应 STM32 HAL_CRC_Calculate + XOR) + // 注意:这里使用的是标准 CRC32 算法 (Polynomial 0x04C11DB7) + // 如果 STM32 端是默认配置,这应该能对上。 + uint32_t calculateCRC32(const uint8_t* data, size_t len) { + uint32_t crc = 0xFFFFFFFF; // Initial Value + for (size_t i = 0; i < len; i++) { + uint32_t byte = data[i]; + // 标准 CRC32 是反转输入和输出的,这里模拟 STM32 常见配置 + // 这里使用最通用的查表法或者位移法 + // 为简化代码,使用位移法 (效率较低但足够) + + // 注意:STM32 硬件CRC如果不开启 REVERSE_INPUT/OUTPUT,行为是 BigEndian 风格 + // 但你在 STM32 代码里写了 `crc ^ 0xFFFFFFFF`,通常是为了匹配标准 CRC32 + // 下面这个是标准 Ethernet CRC32 算法: + crc = crc ^ byte; + for (int j = 0; j < 8; j++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = crc >> 1; + } + } + return crc ^ 0xFFFFFFFF; + + // **注意**:如果发现校验一直不过,STM32 硬件CRC可能没有开启 Input/Output Reverse。 + // 如果是那样,请尝试替换为非反转的 CRC32 算法。 + } + + // 如果上面的 CRC 算不对,试试这个 STM32 默认硬件CRC (无反转) 的版本: + /* + uint32_t calculateCRC32_STM32_Default(const uint8_t* data, size_t len) { + uint32_t crc = 0xFFFFFFFF; + for(size_t i=0; i packet; + packet.push_back(FRAME_HEAD); + packet.push_back(cmd); + packet.push_back(len); + + if (len > 0 && payload != nullptr) { + packet.insert(packet.end(), payload, payload + len); + } + + // 计算 CRC (Header + Cmd + Len + Payload) + uint32_t crc = calculateCRC32(packet.data(), packet.size()); + + // 小端序发送 CRC + packet.push_back((crc >> 0) & 0xFF); + packet.push_back((crc >> 8) & 0xFF); + packet.push_back((crc >> 16) & 0xFF); + packet.push_back((crc >> 24) & 0xFF); + + serial_->writeData(packet.data(), packet.size()); + + std::cout << "[Tx] Cmd: " << std::hex << (int)cmd << " Len: " << (int)len << std::dec + << std::endl; + } + + void processFrame(const uint8_t* frame, size_t totalLen) { + uint8_t cmd = frame[1]; + // uint8_t len = frame[2]; // payload length + const uint8_t* payload = &frame[3]; + + switch (cmd) { + case CMD_REPORT: { + if (totalLen >= sizeof(BatteryPacket_t) + 7) { + BatteryPacket_t data; + memcpy(&data, payload, sizeof(BatteryPacket_t)); + + std::cout << "\n=== STM32 Status Report ===" << std::endl; + std::cout << "Cap: " << (int)data.battery_cap << "%" << std::endl; + std::cout << "Time Full: " << data.time_to_full << " min" << std::endl; + std::cout << "Time Empty: " << data.time_to_empty << " min" << std::endl; + std::cout << "Status: " << (int)data.battery_status << std::endl; + std::cout << "Temp: " << (float)data.temperature / 10.0f << " C" << std::endl; + std::cout << "Mode: " << (int)data.system_mode << std::endl; + std::cout << "Alarm: " << (int)data.alarm_flag << std::endl; + + // 回复 ACK + uint8_t ack[] = "OK"; + sendPacket(CMD_ACK, ack, 2); + } + break; + } + case CMD_ACK: + std::cout << "[Rx] Received ACK from STM32" << std::endl; + break; + default: + std::cout << "[Rx] Unknown CMD: " << std::hex << (int)cmd << std::endl; + break; + } + } + + void receiveLoop() { + uint8_t buf[128]; + while (running_) { + int n = serial_->readData(buf, sizeof(buf)); + if (n > 0) { + // 将新数据追加到 vector 缓冲区 + rxBuffer_.insert(rxBuffer_.end(), buf, buf + n); + + // 尝试解析帧 + while (rxBuffer_.size() >= 7) { // 最小帧长 Header+Cmd+Len+CRC(4) = 7 + // 1. 寻找帧头 + if (rxBuffer_[0] != FRAME_HEAD) { + rxBuffer_.erase(rxBuffer_.begin()); // 丢弃无效字节 + continue; + } + + uint8_t dataLen = rxBuffer_[2]; + size_t frameLen = 3 + dataLen + 4; + + // 2. 检查缓冲区是否有完整的一帧 + if (rxBuffer_.size() < frameLen) { + break; // 数据不够,等待下次接收 + } + + // 3. 提取 CRC 并校验 + uint32_t receivedCRC = 0; + receivedCRC |= rxBuffer_[frameLen - 4] << 0; + receivedCRC |= rxBuffer_[frameLen - 3] << 8; + receivedCRC |= rxBuffer_[frameLen - 2] << 16; + receivedCRC |= rxBuffer_[frameLen - 1] << 24; + + uint32_t calcCRC = calculateCRC32(rxBuffer_.data(), frameLen - 4); + + if (calcCRC == receivedCRC) { + // 校验通过,处理帧 + processFrame(rxBuffer_.data(), frameLen); + // 移除已处理的帧 + rxBuffer_.erase(rxBuffer_.begin(), rxBuffer_.begin() + frameLen); + } else { + std::cerr << "CRC Error! Calc: " << std::hex << calcCRC + << " Recv: " << receivedCRC << std::endl; + // 校验失败,移除头字节,尝试重新寻找下一帧 + rxBuffer_.erase(rxBuffer_.begin()); + } + } + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } +}; + +int main() { + // 1. 配置串口设备 (请根据实际情况修改,例如 /dev/ttyUSB0 或 /dev/ttyS4) + // RK3588 的硬件串口通常是 /dev/ttyS0 ~ /dev/ttyS9,或者 /dev/ttyFIQ0 + std::string devPath = "/dev/ttyS4"; + + std::cout << "Opening serial port: " << devPath << std::endl; + SerialPort serial(devPath, 115200); + + if (!serial.isOpen()) { + return -1; + } + + // 2. 启动协议处理 + CommProtocol protocol(&serial); + protocol.start(); + + // 3. 交互循环 + while (true) { + std::cout << "\nChoose Action:\n1. Set Mode WORK\n2. Set Mode SLEEP\n3. Set Alarm ON\n4. " + "Set Alarm OFF\nInput: "; + int choice; + std::cin >> choice; + + switch (choice) { + case 1: + protocol.sendSetMode(1); // MODE_WORK + break; + case 2: + protocol.sendSetMode(2); // MODE_SLEEP + break; + case 3: + protocol.sendSetAlarm(1); + break; + case 4: + protocol.sendSetAlarm(0); + break; + default: + break; + } + + // 简单延时防止刷屏 + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + + return 0; +} \ No newline at end of file