在現代汽車電子、工業(yè)自動化等領域,CAN(Controller Area Network)總線作為一種高效、可靠的數據通信協議,得到了廣泛應用。然而,CAN協議規(guī)定標準幀和擴展幀中數據段的長度為最大8字節(jié),這一限制源于其設計初衷——用于實時性要求較高的系統,如汽車電子和工業(yè)控制。數據幀短小有助于降低總線負載,提高傳輸效率。但當需要傳輸的數據超過8字節(jié)時,CAN總線如何進行處理?本文將深入探討這一問題,并介紹幾種相關的協議及其實現方式。
一、CAN總線處理超長數據幀的策略
當數據幀長度超過CAN協議規(guī)定的8字節(jié)時,工業(yè)界開發(fā)了一系列高層協議來支持長數據幀的分段傳輸和重組。這些協議通過將數據分割成多個較小的幀進行傳輸,并在接收端進行重組,從而實現了對超長數據幀的支持。
二、相關協議介紹
ISO-TP(ISO 15765-2)
ISO-TP(ISO Transport Protocol)是一種廣泛應用于CAN的傳輸協議,用于解決數據大于8字節(jié)的分段傳輸問題。該協議將數據分成多個幀進行傳輸,包括單幀(Single Frame,SF)、首幀(First Frame,FF)、連續(xù)幀(Consecutive Frame,CF)和流控幀(Flow Control Frame,FC)。
單幀(SF):當數據長度小于或等于7字節(jié)時,可以直接通過單幀發(fā)送。
首幀(FF):當數據長度大于7字節(jié)時,第一個幀(即首幀)中包含數據長度和首段數據。
連續(xù)幀(CF):后續(xù)幀承載剩余數據。
流控幀(FC):接收端通過發(fā)送流控幀來控制數據發(fā)送節(jié)奏,防止數據溢出。
ISO-TP協議廣泛應用于汽車診斷通信中,如UDS(統一診斷服務)協議就建立在ISO-TP之上。
CANopen SDO(Service Data Object)
CANopen是一種面向工業(yè)自動化的高層協議,其SDO(Service Data Object)協議部分支持大于8字節(jié)的數據傳輸。數據通過多個幀分段傳輸,每幀包含索引和子索引信息。此外,CANopen還支持塊傳輸(Block Transfer),允許批量傳輸多個數據幀,提高了傳輸效率。
SAE J1939
SAE J1939是一套基于CAN的協議,廣泛用于重型車輛和農業(yè)機械。它通過TP(Transport Protocol)擴展支持長數據幀傳輸。該協議使用BAM(Broadcast Announce Message)和RTS/CTS(Request to Send / Clear to Send)兩種機制進行大數據分段傳輸。
三、代碼示例
以下是一個簡化的ISO-TP協議實現示例,用于說明如何將超過8字節(jié)的數據分段傳輸。請注意,這只是一個基本框架,實際應用中需要更復雜的錯誤處理和流控機制。
c
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define MAX_DATA_LENGTH 16 // 假設最大數據長度為16字節(jié),用于示例
#define CAN_FRAME_SIZE 8 // CAN數據幀的最大數據長度
typedef struct {
uint32_t sequenceNumber; // 序列號,用于重組數據幀
uint16_t dataLength; // 數據長度
uint8_t data[MAX_DATA_LENGTH]; // 數據緩沖區(qū)
} ISO_TP_Message;
// 發(fā)送單幀或首幀的函數(簡化實現)
bool sendFrame(uint8_t* frame, uint8_t frameType, uint8_t sequenceNumber, uint16_t dataLength, uint8_t* data, uint8_t dataLengthToSend) {
// 填充幀頭和數據(具體實現取決于CAN驅動和硬件)
// ...
// 發(fā)送幀(具體實現取決于CAN驅動和硬件)
// ...
return true; // 假設發(fā)送成功
}
// 分段發(fā)送數據的函數
bool sendLargeData(ISO_TP_Message* message) {
uint8_t frame[CAN_FRAME_SIZE + /*幀頭大小*/]; // 假設幀頭大小已知并已經包含在frame數組中
uint8_t sequenceNumber = 0;
uint16_t dataLength = message->dataLength;
uint8_t* data = message->data;
// 發(fā)送首幀
if (!sendFrame(frame, 0x02, sequenceNumber, dataLength, data, (dataLength > CAN_FRAME_SIZE) ? CAN_FRAME_SIZE : dataLength)) {
return false;
}
sequenceNumber++;
// 發(fā)送連續(xù)幀
while (dataLength > CAN_FRAME_SIZE) {
if (!sendFrame(frame, 0x03, sequenceNumber, dataLength, data + (sequenceNumber - 1) * CAN_FRAME_SIZE, CAN_FRAME_SIZE)) {
return false;
}
sequenceNumber++;
dataLength -= CAN_FRAME_SIZE;
}
// 如果還有剩余數據,發(fā)送最后一個單幀(數據長度小于或等于CAN_FRAME_SIZE)
if (dataLength > 0) {
if (!sendFrame(frame, 0x00, sequenceNumber, dataLength, data + (sequenceNumber - 1) * CAN_FRAME_SIZE, dataLength)) {
return false;
}
}
return true;
}
int main() {
ISO_TP_Message message;
message.sequenceNumber = 0;
message.dataLength = 12; // 假設要發(fā)送的數據長度為12字節(jié)
memcpy(message.data, "HelloWorld", 10); // 填充數據(示例)
message.data[10] = 0x01; // 填充額外數據(示例)
message.data[11] = 0x02; // 填充額外數據(示例)
if (sendLargeData(&message)) {
printf("Data sent successfully!\n");
} else {
printf("Failed to send data.\n");
}
return 0;
}
四、結論
當CAN總線需要傳輸超過8字節(jié)的數據幀時,可以通過高層協議如ISO-TP、CANopen SDO和SAE J1939等實現分段傳輸和重組。這些協議通過將數據分割成多個較小的幀進行傳輸,并在接收端進行重組,從而解決了CAN協議對數據幀長度的限制。在實際應用中,需要根據具體場景選擇合適的協議,并考慮錯誤處理、流控機制等因素,以確保數據傳輸的可靠性和效率。





