• 正文
    • 1、協(xié)議整體架構(gòu)
    • 2、協(xié)議實(shí)現(xiàn)及其應(yīng)用
    • 3、SACP協(xié)議的典型應(yīng)用場(chǎng)景
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

一個(gè)可用于多設(shè)備間的C/C++嵌入式通信協(xié)議的設(shè)計(jì)與實(shí)現(xiàn)-SACP協(xié)議

02/05 15:55
1077
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

素材來源 | GitHub,整理&排版?| 嵌入式應(yīng)用研究院

SACP(Snapmaker Advanced Communication Protocol)是Snapmaker設(shè)備的數(shù)據(jù)通信協(xié)議,用于 控制器(Controller)、PC 端(Host)、HMI(人機(jī)界面) 之間的數(shù)據(jù)傳輸,從該協(xié)議的設(shè)計(jì)思想上看,可以滿足以下幾個(gè)基本目標(biāo):

    可靠性:數(shù)據(jù)包頭部 CRC8 校驗(yàn) + Checksum 數(shù)據(jù)完整性驗(yàn)證靈活性:支持 請(qǐng)求/應(yīng)答機(jī)制(SACP_ATTR_REQ / SACP_ATTR_ACK)模塊化:封裝 數(shù)據(jù)包結(jié)構(gòu),可擴(kuò)展 不同指令集高效性:固定長(zhǎng)度頭部 + 變長(zhǎng)數(shù)據(jù)部分,節(jié)省通信開銷

以下為整個(gè)協(xié)議的實(shí)現(xiàn)板塊總結(jié):

模塊 作用
協(xié)議頭部 規(guī)定數(shù)據(jù)包的格式
CRC8頭部校驗(yàn) 確保頭部完整性
Checksum數(shù)據(jù)校驗(yàn) 確保數(shù)據(jù)完整性
請(qǐng)求-應(yīng)答機(jī)制 設(shè)備間通信
序列號(hào)機(jī)制 跟蹤請(qǐng)求-應(yīng)答關(guān)系
數(shù)據(jù)包封裝-解析 通信可靠性保證

之前我們也講解過關(guān)于MVC框架的實(shí)現(xiàn),基于SACP協(xié)議,很容易拓展為一個(gè)典型的MVC框架:

嵌入式軟件設(shè)計(jì)之美-以實(shí)際項(xiàng)目應(yīng)用MVC框架與狀態(tài)模式(上)

嵌入式軟件設(shè)計(jì)之美-以實(shí)際項(xiàng)目應(yīng)用MVC框架與狀態(tài)模式(下)

以下是整個(gè)協(xié)議的實(shí)現(xiàn)代碼的開源倉(cāng)庫(kù):https://github.com/Snapmaker/SnapmakerController-IDEX

1、協(xié)議整體架構(gòu)

unsetunset1.1、SACP協(xié)議數(shù)據(jù)包結(jié)構(gòu)unsetunset

unsetunset1.1.1、核心數(shù)據(jù)包結(jié)構(gòu)體unsetunset

#pragma?pack(1)
typedef?struct?{
??uint8_t?sof_h;???????????//同步頭高字節(jié)?0xAA,?一個(gè)字節(jié)
??uint8_t?sof_l;???????????//同步頭低字節(jié)?0x55,一個(gè)字節(jié)
??uint16_t?length;?????????//總長(zhǎng)度(頭部+數(shù)據(jù)+校驗(yàn)和),兩個(gè)字節(jié)
??uint8_t?version;?????????//協(xié)議版本(當(dāng)前為0x01),一個(gè)字節(jié)
??uint8_t?recever_id;????//目標(biāo)設(shè)備ID(PC/控制器/HMI),一個(gè)字節(jié)
??uint8_t?crc8;??????//頭部校驗(yàn)(CRC-8)
??uint8_t?sender_id;???????//發(fā)送設(shè)備ID,一個(gè)字節(jié)
??uint8_t?attr;????????????//數(shù)據(jù)包屬性(請(qǐng)求/應(yīng)答),一個(gè)字節(jié)
??uint16_t?sequence;???????//序列號(hào)(用于匹配請(qǐng)求-應(yīng)答包),兩個(gè)字節(jié)
??uint8_t?command_set;?????//命令集(功能分類),一個(gè)字節(jié)
??uint8_t?command_id;??????//具體命令,一個(gè)字節(jié)
??uint8_t?data[0];?????????//可變數(shù)據(jù)參數(shù),數(shù)據(jù)載荷(length-SACP_HEADER_LEN)
}?SACP_struct_t;

該核心數(shù)據(jù)包結(jié)構(gòu)體中的成員在頭文件中預(yù)定義的一些宏:

//關(guān)于協(xié)議、版本號(hào)、頭部長(zhǎng)度的描述
#define?SACP_PDU_SOF_H???0xAA
#define?SACP_PDU_SOF_L???0x55
#define?SACP_VERSION?????0x01
#define?SACP_HEADER_LEN??(15)
//設(shè)備ID標(biāo)識(shí)描述
#define?SACP_ID_PC?????????0
#define?SACP_ID_CONTROLLER?1
#define?SACP_ID_HMI????????2
//數(shù)據(jù)包大小描述
#define?PACK_PARSE_MAX_SIZE?512
#define?PACK_PACKET_MAX_SIZE?1024
//請(qǐng)求-應(yīng)答標(biāo)識(shí)
#define?SACP_ATTR_REQ?0
#define?SACP_ATTR_ACK?1
    SACP_PDU_SOF_H / SACP_PDU_SOF_L: 同步字,數(shù)據(jù)包的起始標(biāo)志 (0xAA55)SACP_VERSION: 協(xié)議版本號(hào),當(dāng)前為 0x01SACP_HEADER_LEN: 頭部長(zhǎng)度,表示 除去 Payload 的包頭部分SACP_ID_PC: PC 端SACP_ID_CONTROLLER: 控制器SACP_ID_HMI: 人機(jī)界面(HMI 屏幕)PACK_PARSE_MAX_SIZE: 單次解析最大數(shù)據(jù)包大小(512 字節(jié))PACK_PACKET_MAX_SIZE: 最大可打包數(shù)據(jù)長(zhǎng)度(1024 字節(jié))SACP_ATTR_REQ: 請(qǐng)求包SACP_ATTR_ACK: 應(yīng)答包

    • 請(qǐng)求-響應(yīng)機(jī)制:控制器發(fā)送請(qǐng)求目標(biāo)設(shè)備解析并返回請(qǐng)求應(yīng)答

unsetunset1.1.2、核心頭部結(jié)構(gòu)unsetunset

該頭部結(jié)構(gòu)是作為數(shù)據(jù)封裝的基礎(chǔ)信息,即用于package()生成完整的SACP_struct_t

typedef?struct?{
??uint8_t?recever_id;
??uint8_t?attribute;
??uint16_t?sequence;
??uint8_t?command_set;
??uint8_t?command_id;
}?SACP_head_base_t;

unsetunset1.1.3、數(shù)據(jù)解析緩存結(jié)構(gòu)unsetunset

typedef?struct?{
??uint16_t?lenght;??//記錄當(dāng)前解析數(shù)據(jù)長(zhǎng)度
??union?{
????uint8_t?buff[PACK_PARSE_MAX_SIZE];?//數(shù)據(jù)緩沖區(qū)
????SACP_struct_t?sacp;?//sacp直接映射到SACP_struct_t
??};
}?SACP_param_t;

2、協(xié)議實(shí)現(xiàn)及其應(yīng)用

unsetunset2.1、數(shù)據(jù)包的解析unsetunset

解析SACP數(shù)據(jù)包,校驗(yàn)數(shù)據(jù)完整性:

ErrCode?ProtocolSACP::parse(uint8_t?*data,?uint16_t?len,?SACP_param_t?&out);

簡(jiǎn)單的應(yīng)用:

SACP_param_t?parsed_data;
ErrCode?result?=?protocol_sacp.parse(received_data,?data_length,?parsed_data);
if?(result?==?E_SUCCESS)?{
????//?解析成功,即通過parsed_data找到對(duì)應(yīng)協(xié)議的字段,分析對(duì)應(yīng)的數(shù)據(jù)來源并進(jìn)行進(jìn)一步的處理
}

unsetunset2.2、數(shù)據(jù)包的封裝unsetunset

封裝SACP請(qǐng)求/應(yīng)答數(shù)據(jù)包:

uint16_t?ProtocolSACP::package(SACP_head_base_t?head,?uint8_t?*in_data,?uint16_t?length,?uint8_t?*out_data);

簡(jiǎn)單的應(yīng)用:

uint8_t?buffer[PACK_PACKET_MAX_SIZE];
SACP_head_base_t?head?=?{SACP_ID_HMI,?SACP_ATTR_REQ,?protocol_sacp.sequence_pop(),?0x10,?0x02};
uint8_t?data_payload[]?=?{0x01,?0x02,?0x03};?//?示例數(shù)據(jù)
uint16_t?packet_length?=?protocol_sacp.package(head,?data_payload,?sizeof(data_payload),?buffer);
send_packet(buffer,?packet_length);?//?發(fā)送數(shù)據(jù)包

完整實(shí)現(xiàn)邏輯(頭文件):protocol_sacp.h

#ifndef?PROTOCOL_ASCP_H
#define?PROTOCOL_ASCP_H

#include?"../J1/common_type.h"
#include?<functional>
//?protocol?relative?macros
#define?SACP_PDU_SOF_H???0xAA
#define?SACP_PDU_SOF_L???0x55
#define?SACP_VERSION?????0x01
#define?SACP_HEADER_LEN??(15)???//?frame_length?-?length_paylod

#define?SACP_ID_PC?????????0
#define?SACP_ID_CONTROLLER?1
#define?SACP_ID_HMI????????2

#define?PACK_PARSE_MAX_SIZE?512
#define?PACK_PACKET_MAX_SIZE?1024

#define?SACP_ATTR_REQ?0
#define?SACP_ATTR_ACK?1

#pragma?pack(1)
typedef?struct?{
??uint8_t?sof_h;
??uint8_t?sof_l;
??uint16_t?length;
??uint8_t?version;??//?0x01
??uint8_t?recever_id;
??uint8_t?crc8;
??uint8_t?sender_id;
??uint8_t?attr;
??uint16_t?sequence;
??uint8_t?command_set;
??uint8_t?command_id;
??uint8_t?data[0];
}?SACP_struct_t;

typedef?struct?{
??uint8_t?recever_id;
??uint8_t?attribute;
??uint16_t?sequence;
??uint8_t?command_set;
??uint8_t?command_id;
}?SACP_head_base_t;

typedef?struct?{
??uint16_t?lenght;??//?The?total?length?of?data
??union?{
????uint8_t?buff[PACK_PARSE_MAX_SIZE];
????SACP_struct_t?sacp;
??};
}?SACP_param_t;
#pragma?pack()

class?ProtocolSACP?{
??public:
????ErrCode?parse(uint8_t?*data,?uint16_t?len,?SACP_param_t?&out);
????//?Package?the?incoming?data
????uint16_t?package(SACP_head_base_t?head,?uint8_t?*in_data,?uint16_t?length,?uint8_t?*out_data);
????uint16_t?sequence_pop()?{return?sequence++;}
??private:
????uint32_t?sequence?=?0;
};

extern?ProtocolSACP?protocol_sacp;
#endif

完整實(shí)現(xiàn)邏輯(源文件):protocol_sacp.cpp

#include?"protocol_sacp.h"
#include?<functional>
#include?"HAL.h"
#include?"../../Marlin/src/core/serial.h"

ProtocolSACP?protocol_sacp;

static?uint8_t?sacp_calc_crc8(uint8_t?*buffer,?uint16_t?len)?{
??int?crc?=?0x00;
??int?poly?=?0x07;
??for?(int?i?=?0;?i?<?len;?i++)?{
????for?(int?j?=?0;?j?<?8;?j++)?{
??????bool?bit?=?((buffer[i]?>>?(7?-?j)?&?1)?==?1);
??????bool?c07?=?((crc?>>?7?&?1)?==?1);
??????crc?<<=?1;
??????if?(c07?^?bit)?{
????????crc?^=?poly;
??????}
????}
??}
??crc?&=?0xff;
??return?crc;
}

uint16_t?calc_checksum(uint8_t?*buffer,?uint16_t?length)?{
??uint32_t?volatile?checksum?=?0;

??if?(!length?||?!buffer)
????return?0;

??for?(int?j?=?0;?j?<?(length?-?1);?j?=?j?+?2)
????checksum?+=?(uint32_t)(buffer[j]?<<?8?|?buffer[j?+?1]);

??if?(length?%?2)
????checksum?+=?buffer[length?-?1];

??while?(checksum?>?0xffff)
????checksum?=?((checksum?>>?16)?&?0xffff)?+?(checksum?&?0xffff);

??checksum?=?~checksum;

??return?(uint16_t)checksum;
}

ErrCode?ProtocolSACP::parse(uint8_t?*data,?uint16_t?len,?SACP_param_t?&out)?{
??uint8_t?*parse_buff?=?out.buff;
??if?(parse_buff[0]?!=?SACP_PDU_SOF_H)?{
????out.lenght?=?0;
??}
??for?(uint16_t?i?=?0;?i?<?len;?i++)?{
????uint8_t?ch?=?data[i];
????if?(out.lenght?==?0)?{
??????if?(ch?==?SACP_PDU_SOF_H)?{
????????parse_buff[out.lenght++]?=?ch;
??????}
????}?else?if?(out.lenght?==?1)?{
??????if?(ch?==?SACP_PDU_SOF_L)?{
????????parse_buff[out.lenght++]?=?ch;
??????}?else?{
????????out.lenght?=?0;
??????}
????}?else?{
??????parse_buff[out.lenght++]?=?ch;
????}

????if?(out.lenght?<?7)?{
??????break;
????}
????else?if?(out.lenght?==?7)?{
??????if?(sacp_calc_crc8(parse_buff,?6)?!=?parse_buff[6])?{
????????out.lenght?=?0;
??????}
????}
????else?{
??????uint16_t?data_len?=?(parse_buff[3]?<<?8?|?parse_buff[2]);
??????uint16_t?total_len?=?data_len?+?7;
??????if?(out.lenght?==?total_len)?{
????????uint16_t?checksum?=?calc_checksum(&parse_buff[7],?data_len?-?2);
????????uint16_t?checksum1?=?(parse_buff[total_len?-?1]?<<?8)?|?parse_buff[total_len?-?2];
????????if?(checksum?==?checksum1)?{
??????????out.lenght?=?0;
??????????return?E_SUCCESS;
????????}?else?{
??????????out.lenght?=?0;
??????????return?E_PARAM;
????????}
??????}?else?if?(out.lenght?>?total_len)?{
????????out.lenght?=?0;
????????return?E_PARAM;
??????}
????}
??}
??return?E_IN_PROGRESS;
}


uint16_t?ProtocolSACP::package(SACP_head_base_t?head,?uint8_t?*in_data,?uint16_t?length,?uint8_t?*out_data)?{
??uint16_t?data_len?=?(length?+?8);?//?header?6?byte,?checknum?2byte
??SACP_struct_t?*out?=??(SACP_struct_t?*)out_data;
??out->sof_h?=?SACP_PDU_SOF_H;
??out->sof_l?=?SACP_PDU_SOF_L;
??out->length?=?data_len;
??out->version?=?SACP_VERSION;
??out->recever_id?=?head.recever_id;
??out->crc8?=?sacp_calc_crc8(out_data,?6);
??out->sender_id?=?SACP_ID_CONTROLLER;
??out->attr?=?head.attribute;
??out->sequence?=?head.sequence;
??out->command_set?=?head.command_set;
??out->command_id?=?head.command_id;
??for?(uint16_t?i?=?0;?i?<?length;?i++)?{
????out->data[i]?=?in_data[i];
??}
??uint16_t?checksum?=?calc_checksum(&out_data[7],?data_len?-?2);??//?-?checknum?2?byte
??length?=?sizeof(SACP_struct_t)?+?length;
??out_data[length++]?=?(uint8_t)(checksum?&?0x00FF);
??out_data[length++]?=?(uint8_t)(checksum>>8);
??return?length;
}

3、SACP協(xié)議的典型應(yīng)用場(chǎng)景

PC 端 -> 控制器

發(fā)送控制指令

控制器 -> HMI

反饋、上報(bào)一些信息通過GUI來進(jìn)行展示

模塊間通信

用戶操作、數(shù)據(jù)解析和發(fā)送、反饋等

基于該協(xié)議的設(shè)計(jì)和實(shí)現(xiàn),還可將其拓展為典型的MVC架構(gòu)。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

本科畢業(yè)于華南理工大學(xué),現(xiàn)美國(guó)卡羅爾工商管理碩士研究生在讀,曾就職于世界名企偉易達(dá)、聯(lián)發(fā)科技等,多年嵌入式產(chǎn)品開發(fā)經(jīng)驗(yàn),在智能玩具、安防產(chǎn)品、平板電腦、手機(jī)開發(fā)有豐富的實(shí)戰(zhàn)開發(fā)經(jīng)驗(yàn),現(xiàn)任深圳市云之手科技有限公司副總經(jīng)理、研發(fā)總工程師。