Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pg_gps
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
957dd
pg_gps
Commits
61fd3055
Commit
61fd3055
authored
Jul 21, 2025
by
学习的菜鸟
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
定位
parents
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
453 additions
and
0 deletions
+453
-0
main.cc
main.cc
+147
-0
serial_port.cc
serial_port.cc
+234
-0
serial_port.hpp
serial_port.hpp
+72
-0
serial_tool
serial_tool
+0
-0
No files found.
main.cc
0 → 100644
View file @
61fd3055
// main.cpp
#include "serial_port.hpp"
#include <iostream>
#include <vector>
#include <iomanip>
#include <csignal>
#include <cstring>
// 全局的串口对象指针,用于信号处理
std
::
unique_ptr
<
SerialPort
>
g_serial_port
;
// 信号处理函数,用于优雅地退出
void
signalHandler
(
int
signum
)
{
std
::
cout
<<
"
\n
Interrupt signal ("
<<
signum
<<
") received."
<<
std
::
endl
;
if
(
g_serial_port
)
{
g_serial_port
->
close
();
}
exit
(
signum
);
}
// 打印收到的消息
void
print_hex
(
const
std
::
vector
<
uint8_t
>&
data
)
{
for
(
uint8_t
byte
:
data
)
{
std
::
cout
<<
std
::
hex
<<
std
::
setw
(
2
)
<<
std
::
setfill
(
'0'
)
<<
static_cast
<
int
>
(
byte
)
<<
" "
;
}
std
::
cout
<<
std
::
dec
;
// 恢复十进制输出
}
// 消息处理回调函数
void
onMessageReceived
(
const
std
::
vector
<
uint8_t
>&
message
)
{
size_t
message_len
=
message
.
size
();
if
(
message_len
==
0
)
{
return
;
}
// 使用我们推荐的 unique_ptr 方法来创建数组
auto
data_array
=
std
::
make_unique
<
uint8_t
[]
>
(
message_len
);
memcpy
(
data_array
.
get
(),
message
.
data
(),
message_len
);
// --- 正确地以十六进制格式打印数组内容 ---
std
::
cout
<<
"
\n
<-- Received "
<<
message_len
<<
" bytes. Copied to array:
\n
"
;
if
(
data_array
[
10
]
=
0x01
){
uint16_t
x_local
=
(
static_cast
<
uint8_t
>
(
data_array
[
13
])
<<
8
)
|
data_array
[
14
];
uint16_t
y_local
=
(
static_cast
<
uint8_t
>
(
data_array
[
15
])
<<
8
)
|
data_array
[
16
];
std
::
cout
<<
std
::
dec
<<
"tag:"
<<
static_cast
<
int
>
(
data_array
[
8
])
<<
std
::
endl
;
std
::
cout
<<
" "
<<
"x_local:"
<<
x_local
<<
"y_local:"
<<
y_local
<<
std
::
endl
;
}
// 恢复为十进制模式是个好习惯,以免影响后续的其他输出
std
::
cout
<<
std
::
dec
<<
std
::
endl
;
// 你也可以在这里进行CRC校验等操作
if
(
message_len
>
2
)
{
uint16_t
received_crc
=
(
static_cast
<
uint16_t
>
(
data_array
[
message_len
-
1
])
<<
8
)
|
data_array
[
message_len
-
2
];
uint16_t
calculated_crc
=
SerialPort
::
crc16
(
data_array
.
get
(),
message_len
-
2
);
std
::
cout
<<
" CRC Check on array data: "
<<
(
received_crc
==
calculated_crc
?
"OK"
:
"FAILED!"
)
<<
std
::
endl
;
}
// 重新打印菜单提示符,让用户界面更友好
std
::
cout
<<
"
\n
Enter your choice: "
<<
std
::
flush
;
}
void
print_menu
()
{
std
::
cout
<<
"
\n
======================================
\n
"
;
std
::
cout
<<
"Select a command to send:
\n
"
;
std
::
cout
<<
" 1: 一次定位
\n
"
;
std
::
cout
<<
" 2: 获取上次定位结果
\n
"
;
std
::
cout
<<
" 3: 持续定位
\n
"
;
std
::
cout
<<
" 4: 退出定位
\n
"
;
std
::
cout
<<
" q: Quit
\n
"
;
std
::
cout
<<
"======================================
\n
"
;
std
::
cout
<<
"Enter your choice: "
<<
std
::
flush
;
}
int
main
(
int
argc
,
char
*
argv
[])
{
// 注册信号处理,以便Ctrl+C可以正常关闭串口
signal
(
SIGINT
,
signalHandler
);
std
::
string
port
=
"/dev/ttyUSB0"
;
if
(
argc
>
1
)
{
port
=
argv
[
1
];
}
g_serial_port
=
std
::
make_unique
<
SerialPort
>
(
port
);
if
(
!
g_serial_port
->
open
())
{
return
1
;
}
// 启动监听线程,并提供回调函数
g_serial_port
->
startListening
(
onMessageReceived
);
// 预设指令(不含CRC)
const
std
::
vector
<
uint8_t
>
cmd_once
=
{
0x01
,
0x10
,
0x00
,
0x3B
,
0x00
,
0x01
,
0x02
,
0x00
,
0x01
};
const
std
::
vector
<
uint8_t
>
cmd_get_last
=
{
0x01
,
0x03
,
0x01
,
0x00
,
0x00
,
0x15
};
const
std
::
vector
<
uint8_t
>
cmd_continuous
=
{
0x01
,
0x10
,
0x00
,
0x3B
,
0x00
,
0x01
,
0x02
,
0x00
,
0x04
,
0xA3
,
0x18
};
const
std
::
vector
<
uint8_t
>
cmd_stop_modbus
=
{
0x01
,
0x10
,
0x00
,
0x3B
,
0x00
,
0x01
,
0x02
,
0x00
,
0x00
,
0xA2
,
0xDB
};
char
choice
;
print_menu
();
while
(
std
::
cin
>>
choice
&&
(
choice
!=
'q'
&&
choice
!=
'Q'
))
{
std
::
cout
<<
"--> Sending command '"
<<
choice
<<
"': "
;
bool
success
=
false
;
switch
(
choice
)
{
case
'1'
:
print_hex
(
cmd_once
);
std
::
cout
<<
std
::
endl
;
success
=
g_serial_port
->
send
(
cmd_once
);
break
;
case
'2'
:
print_hex
(
cmd_get_last
);
std
::
cout
<<
std
::
endl
;
success
=
g_serial_port
->
send
(
cmd_get_last
);
break
;
case
'3'
:
print_hex
(
cmd_continuous
);
std
::
cout
<<
std
::
endl
;
success
=
g_serial_port
->
send
(
cmd_continuous
);
break
;
case
'4'
:
print_hex
(
cmd_stop_modbus
);
std
::
cout
<<
std
::
endl
;
success
=
g_serial_port
->
send
(
cmd_stop_modbus
);
break
;
default:
std
::
cout
<<
"Invalid choice. Please try again."
<<
std
::
endl
;
}
if
(
!
success
)
{
std
::
cerr
<<
"Failed to send command!"
<<
std
::
endl
;
}
// 重新打印菜单,不清空接收到的消息
if
(
choice
!=
'\n'
)
{
print_menu
();
}
}
std
::
cout
<<
"Exiting..."
<<
std
::
endl
;
// g_serial_port的析构函数会自动被调用,关闭串口和线程
return
0
;
}
\ No newline at end of file
serial_port.cc
0 → 100644
View file @
61fd3055
// serial_port.cpp (修改版)
#include "serial_port.hpp"
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <cerrno>
#include <cstring>
// ... 构造函数、析构函数、open、close、isOpen、send、crc16函数保持不变 ...
// ... (为了简洁,这里省略了未改动的代码,请保留您文件中的这些部分) ...
SerialPort
::
SerialPort
(
const
std
::
string
&
port_name
)
:
port_name_
(
port_name
),
fd_
(
-
1
),
is_open_
(
false
),
stop_listener_
(
false
)
{}
SerialPort
::~
SerialPort
()
{
close
();
}
bool
SerialPort
::
open
(
speed_t
baud_rate
)
{
if
(
is_open_
)
{
std
::
cerr
<<
"Warning: Port "
<<
port_name_
<<
" is already open."
<<
std
::
endl
;
return
true
;
}
fd_
=
::
open
(
port_name_
.
c_str
(),
O_RDWR
|
O_NOCTTY
|
O_NDELAY
);
if
(
fd_
==
-
1
)
{
std
::
cerr
<<
"Error: Failed to open serial port "
<<
port_name_
<<
" - "
<<
strerror
(
errno
)
<<
std
::
endl
;
return
false
;
}
fcntl
(
fd_
,
F_SETFL
,
0
);
struct
termios
options
;
if
(
tcgetattr
(
fd_
,
&
options
)
!=
0
)
{
std
::
cerr
<<
"Error: tcgetattr failed - "
<<
strerror
(
errno
)
<<
std
::
endl
;
::
close
(
fd_
);
fd_
=
-
1
;
return
false
;
}
cfsetispeed
(
&
options
,
baud_rate
);
cfsetospeed
(
&
options
,
baud_rate
);
options
.
c_cflag
&=
~
PARENB
;
options
.
c_cflag
&=
~
CSTOPB
;
options
.
c_cflag
&=
~
CSIZE
;
options
.
c_cflag
|=
CS8
;
options
.
c_cflag
|=
(
CLOCAL
|
CREAD
);
options
.
c_iflag
&=
~
(
IXON
|
IXOFF
|
IXANY
);
options
.
c_lflag
&=
~
(
ICANON
|
ECHO
|
ECHOE
|
ISIG
);
options
.
c_oflag
&=
~
OPOST
;
options
.
c_cc
[
VMIN
]
=
0
;
options
.
c_cc
[
VTIME
]
=
0
;
if
(
tcsetattr
(
fd_
,
TCSANOW
,
&
options
)
!=
0
)
{
std
::
cerr
<<
"Error: tcsetattr failed - "
<<
strerror
(
errno
)
<<
std
::
endl
;
::
close
(
fd_
);
fd_
=
-
1
;
return
false
;
}
tcflush
(
fd_
,
TCIOFLUSH
);
is_open_
=
true
;
std
::
cout
<<
"Serial port "
<<
port_name_
<<
" opened successfully."
<<
std
::
endl
;
return
true
;
}
void
SerialPort
::
close
()
{
stopListening
();
if
(
is_open_
)
{
is_open_
=
false
;
::
close
(
fd_
);
fd_
=
-
1
;
std
::
cout
<<
"Serial port "
<<
port_name_
<<
" closed."
<<
std
::
endl
;
}
}
bool
SerialPort
::
isOpen
()
const
{
return
is_open_
;
}
bool
SerialPort
::
send
(
const
std
::
vector
<
uint8_t
>&
data
)
{
if
(
!
is_open_
)
{
std
::
cerr
<<
"Error: Port is not open. Cannot send data."
<<
std
::
endl
;
return
false
;
}
uint16_t
crc
=
crc16
(
data
.
data
(),
data
.
size
());
std
::
vector
<
uint8_t
>
data_with_crc
=
data
;
data_with_crc
.
push_back
(
crc
&
0xFF
);
data_with_crc
.
push_back
((
crc
>>
8
)
&
0xFF
);
ssize_t
written
=
::
write
(
fd_
,
data_with_crc
.
data
(),
data_with_crc
.
size
());
if
(
written
<
0
)
{
std
::
cerr
<<
"Error: Failed to write to serial port - "
<<
strerror
(
errno
)
<<
std
::
endl
;
return
false
;
}
if
(
static_cast
<
size_t
>
(
written
)
!=
data_with_crc
.
size
())
{
std
::
cerr
<<
"Warning: Not all bytes were written to serial port."
<<
std
::
endl
;
}
return
true
;
}
void
SerialPort
::
startListening
(
MessageHandler
handler
)
{
if
(
!
is_open_
)
{
std
::
cerr
<<
"Error: Cannot start listening, port is not open."
<<
std
::
endl
;
return
;
}
if
(
listener_thread_
.
joinable
())
{
std
::
cerr
<<
"Warning: Listener is already running."
<<
std
::
endl
;
return
;
}
message_handler_
=
handler
;
stop_listener_
=
false
;
listener_thread_
=
std
::
thread
(
&
SerialPort
::
listenerLoop
,
this
);
}
void
SerialPort
::
stopListening
()
{
stop_listener_
=
true
;
if
(
listener_thread_
.
joinable
())
{
listener_thread_
.
join
();
}
}
// 【新增】辅助函数:根据Modbus协议解析消息长度
// 返回值:>0 表示预期的消息长度,0 表示数据不足无法判断
size_t
parseMessageLength
(
const
std
::
vector
<
uint8_t
>&
buffer
)
{
// 异常响应帧长度固定为5
if
(
buffer
.
size
()
>=
2
&&
(
buffer
[
1
]
&
0x80
))
{
return
5
;
}
// 正常响应帧
if
(
buffer
.
size
()
>=
3
)
{
uint8_t
func_code
=
buffer
[
1
];
if
(
func_code
==
0x03
||
func_code
==
0x04
)
{
// 读寄存器
uint8_t
byte_count
=
buffer
[
2
];
// 总长度 = 地址(1) + 功能码(1) + 字节数(1) + 数据(N) + CRC(2)
return
3
+
byte_count
+
2
;
}
else
if
(
func_code
==
0x06
||
func_code
==
0x10
)
{
// 写寄存器
// 写响应固定为8字节
return
8
;
}
}
// 数据不足或功能码不识别,无法判断长度
return
0
;
}
// 【重大修改】监听线程的主循环函数
void
SerialPort
::
listenerLoop
()
{
std
::
vector
<
uint8_t
>
msg_buffer
;
uint8_t
rx_buffer
[
256
];
while
(
!
stop_listener_
)
{
fd_set
read_fds
;
FD_ZERO
(
&
read_fds
);
FD_SET
(
fd_
,
&
read_fds
);
// 使用一个较短的超时,避免长时间阻塞,以便能及时检查 stop_listener_
struct
timeval
timeout
;
timeout
.
tv_sec
=
1
;
timeout
.
tv_usec
=
0
;
int
activity
=
select
(
fd_
+
1
,
&
read_fds
,
nullptr
,
nullptr
,
&
timeout
);
if
(
activity
<
0
&&
errno
!=
EINTR
)
{
if
(
!
stop_listener_
)
std
::
cerr
<<
"Error: select() failed."
<<
std
::
endl
;
break
;
}
// 如果有数据可读
if
(
FD_ISSET
(
fd_
,
&
read_fds
))
{
ssize_t
bytes_read
=
::
read
(
fd_
,
rx_buffer
,
sizeof
(
rx_buffer
));
if
(
bytes_read
>
0
)
{
// 将新数据追加到缓冲区
msg_buffer
.
insert
(
msg_buffer
.
end
(),
rx_buffer
,
rx_buffer
+
bytes_read
);
}
else
if
(
bytes_read
<
0
)
{
if
(
!
stop_listener_
)
std
::
cerr
<<
"Error: read() failed."
<<
std
::
endl
;
break
;
}
}
// 【核心逻辑】循环处理缓冲区中的数据,直到数据不够一帧
while
(
true
)
{
size_t
expected_len
=
parseMessageLength
(
msg_buffer
);
// 如果能确定预期长度,并且缓冲区数据足够
if
(
expected_len
>
0
&&
msg_buffer
.
size
()
>=
expected_len
)
{
// 提取一个完整的消息
std
::
vector
<
uint8_t
>
complete_msg
(
msg_buffer
.
begin
(),
msg_buffer
.
begin
()
+
expected_len
);
// 调用回调函数处理消息
if
(
message_handler_
)
{
message_handler_
(
complete_msg
);
}
// 从缓冲区头部删除已处理的消息
msg_buffer
.
erase
(
msg_buffer
.
begin
(),
msg_buffer
.
begin
()
+
expected_len
);
}
else
{
// 数据不够一帧,或无法判断帧长,跳出内层循环,等待更多数据
break
;
}
}
}
}
uint16_t
SerialPort
::
crc16
(
const
uint8_t
*
data
,
size_t
len
)
{
uint16_t
crc
=
0xFFFF
;
for
(
size_t
i
=
0
;
i
<
len
;
i
++
)
{
crc
^=
data
[
i
];
for
(
uint8_t
j
=
0
;
j
<
8
;
j
++
)
{
if
(
crc
&
0x0001
)
{
crc
>>=
1
;
crc
^=
0xA001
;
}
else
{
crc
>>=
1
;
}
}
}
return
crc
;
}
\ No newline at end of file
serial_port.hpp
0 → 100644
View file @
61fd3055
// serial_port.hpp
#ifndef SERIAL_PORT_HPP
#define SERIAL_PORT_HPP
#include <string>
#include <vector>
#include <functional>
#include <thread>
#include <atomic>
#include <mutex>
// POSIX/Linux headers
#include <termios.h>
class
SerialPort
{
public
:
// 定义消息处理回调函数的类型
using
MessageHandler
=
std
::
function
<
void
(
const
std
::
vector
<
uint8_t
>&
)
>
;
// 构造函数:传入串口设备名,如 "/dev/ttyUSB0"
explicit
SerialPort
(
const
std
::
string
&
port_name
);
// 析构函数:自动关闭串口和监听线程
~
SerialPort
();
// 禁止拷贝和赋值
SerialPort
(
const
SerialPort
&
)
=
delete
;
SerialPort
&
operator
=
(
const
SerialPort
&
)
=
delete
;
// 打开并配置串口
// baud_rate: 如 B115200, B9600 等
// returns: true on success, false on failure
bool
open
(
speed_t
baud_rate
=
B115200
);
// 关闭串口
void
close
();
// 检查串口是否打开
bool
isOpen
()
const
;
// 发送数据(函数内部会自动计算并附加CRC16)
// data: 要发送的原始数据
// returns: true on success, false on failure
bool
send
(
const
std
::
vector
<
uint8_t
>&
data
);
// 开始异步监听,当接收到完整消息时调用handler
void
startListening
(
MessageHandler
handler
);
// 停止异步监听
void
stopListening
();
// 静态工具函数:CRC16校验计算
static
uint16_t
crc16
(
const
uint8_t
*
data
,
size_t
len
);
private
:
// 监听线程的主循环函数
void
listenerLoop
();
// 内部状态
std
::
string
port_name_
;
int
fd_
;
// 文件描述符
std
::
atomic
<
bool
>
is_open_
;
// 线程相关
std
::
thread
listener_thread_
;
std
::
atomic
<
bool
>
stop_listener_
;
MessageHandler
message_handler_
;
std
::
mutex
write_mutex_
;
// 保证发送操作的原子性
};
#endif // SERIAL_PORT_HPP
\ No newline at end of file
serial_tool
0 → 100755
View file @
61fd3055
File added
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment