最好的學習方式是什么?模仿。有人會問,那不是山寨么?但是我認為,那是模仿的初級階段,當把別人最好的設計已經融化到自己的血液里,變成自己的東西,而靈活運用的時候,才是真正高級階段。正所謂畫虎畫皮難畫骨。但初級階段仍然是必須經歷的過程,他會使你在達到高級階段的過程中少走很多彎路,下面我們來邁出這一步。先研究一下別人的簡單例子。
最好的例子莫過於Vector本身的Demo了,這個在安裝完CANoe之后就會被自動安裝。先看最簡單的一個,名字叫Easy,但並不簡單哦,比我們之前介紹的所有的東西都整合再一起了,很簡單,但很全面。但是假如你說,這個我自己也可以完全自己寫出來(並不是僅僅是看懂哦),那么我可以肯定的說,在工作中,你完全可以勝任一般的任務要求哦~,剩下的只是工作量的問題了。但我相信到現在為止,你們很多人,都無法寫出這樣的程序,所以我建議你們把這個程序好好的研究明白,這點很重要。廢話不多說,上圖,下面是打開運行后的界面。
通過面板可以控制,及顯示很多動畫效果,做的非常的漂亮。在其余的窗體也將主要的數據以圖表等表現方式呈現出來。
我們先看一下DBC的內容吧
Signals:
EngineSpeed 車速信息
FlashLight 雙跳燈
HeadLight 大燈
OnOff 引擎狀態
Messages:
EngineState 引擎狀態:包含的信號有OnOff,EngineSpeed
LightState 燈光狀態:包含的信號有FlashLight,HeadLight
Network nodes:
Display 顯示節點,接收所有消息
Engine 引擎節點,發送EngineState 消息
Light 燈光節點,發送LightState 消息
Environment variables: 環境變量,一般與界面的組件相關聯,這樣就實現了圖形化界面的控制與顯示,下面就是關聯的界面組件
注意一下信號的信息:
Definition頁面的,Init.Val的輸入框使能了,之前是灰色的狀態,為什么呢?點擊一下藍色的帶下划線的連接,彈出窗台如下:
意思是說這個值的設置,必須要定義的屬性才能有效,之前一直沒有提到信號的屬性,這次還是第一次遇到哦。個人理解信號屬性是表明信號的特點的一系列參數,當然消息和節點也都有對應的屬性。為了更加詳細的了解這個屬性,我們求助於幫助。
哦,明白了,原來是用來初始化數據的哦。其實在Definition表示的是物理值,都要轉換成Raw值保存到GenSigStartValue屬性中。在屬性的創建我們之前也沒有提到過,這里講一下,請在CANdb++ Editor菜單中,View->Attribute Definitions
右鍵,New,填寫好信息即可。屬性背后跟行為是密切相關的,甚至跟底層dll,其他的一些屬性請參考Help文檔,當然重要的屬性我們也會跟大家在后面提到。
dbc還有一些細節,就是接受的消息的定義,之間也沒介紹過,例如Display節點只接收消息,那么你就應該在節點的屬性上進行配置,方法是右擊節點然后點Edit Node,在Mapped Rx Sig.中就可以定義接收的信號了,Add…
其實不定義接收消息也是可以的,但會在File->Consistency check 的檢查中中顯示出無接收節點等的報警。例如前面第一講例子的dbc的檢查如下:
再看一下CAPL程序。
engine.can 程序如下:
variables
{
}
on envvar EnvEngineStateSwitch //當撥動開關的時候,會更改發動機發出的信號
{
$EngineState::OnOff = @this; //注意信號和環境變量直接賦值時的符號,信號用$,環境變量用@
if(@this)
$EngineState::EngineSpeed = @EnvEngineSpeedEntry;
else
$EngineState::EngineSpeed = 0;
}
on envvar EnvEngineSpeedEntry //當移動車速滑條時,會更改發動機發出的信號
{
if(@EnvEngineStateSwitch)
{
$EngineState::EngineSpeed = @this;
}
}
on start //程序開始運行的時候,將調用所有的環境變量的事件
{
CallAllOnEnvVar(); // call all envvar procedures of this model and
// thus consider the START VALUES of all environment
// variables for:
// - initialization of all message variables
// - starting of any timers
// - sending messages (output) with start values
}
light.can 的程序如下:
variables
{
msTimer tFlashLightFrequency; //定義閃燈定時器
const int gFlashLightFrequency = 500; //定義閃燈頻率,初始化為500ms
int gHazardLightsStatus = 0; //定義危險燈信號
int gDebugCounterTX = 0; //用於調試,記錄TX報文個數
int gDebugCounterTXRQ = 0; //用於調試,記錄TXRQ報文個數
int gDebugCounterRX = 0; //用於調試,記錄RX報文個數
}
on envvar EnvHeadLightSwitch //大燈開關狀態更改時,更新燈光消息的信號
{
// assign EV value to the message signal
$LightState::HeadLight = @this;
}
on start
{
CallAllOnEnvVar(); // call all envvar procedures of this model and
// thus consider the START VALUES of all environment
// variables for:
// - initialization of all message variables
// - starting of any timers
// - sending messages (output) with start values
setWriteDbgLevel(0); // set DbgLevel = 1 to get more information in Write-Window
}
on message LightState //調試用,打印相關信息
{
if (this.dir == TX)
{
gDebugCounterTX++;
if(gDebugCounterTX == 10)
{
writeDbgLevel(1,"LightState TX received by node %NODE_NAME%");
gDebugCounterTX = 0;
}
}
if(this.dir == TXREQUEST)
{
gDebugCounterTXRQ++;
if(gDebugCounterTXRQ == 10)
{
writeDbgLevel(1,"LightState TXREQUEST received by node %NODE_NAME%");
gDebugCounterTXRQ = 0;
}
}
if (this.dir == RX)
{
gDebugCounterRX++;
if(gDebugCounterRX == 10)
{
writeDbgLevel(1,"Error: LightState RX received by node %NODE_NAME%");
gDebugCounterRX = 0;
}
}
}
on envVar EnvHazardLightsSwitch //危險警示燈開關變化時,更新燈光消息的閃燈信號
{
if (@this)
{
gHazardLightsStatus = 1;
setTimer(tFlashLightFrequency, gFlashLightFrequency);
}
else
{
cancelTimer(tFlashLightFrequency);
gHazardLightsStatus = 0;
}
$LightState::FlashLight = gHazardLightsStatus;
}
on timer tFlashLightFrequency //危險報警燈間隔閃爍的控制
{
gHazardLightsStatus = (gHazardLightsStatus == 1 ? 0 : 1);
$LightState::FlashLight = gHazardLightsStatus;
setTimer(this, gFlashLightFrequency);
}
on key '0' //按鍵事件,定義打印調試信息的等級
{
setwriteDbgLevel(0);
}
on key '1' //按鍵事件,定義打印調試信息的等級
{
setwriteDbgLevel(1);
}
以上程序,有C語言基礎的同學應該都可以看得懂,這里不用詳細介紹了。
看完程序大家可能有個疑問,沒有調用任何發送CAN消息的函數(只是更改其中的信號),但報文卻真的發出去了,這是為什么呢?
這是因為周期發送消息的工作,已經在消息的屬性中定義了,這樣消息會自動周期的發送。如下:
這個在消息的屬性查看中的界面,當然也可以在上面我們介紹的View->Attribute Definitions,進行修改和查看,但區別是,這個只是針對個別消息的,View->Attribute Definitions,是針對所有的情況。還有消息屬性中,對此進行歸類,以上歸類到Interaction Layer這個是CAN通訊的交互層。上面的各個屬性的具體含義,請參考幫助文檔,都有詳細的說明。
下面說一下界面。
選中一個界面組件,在狀態欄中可顯示他的類型,關聯的對象等信息。右邊為屬性窗口,定義選中組件的屬性
這個組件類型為:Switch/Indicator
屬性欄中:
Image 表示該組件使用的圖片,因為要表示幾種狀態,所以做成這樣,尺寸105x34 pix
State Count 表示狀態的個數
其他的屬性不一一介紹了,自己試一下基本可以知道,實在不行求助幫助文檔,這里不一 一介紹了。
到現在整個工程的剖析基本上結束了,但說過的這些不足以覆蓋所有的細節,但基本脈絡已經很清晰了,剩下的可以自己研究,都不難理解。個人建議,在實際工作中創建自己的工程,當遇到問題是,參考例子中的實現方式,這樣更加幫助理解。進步也最快。