?
上位機調(diào)用CAN接口卡發(fā)送數(shù)據(jù)時,受上位機系統(tǒng)調(diào)度耗時的影響,實際CAN卡發(fā)送時會有時間上的誤差,是否有CAN卡可以將發(fā)送定時放到設備中來完成,從而規(guī)避掉上位機的調(diào)度影響呢?本文將為大家具體分析。 ?使用CAN接口卡是CAN通訊領域無法避開的話題,它提供各種的接口類型,兼容多種上位機系統(tǒng),簡單易用的二次開發(fā)接口函數(shù)庫。此外,windows平臺還提供了專業(yè)的應用層協(xié)議庫(DBC解析庫、UDS庫等),比起用ARM直接開發(fā)CAN(FD),用戶使用接口卡二次開發(fā),可以直接調(diào)用高層協(xié)議函數(shù)庫,可以極大的節(jié)省應用層協(xié)議棧的開發(fā)成本。用戶只需關注自己的業(yè)務邏輯即可,大大的縮短項目開發(fā)周期。如此方便的用法也產(chǎn)生了一個問題,接口卡必須依賴于上位機的調(diào)用,不管windows還是linux系統(tǒng),非實時系統(tǒng)就涉及到一個延時問題——系統(tǒng)調(diào)度的延時。例如當上位機執(zhí)行到transmit發(fā)送函數(shù),到系統(tǒng)執(zhí)行這個動作,驅(qū)動將buffer下發(fā)給CAN接口卡的時間。系統(tǒng)調(diào)度時間是不可控的,取決于多方因素:程序開發(fā)的語言,電腦的性能,CPU當前的占用率等,一般都為毫秒級誤差。因此,當用戶需要軟件定時來發(fā)送報文時,無法保證很低的時間誤差。問
是否有辦法規(guī)避上位機調(diào)度的延時?
答
方法是有的。USBCANFD提供了兩種方法,一定程度上規(guī)避上位機調(diào)度的時延問題:
-
硬件定時發(fā)送;
-
隊列發(fā)送。
?
??
??硬件定時發(fā)送
USBCANFD 支持每通道最大 100?條定時發(fā)送列表,只需將待發(fā)送數(shù)據(jù)及周期設置到設備并使能,設備將自動進行發(fā)送。相比于 PC 端的發(fā)送,定時發(fā)送精度高,周期準。在設備進行定時發(fā)送任務時,PC 端仍可調(diào)用數(shù)據(jù)發(fā)送接口進行數(shù)據(jù)發(fā)送。軟件實現(xiàn)方法,在ZCAN_StartCAN之后,繼續(xù)通過setvalue方式將定時發(fā)送結(jié)構(gòu)體下載到設備中:- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
優(yōu)點:1.周期穩(wěn)定,精度100us;2.可修改報文內(nèi)容隨時覆蓋;3.可根據(jù)需求單獨對某條定時報文進行禁用操作。缺點:1.數(shù)據(jù)不是自動變化的,如涉及到內(nèi)容變化,需要再次設置定時;2.不適用于非周期性的報文。ZCAN_AUTO_TRANSMIT_OBJ auto_can; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//從CAN定時發(fā)送結(jié)構(gòu)體生成實例
ZCANFD_AUTO_TRANSMIT_OBJ auto_canfd; ? ? ? ? ? ? ? ? ? ? ? ? ? ?//從CANFD定時發(fā)送結(jié)構(gòu)體生成實例
memset(&auto_can, 0, sizeof(auto_can));
auto_can.index = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 定時列表索引0
auto_can.enable = 1; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 使能此索引,每條可單獨設置
auto_can.interval = 100; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 定時發(fā)送間隔100ms
get_can_frame(auto_can.obj, 0); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 構(gòu)造CAN報文
prop->SetValue("1/auto_send", (const char*)&auto_can); ? ? ? ? ? ?// 設置定時發(fā)送
memset(&auto_can, 0, sizeof(auto_can));
auto_can.index = 1; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 定時列表索引1
auto_can.enable = 1; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 使能此索引,每條可單獨設置
auto_can.interval = 200; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 定時發(fā)送間隔200ms
get_can_frame(auto_can.obj, 1); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 構(gòu)造CAN報文
prop->SetValue("1/auto_send", (const char*)&auto_can); ? ? ? ? ?// 設置定時發(fā)送
memset(&auto_canfd, 0, sizeof(auto_canfd));
auto_canfd.index = 2; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 定時列表索引2
auto_canfd.enable = 1; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 使能此索引,每條可單獨設置
auto_canfd.interval = 500; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 定時發(fā)送間隔500ms
get_canfd_frame(auto_canfd.obj, 2); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 構(gòu)造CANFD報文
prop->SetValue("1/auto_send_canfd", (const char*)&auto_canfd); ? ?// 設置定時發(fā)送
prop->SetValue("1/apply_auto_send", "0"); ? ? ? ? ? ? ? ? ? ? ? ?// 使能定時發(fā)送
Sleep(5000); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 等待發(fā)送5s
prop->SetValue("1/clear_auto_send", "0"); ? ? ? ? ? ? ? ? ? ? ? ?// 清除定時發(fā)送
?
隊列發(fā)送
通過隊列發(fā)送,用戶可以提前準備好多幀報文,設定報文之間的間隔,將準備好的報文發(fā)送給設備,設備按照預定義的幀間隔進行精準發(fā)送,通過此方式可提高發(fā)送幀之間的幀間隔精度。與定時發(fā)送相比,隊列發(fā)送每幀只發(fā)送一次,需由用戶不斷準備報文并批量發(fā)送到設備。USBCANFD-200U先通過SetValue將設備的發(fā)送模式切換成隊列發(fā)送模式。隊列發(fā)送緩存大小為100幀,隊列發(fā)送過程中,可以通過GetValue查詢當前隊列緩存的剩余空間。隊列發(fā)送有兩種方法實現(xiàn):-
一種是合并發(fā)送ZCAN_TransmitData——對應發(fā)送結(jié)構(gòu)體ZCANDataObj;
-
另一種是單通道發(fā)送ZCAN_Transmit和ZCAN_TransmitFD——對應發(fā)送結(jié)構(gòu)體ZCAN_Transmit_Data和ZCAN_TransmitFD_Data。
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
Prop->Setvalue(“0/set_send_mode”, “1”); ? ? ? ? ? ? ? ? ? ? ? ?//USBCANFD需要切換發(fā)送模式,CANFDNET無需此步驟
…
void get_can_frame_queue(ZCANDataObj& data, int ch, canid_t id, bool is_fd, UINT delay)
{
memset(&data, 0, sizeof(data)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//初始化data結(jié)構(gòu)體
data.dataType = ZCAN_DT_ZCAN_CAN_CANFD_DATA;
data.chnl = ch; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//通道號
ZCANCANFDData & can_data = data.data.zcanCANFDData;
can_data.frame.can_id = MAKE_CAN_ID(id, 0, 0, 0); ? ? ? ? ? ? ?// CAN ID + STD/EXT + DATA/RMT
can_data.frame.len = is_fd ? 64 : 8; ? ? ? ? ? ? ? ? ? ? ? ? ? // 數(shù)據(jù)長度 8/64
can_data.flag.unionVal.transmitType = 0; ? ? ? ? ? ? ? ? ? ? ? // 正常發(fā)送
can_data.flag.unionVal.txEchoRequest = 1; ? ? ? ? ? ? ? ? ? ? ?// 設置發(fā)送回顯
can_data.flag.unionVal.frameType = is_fd ? 1 : 0; ? ? ? ? ? ? ?// CAN or CANFD
can_data.flag.unionVal.txDelay = ZCAN_TX_DELAY_UNIT_MS; ? ? ? ?// 隊列延時單位毫秒
can_data.timeStamp = delay; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 隊列延時時間,最大值 65535
for (int i = 0; i < can_data.frame.len; ++i) { ? ? ? ? ? ? ? ? // 填充 CAN 報文 DATA
can_data.frame.data[i] = i;
}
…
Ret = ZCAN.TransmitData(device_handle, data ,len);
第二種方法ZCAN_Transmit的代碼實現(xiàn):
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
Prop->Setvalue(“0/set_send_mode”, “1”); //USBCANFD需要切換發(fā)送模式,CANFDNET無需此步驟
…
ZCAN_Transmit_Data can_data[10]={};
ZCAN_TransmitFD_Data canfd_data[10]={};
memset(& can_data, 0, sizeof(can_data)); //初始化data結(jié)構(gòu)體
memset(& canfd_data, 0, sizeof(canfd_data)); //初始化data結(jié)構(gòu)體
…
can_data[0].frame.can_id =0x100;
can_data[0].frame.__pad =0x80; //使能CAN幀隊列發(fā)送
can_data[0].frame.__res0 =0x64; // 低位,設置100ms
can_data[0].frame.__res1 =0x00; // 高位
…
canfd_data[0].frame.can_id =0x200;
canfd_data[0].frame.flags????=0x80;????????????????//使能非加速CANFD隊列發(fā)送,0x81使能加速CANFD隊列發(fā)送
canfd_data[0].frame.__res0 =0x64; // 低位,設置100ms
canfd_data[0].frame.__res1 =0x00; // 高位
…
ret = ZCAN.Transmit(channel_handle, can_data, 10);
ret_fd = ZCAN.TransmitFD(channel_handle, canfd_data, 10);
隊列發(fā)送的優(yōu)缺點:
- 優(yōu)點:定時間隔準確,最小精度為100us;
- 缺點:設備分配的緩存大小有限,實際使用中需要結(jié)合getvalue去查緩存剩余空間,避免發(fā)送幀丟失。
以上兩種方法分別適用不同場景,根據(jù)實際應用需求,靈活使用,可以很大程度規(guī)避上位機調(diào)度帶來的時延問題,對用戶的通訊起到更穩(wěn)定和精準的控制。
?
審核編輯:湯梓紅
評論
查看更多