スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ArduinoでCAN通信(その8:CCP通信・テスト)

諸々の準備ができたので、やっとCCP通信をトライします。

【ECUと通信準備ができたArduino】
Arduino_CAN_ccp5.jpg

24Vという大きな印字があるアルミの箱がECUです。これでエンジンや関連機器を制御してます。
今回はCCP通信テストだけなので、24V電源(赤が+B、右カプラにGNDあり)とCAN通信線(右コネクタ)だけです。

Arduinoは先のseeedのCAN-BUSソースに合わせたSPI.h仕様品です。

【ソフトウエア構成・概略内容です】
Arduino_CAN_ccp2.jpg

何から何まで最初から作るのは大変なので、昔プロに依頼して作ったVB6ソースを移植します。
流石、プロが作ったソフトは整然と作ってあります。複雑な通信手順を4種の関数にまとめ、表に見えるのは上図の14行で通信手順が完了します。整然としてます。(注意:この作りはあるECUでのケースです。CCP通信の標準手順であるかは未確認です)

この通信手順で最も特殊なのが、最初の行の、transmitDataFirst() 関数です。これはこのECU独自のスタートアップ手順のようで、これが無いとCCP通信が起動しません。(内容は割愛します)

次の2行はCCP通信の起動手順のようです。

次に続く7行が、ECUアドレスを指定して、そこのデータを送信してもらう手順です。Flort32という関数名称が示すように、IEEE形式の32Bitsバイナリでデータ転送されます。この部分はそのECU固有の値ですので省略します。番号は0から6まで7個指定できます。

続くのはByteタイプのデータ指定です。番号は7で1個指定できます(能力的にはかなり可能なはずですが)
続く3行がCCP通信手続きの終了部分です。

(関数内部の処理は秘匿性が高い部分なので割愛します)

【動作結果】
Arduino_CAN_ccp4.jpg

参考までに
CAN通信の状況を表示する赤いLEDは、上から送信、受信、受信割り込みです。
この写真では R という受信ランプが光って見えます。ECUが内部アドレスの値を返してきてます。
実は、受信割り込みの I というLEDも同様に光り、更に シリアル通信タイミング大きく光るのですが写真ではそれが映っていません。


先のccp通信起動をECUに流すと、ECUから継続的に、指定したECUアドレスデータが転送されてきます。

Arduino_CAN_ccp3.jpg

ECUから送られて来たデータをシリアル・モニタで表示したところです。

何と10mSec未満の間隔で送られてきますので、8ビットArduino で処理するのは大変そうです。
CANトランシーバは能力があるので、受信したデータをそのままArduinoに送って来たのをそのままシリアル・モニタに横流ししたのが上図です。これを少し解説すると、

・最初の数字:01から06は、関数 transmitFloat32()で指定したECUアドレスから返された値が、続く7Byteに収録されています。IEEE32バイナリフォーマットです。
・Byte指定した 7 のデータが返って無いように見えますが、06の最終 Byte 部分に格納されています。

これらのデータは転送されたまま収録バッファにしまい込んで置き、使用したい間隔(0.1秒とか0.5秒間隔)で抽出し、IEEE32バイナリを通常の float や int 形式に演算・キャストして使えば良いと思える。

忘備録的ですが、IEEE32 フォーマットデータを Float にキャストする資料を記載します。

【IEEE32 フォーマットについて】
こちらの記事がとても良く書いていると思います。英語ですが。

【私のプログラミング(やや冗長)】

void transFloat(){
  INT8U i,j,pE;
  INT32U pF;
  INT16U pS;

  for(i=0;i<6;i++){
    pE = i_dat[i][1];
    pF = (INT32U)(((i_dat[i][2] % 0x80)) * 0x10000) + (INT32U)((i_dat[i][3]) * 0x100) +
     (INT32U)((i_dat[i][4]) + 0x800000);
    if((pE / 0x80) == 0){
      pS = 1;
    }else{
      pS = -1;
    }
    pE = (pE % 0x80) * 2 + (INT8U)(i_dat[i][2] / 0x80);
    f_dat[i] = (float)(pS * (float)(pF / pow(2,(0x96-pE)))); 
  }
補足:i_dat[ i ] [ 1..4] に ECU から転送されてきた IEEE32 データが格納されています。
    演算処理後、 f_dat[ i ] にFloat が収納されます。

実はこの使用したい間隔に、以前と同じ MsTimer2 を使おうと思ったのですが、何故か seeed の CAN-BUS Sheild ソフトとぶつかるようです。よって今回は、 timer1 で発生させているカウンター値をポーリングし、そこで演算処理しました(この方法は良くないので、当初のタイマー割り込みで演算処理する方式に変える予定です)

【演算処理した結果】MS P ゴシックなので表示桁がずれてます

----- 5月28日の記事の結果

TIME :01m01.56s
00 00 00 00 00 04 46 - 01m02. 0s
01 00 00 00 00 04 46 - 01m02. 0s
02 C2 00 00 00 04 46 - 01m02. 1s
03 C2 00 36 00 04 46 - 01m02. 1s
04 C2 00 36 00 04 46 - 01m02. 2s
05 00 00 36 00 04 46 - 01m02. 3s

NE   :   0
VS   :   0
Q    :   0
ATM  :  98
THEX1:  98
THEX2:   0
QP   :   0
MOD  :  90

外部信号が全く入っていないECUなので、唯一動作している、大気圧 ATM が 98 という値を表示しています(実際は float なので分解能はもっと高いです)。これは 98 kPaという意味で、基準大気圧 101.325kPa よりも低い状態にあるということです。

THEX1 は不定状態にあるので、ATM の値を引いてしまっているのかもしれません。(Arduino 内部の問題かもしてませんが、真相は不明です)

注:上記通信データリストには、06 が抜けています。小生のコペピ作業ミスのようです。

----- 5月29日 再テスト結果を追記

00 00 00 00 00 04 46 - 12m26. 0s
01 00 00 C2 00 04 46 - 12m26. 0s
02 42 00 00 08 04 46 - 12m26. 1s
03 42 00 00 A0 04 46 - 12m26. 2s
04 42 00 C1 A0 04 46 - 12m26. 2s
05 42 C2 C1 48 04 46 - 12m26. 3s
06 00 00 C1 00 04 46 - 12m26. 4s
NE   :   0
VS   :?
Q    :?
ATM  :  32
THEX1:  31
THEX2:  96
QP   :?
MOD  : 90
TIME :12m26. 7s
00 00 00 00 00 04 46 - 12m26.50s
01 00 00 C2 00 04 46 - 12m26.50s
02 42 00 00 02 04 46 - 12m26.51s
03 42 00 00 A0 04 46 - 12m26.52s
04 42 00 C1 A0 04 46 - 12m26.53s
05 42 C2 C1 48 04 46 - 12m26.53s
06 00 00 C1 00 04 46 - 12m26.54s
NE   :   0
VS   :?
Q    :?
ATM  :  32
THEX1:  31
THEX2:  96
QP   :?
MOD  : 90
TIME :12m26.57s

気になったことが3点あったので再テストしました。

・06 と書かれた行が抜けていたこと:コピペミスではなく、私のソフトミスでした。
 0 から 6 までを、for 文でネストする際はVBでは、for i = 0 to 6 と記述します。
 これを、c言語では、for(i=0;i<=6;i++)か、for(i=0;i<7;i++)と書かねばならないのですが、for(i=0;i<6;i++)でした。
 つまり 6 が抜けてしまってました。

・例えば500mSec毎に数値化処理し、シリアル転送する処理時間はどの程度か?
 追加した結果リストはシリアル転送したものですが、*.00secか、*.05sec 毎にこの処理subを呼び出すのですが、リスト最初行の時間 12m26. 0s が示すように、シリアル送出直前にある演算処理はほとんど時間がかかっていません(10mSec以下)。しかしシリアル送出には15行で70mSec程度かかるようです。この部分は必要最小限にすべきのようです(500mSec間隔ならば全く余裕があります)

・floatデータをシリアル送出するには
 ?が付いている部分が浮動小数点数値を、sprintf の%f 指定した部分ですが、Arduino UNO 系IDE ではNGのようです。ここは数値を文字列に事前に変換するようです。

 --- 本件に関してさらに追記 ---

 Arduino UNO系 IDE では dtostrf() 関数を使用するとありました。
 dtostrf(実数, 全桁数, 少数以下桁数, 代入先文字変数)
 今回の例では、sprint 関数内に入れました。
 sprintf(StrBuf, "NE   :%s", dtostrf(f_dat[0], 5, 0, sstr));
 このように記述すると、sprintf 関数は dtostrf 関数の格納先 sstr を %s に格納してくれるようです。
 (注:c言語で格納という表現は正しくないのかもしれませんが)

----- 追記はここまで

----- 6月5日に追記します

更に何度か通信テストを続けて行く内に、ccp通信が確立されない場合が発生することがありました。

その原因は
「ECUが外部機器との通信を試みるため、拡張IDによる特定IDでの通信出力を頻繁にするため、Arduinoが起動直後だけでccp通信要求を出しているので失敗する場合がある」ということが判りました。

対応策
1)特定IDへのECUからの通信出力が10回以上連続で検出されたら、ccp通信要求を再発行する。
2)特定IDへのECUからの通信要求は続く場合もあるので、受信IDを見てデータ処理有無を分岐する。
//------ コード例
   if(id == 0x********){       //特定ID、これは都合により割愛します
      icount++;
      if(icount > 10){
         // sprintf(StrBuf, "Restart ccp");   // この2行はデバック用
         // Serial.println(StrBuf);
         transmitDataStart();   // ccp通信要求
         icount =0;
      }
         break;
      }else if(id == 0x7E2){       // 0x7E2 が通信ツールのIDです。このIDは一般に使われるIDです。
         icount = 0;
    //以下データ処理へ続く

// ------ コードここまで

// ------ 追記ここまで

以上で、Arduino が 8ビットマイコンの入門器ながらも、プロ・ユースのCPP通信にも十分使えるメドが立ちました。
今は汎用機器の組み合わせなので2階建てですが、専用基板を起こせばコンパクトで且つパワフルなCAN通信器ができる可能性があります。

用途:

・CAN通信の勉強ツール:送信や受信のArduino ソースが使用できるハード環境を提供

・CAN通信を使った実用ツール
 ・ホビーユースで車両からのCAN信号取得する→BlueToothで外部機器に伝送→用途に即したデータ処理
 ・実務ユースでCCP通信などのツール構築→BlueToothで外部機器に伝送→CANデータを様々な用途に使用

考えらえることは様々にあります。使い方はアイデア次第でしょうね。

専用基板製作のキックオフになりそうです。

【構想中のArduino 一体 CAN-BUS 基板】
Arduino_CAN_27_brd_3.jpg   






スポンサーサイト
プロフィール

haiga

Author:haiga
私のブログへようこそ!
電気オンチが始めた自作オーディオです
2010/3/17 電子工作をプラスしました。

自作オーディオの楽しみ共有のため、私が作ったパーツ提供をしてます。質問や要望を遠慮なくコメント欄に書き込んでください。

FC2カウンター
その日1回目にアクセスいただいた方の総カウントです
最新記事
最新コメント
最新トラックバック
カテゴリ
月別アーカイブ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。