1. 程式人生 > >CANoe 入門 Step by step系列(三)簡單例子的剖析

CANoe 入門 Step by step系列(三)簡單例子的剖析

 最好的學習方式是什麼?模仿。有人會問,那不是山寨麼?但是我認為,那是模仿的初級階段,當把別人最好的設計已經融化到自己的血液裡,變成自己的東西,而靈活運用的時候,才是真正高階階段。正所謂畫虎畫皮難畫骨。但初級階段仍然是必須經歷的過程,他會使你在達到高階階段的過程中少走很多彎路,下面我們來邁出這一步。先研究一下別人的簡單例子。

    最好的例子莫過於Vector本身的Demo了,這個在安裝完CANoe之後就會被自動安裝。先看最簡單的一個,名字叫Easy,但並不簡單哦,比我們之前介紹的所有的東西都整合再一起了,很簡單,但很全面。但是假如你說,這個我自己也可以完全自己寫出來(並不是僅僅是看懂哦),那麼我可以肯定的說,在工作中,你完全可以勝任一般的任務要求哦~,剩下的只是工作量的問題了。但我相信到現在為止,你們很多人,都無法寫出這樣的程式,所以我建議你們把這個程式好好的研究明白,這點很重要。廢話不多說,上圖,下面是開啟執行後的介面。

tmpD0

通過面板可以控制,及顯示很多動畫效果,做的非常的漂亮。在其餘的窗體也將主要的資料以圖表等表現方式呈現出來。

我們先看一下DBC的內容吧

Signals:

EngineSpeed  車速資訊

FlashLight      雙跳燈

HeadLight      大燈

OnOff            引擎狀態

Messages:

EngineState  引擎狀態:包含的訊號有OnOff,EngineSpeed

LightState    燈光狀態:包含的訊號有FlashLight,HeadLight

Network nodes:

Display        顯示節點,接收所有訊息

Engine        引擎節點,傳送EngineState 訊息

Light          燈光節點,傳送LightState 訊息

Environment variables:   環境變數,一般與介面的元件相關聯,這樣就實現了圖形化介面的控制與顯示,下面就是關聯的介面元件

EnvEngineSpeedDspMeter  tmp60

EnvEngineSpeedDspText   tmp61

EnvEngineSpeedEntry       tmp5D

EnvEngineStateDsp          tmp5E

EnvEngineStateSwitch       tmp56

EnvHazardLightsSwitch     

tmp57

EnvHeadLightSwitch         tmp58

EnvLightDsp                    tmp62

注意一下訊號的資訊:

tmp156tmp157

Definition頁面的,Init.Val的輸入框使能了,之前是灰色的狀態,為什麼呢?點選一下藍色的帶下劃線的連線,彈出窗臺如下:

tmp158

意思是說這個值的設定,必須要定義的屬性才能有效,之前一直沒有提到訊號的屬性,這次還是第一次遇到哦。個人理解訊號屬性是表明訊號的特點的一系列引數,當然訊息和節點也都有對應的屬性。為了更加詳細的瞭解這個屬性,我們求助於幫助。

tmp15E

哦,明白了,原來是用來初始化資料的哦。其實在Definition表示的是物理值,都要轉換成Raw值儲存到GenSigStartValue屬性中。在屬性的建立我們之前也沒有提到過,這裡講一下,請在CANdb++ Editor選單中,View->Attribute Definitions

tmp163

右鍵,New,填寫好資訊即可。屬性背後跟行為是密切相關的,甚至跟底層dll,其他的一些屬性請參考Help文件,當然重要的屬性我們也會跟大家在後面提到。

dbc還有一些細節,就是接受的訊息的定義,之間也沒介紹過,例如Display節點只接收訊息,那麼你就應該在節點的屬性上進行配置,方法是右擊節點然後點Edit Node,在Mapped Rx Sig.中就可以定義接收的訊號了,Add…

tmp16C

其實不定義接收訊息也是可以的,但會在File->Consistency check 的檢查中中顯示出無接收節點等的報警。例如前面第一講例子的dbc的檢查如下:

tmp16E

再看一下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訊息的函式(只是更改其中的訊號),但報文卻真的發出去了,這是為什麼呢?

這是因為週期傳送訊息的工作,已經在訊息的屬性中定義了,這樣訊息會自動週期的傳送。如下:

tmp29

這個在訊息的屬性檢視中的介面,當然也可以在上面我們介紹的View->Attribute Definitions,進行修改和檢視,但區別是,這個只是針對個別訊息的,View->Attribute Definitions,是針對所有的情況。還有訊息屬性中,對此進行歸類,以上歸類到Interaction Layer這個是CAN通訊的互動層。上面的各個屬性的具體含義,請參考幫助文件,都有詳細的說明。

下面說一下介面。

tmp4B

選中一個介面元件,在狀態列中可顯示他的型別,關聯的物件等資訊。右邊為屬性視窗,定義選中元件的屬性

tmp4D

這個元件型別為:Switch/Indicator

屬性欄中:

Image 表示該元件使用的圖片,因為要表示幾種狀態,所以做成這樣,尺寸105x34 pix

tmp4F

State Count 表示狀態的個數

其他的屬性不一一介紹了,自己試一下基本可以知道,實在不行求助幫助文件,這裡不一 一介紹了。

到現在整個工程的剖析基本上結束了,但說過的這些不足以覆蓋所有的細節,但基本脈絡已經很清晰了,剩下的可以自己研究,都不難理解。個人建議,在實際工作中建立自己的工程,當遇到問題是,參考例子中的實現方式,這樣更加幫助理解。進步也最快。