Linux串口編程主要流程 :1、open打開串口設備,獲取串口設備檔案描述符(Linux一切都是檔案)-->2、設定set波特率,初始化ini等-->3、對資料解碼-->4、read()、write()操作檔案描述符進行串口通信
在主函式中,初始化ros節點,啟動,定義發布訊息的名稱, 建構式,初始化介面名稱以及波特率(每秒鐘傳送的碼元符號的個數),從而初始化這個類
while回圈,只要節點在運行就接收和解碼資料,并輸出到topic中,
int main(int argc, char** argv) {
ROS_INFO("[info] init begin");
ros::init(argc, argv, "virtual_serial"); //初始化節點
ros::NodeHandle n; //啟動節點
ros::Publisher chatter_pub = n.advertise<gripper_send::rec_num_msgs>("gripper_send", 1000); //定義發布訊息的名稱及sulv
ros::Rate loop_rate(50); //控制發布速度,與sleep配合使用,每0.02秒休息一次
DataGripper data_gripper(dev_name, 115200); //建構式,看串口是否打開并初始化
while (ros::ok()) { //檢查節點是否關閉
//read uart data
data_gripper.DataReceive(); //接收資料
data_gripper.DataDecode(); //解碼資料
//write to msg;
gripper_send::rec_num_msgs msg;
msg.finger1_state_Value[0] = data_gripper.motor1_state_Value_[0];
msg.finger2_state_Value[0] = data_gripper.motor2_state_Value_[0];
msg.finger3_state_Value[0] = data_gripper.motor3_state_Value_[0];
msg.finger1_state_Value[1] = data_gripper.motor1_state_Value_[1];
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
以下是gripper_send.h檔案,主要包括一個結構體,和一個類,具體見備注,
#ifndef SRC_UART_TUTORIAL_INCLUDE_GRIPPER_REC_H_
#define SRC_UART_TUTORIAL_INCLUDE_GRIPPER_REC_H_
#include <ros/ros.h> // 包含ROS的頭檔案
#include <iostream>
#include <queue>
constexpr int RX_USB_SIZE = 150; //設定臨時存盤的位元組容量
struct RecStruct { //設定一個結構體,包括布林值,臨時存盤的陣列,計數器
bool isValue = false;
char buffer[16];
uint16_t buff_ptr = 0;
};
class DataGripper {
public:
DataGripper(char *dev_name, uint32_t baund); //建構式,判斷串口是否成功打開和初始化
~DataGripper(); //解構式
bool DataReceive(); //判斷是否接受到信號
void DataDecode(); //判斷幀頭幀尾,將中間的有效資料存在rec_decode_.buffer中
void GetData(char *effective_data); //對信號進行解碼
short motor1_state_Value_[3]; //三個電機的狀態
short motor2_state_Value_[3];
short motor3_state_Value_[3];
private:
char rcv_buf_[RX_USB_SIZE];
std::queue<char> rec_que_;
char slide_windows_[4]; //滑動視窗
int fd_ = -1; //檔案描述符
int uart_open_err_ = false;
struct RecStruct rec_decode_;
};
#endif // SRC_UART_TUTORIAL_INCLUDE_GRIPPER_REC_H_
串口協議是:
A0 B8 21 0A XX XX XX XX XX XX XX XX XX XX XX 59 A6
------幀頭-------- ------------------------有效資料--------------- --幀尾--
- 接收函式:data_gripper.DataReceive()
會接收到很多資料,利用UARTO_Recv 統計出此次接收資料的數量,之后利用for回圈,將每一個資料都存入rec_que_中,
UART0_Recv函式的第一個輸入fd_是檔案描述符,第二個是臨時存盤資料的陣列,第三個是歷史存盤資料的大小,輸出是,有效資料的長度,
最后把資料都存在rec_que_中,
bool DataGripper::DataReceive() {
int len = 0;
len = UART0_Recv(fd_, rcv_buf_, 100);
if (len > 0) {
for (int i = 0; i < len; i++) {
rec_que_.push(rcv_buf_[i]);
}
return true;
}
return false;
}
- 判斷幀頭幀尾并提取有效資料:void DataGripper::DataDecode()
思路:只要rec_que_佇列不為空,就把頭位置的資料pop出來,存入rec_temp中,之后創立一個滑動視窗(大小與幀頭相同),將每次的新資料從后向前一次更新一次,每次更新后,都判斷是否滑動視窗里的每個值都與幀頭完全重合,若完全重合,則布林值rec_decode_.isValue 為真,執行下一個if操作,若不為真,則說明還未找到幀頭,需要重新從rec_que_中重新取一個資料,繼續判斷幀頭,這里無論成功與否,rec_decode_.buff_ptr(計數器)都會歸零,
如果找到幀頭之后,執行if內的操作, 有效資料一共是11個,加上針尾一共是13個,由于使用滑動視窗時,我們可以把幀頭的最后一位也統計了進來,所以一共是14個,由于++操作在后面,所以當rec_decode_.buff_ptr(計數器) 等于13的時候,開始判斷幀尾(總結一個公式:總資料長度 - 幀頭長度),容納后判斷幀尾,判斷成功之后,使用GetData(&rec_decode_.buffer[2]) 進行解碼,(第一位是幀頭的最后一位,然后有效資料的第一位沒有用,所以從第三位開始,所以是2),
void DataGripper::DataDecode() {
char rec_temp;
while (!rec_que_.empty()) {
rec_temp = rec_que_.front();
rec_que_.pop();
for (int i = 0; i < 3; i++)
slide_windows_[i] = slide_windows_[i + 1];
slide_windows_[3] = rec_temp;
for(int i=0; i<4; ++i)
slide_windows_[i] = static_cast<uint8_t>(slide_windows_[i]);
while (1) {
if (static_cast<uint8_t>(slide_windows_[0]) != 0xa0) break;
if (static_cast<uint8_t>(slide_windows_[1]) != 0xb8) break;
if (static_cast<uint8_t>(slide_windows_[2]) != 0x21) break;
if (static_cast<uint8_t>(slide_windows_[3]) != 0x0a) break;
rec_decode_.buff_ptr = 0;
rec_decode_.isValue = true;
break;
}
if (rec_decode_.isValue == true) {
rec_decode_.buffer[rec_decode_.buff_ptr] = rec_temp;
if (rec_decode_.buff_ptr == 13) {
if (static_cast<uint8_t>(rec_decode_.buffer[12]) == 0x59 && static_cast<uint8_t>(rec_decode_.buffer[13]) == 0xA6) {
GetData(&rec_decode_.buffer[2]);
//std::cout<<motor1_state_Value[0]<<" "<<motor1_state_Value[1]<< " "<<motor2_state_Value[0]<<" "<<motor3_state_Value[0]<<std::endl;
}
rec_decode_.buff_ptr = 0;
rec_decode_.isValue = false;
} else {
rec_decode_.buff_ptr++;
}
}
}
}
- 解碼:void DataGripper::GetData(char *effective_data)
解碼目標:motor1_state_Value_[0](電機1的位置)為32個位元組,四個有效資料拼在一起, motor1_state_Value_[1](電機1的電流)為16個位元組,兩個有效資料
拼在一起,motor2_state_Value_[0](電機2的位置)為16個位元組,motor3_state_Value_[0](電機3的位置),為16個位元組,
void DataGripper::GetData(char *effective_data) {
short pos_long_h = 0;
short pos_long_l = 0;
pos_long_h = effective_data[0];
pos_long_h = (pos_long_h << 8 ) | effective_data[1];
pos_long_l = effective_data[2];
pos_long_l = (pos_long_l << 8 ) | effective_data[3];
long motor1_pos = pos_long_h;
motor1_pos = (motor1_pos << 16) | pos_long_l;
short motor1_cur = effective_data[4];
motor1_cur = (motor1_cur << 8) | effective_data[5];
short motor2_pos = effective_data[6];
motor2_pos = (motor2_pos << 8) | effective_data[7];
short motor3_pos = effective_data[8];
motor3_pos = (motor3_pos << 8) | effective_data[9];
motor1_state_Value_[0] = static_cast<short>(motor1_pos);
motor1_state_Value_[1] = motor1_cur;
motor2_state_Value_[0] = motor2_pos;
motor3_state_Value_[0] = motor3_pos;
}
最后把解碼后的資料,輸出到msg,保存到名字是gripper_send 的 ros topic里邊,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/238128.html
標籤:其他
上一篇:2020-12-20
