Vehicle_Road_Counter/read_bms.cpp

195 lines
7.9 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <cstring>
#include <cstdint>
#include <vector>
#include <iomanip>
#include <chrono>
// --- 协议定义 (必须与STM32端完全一致) ---
#pragma pack(push, 1) // 强制1字节对齐
typedef struct {
uint16_t voltage; // 电池电压 mV
int16_t current; // 电池电流 mA
uint8_t capacity; // 电池电量 %
int16_t temp; // 电池温度 0.1K (需转换)
uint16_t rem_capacity; // 剩余容量
uint16_t chg_voltage; // 充电电压
uint16_t chg_current; // 充电电流
uint8_t status_dsg; // 充放电状态
uint16_t cycle_count; // 循环次数
uint8_t rsoc; // 相对电量
} BMS_Payload_t;
#pragma pack(pop)
// 常量定义
const uint8_t FRAME_HEADER_0 = 0xAA;
const uint8_t FRAME_HEADER_1 = 0x55;
const uint8_t FRAME_TAIL_0 = 0x0D;
const uint8_t FRAME_TAIL_1 = 0x0A;
const uint8_t REQUEST_CMD = 0xA5; // 请求指令
const int EXPECTED_PAYLOAD_LEN = sizeof(BMS_Payload_t); // 应该为 17
// --- 串口配置函数 ---
int open_serial_port(const char* portname) {
int fd = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_serial_port: Unable to open port");
return -1;
}
struct termios options;
tcgetattr(fd, &options);
// 设置波特率 115200
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
// 控制模式 (CLOCAL: 本地连接, CREAD: 开启接收)
options.c_cflag |= (CLOCAL | CREAD);
// 设置 8N1 (8数据位, 无校验, 1停止位)
options.c_cflag &= ~PARENB; // 无校验
options.c_cflag &= ~CSTOPB; // 1停止位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8数据位
// *** 关键设置Raw 模式 ***
// 禁用规范模式 (ICANON)、回显 (ECHO)、信号 (ISIG)
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 禁用输出处理 (OPOST)
options.c_oflag &= ~OPOST;
// 禁用输入软件流控 (IXON, IXOFF, IXANY) 和 CR/LF 转换
options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | INLCR | IGNCR);
// 设置读取超时 (VMIN=0, VTIME=10 => 非阻塞读不到数据等待1秒返回)
// 这里为了简单演示,使用阻塞式读取一点点数据
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 1; // 0.1秒超时
tcsetattr(fd, TCSANOW, &options);
fcntl(fd, F_SETFL, 0); // 恢复为阻塞模式 (配合 VMIN/VTIME)
return fd;
}
// --- 解析状态机枚举 ---
enum State {
STATE_WAIT_HEADER_0,
STATE_WAIT_HEADER_1,
STATE_WAIT_LEN,
STATE_READ_PAYLOAD,
STATE_WAIT_CHECKSUM,
STATE_WAIT_TAIL_0,
STATE_WAIT_TAIL_1
};
// --- 主程序 ---
int main() {
const char* port = "/dev/ttyS4";
int serial_fd = open_serial_port(port);
if (serial_fd < 0) return 1;
std::cout << "Listening on " << port << " at 115200 baud..." << std::endl;
// 解析器状态变量
State current_state = STATE_WAIT_HEADER_0;
std::vector<uint8_t> payload_buffer;
uint8_t expected_len = 0;
uint8_t calc_checksum = 0;
uint8_t recv_checksum = 0;
uint8_t buf[128]; // 临时读取缓冲区
auto last_req_time = std::chrono::steady_clock::now();
bool first_run = true;
while (true) {
// --- 1. 发送请求逻辑 (每秒一次) ---
auto now = std::chrono::steady_clock::now();
// 如果是第一次运行,或者距离上次发送超过 1 秒 (1000ms)
if (first_run || std::chrono::duration_cast<std::chrono::milliseconds>(now - last_req_time).count() >= 1000) {
// 发送指令 0xA5
write(serial_fd, &REQUEST_CMD, 1);
// 打印调试信息 (可选)
// std::cout << "[TX] Request sent" << std::endl;
last_req_time = now;
first_run = false;
}
// --- 2. 接收处理逻辑 (保持不变) ---
int n = read(serial_fd, buf, sizeof(buf));
if (n > 0) {
for (int i = 0; i < n; i++) {
uint8_t byte = buf[i];
switch (current_state) {
case STATE_WAIT_HEADER_0:
if (byte == FRAME_HEADER_0) current_state = STATE_WAIT_HEADER_1;
break;
case STATE_WAIT_HEADER_1:
if (byte == FRAME_HEADER_1) current_state = STATE_WAIT_LEN;
else if (byte == FRAME_HEADER_0) current_state = STATE_WAIT_HEADER_1;
else current_state = STATE_WAIT_HEADER_0;
break;
case STATE_WAIT_LEN:
expected_len = byte;
if (expected_len == sizeof(BMS_Payload_t)) {
payload_buffer.clear(); payload_buffer.reserve(expected_len);
current_state = STATE_READ_PAYLOAD;
} else {
current_state = STATE_WAIT_HEADER_0;
}
break;
case STATE_READ_PAYLOAD:
payload_buffer.push_back(byte);
if (payload_buffer.size() == expected_len) current_state = STATE_WAIT_CHECKSUM;
break;
case STATE_WAIT_CHECKSUM:
recv_checksum = byte;
current_state = STATE_WAIT_TAIL_0;
break;
case STATE_WAIT_TAIL_0:
if (byte == FRAME_TAIL_0) current_state = STATE_WAIT_TAIL_1;
else current_state = STATE_WAIT_HEADER_0;
break;
case STATE_WAIT_TAIL_1:
if (byte == FRAME_TAIL_1) {
// 计算校验和
uint8_t calc_sum = 0;
for (uint8_t b : payload_buffer) calc_sum += b;
if (calc_sum == recv_checksum) {
BMS_Payload_t* bms = (BMS_Payload_t*)payload_buffer.data();
std::cout << "\n[RX] Data Updated: ------------------------" << std::endl;
std::cout << "Voltage: " << bms->voltage << " mV" << std::endl;
std::cout << "Current: " << bms->current << " mA" << std::endl;
std::cout << "Capacity: " << (int)bms->capacity << " %" << std::endl;
std::cout << "Temp: " << (float)bms->temp * 0.1f << " C" << std::endl;
std::cout << "Rem Cap: " << bms->rem_capacity << " mAh" << std::endl;
std::cout << "Chg Voltage: " << bms->chg_voltage << " mV" << std::endl;
std::cout << "Chg Current: " << bms->chg_current << " mA" << std::endl;
std::cout << "Status DSG: " << (int)bms->status_dsg << std::endl;
std::cout << "Cycle Count: " << bms->cycle_count << std::endl;
std::cout << "RSOC: " << (int)bms->rsoc << " %" << std::endl;
} else {
std::cerr << "Checksum Error!" << std::endl;
}
}
current_state = STATE_WAIT_HEADER_0;
break;
}
}
} else {
// 短暂休眠避免CPU占用过高但不要睡太久以免错过数据
usleep(5000);
}
}
close(serial_fd);
return 0;
}